Hi everyone - I need to build a custom component that recognizes various forms of input - such as recognizing open ended questions, positive language, etc.
I tried to adapt the SentimentAnalyzer (from this deprecated post) to the new Graph version in Rasa 3+ by following the documentation here to build a sentiment analyzer first, using a pre-trained model.
But I couldn’t get it to work.
I have my rasa server up and running already.
When I just remove the sentimentAnalyzer from the pipeline, the conversation with the bot goes on as normal (but has no custom sentiment analyzer of course). So I believe something is wrong with either (1) my policy config (2) the code in the custom component itself (below)
Can someone please help me? Thanks
When I run the shell, it just gets stuck after user’s input before raising the following exception:
ERROR asyncio - Task exception was never retrieved
future: <Task finished name='Task-9' coro=<SignalRouter._dispatch() done, defined at C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\sanic\signals.py:121> exception=ClientResponseError(RequestInfo(url=URL('http://localhost:5005/webhooks/rest/webhook?stream=true&token='), method='POST', headers=<CIMultiDictProxy('Host': 'localhost:5005', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'User-Agent': 'Python/3.8 aiohttp/3.7.4', 'Content-Length': '69', 'Content-Type': 'application/json')>, real_url=URL('http://localhost:5005/webhooks/rest/webhook?stream=true&token=')), (), status=503, message='Service Unavailable', headers=<CIMultiDictProxy('Content-Length': '79', 'Connection': 'close', 'Content-Type': 'application/json')>)>
Traceback (most recent call last):
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\sanic\signals.py", line 161, in _dispatch
retval = await maybe_coroutine
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\sanic\app.py", line 1581, in run_delayed_task
await prepped
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\rasa\core\run.py", line 135, in run_cmdline_io
await console.record_messages(
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\rasa\core\channels\console.py", line 218, in record_messages
async for response in bot_responses_stream:
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\rasa\core\channels\console.py", line 164, in _send_message_receive_stream
async with session.post(url, json=payload, raise_for_status=True) as resp:
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\aiohttp\client.py", line 1117, in __aenter__
self._resp = await self._coro
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\aiohttp\client.py", line 625, in _request
resp.raise_for_status()
File "C:\Users\User\anaconda3\envs\AI-Coach\lib\site-packages\aiohttp\client_reqrep.py", line 1000, in raise_for_status
raise ClientResponseError(
aiohttp.client_exceptions.ClientResponseError: 503, message='Service Unavailable', url=URL('http://localhost:5005/webhooks/rest/webhook?stream=true&token=')
In a nutshell, here’s what I did with the code: config.yml
- name: SpacyNLP
model: "en_core_web_md"
case_sensitive: False
- name: SpacyTokenizer
- name: CountVectorsFeaturizer
analyzer: char_wb
min_ngram: 1
max_ngram: 4
**- name: custom_components.sentiment.SentimentAnalyzer**
- name: DIETClassifier
epochs: 100
constrain_similarities: true
- name: EntitySynonymMapper
- name: ResponseSelector
epochs: 100
constrain_similarities: true
- name: FallbackClassifier
threshold: 0.3 # if Intent Classifier cannot predict above this threshold, nlu_fallback is triggered
ambiguity_threshold: 0.1 # if the 2 top predicted intent have a diff. in prediction is smaller than this number (i.e., uncertainty), then nlu_fallback is triggered
sentiment.py (the custom component code)
# TODO: Correctly register your component with its type
@ DefaultV1Recipe.register(
[DefaultV1Recipe.ComponentType.ENTITY_EXTRACTOR],
is_trainable=True,
model_from="SpacyNLP"
)
class SentimentAnalyzer(GraphComponent):
@ classmethod
def create(
cls,
config: Dict[Text, Any],
model_storage: ModelStorage,
resource: Resource,
execution_context: ExecutionContext,
) -> GraphComponent:
# Creates a new component (see parent class for full docstring)."""
return cls(config, execution_context.node_name)
def __init__(
self,
config: Dict[Text, Any],
name: Text,
) -> None:
# super().__init__(config)
pass
@ classmethod
def load(
cls,
config: Dict[Text, Any],
model_storage: ModelStorage,
resource: Resource,
execution_context: ExecutionContext,
**kwargs: Any,
) -> GraphComponent:
def train(self, training_data: TrainingData, **kwargs: Any) -> Resource:
pass
def process_training_data(self, training_data: TrainingData, **kwargs: Any) -> TrainingData:
return training_data
def convert_to_rasa(self, value, confidence):
"""Convert model output into the Rasa NLU compatible output format."""
entity = {"value": value,
"confidence": confidence,
"entity": "sentiment",
"extractor": "sentiment_extractor"}
return entity
def process(self, messages: List[Message], **kwargs: Any) -> List[Message]:
# Retrieve the text message, pass it to the classifier
# and append the prediction results to the message class."""
sid = SentimentIntensityAnalyzer()
res = sid.polarity_scores(messages.text)
key, value = max(res.items(), key=lambda x: x[1])
entity = self.convert_to_rasa(key, value)
message_from_bytes.set("entities", [entity], add_to_output=True)
return messages
Other info:
- Rasa --version: 3.2.7
- Sanic --version: 21.12.2
- Python --version: 3.8.13