From b4397a3603f595a6c08bbbe4e89670a9f53df3c7 Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 21 May 2024 18:51:40 +0100 Subject: [PATCH 1/5] feat/ovos.utterance.handled emit a bus message when utterance is handled to unambigously identify when handling was completed. "ovos.utterance.handled" allows clients such as hivemind to know when to stop expecting more "speak" events also implements TODO for cancel sound, sound does not exist and does not play by default --- ovos_core/intent_services/__init__.py | 107 ++++++++++++++------------ 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 400dc960f10c..f0dd1c3e1ffa 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -298,12 +298,22 @@ def _emit_match_message(self, match: IntentMatch, message: Message): # Launch skill if not handled by the match function if match.intent_type: - # keep all original message.data and update with intent - # match, mycroft-core only keeps "utterances" + # keep all original message.data and update with intent match data = dict(message.data) data.update(match.intent_data) + # NOTE: message.reply to ensure correct message destination reply = message.reply(match.intent_type, data) self.bus.emit(reply) + self.bus.emit(message.reply("ovos.utterance.handled")) + + def send_cancel_event(self, message): + LOG.info("utterance canceled, cancel_word:" + message.context.get("cancel_word")) + # play dedicated cancel sound + sound = Configuration().get('sounds', {}).get('cancel', "snd/cancel.mp3") + # NOTE: message.reply to ensure correct message destination + self.bus.emit(message.reply('mycroft.audio.play_sound', {"uri": sound})) + self.bus.emit(message.reply("ovos.utterance.cancelled")) + self.bus.emit(message.reply("ovos.utterance.handled")) def handle_utterance(self, message: Message): """Main entrypoint for handling user utterances @@ -332,60 +342,53 @@ def handle_utterance(self, message: Message): Args: message (Message): The messagebus data """ - try: + # Get utterance utterance_plugins additional context + message = self._handle_transformers(message) - # Get utterance utterance_plugins additional context - message = self._handle_transformers(message) + if message.context.get("canceled"): + self.send_cancel_event(message) + return - if message.context.get("canceled"): - # TODO - play dedicated sound - LOG.info("utterance canceled, cancel_word:" + message.context.get("cancel_word")) - self.bus.emit(message.reply("ovos.utterance.cancelled")) - return + # tag language of this utterance + lang = self.disambiguate_lang(message) - # tag language of this utterance - lang = self.disambiguate_lang(message) + try: + setup_locale(lang) + except Exception as e: + LOG.exception(f"Failed to set lingua_franca default lang to {lang}") - try: - setup_locale(lang) - except Exception as e: - LOG.exception(f"Failed to set lingua_franca default lang to {lang}") - - utterances = message.data.get('utterances', []) - - stopwatch = Stopwatch() - - # get session - sess = self._validate_session(message, lang) - message.context["session"] = sess.serialize() - - # match - match = None - with stopwatch: - # Loop through the matching functions until a match is found. - for match_func in self.get_pipeline(session=sess): - match = match_func(utterances, lang, message) - if match: - try: - self._emit_match_message(match, message) - break - except: - LOG.exception(f"{match_func} returned an invalid match") - LOG.debug(f"no match from {match_func}") - else: - # Nothing was able to handle the intent - # Ask politely for forgiveness for failing in this vital task - self.send_complete_intent_failure(message) + utterances = message.data.get('utterances', []) - LOG.debug(f"intent matching took: {stopwatch.time}") + stopwatch = Stopwatch() - # sync any changes made to the default session, eg by ConverseService - if sess.session_id == "default": - SessionManager.sync(message) - return match, message.context, stopwatch + # get session + sess = self._validate_session(message, lang) + message.context["session"] = sess.serialize() + + # match + match = None + with stopwatch: + # Loop through the matching functions until a match is found. + for match_func in self.get_pipeline(session=sess): + match = match_func(utterances, lang, message) + if match: + try: + self._emit_match_message(match, message) + break + except: + LOG.exception(f"{match_func} returned an invalid match") + LOG.debug(f"no match from {match_func}") + else: + # Nothing was able to handle the intent + # Ask politely for forgiveness for failing in this vital task + self.send_complete_intent_failure(message) - except Exception as err: - LOG.exception(err) + LOG.debug(f"intent matching took: {stopwatch.time}") + + # sync any changes made to the default session, eg by ConverseService + if sess.session_id == "default": + SessionManager.sync(message) + return match, message.context, stopwatch def send_complete_intent_failure(self, message): """Send a message that no skill could handle the utterance. @@ -394,8 +397,10 @@ def send_complete_intent_failure(self, message): message (Message): original message to forward from """ sound = Configuration().get('sounds', {}).get('error', "snd/error.mp3") - self.bus.emit(message.forward('mycroft.audio.play_sound', {"uri": sound})) - self.bus.emit(message.forward('complete_intent_failure')) + # NOTE: message.reply to ensure correct message destination + self.bus.emit(message.reply('mycroft.audio.play_sound', {"uri": sound})) + self.bus.emit(message.reply('complete_intent_failure')) + self.bus.emit(message.reply("ovos.utterance.handled")) def handle_register_vocab(self, message): """Register adapt vocabulary. From 027829d77fa399e2fa1f4fc2b2bd6952b191be8b Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 28 May 2024 06:17:24 +0100 Subject: [PATCH 2/5] update tests --- mycroft/skills/common_play_skill.py | 4 +- ovos_core/intent_services/__init__.py | 1 - ovos_core/intent_services/commonqa_service.py | 9 +- ovos_core/intent_services/converse_service.py | 9 +- ovos_core/intent_services/fallback_service.py | 6 +- ovos_core/intent_services/ocp_service.py | 77 +++---- ovos_core/intent_services/stop_service.py | 22 +- requirements/mycroft.txt | 4 +- test/end2end/routing/test_sched.py | 1 + test/end2end/routing/test_session.py | 1 + test/end2end/session/test_complete_failure.py | 6 +- test/end2end/session/test_converse.py | 49 +++-- test/end2end/session/test_fallback.py | 11 +- test/end2end/session/test_get_response.py | 34 +-- test/end2end/session/test_ocp.py | 63 ++++-- test/end2end/session/test_sched.py | 42 ++-- test/end2end/session/test_session.py | 21 +- test/end2end/session/test_stop.py | 11 +- test/unittests/skills/test_intent_service.py | 205 ++++++++++-------- 19 files changed, 317 insertions(+), 259 deletions(-) diff --git a/mycroft/skills/common_play_skill.py b/mycroft/skills/common_play_skill.py index db9fbd3b1dd1..04a94ec3386d 100644 --- a/mycroft/skills/common_play_skill.py +++ b/mycroft/skills/common_play_skill.py @@ -16,7 +16,7 @@ from enum import Enum, IntEnum from abc import ABC, abstractmethod from ovos_bus_client.message import Message -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills import OVOSSkill from ovos_bus_client.apis.ocp import ClassicAudioServiceInterface as AudioService @@ -45,7 +45,7 @@ class CPSTrackStatus(IntEnum): END_OF_MEDIA = 90 # playback finished, is the default state when CPS loads -class CommonPlaySkill(MycroftSkill, ABC): +class CommonPlaySkill(OVOSSkill, ABC): """ To integrate with the common play infrastructure of Mycroft skills should use this base class and override the two methods `CPS_match_query_phrase` (for checking if the skill can play the diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index f0dd1c3e1ffa..aae154c5a926 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -304,7 +304,6 @@ def _emit_match_message(self, match: IntentMatch, message: Message): # NOTE: message.reply to ensure correct message destination reply = message.reply(match.intent_type, data) self.bus.emit(reply) - self.bus.emit(message.reply("ovos.utterance.handled")) def send_cancel_event(self, message): LOG.info("utterance canceled, cancel_word:" + message.context.get("cancel_word")) diff --git a/ovos_core/intent_services/commonqa_service.py b/ovos_core/intent_services/commonqa_service.py index 3c0ff0419614..6f513e2afa80 100644 --- a/ovos_core/intent_services/commonqa_service.py +++ b/ovos_core/intent_services/commonqa_service.py @@ -152,10 +152,11 @@ def match(self, utterances: str, lang: str, message: Message): message.data["utterance"] = utterance answered, skill_id = self.handle_question(message) if answered: - match = ovos_core.intent_services.IntentMatch('CommonQuery', - None, {}, - skill_id, - utterance) + match = ovos_core.intent_services.IntentMatch(intent_service='CommonQuery', + intent_type="ovos.utterance.handled", # emit instead of intent message + intent_data={}, + skill_id=skill_id, + utterance=utterance) break return match diff --git a/ovos_core/intent_services/converse_service.py b/ovos_core/intent_services/converse_service.py index 84800c4aef5d..61e6563353fd 100644 --- a/ovos_core/intent_services/converse_service.py +++ b/ovos_core/intent_services/converse_service.py @@ -312,6 +312,8 @@ def converse_with_skills(self, utterances, lang, message): Returns: IntentMatch if handled otherwise None. """ + session = SessionManager.get(message) + # we call flatten in case someone is sending the old style list of tuples utterances = flatten_list(utterances) # filter allowed skills @@ -319,7 +321,12 @@ def converse_with_skills(self, utterances, lang, message): # check if any skill wants to handle utterance for skill_id in self._collect_converse_skills(message): if self.converse(utterances, skill_id, lang, message): - return ovos_core.intent_services.IntentMatch('Converse', None, None, skill_id, utterances[0]) + state = session.utterance_states.get(skill_id, UtteranceState.INTENT) + return ovos_core.intent_services.IntentMatch(intent_service='Converse', + intent_type="ovos.utterance.handled" if state != UtteranceState.RESPONSE else None, # emit instead of intent message + intent_data={}, + skill_id=skill_id, + utterance=utterances[0]) return None def handle_get_response_enable(self, message): diff --git a/ovos_core/intent_services/fallback_service.py b/ovos_core/intent_services/fallback_service.py index d3b81de3db79..93124250e028 100644 --- a/ovos_core/intent_services/fallback_service.py +++ b/ovos_core/intent_services/fallback_service.py @@ -165,7 +165,11 @@ def _fallback_range(self, utterances, lang, message, fb_range): for skill_id, prio in sorted_handlers: result = self.attempt_fallback(utterances, skill_id, lang, message) if result: - return ovos_core.intent_services.IntentMatch('Fallback', None, {}, skill_id, utterances[0]) + return ovos_core.intent_services.IntentMatch(intent_service='Fallback', + intent_type=None, + intent_data={}, + skill_id=skill_id, + utterance=utterances[0]) return None def high_prio(self, utterances, lang, message): diff --git a/ovos_core/intent_services/ocp_service.py b/ovos_core/intent_services/ocp_service.py index 78a4a9bba64c..2dc3b718e19f 100644 --- a/ovos_core/intent_services/ocp_service.py +++ b/ovos_core/intent_services/ocp_service.py @@ -175,21 +175,21 @@ def register_ocp_api_events(self): """ Register messagebus handlers for OCP events """ - self.bus.on("ovos.common_play.search", self.handle_search_query) - self.bus.on("ovos.common_play.play_search", self.handle_play_search) - self.bus.on('ovos.common_play.status.response', self.handle_player_state_update) - self.bus.on('ovos.common_play.track.state', self.handle_track_state_update) - self.bus.on('ovos.common_play.SEI.get.response', self.handle_get_SEIs) - - self.bus.on('ovos.common_play.register_keyword', self.handle_skill_keyword_register) - self.bus.on('ovos.common_play.deregister_keyword', self.handle_skill_keyword_deregister) - self.bus.on('ovos.common_play.announce', self.handle_skill_register) - - self.bus.on("mycroft.audio.playing_track", self._handle_legacy_audio_start) - self.bus.on("mycroft.audio.queue_end", self._handle_legacy_audio_end) - self.bus.on("mycroft.audio.service.pause", self._handle_legacy_audio_pause) - self.bus.on("mycroft.audio.service.resume", self._handle_legacy_audio_resume) - self.bus.on("mycroft.audio.service.stop", self._handle_legacy_audio_stop) + self.add_event("ovos.common_play.search", self.handle_search_query) + self.add_event("ovos.common_play.play_search", self.handle_play_search) + self.add_event('ovos.common_play.status.response', self.handle_player_state_update) + self.add_event('ovos.common_play.track.state', self.handle_track_state_update) + self.add_event('ovos.common_play.SEI.get.response', self.handle_get_SEIs) + + self.add_event('ovos.common_play.register_keyword', self.handle_skill_keyword_register) + self.add_event('ovos.common_play.deregister_keyword', self.handle_skill_keyword_deregister) + self.add_event('ovos.common_play.announce', self.handle_skill_register) + + self.add_event("mycroft.audio.playing_track", self._handle_legacy_audio_start) + self.add_event("mycroft.audio.queue_end", self._handle_legacy_audio_end) + self.add_event("mycroft.audio.service.pause", self._handle_legacy_audio_pause) + self.add_event("mycroft.audio.service.resume", self._handle_legacy_audio_resume) + self.add_event("mycroft.audio.service.stop", self._handle_legacy_audio_stop) self.bus.emit(Message("ovos.common_play.status")) # sync player state on launch def register_ocp_intents(self): @@ -204,17 +204,17 @@ def register_ocp_intents(self): self.intent_matchers[lang].add_intent( intent_name.replace(".intent", ""), samples) - self.bus.on("ocp:play", self.handle_play_intent) - self.bus.on("ocp:play_favorites", self.handle_play_favorites_intent) - self.bus.on("ocp:open", self.handle_open_intent) - self.bus.on("ocp:next", self.handle_next_intent) - self.bus.on("ocp:prev", self.handle_prev_intent) - self.bus.on("ocp:pause", self.handle_pause_intent) - self.bus.on("ocp:resume", self.handle_resume_intent) - self.bus.on("ocp:media_stop", self.handle_stop_intent) - self.bus.on("ocp:search_error", self.handle_search_error_intent) - self.bus.on("ocp:like_song", self.handle_like_intent) - self.bus.on("ocp:legacy_cps", self.handle_legacy_cps) + self.add_event("ocp:play", self.handle_play_intent, is_intent=True) + self.add_event("ocp:play_favorites", self.handle_play_favorites_intent, is_intent=True) + self.add_event("ocp:open", self.handle_open_intent, is_intent=True) + self.add_event("ocp:next", self.handle_next_intent, is_intent=True) + self.add_event("ocp:prev", self.handle_prev_intent, is_intent=True) + self.add_event("ocp:pause", self.handle_pause_intent, is_intent=True) + self.add_event("ocp:resume", self.handle_resume_intent, is_intent=True) + self.add_event("ocp:media_stop", self.handle_stop_intent, is_intent=True) + self.add_event("ocp:search_error", self.handle_search_error_intent, is_intent=True) + self.add_event("ocp:like_song", self.handle_like_intent, is_intent=True) + self.add_event("ocp:legacy_cps", self.handle_legacy_cps, is_intent=True) @property def available_SEI(self): @@ -1015,30 +1015,7 @@ def handle_legacy_cps(self, message: Message): def shutdown(self): self.mycroft_cps.shutdown() - self.bus.remove("ovos.common_play.search", self.handle_search_query) - self.bus.remove("ovos.common_play.play_search", self.handle_play_search) - self.bus.remove('ovos.common_play.status.response', self.handle_player_state_update) - self.bus.remove('ovos.common_play.track.state', self.handle_track_state_update) - self.bus.remove('ovos.common_play.SEI.get.response', self.handle_get_SEIs) - self.bus.remove('ovos.common_play.register_keyword', self.handle_skill_keyword_register) - self.bus.remove('ovos.common_play.deregister_keyword', self.handle_skill_keyword_deregister) - self.bus.remove('ovos.common_play.announce', self.handle_skill_register) - self.bus.remove("mycroft.audio.playing_track", self._handle_legacy_audio_start) - self.bus.remove("mycroft.audio.queue_end", self._handle_legacy_audio_end) - self.bus.remove("mycroft.audio.service.pause", self._handle_legacy_audio_pause) - self.bus.remove("mycroft.audio.service.resume", self._handle_legacy_audio_resume) - self.bus.remove("mycroft.audio.service.stop", self._handle_legacy_audio_stop) - self.bus.remove("ocp:play", self.handle_play_intent) - self.bus.remove("ocp:play_favorites", self.handle_play_favorites_intent) - self.bus.remove("ocp:open", self.handle_open_intent) - self.bus.remove("ocp:next", self.handle_next_intent) - self.bus.remove("ocp:prev", self.handle_prev_intent) - self.bus.remove("ocp:pause", self.handle_pause_intent) - self.bus.remove("ocp:resume", self.handle_resume_intent) - self.bus.remove("ocp:media_stop", self.handle_stop_intent) - self.bus.remove("ocp:search_error", self.handle_search_error_intent) - self.bus.remove("ocp:like_song", self.handle_like_intent) - self.bus.remove("ocp:legacy_cps", self.handle_legacy_cps) + self.default_shutdown() # remove events registered via self.add_event class LegacyCommonPlay: diff --git a/ovos_core/intent_services/stop_service.py b/ovos_core/intent_services/stop_service.py index 13f563b70270..e5a640346ce2 100644 --- a/ovos_core/intent_services/stop_service.py +++ b/ovos_core/intent_services/stop_service.py @@ -149,8 +149,11 @@ def match_stop_high(self, utterances, lang, message): # check if any skill can stop for skill_id in self._collect_stop_skills(message): if self.stop_skill(skill_id, message): - return ovos_core.intent_services.IntentMatch('Stop', None, {"conf": conf}, - skill_id, utterance) + return ovos_core.intent_services.IntentMatch(intent_service='Stop', + intent_type="ovos.utterance.handled", + intent_data={"conf": conf}, + skill_id=skill_id, + utterance=utterance) return None def match_stop_medium(self, utterances, lang, message): @@ -210,13 +213,20 @@ def match_stop_low(self, utterances, lang, message): # check if any skill can stop for skill_id in self._collect_stop_skills(message): if self.stop_skill(skill_id, message): - return ovos_core.intent_services.IntentMatch('Stop', None, {"conf": conf}, - skill_id, utterance) + return ovos_core.intent_services.IntentMatch(intent_service='Stop', + intent_type="ovos.utterance.handled", + # emit instead of intent message + intent_data={"conf": conf}, + skill_id=skill_id, utterance=utterance) # emit a global stop, full stop anything OVOS is doing self.bus.emit(message.reply("mycroft.stop", {})) - return ovos_core.intent_services.IntentMatch('Stop', None, {"conf": conf}, - None, utterance) + return ovos_core.intent_services.IntentMatch(intent_service='Stop', + intent_type="ovos.utterance.handled", + # emit instead of intent message {"conf": conf}, + intent_data={}, + skill_id=None, + utterance=utterance) def voc_match(self, utt: str, voc_filename: str, lang: str, exact: bool = False): diff --git a/requirements/mycroft.txt b/requirements/mycroft.txt index 790325443198..0922bce95f67 100644 --- a/requirements/mycroft.txt +++ b/requirements/mycroft.txt @@ -1,7 +1,7 @@ # ovos modules for compat with mycroft namespace ovos_PHAL<0.1.0, >=0.0.5a14 ovos-audio~=0.0, >=0.0.2a32 -ovos-listener~=0.0, >=0.0.3a2 +#ovos-listener~=0.0, >=0.0.3a2 ovos-gui~=0.0, >=0.0.4a5 ovos-messagebus~=0.0 @@ -10,7 +10,7 @@ ovos-stt-plugin-server>=0.0.3, <0.1.0 ovos-tts-plugin-mimic>=0.2.8, <0.3.0 ovos-tts-plugin-server>=0.0.2a6 -ovos-ww-plugin-pocketsphinx~=0.1 +#ovos-ww-plugin-pocketsphinx~=0.1 ovos-ww-plugin-precise~=0.1 ovos-vad-plugin-webrtcvad>=0.0.1, <0.1.0 diff --git a/test/end2end/routing/test_sched.py b/test/end2end/routing/test_sched.py index cf65ef225428..f1cc23f362f7 100644 --- a/test/end2end/routing/test_sched.py +++ b/test/end2end/routing/test_sched.py @@ -60,6 +60,7 @@ def wait_for_n_messages(n): "mycroft.scheduler.schedule_event", "mycroft.skill.handler.complete", # intent code end + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default", # session update (end of utterance default sync) # skill event triggering after 3 seconds diff --git a/test/end2end/routing/test_session.py b/test/end2end/routing/test_session.py index 8dba6afb0439..934d4415de7d 100644 --- a/test/end2end/routing/test_session.py +++ b/test/end2end/routing/test_session.py @@ -59,6 +59,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default" ] wait_for_n_messages(len(expected_messages)) diff --git a/test/end2end/session/test_complete_failure.py b/test/end2end/session/test_complete_failure.py index 0f15653732d8..b72c3ff8008a 100644 --- a/test/end2end/session/test_complete_failure.py +++ b/test/end2end/session/test_complete_failure.py @@ -70,6 +70,7 @@ def wait_for_n_messages(n): # complete intent failure "mycroft.audio.play_sound", "complete_intent_failure", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default" ] wait_for_n_messages(len(expected_messages)) @@ -94,10 +95,11 @@ def wait_for_n_messages(n): self.assertEqual(messages[3].msg_type, "mycroft.audio.play_sound") self.assertEqual(messages[3].data["uri"], "snd/error.mp3") self.assertEqual(messages[4].msg_type, "complete_intent_failure") + self.assertEqual(messages[5].msg_type, "ovos.utterance.handled") # verify default session is now updated - self.assertEqual(messages[5].msg_type, "ovos.session.update_default") - self.assertEqual(messages[5].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") @skip("TODO works if run standalone, otherwise has side effects in other tests") def test_complete_failure_lang_detect(self): diff --git a/test/end2end/session/test_converse.py b/test/end2end/session/test_converse.py index 2127b9ece982..98ed733b01dd 100644 --- a/test/end2end/session/test_converse.py +++ b/test/end2end/session/test_converse.py @@ -71,6 +71,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" ] @@ -111,17 +112,17 @@ def wait_for_n_messages(n): self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") self.assertEqual(messages[9].data["name"], "TestAbortSkill.handle_converse_off") - + self.assertEqual(messages[10].msg_type, "ovos.utterance.handled") # verify default session is now updated - self.assertEqual(messages[10].msg_type, "ovos.session.update_default") - self.assertEqual(messages[10].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[10].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(messages[10].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) messages = [] @@ -153,6 +154,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" ] @@ -213,17 +215,18 @@ def wait_for_n_messages(n): self.assertEqual(messages[13].data["name"], "HelloWorldSkill.handle_hello_world_intent") # verify default session is now updated - self.assertEqual(messages[14].msg_type, "ovos.session.update_default") - self.assertEqual(messages[14].data["session_data"]["session_id"], "default") + self.assertEqual(messages[14].msg_type, "ovos.utterance.handled") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[14].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.other_skill_id) self.assertEqual(sess.active_skills[1][0], self.skill_id) - self.assertEqual(messages[14].data["session_data"]["active_skills"][0][0], self.other_skill_id) - self.assertEqual(messages[14].data["session_data"]["active_skills"][1][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.other_skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][1][0], self.skill_id) messages = [] @@ -257,6 +260,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" ] @@ -318,17 +322,18 @@ def wait_for_n_messages(n): self.assertEqual(messages[15].data["name"], "TestAbortSkill.handle_converse_on") # verify default session is now updated - self.assertEqual(messages[16].msg_type, "ovos.session.update_default") - self.assertEqual(messages[16].data["session_data"]["session_id"], "default") + self.assertEqual(messages[16].msg_type, "ovos.utterance.handled") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[16].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.skill_id) self.assertEqual(sess.active_skills[1][0], self.other_skill_id) - self.assertEqual(messages[16].data["session_data"]["active_skills"][0][0], self.skill_id) - self.assertEqual(messages[16].data["session_data"]["active_skills"][1][0], self.other_skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][1][0], self.other_skill_id) messages = [] @@ -353,6 +358,7 @@ def wait_for_n_messages(n): f"{self.skill_id}.activate", "ovos.session.update_default", "skill.converse.response", # CONVERSED + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" ] @@ -397,17 +403,18 @@ def wait_for_n_messages(n): self.assertTrue(messages[10].data["result"]) # CONVERSED # verify default session is now updated - self.assertEqual(messages[11].msg_type, "ovos.session.update_default") - self.assertEqual(messages[11].data["session_data"]["session_id"], "default") + self.assertEqual(messages[11].msg_type, "ovos.utterance.handled") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[11].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.skill_id) self.assertEqual(sess.active_skills[1][0], self.other_skill_id) - self.assertEqual(messages[11].data["session_data"]["active_skills"][0][0], self.skill_id) - self.assertEqual(messages[11].data["session_data"]["active_skills"][1][0], self.other_skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][1][0], self.other_skill_id) messages = [] @@ -515,6 +522,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", # "deactivated" "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" ] @@ -553,6 +561,7 @@ def wait_for_n_messages(n): "ovos.session.update_default", # needs ovos-workshop PR "skill.converse.response", # conversed! + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated "ovos.session.update_default" diff --git a/test/end2end/session/test_fallback.py b/test/end2end/session/test_fallback.py index 23ec397c81f7..e6a8d0ea1f05 100644 --- a/test/end2end/session/test_fallback.py +++ b/test/end2end/session/test_fallback.py @@ -71,6 +71,7 @@ def wait_for_n_messages(n): "ovos.session.update_default", f"ovos.skills.fallback.{self.skill_id}.response", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default" ] wait_for_n_messages(len(expected_messages)) @@ -120,8 +121,8 @@ def wait_for_n_messages(n): self.assertEqual(messages[11].data["fallback_handler"], "UnknownSkill.handle_fallback") # verify default session is now updated - self.assertEqual(messages[12].msg_type, "ovos.session.update_default") - self.assertEqual(messages[12].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test second message with no session resumes default active skills messages = [] @@ -195,7 +196,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", f"{self.skill_id}.activate", - f"ovos.skills.fallback.{self.skill_id}.response" + f"ovos.skills.fallback.{self.skill_id}.response", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -291,7 +293,8 @@ def wait_for_n_messages(n): "intent.service.skills.deactivated", f"{self.skill_id}.deactivate", # activate events suppressed - f"ovos.skills.fallback.{self.skill_id}.response" + f"ovos.skills.fallback.{self.skill_id}.response", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) diff --git a/test/end2end/session/test_get_response.py b/test/end2end/session/test_get_response.py index 3ca22b394e12..ae48bdbe990a 100644 --- a/test/end2end/session/test_get_response.py +++ b/test/end2end/session/test_get_response.py @@ -86,7 +86,7 @@ def on_speak(msg): "recognizer_loop:audio_output_start", "recognizer_loop:audio_output_end", "mycroft.skill.handler.complete", # original intent finished executing - + "ovos.utterance.handled", # session updated at end of intent pipeline "ovos.session.update_default" ] @@ -159,10 +159,10 @@ def on_speak(msg): self.assertEqual(messages[19].data["name"], "TestAbortSkill.handle_test_get_response") # verify default session is now updated - self.assertEqual(messages[20].msg_type, "ovos.session.update_default") - self.assertEqual(messages[20].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[20].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") def test_with_response(self): @@ -247,7 +247,7 @@ def answer_get_response(msg): "recognizer_loop:audio_output_start", "recognizer_loop:audio_output_end", "mycroft.skill.handler.complete", # original intent finished executing - + "ovos.utterance.handled", # session updated at end of intent pipeline "ovos.session.update_default" @@ -329,10 +329,10 @@ def answer_get_response(msg): self.assertEqual(messages[24].data["name"], "TestAbortSkill.handle_test_get_response") # verify default session is now updated - self.assertEqual(messages[25].msg_type, "ovos.session.update_default") - self.assertEqual(messages[25].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[25].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") def test_cancel_response(self): @@ -417,6 +417,7 @@ def answer_get_response(msg): "recognizer_loop:audio_output_end", "mycroft.skill.handler.complete", # original intent finished executing + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated at end of intent pipeline "ovos.session.update_default" @@ -432,7 +433,6 @@ def answer_get_response(msg): # verify that "session" is injected # (missing in utterance message) and kept in all messages for m in messages[1:]: - print(m.msg_type, m.context["session"]["session_id"]) self.assertEqual(m.context["session"]["session_id"], "default") # verify intent triggers @@ -498,10 +498,10 @@ def answer_get_response(msg): self.assertEqual(messages[24].data["name"], "TestAbortSkill.handle_test_get_response") # verify default session is now updated - self.assertEqual(messages[25].msg_type, "ovos.session.update_default") - self.assertEqual(messages[25].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[25].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") def test_with_reprompt(self): @@ -585,6 +585,7 @@ def answer_get_response(msg): "speak", # speak "ok" inside intent "mycroft.skill.handler.complete", # original intent finished executing + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated at end of intent pipeline "ovos.session.update_default" @@ -654,10 +655,10 @@ def answer_get_response(msg): self.assertEqual(messages[21].data["name"], "TestAbortSkill.handle_test_get_response3") # verify default session is now updated - self.assertEqual(messages[22].msg_type, "ovos.session.update_default") - self.assertEqual(messages[22].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[22].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") def test_nested(self): @@ -784,6 +785,7 @@ def answer_get_response(msg): "mycroft.skill.handler.complete", # original intent finished executing + "ovos.utterance.handled", # handle_utterance returned (intent service) # session updated at end of intent pipeline "ovos.session.update_default" @@ -860,4 +862,4 @@ def answer_get_response(msg): self.assertEqual(messages[50].msg_type, "mycroft.skill.handler.complete") self.assertEqual(messages[50].data["name"], "TestAbortSkill.handle_test_get_response_cascade") - self.assertEqual(messages[51].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") diff --git a/test/end2end/session/test_ocp.py b/test/end2end/session/test_ocp.py index 9f39f0bfccd9..c768cb619d44 100644 --- a/test/end2end/session/test_ocp.py +++ b/test/end2end/session/test_ocp.py @@ -77,7 +77,8 @@ def wait_for_n_messages(n): # no good results "ovos.common_play.reset", "enclosure.active_skill", - "speak" # error + "speak", # error, + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -146,7 +147,8 @@ def wait_for_n_messages(n): # good results because of radio media type "ovos.common_play.reset", "add_context", # NowPlaying context - "ovos.common_play.play" # OCP api + "ovos.common_play.play", # OCP api, + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -155,7 +157,7 @@ def wait_for_n_messages(n): for idx, m in enumerate(messages): self.assertEqual(m.msg_type, expected_messages[idx]) - play = messages[-1] + play = messages[-2] self.assertEqual(play.data["media"]["uri"], "https://fake_4.mp3") def test_unk_media_match(self): @@ -220,7 +222,8 @@ def wait_for_n_messages(n): # no good results "ovos.common_play.reset", "enclosure.active_skill", - "speak" # error + "speak", # error + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -289,7 +292,8 @@ def wait_for_n_messages(n): # good results "ovos.common_play.reset", "add_context", # NowPlaying context - "ovos.common_play.play" # OCP api + "ovos.common_play.play", # OCP api + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -361,7 +365,8 @@ def wait_for_n_messages(n): # good results because of radio media type "ovos.common_play.reset", "add_context", # NowPlaying context - 'mycroft.audio.service.play' # LEGACY api + 'mycroft.audio.service.play', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -418,7 +423,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:pause", - 'mycroft.audio.service.pause' # LEGACY api + 'mycroft.audio.service.pause', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -474,7 +480,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:resume", - 'mycroft.audio.service.resume' # LEGACY api + 'mycroft.audio.service.resume', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -530,7 +537,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:media_stop", - 'mycroft.audio.service.stop' # LEGACY api + 'mycroft.audio.service.stop', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -586,7 +594,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:next", - 'mycroft.audio.service.next' # LEGACY api + 'mycroft.audio.service.next', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -640,7 +649,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:prev", - 'mycroft.audio.service.prev' # LEGACY api + 'mycroft.audio.service.prev', # LEGACY api + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -693,7 +703,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:pause", - 'ovos.common_play.pause' + 'ovos.common_play.pause', + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -746,7 +757,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:resume", - 'ovos.common_play.resume' + 'ovos.common_play.resume', + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -800,7 +812,8 @@ def wait_for_n_messages(n): "ovos.common_play.activate", "ocp:media_stop", 'ovos.common_play.stop', - "ovos.common_play.stop.response" + "ovos.common_play.stop.response", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -853,7 +866,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:next", - 'ovos.common_play.next' + 'ovos.common_play.next', + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -906,7 +920,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", "ovos.common_play.activate", "ocp:prev", - 'ovos.common_play.previous' + 'ovos.common_play.previous', + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -959,7 +974,8 @@ def wait_for_n_messages(n): "recognizer_loop:utterance", "ovos.common_play.status", "mycroft.audio.play_sound", - "complete_intent_failure" + "complete_intent_failure", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -1009,7 +1025,8 @@ def wait_for_n_messages(n): "ocp:legacy_cps", # legacy cps api "play:query", - "mycroft.audio.play_sound" # error - no results + "mycroft.audio.play_sound", # error - no results + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -1068,8 +1085,8 @@ def wait_for_n_messages(n): "ocp:legacy_cps", # legacy cps api "play:query", - "play:query.response", # searching - "play:query.response", # report results + "play:query.response", # searching + "play:query.response", # report results "play:start", # skill selected "mycroft.audio.service.track_info", # check is legacy audio service is playing # global stop signal @@ -1084,7 +1101,8 @@ def wait_for_n_messages(n): "intent.service.skills.activated", f"{self.skill_id}.activate", # skill callback code - "mycroft.audio.service.play" + "mycroft.audio.service.play", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -1093,6 +1111,5 @@ def wait_for_n_messages(n): for idx, m in enumerate(messages): self.assertEqual(m.msg_type, expected_messages[idx]) - play = messages[-1] + play = messages[-2] self.assertEqual(play.data["tracks"], ["https://fake.mp3"]) - diff --git a/test/end2end/session/test_sched.py b/test/end2end/session/test_sched.py index c0763bf2a3ca..e2a5ec8f3554 100644 --- a/test/end2end/session/test_sched.py +++ b/test/end2end/session/test_sched.py @@ -58,6 +58,7 @@ def wait_for_n_messages(n): "speak", "mycroft.scheduler.schedule_event", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default", # event triggering after 3 seconds "skill-ovos-schedule.openvoiceos:my_event", @@ -107,30 +108,30 @@ def wait_for_n_messages(n): self.assertEqual(messages[10].data["name"], "ScheduleSkill.handle_sched_intent") # verify default session is now updated - self.assertEqual(messages[11].msg_type, "ovos.session.update_default") - self.assertEqual(messages[11].data["session_data"]["session_id"], "default") + self.assertEqual(messages[12].msg_type, "ovos.session.update_default") + self.assertEqual(messages[12].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[11].data["session_data"]) + sess = Session.deserialize(messages[12].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(messages[11].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[12].data["session_data"]["active_skills"][0][0], self.skill_id) # ensure context in triggered event is the same from message that triggered the intent self.assertEqual(messages[4].msg_type, "intent.service.skills.activated") intent_context = messages[4].context # when skill added to active list (last context change) - self.assertEqual(messages[12].msg_type, "skill-ovos-schedule.openvoiceos:my_event") - self.assertEqual(messages[12].context, intent_context) - self.assertEqual(messages[13].msg_type, "enclosure.active_skill") + self.assertEqual(messages[13].msg_type, "skill-ovos-schedule.openvoiceos:my_event") self.assertEqual(messages[13].context, intent_context) - self.assertEqual(messages[14].msg_type, "speak") - self.assertEqual(messages[14].data["lang"], "en-us") - self.assertFalse(messages[14].data["expect_response"]) - self.assertEqual(messages[14].data["meta"]["dialog"], "trigger") - self.assertEqual(messages[14].data["meta"]["skill"], self.skill_id) + self.assertEqual(messages[14].msg_type, "enclosure.active_skill") self.assertEqual(messages[14].context, intent_context) + self.assertEqual(messages[15].msg_type, "speak") + self.assertEqual(messages[15].data["lang"], "en-us") + self.assertFalse(messages[15].data["expect_response"]) + self.assertEqual(messages[15].data["meta"]["dialog"], "trigger") + self.assertEqual(messages[15].data["meta"]["skill"], self.skill_id) + self.assertEqual(messages[15].context, intent_context) def test_explicit_session(self): SessionManager.sessions = {} @@ -180,6 +181,7 @@ def wait_for_n_messages(n): "speak", "mycroft.scheduler.schedule_event", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) # event triggering after 3 seconds "skill-ovos-schedule.openvoiceos:my_event", "enclosure.active_skill", @@ -230,16 +232,16 @@ def wait_for_n_messages(n): self.assertEqual(messages[4].msg_type, "intent.service.skills.activated") intent_context = messages[4].context # when skill added to active list (last context change) - self.assertEqual(messages[10].msg_type, "skill-ovos-schedule.openvoiceos:my_event") - self.assertEqual(messages[10].context, intent_context) - self.assertEqual(messages[11].msg_type, "enclosure.active_skill") + self.assertEqual(messages[11].msg_type, "skill-ovos-schedule.openvoiceos:my_event") self.assertEqual(messages[11].context, intent_context) - self.assertEqual(messages[12].msg_type, "speak") - self.assertEqual(messages[12].data["lang"], "en-us") - self.assertFalse(messages[12].data["expect_response"]) - self.assertEqual(messages[12].data["meta"]["dialog"], "trigger") - self.assertEqual(messages[12].data["meta"]["skill"], self.skill_id) + self.assertEqual(messages[12].msg_type, "enclosure.active_skill") self.assertEqual(messages[12].context, intent_context) + self.assertEqual(messages[13].msg_type, "speak") + self.assertEqual(messages[13].data["lang"], "en-us") + self.assertFalse(messages[13].data["expect_response"]) + self.assertEqual(messages[13].data["meta"]["dialog"], "trigger") + self.assertEqual(messages[13].data["meta"]["skill"], self.skill_id) + self.assertEqual(messages[13].context, intent_context) def tearDown(self) -> None: self.core.stop() diff --git a/test/end2end/session/test_session.py b/test/end2end/session/test_session.py index 8d29fc962e5f..33fa4db9f6d5 100644 --- a/test/end2end/session/test_session.py +++ b/test/end2end/session/test_session.py @@ -61,6 +61,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default" ] wait_for_n_messages(len(expected_messages)) @@ -106,15 +107,15 @@ def wait_for_n_messages(n): self.assertEqual(messages[9].data["name"], "HelloWorldSkill.handle_hello_world_intent") # verify default session is now updated - self.assertEqual(messages[10].msg_type, "ovos.session.update_default") - self.assertEqual(messages[10].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[10].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(messages[10].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) def test_explicit_default_session(self): SessionManager.sessions = {} @@ -167,6 +168,7 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) "ovos.session.update_default" ] wait_for_n_messages(len(expected_messages)) @@ -216,15 +218,15 @@ def wait_for_n_messages(n): self.assertEqual(messages[11].data["name"], "HelloWorldSkill.handle_hello_world_intent") # verify default session is now updated - self.assertEqual(messages[12].msg_type, "ovos.session.update_default") - self.assertEqual(messages[12].data["session_data"]["session_id"], "default") + self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") + self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") # test deserialization of payload - sess = Session.deserialize(messages[12].data["session_data"]) + sess = Session.deserialize(messages[-1].data["session_data"]) self.assertEqual(sess.session_id, "default") # test that active skills list has been updated - self.assertEqual(messages[12].data["session_data"]["active_skills"][0][0], self.skill_id) + self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) self.assertEqual(sess.active_skills[0][0], self.skill_id) self.assertNotEqual(sess.active_skills[0][1], now) @@ -282,7 +284,8 @@ def wait_for_n_messages(n): f"{self.skill_id}.activate", "enclosure.active_skill", "speak", - "mycroft.skill.handler.complete" + "mycroft.skill.handler.complete", + "ovos.utterance.handled", # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) diff --git a/test/end2end/session/test_stop.py b/test/end2end/session/test_stop.py index 1d6e45839dbe..f6836c2c257d 100644 --- a/test/end2end/session/test_stop.py +++ b/test/end2end/session/test_stop.py @@ -115,7 +115,8 @@ def wait_for_n_messages(n): # skill code executing "enclosure.active_skill", "speak", - "mycroft.skill.handler.complete" + "mycroft.skill.handler.complete", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -288,7 +289,8 @@ def wait_for_n_messages(n): # skill code executing "enclosure.active_skill", "speak", - "mycroft.skill.handler.complete" + "mycroft.skill.handler.complete", + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -332,7 +334,8 @@ def wait_for_n_messages(n): "enclosure.active_skill", "speak", # "utterance":"stop 123" - f"{self.new_skill_id}.stop.response", # skill reports it stopped (new style) + f"{self.new_skill_id}.stop.response", # skill reports it stopped (new style), + "ovos.utterance.handled" # handle_utterance returned (intent service) ] wait_for_n_messages(len(expected_messages)) @@ -348,7 +351,7 @@ def wait_for_n_messages(n): self.assertEqual(m.data["utterance"], "stop 123") # confirm "skill-new-stop" was the one that reported success - handler = messages[-1] + handler = messages[-2] self.assertEqual(handler.msg_type, f"{self.new_skill_id}.stop.response") self.assertEqual(handler.data["result"], True) diff --git a/test/unittests/skills/test_intent_service.py b/test/unittests/skills/test_intent_service.py index a9ca59bfb49a..5dbaafc7a7a8 100644 --- a/test/unittests/skills/test_intent_service.py +++ b/test/unittests/skills/test_intent_service.py @@ -256,107 +256,124 @@ def test_get_no_match_after_detach_skill(self): self.assertEqual(reply.data['intent'], None) def test_shutdown(self): - intent_service = IntentService(MockEmitter(), config={"experimental_ocp_pipeline": False, "padatious": {"disabled": True}}) intent_service.shutdown() - self.assertListEqual(intent_service.bus.removed, - ['padatious:register_intent', - 'padatious:register_entity', - 'detach_intent', - 'detach_skill', - 'question:query.response', - 'common_query.question', - 'ovos.common_query.pong', - 'mycroft.speech.recognition.unknown', - 'intent.service.skills.deactivate', - 'intent.service.skills.activate', - 'active_skill_request', - 'intent.service.active_skills.get', - 'skill.converse.get_response.enable', - 'skill.converse.get_response.disable', - 'ovos.skills.fallback.register', - 'ovos.skills.fallback.deregister', - 'register_vocab', - 'register_intent', - 'recognizer_loop:utterance', - 'detach_intent', - 'detach_skill', - 'add_context', - 'remove_context', - 'clear_context', - 'mycroft.skills.loaded', - 'intent.service.intent.get', - 'intent.service.skills.get', - 'intent.service.adapt.get', - 'intent.service.adapt.manifest.get', - 'intent.service.adapt.vocab.manifest.get', - 'intent.service.padatious.get', - 'intent.service.padatious.manifest.get', - 'intent.service.padatious.entities.manifest.get']) + self.assertEqual(intent_service.bus.removed, + ['padatious:register_intent', + 'padatious:register_entity', + 'detach_intent', + 'detach_skill', + 'question:query.response', + 'common_query.question', + 'ovos.common_query.pong', + 'mycroft.speech.recognition.unknown', + 'intent.service.skills.deactivate', + 'intent.service.skills.activate', + 'active_skill_request', + 'intent.service.active_skills.get', + 'skill.converse.get_response.enable', + 'skill.converse.get_response.disable', + 'ovos.skills.fallback.register', + 'ovos.skills.fallback.deregister', + 'register_vocab', + 'register_intent', + 'recognizer_loop:utterance', + 'detach_intent', + 'detach_skill', + 'add_context', + 'remove_context', + 'clear_context', + 'mycroft.skills.loaded', + 'intent.service.intent.get', + 'intent.service.skills.get', + 'intent.service.adapt.get', + 'intent.service.adapt.manifest.get', + 'intent.service.adapt.vocab.manifest.get', + 'intent.service.padatious.get', + 'intent.service.padatious.manifest.get', + 'intent.service.padatious.entities.manifest.get']) intent_service = IntentService(MockEmitter(), config={"experimental_ocp_pipeline": True, "padatious": {"disabled": True}}) intent_service.shutdown() - self.assertListEqual(intent_service.bus.removed, - ['padatious:register_intent', - 'padatious:register_entity', - 'detach_intent', - 'detach_skill', - 'question:query.response', - 'common_query.question', - 'ovos.common_query.pong', - 'mycroft.speech.recognition.unknown', - 'intent.service.skills.deactivate', - 'intent.service.skills.activate', - 'active_skill_request', - 'intent.service.active_skills.get', - 'skill.converse.get_response.enable', - 'skill.converse.get_response.disable', - 'ovos.skills.fallback.register', - 'ovos.skills.fallback.deregister', - 'play:query.response', - 'ovos.common_play.search', - 'ovos.common_play.play_search', - 'ovos.common_play.status.response', - 'ovos.common_play.track.state', - 'ovos.common_play.SEI.get.response', - 'ovos.common_play.register_keyword', - 'ovos.common_play.deregister_keyword', - 'ovos.common_play.announce', - 'mycroft.audio.playing_track', - 'mycroft.audio.queue_end', - 'mycroft.audio.service.pause', - 'mycroft.audio.service.resume', - 'mycroft.audio.service.stop', - 'ocp:play', - 'ocp:play_favorites', - 'ocp:open', - 'ocp:next', - 'ocp:prev', - 'ocp:pause', - 'ocp:resume', - 'ocp:media_stop', - 'ocp:search_error', - 'ocp:like_song', - 'ocp:legacy_cps', - 'register_vocab', - 'register_intent', - 'recognizer_loop:utterance', - 'detach_intent', - 'detach_skill', - 'add_context', - 'remove_context', - 'clear_context', - 'mycroft.skills.loaded', - 'intent.service.intent.get', - 'intent.service.skills.get', - 'intent.service.adapt.get', - 'intent.service.adapt.manifest.get', - 'intent.service.adapt.vocab.manifest.get', - 'intent.service.padatious.get', - 'intent.service.padatious.manifest.get', - 'intent.service.padatious.entities.manifest.get']) + self.assertEqual(set(intent_service.bus.removed), + {'active_skill_request', + 'add_context', + 'clear_context', + 'common_query.question', + 'detach_intent', + 'detach_skill', + 'intent.service.active_skills.get', + 'intent.service.adapt.get', + 'intent.service.adapt.manifest.get', + 'intent.service.adapt.vocab.manifest.get', + 'intent.service.intent.get', + 'intent.service.padatious.entities.manifest.get', + 'intent.service.padatious.get', + 'intent.service.padatious.manifest.get', + 'intent.service.skills.activate', + 'intent.service.skills.activated', + 'intent.service.skills.deactivate', + 'intent.service.skills.deactivated', + 'intent.service.skills.get', + 'mycroft.audio.playing_track', + 'mycroft.audio.queue_end', + 'mycroft.audio.service.pause', + 'mycroft.audio.service.resume', + 'mycroft.audio.service.stop', + 'mycroft.skill.disable_intent', + 'mycroft.skill.enable_intent', + 'mycroft.skill.remove_cross_context', + 'mycroft.skill.set_cross_context', + 'mycroft.skills.loaded', + 'mycroft.skills.settings.changed', + 'mycroft.speech.recognition.unknown', + 'mycroft.stop', + 'ocp:legacy_cps', + 'ocp:like_song', + 'ocp:media_stop', + 'ocp:next', + 'ocp:open', + 'ocp:pause', + 'ocp:play', + 'ocp:play_favorites', + 'ocp:prev', + 'ocp:resume', + 'ocp:search_error', + 'ovos.common_play.SEI.get.response', + 'ovos.common_play.activate', + 'ovos.common_play.announce', + 'ovos.common_play.converse.get_response', + 'ovos.common_play.converse.ping', + 'ovos.common_play.converse.request', + 'ovos.common_play.deactivate', + 'ovos.common_play.deregister_keyword', + 'ovos.common_play.play_search', + 'ovos.common_play.register_keyword', + 'ovos.common_play.search', + 'ovos.common_play.set', + 'ovos.common_play.status.response', + 'ovos.common_play.stop', + 'ovos.common_play.stop.ping', + 'ovos.common_play.track.state', + 'ovos.common_query.pong', + 'ovos.skills.fallback.deregister', + 'ovos.skills.fallback.register', + 'ovos.skills.settings_changed', + 'padatious:register_entity', + 'padatious:register_intent', + 'play:query.response', + 'question:query.response', + 'recognizer_loop:utterance', + 'register_intent', + 'register_vocab', + 'remove_context', + 'skill.converse.get_response', + 'skill.converse.get_response.disable', + 'skill.converse.get_response.enable', + 'skill.converse.ping', + 'skill.converse.request'}) class TestAdaptIntent(TestCase): From 8ec30f6b1aba231deebc27d5fbb2762dd43ef95f Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 28 May 2024 06:28:16 +0100 Subject: [PATCH 3/5] requirements --- requirements/mycroft.txt | 4 ++-- requirements/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/mycroft.txt b/requirements/mycroft.txt index 0922bce95f67..790325443198 100644 --- a/requirements/mycroft.txt +++ b/requirements/mycroft.txt @@ -1,7 +1,7 @@ # ovos modules for compat with mycroft namespace ovos_PHAL<0.1.0, >=0.0.5a14 ovos-audio~=0.0, >=0.0.2a32 -#ovos-listener~=0.0, >=0.0.3a2 +ovos-listener~=0.0, >=0.0.3a2 ovos-gui~=0.0, >=0.0.4a5 ovos-messagebus~=0.0 @@ -10,7 +10,7 @@ ovos-stt-plugin-server>=0.0.3, <0.1.0 ovos-tts-plugin-mimic>=0.2.8, <0.3.0 ovos-tts-plugin-server>=0.0.2a6 -#ovos-ww-plugin-pocketsphinx~=0.1 +ovos-ww-plugin-pocketsphinx~=0.1 ovos-ww-plugin-precise~=0.1 ovos-vad-plugin-webrtcvad>=0.0.1, <0.1.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a7f019b8a08e..cbc0ecbf1e52 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -12,7 +12,7 @@ ovos-plugin-manager<0.1.0, >=0.0.25 ovos-config~=0.0,>=0.0.13a8 ovos-lingua-franca>=0.4.7 ovos-backend-client~=0.1.0 -ovos-workshop<0.1.0, >=0.0.16a27 +ovos-workshop<0.1.0, >=0.0.16a30 # provides plugins and classic machine learning framework ovos-classifiers<0.1.0, >=0.0.0a53 From 5607063e2df0b2df722282cf0511e054e601271b Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 28 May 2024 06:45:04 +0100 Subject: [PATCH 4/5] debug test failures in GH --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index eff3f5f6ec96..5fe91cc94694 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -34,7 +34,7 @@ jobs: strategy: max-parallel: 3 matrix: - python-version: [ 3.8, 3.9] + python-version: [3.9, "3.10", "3.11"] runs-on: ubuntu-latest timeout-minutes: 15 steps: From 6375dc139c8e93194d3899a23ed77e8ddb6730d1 Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 28 May 2024 06:47:43 +0100 Subject: [PATCH 5/5] debug test failures in GH --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 5fe91cc94694..26b0ad352332 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -34,7 +34,7 @@ jobs: strategy: max-parallel: 3 matrix: - python-version: [3.9, "3.10", "3.11"] + python-version: [3.9] runs-on: ubuntu-latest timeout-minutes: 15 steps: