async def async_test_ws_intent(self): """Test api/events/intent endpoint""" # Start listening event_queue = asyncio.Queue() connected = asyncio.Event() receive_task = asyncio.ensure_future( self.async_ws_receive("events/intent", event_queue, connected)) await asyncio.wait_for(connected.wait(), timeout=5) # Send in a message nlu_intent = NluIntent( input="turn on the living room lamp", id=str(uuid4()), intent=Intent(intent_name="ChangeLightState", confidence_score=1), slots=[ Slot( entity="state", slot_name="state", value={"value": "on"}, confidence=1.0, raw_value="on", ), Slot( entity="name", slot_name="name", value={"value": "living room lamp"}, confidence=1.0, raw_value="living room lamp", ), ], site_id=self.site_id, session_id=self.session_id, ) self.client.publish( nlu_intent.topic(intent_name=nlu_intent.intent.intent_name), nlu_intent.payload(), ) # Wait for response event = json.loads(await asyncio.wait_for(event_queue.get(), timeout=5)) # Expected Rhasspy JSON format as a response _LOGGER.debug(nlu_intent) expected = nlu_intent.to_rhasspy_dict() # Extra info expected["siteId"] = self.site_id expected["sessionId"] = self.session_id expected["customData"] = None expected["wakewordId"] = None expected["lang"] = None self.assertEqual(event, expected) # Stop listening receive_task.cancel()
def handle_query(self, query: NluQuery): """Do intent recognition.""" def intent_filter(intent_name: str) -> bool: """Filter out intents.""" if query.intentFilter: return intent_name in query.intentFilter return True recognitions = recognize(query.input, self.graph, intent_filter=intent_filter) if recognitions: # Use first recognition only. recognition = recognitions[0] assert recognition is not None assert recognition.intent is not None self.publish( NluIntent( input=query.input, id=query.id, siteId=query.siteId, sessionId=query.sessionId, intent=Intent( intentName=recognition.intent.name, confidenceScore=recognition.intent.confidence, ), slots=[ Slot( entity=e.entity, slotName=e.entity, confidence=1, value=e.value, raw_value=e.raw_value, range=SlotRange(start=e.raw_start, end=e.raw_end), ) for e in recognition.entities ], ), intentName=recognition.intent.name, ) else: # Not recognized self.publish( NluIntentNotRecognized( input=query.input, id=query.id, siteId=query.siteId, sessionId=query.sessionId, ))
def test_recognized(self): """Verify valid input leads to a recognition.""" query_id = str(uuid.uuid4()) text = "set the bedroom light to red" self.hermes.publish = MagicMock() self.hermes.handle_query( NluQuery(input=text, id=query_id, siteId=self.siteId, sessionId=self.sessionId)) self.hermes.publish.assert_called_with( NluIntent( input=text, id=query_id, intent=Intent(intentName="SetLightColor", confidenceScore=1), slots=[ Slot( entity="name", slotName="name", value="bedroom", raw_value="bedroom", confidence=1, range=SlotRange(8, 15), ), Slot( entity="color", slotName="color", value="red", raw_value="red", confidence=1, range=SlotRange(25, 28), ), ], siteId=self.siteId, sessionId=self.sessionId, ), intent_name="SetLightColor", )
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[typing.Union[NluIntentParsed, typing.Tuple[ NluIntent, TopicArgs], NluIntentNotRecognized, NluError, ]]: """Do intent recognition.""" original_input = query.input try: if not self.intent_graph and self.graph_path and self.graph_path.is_file( ): # Load graph from file _LOGGER.debug("Loading %s", self.graph_path) with open(self.graph_path, mode="rb") as graph_file: self.intent_graph = rhasspynlu.gzip_pickle_to_graph( graph_file) if self.intent_graph: def intent_filter(intent_name: str) -> bool: """Filter out intents.""" if query.intent_filter: return intent_name in query.intent_filter return True # Replace digits with words if self.replace_numbers: # Have to assume whitespace tokenization words = rhasspynlu.replace_numbers(query.input.split(), self.language) query.input = " ".join(words) input_text = query.input # Fix casing for output event if self.word_transform: input_text = self.word_transform(input_text) if self.failure_token and (self.failure_token in query.input.split()): # Failure token was found in input recognitions = [] else: # Pass in raw query input so raw values will be correct recognitions = recognize( query.input, self.intent_graph, intent_filter=intent_filter, word_transform=self.word_transform, fuzzy=self.fuzzy, extra_converters=self.extra_converters, ) else: _LOGGER.error("No intent graph loaded") recognitions = [] if NluHermesMqtt.is_success(recognitions): # Use first recognition only. recognition = recognitions[0] assert recognition is not None assert recognition.intent is not None intent = Intent( intent_name=recognition.intent.name, confidence_score=recognition.intent.confidence, ) slots = [ Slot( entity=(e.source or e.entity), slot_name=e.entity, confidence=1.0, value=e.value_dict, raw_value=e.raw_value, range=SlotRange( start=e.start, end=e.end, raw_start=e.raw_start, raw_end=e.raw_end, ), ) for e in recognition.entities ] if query.custom_entities: # Copy user-defined entities for entity_name, entity_value in query.custom_entities.items( ): slots.append( Slot( entity=entity_name, confidence=1.0, value={"value": entity_value}, )) # intentParsed yield NluIntentParsed( input=recognition.text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=intent, slots=slots, ) # intent yield ( NluIntent( input=recognition.text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=intent, slots=slots, asr_tokens=[ NluIntent.make_asr_tokens(recognition.tokens) ], asr_confidence=query.asr_confidence, raw_input=original_input, wakeword_id=query.wakeword_id, lang=(query.lang or self.lang), custom_data=query.custom_data, ), { "intent_name": recognition.intent.name }, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, custom_data=query.custom_data, ) except Exception as e: _LOGGER.exception("handle_query") yield NluError( site_id=query.site_id, session_id=query.session_id, error=str(e), context=original_input, )
'"speech_confidence": 1, "text": "wie wird das wetter heute 10", ' '"tokens": ["wie", "wird", "das", "wetter", "heute", "10"], "wakeword_id": null}', "request_weather_full_interval": '{"entities": [{"end": 25, "entity": "when_day", "raw_end": 25, "raw_start": 20, "raw_value": "heute", ' '"start": 20, "value": "heute", "value_details": {"kind": "Unknown", "value": "heute"}}, {"end": 32, ' '"entity": "when_time", "raw_end": 32, "raw_start": 26, "raw_value": "mittag", "start": 26, ' '"value": "Mittag", "value_details": {"kind": "Unknown", "value": "Mittag"}}], ' '"intent": {"confidence": 1, "name": "GetWeatherForecast"}, "raw_text": "wie wird das wetter heute mittag", ' '"raw_tokens": ["wie", "wird", "das", "wetter", "heute", "mittag"], "recognize_seconds": 0.11356039298698306, ' '"slots": {"when_day": "heute", "when_time": "Mittag"}, "speech_confidence": 1, ' '"text": "wie wird das wetter heute Mittag", "tokens": ["wie", "wird", "das", "wetter", "heute", "Mittag"], ' '"wakeword_id": null}' } day_slot = Slot(entity="test", slot_name="when_day", value={"value": "heute"}, raw_value="heute") nlu_intent = { "request_weather_full_day": NluIntent("Wie wird das Wetter heute?", Intent("GetWeatherForecastFull", 1), slots=[day_slot]), "request_weather_full_time": NluIntent("Wie wird das Wetter heute um 10 Uhr?", Intent("GetWeatherForecastFull", 1), slots=[day_slot, Slot(entity="test", slot_name="when_time", value={"value": 10}, raw_value="zehn")]), "request_weather_full_interval": NluIntent("Wie wird das Wetter heute mittag?", Intent("GetWeatherForecastFull", 1), slots=[day_slot, Slot(entity="test", slot_name="when_time", value={"value": "mittag"}, raw_value="mittag")]) } console_args = { "request_weather_full_day": argparse.ArgumentParser("-d", "heute", "-t", "mittag"), "request_weather_full_time": argparse.ArgumentParser("-d", "heute", "-t", "10"), "request_weather_full_interval": argparse.ArgumentParser("-d", "heute", "-t", "mittag") } intents = { "rhasspy_intent": rhasspy_intent, "nlu_intent": nlu_intent, "console_args": console_args }
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[typing.Union[NluIntentParsed, typing.Tuple[ NluIntent, TopicArgs], NluIntentNotRecognized, NluError, ]]: """Do intent recognition.""" original_input = query.input try: self.maybe_load_engine() assert self.engine, "Snips engine not loaded. You may need to train." input_text = query.input # Fix casing for output event if self.word_transform: input_text = self.word_transform(input_text) # Do parsing result = self.engine.parse(input_text, query.intent_filter) intent_name = result.get("intent", {}).get("intentName") if intent_name: slots = [ Slot( slot_name=s["slotName"], entity=s["entity"], value=s["value"], raw_value=s["rawValue"], range=SlotRange(start=s["range"]["start"], end=s["range"]["end"]), ) for s in result.get("slots", []) ] # intentParsed yield NluIntentParsed( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent(intent_name=intent_name, confidence_score=1.0), slots=slots, ) # intent yield ( NluIntent( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent(intent_name=intent_name, confidence_score=1.0), slots=slots, asr_tokens=[ NluIntent.make_asr_tokens(query.input.split()) ], raw_input=original_input, wakeword_id=query.wakeword_id, lang=query.lang, ), { "intent_name": intent_name }, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, ) except Exception as e: _LOGGER.exception("handle_query") yield NluError( site_id=query.site_id, session_id=query.session_id, error=str(e), context=original_input, )
async def async_test_handle_query(self): """Verify valid input leads to a query message.""" query_id = str(uuid.uuid4()) text = "set the bedroom light to red" query = NluQuery(input=text, id=query_id, site_id=self.site_id, session_id=self.session_id) results = [] async for result in self.hermes.on_message(query): results.append(result) # Check results intent = Intent(intent_name="SetLightColor", confidence_score=1.0) slots = [ Slot( entity="name", slot_name="name", value={ "kind": "Unknown", "value": "bedroom" }, raw_value="bedroom", confidence=1.0, range=SlotRange(start=8, end=15, raw_start=8, raw_end=15), ), Slot( entity="color", slot_name="color", value={ "kind": "Unknown", "value": "red" }, raw_value="red", confidence=1.0, range=SlotRange(start=25, end=28, raw_start=25, raw_end=28), ), ] self.assertEqual( results, [ NluIntentParsed( input=text, id=query_id, site_id=self.site_id, session_id=self.session_id, intent=intent, slots=slots, ), ( NluIntent( input=text, id=query_id, site_id=self.site_id, session_id=self.session_id, intent=intent, slots=slots, asr_tokens=[NluIntent.make_asr_tokens(text.split())], raw_input=text, ), { "intent_name": intent.intent_name }, ), ], )
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[typing.Union[NluIntentParsed, typing.Tuple[ NluIntent, TopicArgs], NluIntentNotRecognized, NluError, ]]: """Do intent recognition.""" # Check intent graph try: if (not self.intent_graph and self.intent_graph_path and self.intent_graph_path.is_file()): _LOGGER.debug("Loading %s", self.intent_graph_path) with open(self.intent_graph_path, mode="rb") as graph_file: self.intent_graph = rhasspynlu.gzip_pickle_to_graph( graph_file) # Check examples if (self.intent_graph and self.examples_path and self.examples_path.is_file()): def intent_filter(intent_name: str) -> bool: """Filter out intents.""" if query.intent_filter: return intent_name in query.intent_filter return True original_text = query.input # Replace digits with words if self.replace_numbers: # Have to assume whitespace tokenization words = rhasspynlu.replace_numbers(query.input.split(), self.language) query.input = " ".join(words) input_text = query.input # Fix casing if self.word_transform: input_text = self.word_transform(input_text) recognitions: typing.List[rhasspynlu.intent.Recognition] = [] if input_text: recognitions = rhasspyfuzzywuzzy.recognize( input_text, self.intent_graph, str(self.examples_path), intent_filter=intent_filter, extra_converters=self.extra_converters, ) else: _LOGGER.error("No intent graph or examples loaded") recognitions = [] # Use first recognition only if above threshold if (recognitions and recognitions[0] and recognitions[0].intent and (recognitions[0].intent.confidence >= self.confidence_threshold)): recognition = recognitions[0] assert recognition.intent intent = Intent( intent_name=recognition.intent.name, confidence_score=recognition.intent.confidence, ) slots = [ Slot( entity=(e.source or e.entity), slot_name=e.entity, confidence=1.0, value=e.value_dict, raw_value=e.raw_value, range=SlotRange( start=e.start, end=e.end, raw_start=e.raw_start, raw_end=e.raw_end, ), ) for e in recognition.entities ] if query.custom_entities: # Copy user-defined entities for entity_name, entity_value in query.custom_entities.items( ): slots.append( Slot( entity=entity_name, confidence=1.0, value={"value": entity_value}, )) # intentParsed yield NluIntentParsed( input=recognition.text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=intent, slots=slots, ) # intent yield ( NluIntent( input=recognition.text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=intent, slots=slots, asr_tokens=[ NluIntent.make_asr_tokens(recognition.tokens) ], asr_confidence=query.asr_confidence, raw_input=original_text, wakeword_id=query.wakeword_id, lang=(query.lang or self.lang), custom_data=query.custom_data, ), { "intent_name": recognition.intent.name }, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, custom_data=query.custom_data, ) except Exception as e: _LOGGER.exception("handle_query") yield NluError( site_id=query.site_id, session_id=query.session_id, error=str(e), context=original_text, )
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[ typing.Union[ typing.Tuple[NluIntent, TopicArgs], NluIntentParsed, NluIntentNotRecognized, NluError, ] ]: """Do intent recognition.""" try: input_text = query.input # Fix casing if self.word_transform: input_text = self.word_transform(input_text) if self.nlu_url: # Use remote server _LOGGER.debug(self.nlu_url) params = {} # Add intent filter if query.intent_filter: params["intentFilter"] = ",".join(query.intent_filter) async with self.http_session.post( self.nlu_url, data=input_text, params=params, ssl=self.ssl_context ) as response: response.raise_for_status() intent_dict = await response.json() elif self.nlu_command: # Run external command _LOGGER.debug(self.nlu_command) proc = await asyncio.create_subprocess_exec( *self.nlu_command, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, ) input_bytes = (input_text.strip() + "\n").encode() output, error = await proc.communicate(input_bytes) if error: _LOGGER.debug(error.decode()) intent_dict = json.loads(output) else: _LOGGER.warning("Not handling NLU query (no URL or command)") return intent_name = intent_dict["intent"].get("name", "") if intent_name: # Recognized tokens = query.input.split() slots = [ Slot( entity=e["entity"], slot_name=e["entity"], confidence=1, value=e.get("value_details", {"value": ["value"]}), raw_value=e.get("raw_value", e["value"]), range=SlotRange( start=e.get("start", 0), end=e.get("end", 1), raw_start=e.get("raw_start"), raw_end=e.get("raw_end"), ), ) for e in intent_dict.get("entities", []) ] yield NluIntentParsed( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent( intent_name=intent_name, confidence_score=intent_dict["intent"].get("confidence", 1.0), ), slots=slots, ) yield ( NluIntent( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent( intent_name=intent_name, confidence_score=intent_dict["intent"].get( "confidence", 1.0 ), ), slots=slots, asr_tokens=[NluIntent.make_asr_tokens(tokens)], raw_input=query.input, wakeword_id=query.wakeword_id, lang=query.lang, ), {"intent_name": intent_name}, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, ) except Exception as e: _LOGGER.exception("handle_query") yield NluError( error=repr(e), context=repr(query), site_id=query.site_id, session_id=query.session_id, )
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[typing.Union[ NluIntentParsed, NluIntentNotRecognized, NluError, ]]: """Do intent recognition.""" try: # Replace digits with words if self.replace_numbers: # Have to assume whitespace tokenization words = rhasspynlu.replace_numbers(query.input.split(), self.number_language) query.input = " ".join(words) input_text = query.input # Fix casing for output event if self.word_transform: input_text = self.word_transform(input_text) parse_url = urljoin(self.rasa_url, "model/parse") _LOGGER.debug(parse_url) async with self.http_session.post( parse_url, json={ "text": input_text, "project": self.rasa_project }, ssl=self.ssl_context, ) as response: response.raise_for_status() intent_json = await response.json() intent = intent_json.get("intent", {}) intent_name = intent.get("name", "") if intent_name and (query.intent_filter is None or intent_name in query.intent_filter): confidence_score = float(intent.get("confidence", 0.0)) slots = [ Slot( entity=e.get("entity", ""), slot_name=e.get("entity", ""), confidence=float(e.get("confidence", 0.0)), value={ "kind": "Unknown", "value": e.get("value", ""), "additional_info": e.get("additional_info", {}), "extractor": e.get("extractor", None), }, raw_value=e.get("value", ""), range=SlotRange( start=int(e.get("start", 0)), end=int(e.get("end", 1)), raw_start=int(e.get("start", 0)), raw_end=int(e.get("end", 1)), ), ) for e in intent_json.get("entities", []) ] # intentParsed yield NluIntentParsed( input=input_text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent(intent_name=intent_name, confidence_score=confidence_score), slots=slots, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, ) except Exception as e: _LOGGER.exception("nlu query") yield NluError( site_id=query.site_id, session_id=query.session_id, error=str(e), context=query.input, )
async def handle_query( self, query: NluQuery ) -> typing.AsyncIterable[typing.Union[NluIntentParsed, typing.Tuple[ NluIntent, TopicArgs], NluIntentNotRecognized, NluError, ]]: """Do intent recognition.""" try: original_input = query.input # Replace digits with words if self.replace_numbers: # Have to assume whitespace tokenization words = rhasspynlu.replace_numbers(query.input.split(), self.number_language) query.input = " ".join(words) input_text = query.input # Fix casing for output event if self.word_transform: input_text = self.word_transform(input_text) parse_url = urljoin(self.rasa_url, "model/parse") _LOGGER.debug(parse_url) async with self.http_session.post( parse_url, json={ "text": input_text, "project": self.rasa_project }, ssl=self.ssl_context, ) as response: response.raise_for_status() intent_json = await response.json() intent = intent_json.get("intent", {}) intent_name = intent.get("name", "") if intent_name and (query.intent_filter is None or intent_name in query.intent_filter): confidence_score = float(intent.get("confidence", 0.0)) slots = [ Slot( entity=e.get("entity", ""), slot_name=e.get("entity", ""), confidence=float(e.get("confidence", 0.0)), value={ "kind": "Unknown", "value": e.get("value", "") }, raw_value=e.get("value", ""), range=SlotRange( start=int(e.get("start", 0)), end=int(e.get("end", 1)), raw_start=int(e.get("start", 0)), raw_end=int(e.get("end", 1)), ), ) for e in intent_json.get("entities", []) ] if query.custom_entities: # Copy user-defined entities for entity_name, entity_value in query.custom_entities.items( ): slots.append( Slot( entity=entity_name, confidence=1.0, value={"value": entity_value}, )) # intentParsed yield NluIntentParsed( input=input_text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent(intent_name=intent_name, confidence_score=confidence_score), slots=slots, ) # intent yield ( NluIntent( input=input_text, id=query.id, site_id=query.site_id, session_id=query.session_id, intent=Intent( intent_name=intent_name, confidence_score=confidence_score, ), slots=slots, asr_tokens=[ NluIntent.make_asr_tokens(input_text.split()) ], asr_confidence=query.asr_confidence, raw_input=original_input, lang=(query.lang or self.lang), custom_data=query.custom_data, ), { "intent_name": intent_name }, ) else: # Not recognized yield NluIntentNotRecognized( input=query.input, id=query.id, site_id=query.site_id, session_id=query.session_id, custom_data=query.custom_data, ) except Exception as e: _LOGGER.exception("nlu query") yield NluError( site_id=query.site_id, session_id=query.session_id, error=str(e), context=query.input, )
"""Tests for rhasspyhermes_app intent.""" # pylint: disable=protected-access,too-many-function-args import asyncio import pytest from rhasspyhermes.intent import Intent from rhasspyhermes.nlu import NluIntent from rhasspyhermes_app import HermesApp INTENT_NAME = "GetTime" INTENT_TOPIC = f"hermes/intent/{INTENT_NAME}" INTENT = Intent(INTENT_NAME, 1.0) NLU_INTENT = NluIntent("what time is it", INTENT) INTENT_NAME2 = "GetTemperature" INTENT_TOPIC2 = f"hermes/intent/{INTENT_NAME2}" INTENT2 = Intent(INTENT_NAME2, 1.0) NLU_INTENT2 = NluIntent("what's the temperature", INTENT2) INTENT_NAME3 = "GetWeather" INTENT_TOPIC3 = f"hermes/intent/{INTENT_NAME3}" INTENT3 = Intent(INTENT_NAME3, 1.0) NLU_INTENT3 = NluIntent("how's the weather", INTENT3) _LOOP = asyncio.get_event_loop() @pytest.mark.asyncio async def test_callbacks_intent(mocker): """Test intent callbacks."""