From fd52258071e0efd50d7a86a5b42fc17c1550b6a7 Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 03:13:51 +0000 Subject: [PATCH 01/10] feat:pipeline plugin factory loads pipeline plugins from config :tada: no longer tied to adapt/padatious --- ovos_core/intent_services/__init__.py | 234 +++++------------- ovos_core/intent_services/converse_service.py | 32 ++- ovos_core/intent_services/fallback_service.py | 58 +++-- ovos_core/intent_services/stop_service.py | 89 ++++--- 4 files changed, 177 insertions(+), 236 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 009935a668d1..58e967240c64 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -13,21 +13,19 @@ # limitations under the License. # from collections import defaultdict -from typing import Tuple, Callable, Union +from typing import Tuple, Callable, List, Union, Dict -from ovos_adapt.opm import AdaptPipeline -from ovos_commonqa.opm import CommonQAService - -from ocp_pipeline.opm import OCPPipelineMatcher from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager from ovos_bus_client.util import get_message_lang + from ovos_config.config import Configuration from ovos_config.locale import get_valid_languages from ovos_core.intent_services.converse_service import ConverseService from ovos_core.intent_services.fallback_service import FallbackService from ovos_core.intent_services.stop_service import StopService from ovos_core.transformers import MetadataTransformersService, UtteranceTransformersService +from ovos_plugin_manager.pipeline import OVOSPipelineFactory from ovos_plugin_manager.templates.pipeline import PipelineMatch, IntentHandlerMatch from ovos_utils.lang import standardize_lang_tag from ovos_utils.log import LOG, log_deprecation, deprecated @@ -50,15 +48,9 @@ def __init__(self, bus, config=None): # Dictionary for translating a skill id to a name self.skill_names = {} - self._adapt_service = None - self._padatious_service = None - self._padacioso_service = None - self._fallback = None - self._converse = None - self._common_qa = None - self._stop = None - self._ocp = None - self._load_pipeline_plugins() + for p in OVOSPipelineFactory.get_installed_pipelines(): + LOG.debug(f"Found pipeline: {p}") + OVOSPipelineFactory.create(use_cache=True, bus=self.bus) # pre-loa self.utterance_plugins = UtteranceTransformersService(bus) self.metadata_plugins = MetadataTransformersService(bus) @@ -77,58 +69,11 @@ def __init__(self, bus, config=None): # Intents API self.registered_vocab = [] self.bus.on('intent.service.intent.get', self.handle_get_intent) - self.bus.on('intent.service.skills.get', self.handle_get_skills) - self.bus.on('mycroft.skills.loaded', self.update_skill_name_dict) # internal, track skills that call self.deactivate to avoid reactivating them again self._deactivations = defaultdict(list) self.bus.on('intent.service.skills.deactivate', self._handle_deactivate) - def _load_pipeline_plugins(self): - # TODO - replace with plugin loader from OPM - self._adapt_service = AdaptPipeline(bus=self.bus, config=self.config.get("adapt", {})) - if "padatious" not in self.config: - self.config["padatious"] = Configuration().get("padatious", {}) - try: - if self.config["padatious"].get("disabled"): - LOG.info("padatious forcefully disabled in config") - else: - from ovos_padatious.opm import PadatiousPipeline - if "instant_train" not in self.config["padatious"]: - self.config["padatious"]["instant_train"] = False - self._padatious_service = PadatiousPipeline(self.bus, self.config["padatious"]) - except ImportError: - LOG.error(f'Failed to create padatious intent handlers, padatious not installed') - - # by default only load padacioso is padatious is not available - # save memory if padacioso isnt needed - disable_padacioso = self.config.get("disable_padacioso", self._padatious_service is not None) - if not disable_padacioso: - self._padacioso_service = PadaciosoService(self.bus, self.config["padatious"]) - elif "disable_padacioso" not in self.config: - LOG.debug("Padacioso pipeline is disabled, only padatious is loaded. " - "set 'disable_padacioso': false in mycroft.conf if you want it to load alongside padatious") - self._fallback = FallbackService(self.bus) - self._converse = ConverseService(self.bus) - self._common_qa = CommonQAService(self.bus, self.config.get("common_query")) - self._stop = StopService(self.bus) - self._ocp = OCPPipelineMatcher(self.bus, config=self.config.get("OCP", {})) - - def update_skill_name_dict(self, message): - """Messagebus handler, updates dict of id to skill name conversions.""" - self.skill_names[message.data['id']] = message.data['name'] - - def get_skill_name(self, skill_id): - """Get skill name from skill ID. - - Args: - skill_id: a skill id as encoded in Intent handlers. - - Returns: - (str) Skill name or the skill id if the skill wasn't found - """ - return self.skill_names.get(skill_id, skill_id) - def _handle_transformers(self, message): """ Pipe utterance through transformer plugins to get more metadata. @@ -170,69 +115,31 @@ def disambiguate_lang(message): return default_lang - def get_pipeline(self, skips=None, session=None) -> Tuple[str, Callable]: + def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> List[Tuple[str, Callable]]: """return a list of matcher functions ordered by priority utterances will be sent to each matcher in order until one can handle the utterance the list can be configured in mycroft.conf under intents.pipeline, in the future plugins will be supported for users to define their own pipeline""" + skips = skips or [] + session = session or SessionManager.get() - # Create matchers - # TODO - from plugins - padatious_matcher = None - if self._padatious_service is None: - needs_pada = any("padatious" in p for p in session.pipeline) - if self._padacioso_service is not None: - if needs_pada: - LOG.warning("padatious is not available! using padacioso in it's place, " - "intent matching will be extremely slow in comparison") - padatious_matcher = self._padacioso_service - elif needs_pada: - LOG.warning("padatious is not available! only adapt (keyword based) intents will match!") - else: - padatious_matcher = self._padatious_service - - matchers = { - "converse": self._converse.converse_with_skills, - "stop_high": self._stop.match_stop_high, - "stop_medium": self._stop.match_stop_medium, - "stop_low": self._stop.match_stop_low, - "adapt_high": self._adapt_service.match_high, - "common_qa": self._common_qa.match, - "fallback_high": self._fallback.high_prio, - "adapt_medium": self._adapt_service.match_medium, - "fallback_medium": self._fallback.medium_prio, - "adapt_low": self._adapt_service.match_low, - "fallback_low": self._fallback.low_prio - } - if self._padacioso_service is not None: - matchers.update({ - "padacioso_high": self._padacioso_service.match_high, - "padacioso_medium": self._padacioso_service.match_medium, - "padacioso_low": self._padacioso_service.match_low, - - }) - if padatious_matcher is not None: - matchers.update({ - "padatious_high": padatious_matcher.match_high, - "padatious_medium": padatious_matcher.match_medium, - "padatious_low": padatious_matcher.match_low, - - }) - if self._ocp is not None: - matchers.update({ - "ocp_high": self._ocp.match_high, - "ocp_medium": self._ocp.match_medium, - "ocp_fallback": self._ocp.match_fallback, - "ocp_legacy": self._ocp.match_legacy}) - skips = skips or [] - pipeline = [k for k in session.pipeline if k not in skips] - if any(k not in matchers for k in pipeline): + if skips: + log_deprecation("'skips' kwarg has been deprecated!", "1.0.0") + skips = [OVOSPipelineFactory._MAP.get(p, p) for p in skips] + + pipeline = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline + if p not in skips] + + matchers = OVOSPipelineFactory.create(pipeline, use_cache=True, bus=self.bus, + skip_stage_matchers=skip_stage_matchers) + + if any(k[0] not in pipeline for k in matchers): LOG.warning(f"Requested some invalid pipeline components! " f"filtered {[k for k in pipeline if k not in matchers]}") pipeline = [k for k in pipeline if k in matchers] LOG.debug(f"Session pipeline: {pipeline}") - return [(k, matchers[k]) for k in pipeline] + return matchers @staticmethod def _validate_session(message, lang): @@ -270,27 +177,27 @@ def _handle_deactivate(self, message): def _emit_match_message(self, match: Union[IntentHandlerMatch, PipelineMatch], message: Message): """ Emit a reply message for a matched intent, updating session and skill activation. - + This method processes matched intents from either a pipeline matcher or an intent handler, creating a reply message with matched intent details and managing skill activation. - + Args: match (Union[IntentHandlerMatch, PipelineMatch]): The matched intent object containing utterance and matching information. message (Message): The original messagebus message that triggered the intent match. - + Details: - Handles two types of matches: PipelineMatch and IntentHandlerMatch - Creates a reply message with matched intent data - Activates the corresponding skill if not previously deactivated - Updates session information - Emits the reply message on the messagebus - + Side Effects: - Modifies session state - Emits a messagebus event - Can trigger skill activation events - + Returns: None """ @@ -334,18 +241,18 @@ def _emit_match_message(self, match: Union[IntentHandlerMatch, PipelineMatch], m def send_cancel_event(self, message): """ Emit events and play a sound when an utterance is canceled. - + Logs the cancellation with the specific cancel word, plays a predefined cancel sound, and emits multiple events to signal the utterance cancellation. - + Parameters: message (Message): The original message that triggered the cancellation. - + Events Emitted: - 'mycroft.audio.play_sound': Plays a cancel sound from configuration - 'ovos.utterance.cancelled': Signals that the utterance was canceled - 'ovos.utterance.handled': Indicates the utterance processing is complete - + Notes: - Uses the default cancel sound path 'snd/cancel.mp3' if not specified in configuration - Ensures events are sent as replies to the original message @@ -506,12 +413,7 @@ def handle_get_intent(self, message): sess = SessionManager.get(message) # Loop through the matching functions until a match is found. - for pipeline, match_func in self.get_pipeline(skips=["converse", - "common_qa", - "fallback_high", - "fallback_medium", - "fallback_low"], - session=sess): + for pipeline, match_func in self.get_pipeline(session=sess, skip_stage_matchers=True): match = match_func([utterance], lang, message) if match: if match.match_type: @@ -528,36 +430,16 @@ def handle_get_intent(self, message): self.bus.emit(message.reply("intent.service.intent.reply", {"intent": None})) - def handle_get_skills(self, message): - """Send registered skills to caller. - - Argument: - message: query message to reply to. - """ - self.bus.emit(message.reply("intent.service.skills.reply", - {"skills": self.skill_names})) - def shutdown(self): self.utterance_plugins.shutdown() self.metadata_plugins.shutdown() - self._adapt_service.shutdown() - if self._padacioso_service: - self._padacioso_service.shutdown() - if self._padatious_service: - self._padatious_service.shutdown() - self._common_qa.shutdown() - self._converse.shutdown() - self._fallback.shutdown() - if self._ocp: - self._ocp.shutdown() + OVOSPipelineFactory.shutdown() self.bus.remove('recognizer_loop:utterance', self.handle_utterance) self.bus.remove('add_context', self.handle_add_context) self.bus.remove('remove_context', self.handle_remove_context) self.bus.remove('clear_context', self.handle_clear_context) - self.bus.remove('mycroft.skills.loaded', self.update_skill_name_dict) self.bus.remove('intent.service.intent.get', self.handle_get_intent) - self.bus.remove('intent.service.skills.get', self.handle_get_skills) ########### # DEPRECATED STUFF @@ -570,9 +452,31 @@ def registered_intents(self): DeprecationWarning, stacklevel=2, ) - lang = get_message_lang() - return [parser.__dict__ - for parser in self._adapt_service.engines[lang].intent_parsers] + return [] + + @property + def skill_names(self) -> Dict: + """DEPRECATED""" + log_deprecation("skill names have been replaced by skill_id", "1.0.0") + return {} + + @skill_names.setter + def skill_names(self, v): + log_deprecation("skill names have been replaced by skill_id", "1.0.0") + + @deprecated("skill names have been replaced by skill_id", "1.0.0") + def update_skill_name_dict(self, message): + """DEPRECATED""" + + @deprecated("skill names have been replaced by skill_id", "1.0.0") + def get_skill_name(self, skill_id): + """DEPRECATED""" + return skill_id + + @deprecated("skill names have been replaced by skill_id", "1.0.0") + def handle_get_skills(self, message): + """DEPRECATED""" + return [] @property def adapt_service(self): @@ -583,7 +487,7 @@ def adapt_service(self): ) log_deprecation("direct access to self.adapt_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._adapt_service + return None @property def padatious_service(self): @@ -594,7 +498,7 @@ def padatious_service(self): ) log_deprecation("direct access to self.padatious_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._padatious_service + return None @property def padacioso_service(self): @@ -605,7 +509,7 @@ def padacioso_service(self): ) log_deprecation("direct access to self.padacioso_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._padacioso_service + return None @property def fallback(self): @@ -616,7 +520,7 @@ def fallback(self): ) log_deprecation("direct access to self.fallback is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._fallback + return None @property def converse(self): @@ -627,7 +531,7 @@ def converse(self): ) log_deprecation("direct access to self.converse is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._converse + return None @property def common_qa(self): @@ -638,7 +542,7 @@ def common_qa(self): ) log_deprecation("direct access to self.common_qa is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._common_qa + return None @property def stop(self): @@ -649,7 +553,7 @@ def stop(self): ) log_deprecation("direct access to self.stop is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._stop + return None @property def ocp(self): @@ -660,7 +564,7 @@ def ocp(self): ) log_deprecation("direct access to self.ocp is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - return self._ocp + return None @adapt_service.setter def adapt_service(self, value): @@ -671,7 +575,6 @@ def adapt_service(self, value): ) log_deprecation("direct access to self.adapt_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._adapt_service = value @padatious_service.setter def padatious_service(self, value): @@ -682,7 +585,6 @@ def padatious_service(self, value): ) log_deprecation("direct access to self.padatious_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._padatious_service = value @padacioso_service.setter def padacioso_service(self, value): @@ -693,7 +595,6 @@ def padacioso_service(self, value): ) log_deprecation("direct access to self.padacioso_service is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._padacioso_service = value @fallback.setter def fallback(self, value): @@ -704,7 +605,6 @@ def fallback(self, value): ) log_deprecation("direct access to self.fallback is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._fallback = value @converse.setter def converse(self, value): @@ -715,7 +615,6 @@ def converse(self, value): ) log_deprecation("direct access to self.converse is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._converse = value @common_qa.setter def common_qa(self, value): @@ -726,7 +625,6 @@ def common_qa(self, value): ) log_deprecation("direct access to self.common_qa is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._common_qa = value @stop.setter def stop(self, value): @@ -737,7 +635,6 @@ def stop(self, value): ) log_deprecation("direct access to self.stop is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._stop = value @ocp.setter def ocp(self, value): @@ -748,7 +645,6 @@ def ocp(self, value): ) log_deprecation("direct access to self.ocp is deprecated, " "pipelines are in the progress of being replaced with plugins", "1.0.0") - self._ocp = value @deprecated("handle_get_adapt moved to adapt service, this method does nothing", "1.0.0") def handle_get_adapt(self, message: Message): diff --git a/ovos_core/intent_services/converse_service.py b/ovos_core/intent_services/converse_service.py index 4d6421f97ed3..d50400a0a460 100644 --- a/ovos_core/intent_services/converse_service.py +++ b/ovos_core/intent_services/converse_service.py @@ -1,24 +1,27 @@ import time from threading import Event -from typing import Optional, List +from typing import Optional, Dict, List, Union +from ovos_bus_client.client import MessageBusClient from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager, UtteranceState, Session from ovos_bus_client.util import get_message_lang from ovos_config.config import Configuration -from ovos_config.locale import setup_locale -from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelinePlugin +from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelineStageMatcher from ovos_utils import flatten_list +from ovos_utils.fakebus import FakeBus from ovos_utils.lang import standardize_lang_tag -from ovos_utils.log import LOG +from ovos_utils.log import LOG, deprecated from ovos_workshop.permissions import ConverseMode, ConverseActivationMode -class ConverseService(PipelinePlugin): +class ConverseService(PipelineStageMatcher): """Intent Service handling conversational skills.""" - def __init__(self, bus): - self.bus = bus + def __init__(self, bus: Optional[Union[MessageBusClient, FakeBus]] = None, + config: Optional[Dict] = None): + config = config or Configuration().get("skills", {}).get("converse", {}) + super().__init__(bus, config) self._consecutive_activations = {} self.bus.on('mycroft.speech.recognition.unknown', self.reset_converse) self.bus.on('intent.service.skills.deactivate', self.handle_deactivate_skill_request) @@ -27,7 +30,6 @@ def __init__(self, bus): self.bus.on('intent.service.active_skills.get', self.handle_get_active_skills) self.bus.on("skill.converse.get_response.enable", self.handle_get_response_enable) self.bus.on("skill.converse.get_response.disable", self.handle_get_response_disable) - super().__init__(config=Configuration().get("skills", {}).get("converse") or {}) @property def active_skills(self): @@ -312,17 +314,17 @@ def converse(self, utterances: List[str], skill_id: str, lang: str, message: Mes f'increasing "max_skill_runtime" in mycroft.conf might help alleviate this issue') return False - def converse_with_skills(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """ Attempt to converse with active skills for a given set of utterances. - + Iterates through active skills to find one that can handle the utterance. Filters skills based on timeout and blacklist status. - + Args: utterances (List[str]): List of utterance strings to process lang (str): 4-letter ISO language code for the utterances message (Message): Message context for generating a reply - + Returns: PipelineMatch: Match details if a skill successfully handles the utterance, otherwise None - handled (bool): Whether the utterance was fully handled @@ -330,7 +332,7 @@ def converse_with_skills(self, utterances: List[str], lang: str, message: Messag - skill_id (str): ID of the skill that handled the utterance - updated_session (Session): Current session state after skill interaction - utterance (str): The original utterance processed - + Notes: - Standardizes language tag - Filters out blacklisted skills @@ -414,6 +416,10 @@ def handle_get_active_skills(self, message: Message): self.bus.emit(message.reply("intent.service.active_skills.reply", {"skills": self.get_active_skills(message)})) + @deprecated("'converse_with_skills' has been renamed to 'match'", "2.0.0") + def converse_with_skills(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match(utterances, lang, message) + def shutdown(self): self.bus.remove('mycroft.speech.recognition.unknown', self.reset_converse) self.bus.remove('intent.service.skills.deactivate', self.handle_deactivate_skill_request) diff --git a/ovos_core/intent_services/fallback_service.py b/ovos_core/intent_services/fallback_service.py index df2d5cb042f3..46862a382259 100644 --- a/ovos_core/intent_services/fallback_service.py +++ b/ovos_core/intent_services/fallback_service.py @@ -12,41 +12,43 @@ # See the License for the specific language governing permissions and # limitations under the License. # -"""Intent service for Mycroft's fallback system.""" +"""Intent service for OVOS's fallback system.""" import operator import time from collections import namedtuple -from typing import Optional, List +from typing import Optional, Dict, List, Union +from ovos_bus_client.client import MessageBusClient from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager from ovos_config import Configuration -from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelinePlugin +from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelineStageConfidenceMatcher from ovos_utils import flatten_list +from ovos_utils.fakebus import FakeBus from ovos_utils.lang import standardize_lang_tag -from ovos_utils.log import LOG +from ovos_utils.log import LOG, deprecated, log_deprecation from ovos_workshop.permissions import FallbackMode FallbackRange = namedtuple('FallbackRange', ['start', 'stop']) -class FallbackService(PipelinePlugin): +class FallbackService(PipelineStageConfidenceMatcher): """Intent Service handling fallback skills.""" - def __init__(self, bus): - self.bus = bus - self.fallback_config = Configuration()["skills"].get("fallbacks", {}) + def __init__(self, bus: Optional[Union[MessageBusClient, FakeBus]] = None, + config: Optional[Dict] = None): + config = config or Configuration().get("skills", {}).get("fallbacks", {}) + super().__init__(bus, config) self.registered_fallbacks = {} # skill_id: priority self.bus.on("ovos.skills.fallback.register", self.handle_register_fallback) self.bus.on("ovos.skills.fallback.deregister", self.handle_deregister_fallback) - super().__init__(self.fallback_config) def handle_register_fallback(self, message: Message): skill_id = message.data.get("skill_id") priority = message.data.get("priority") or 101 # check if .conf is overriding the priority for this skill - priority_overrides = self.fallback_config.get("fallback_priorities", {}) + priority_overrides = self.config.get("fallback_priorities", {}) if skill_id in priority_overrides: new_priority = priority_overrides.get(skill_id) LOG.info(f"forcing {skill_id} fallback priority from {priority} to {new_priority}") @@ -71,12 +73,12 @@ def _fallback_allowed(self, skill_id: str) -> bool: Returns: permitted (bool): True if skill can fallback """ - opmode = self.fallback_config.get("fallback_mode", FallbackMode.ACCEPT_ALL) + opmode = self.config.get("fallback_mode", FallbackMode.ACCEPT_ALL) if opmode == FallbackMode.BLACKLIST and skill_id in \ - self.fallback_config.get("fallback_blacklist", []): + self.config.get("fallback_blacklist", []): return False elif opmode == FallbackMode.WHITELIST and skill_id not in \ - self.fallback_config.get("fallback_whitelist", []): + self.config.get("fallback_whitelist", []): return False return True @@ -147,7 +149,7 @@ def attempt_fallback(self, utterances: List[str], skill_id: str, lang: str, mess "lang": lang}) result = self.bus.wait_for_response(fb_msg, f"ovos.skills.fallback.{skill_id}.response", - timeout=self.fallback_config.get("max_skill_runtime", 10)) + timeout=self.config.get("max_skill_runtime", 10)) if result and 'error' in result.data: error_msg = result.data['error'] LOG.error(f"{skill_id}: {error_msg}") @@ -202,21 +204,43 @@ def _fallback_range(self, utterances: List[str], lang: str, utterance=utterances[0]) return None - def high_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_high(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """Pre-padatious fallbacks.""" return self._fallback_range(utterances, lang, message, FallbackRange(0, 5)) - def medium_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_medium(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """General fallbacks.""" return self._fallback_range(utterances, lang, message, FallbackRange(5, 90)) - def low_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_low(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """Low prio fallbacks with general matching such as chat-bot.""" return self._fallback_range(utterances, lang, message, FallbackRange(90, 101)) + @deprecated("'low_prio' has been renamed to 'match_low'", "2.0.0") + def low_prio(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_low(utterances, lang, message) + + @deprecated("'medium_prio' has been renamed to 'match_medium'", "2.0.0") + def medium_prio(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_medium(utterances, lang, message) + + @deprecated("'high_prio' has been renamed to 'high_low'", "2.0.0") + def high_prio(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_high(utterances, lang, message) + + @property + def fallback_config(self) -> Dict: + log_deprecation("'self.fallback_config' is deprecated, access 'self.config' directly instead", "1.0.0") + return self.config + + @fallback_config.setter + def fallback_config(self, val): + log_deprecation("'self.fallback_config' is deprecated, access 'self.config' directly instead", "1.0.0") + self.config = val + def shutdown(self): self.bus.remove("ovos.skills.fallback.register", self.handle_register_fallback) self.bus.remove("ovos.skills.fallback.deregister", self.handle_deregister_fallback) diff --git a/ovos_core/intent_services/stop_service.py b/ovos_core/intent_services/stop_service.py index 3d0c82651ffe..f0466542ee60 100644 --- a/ovos_core/intent_services/stop_service.py +++ b/ovos_core/intent_services/stop_service.py @@ -2,29 +2,32 @@ import re from os.path import dirname from threading import Event -from typing import Optional, List +from typing import Optional, Dict, List, Union from langcodes import closest_match - +from ovos_bus_client.client import MessageBusClient from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager + from ovos_config.config import Configuration -from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelinePlugin +from ovos_plugin_manager.templates.pipeline import PipelineMatch, PipelineStageConfidenceMatcher from ovos_utils import flatten_list +from ovos_utils.fakebus import FakeBus from ovos_utils.bracket_expansion import expand_template from ovos_utils.lang import standardize_lang_tag -from ovos_utils.log import LOG +from ovos_utils.log import LOG, deprecated from ovos_utils.parse import match_one -class StopService(PipelinePlugin): +class StopService(PipelineStageConfidenceMatcher): """Intent Service thats handles stopping skills.""" - def __init__(self, bus): - self.bus = bus + def __init__(self, bus: Optional[Union[MessageBusClient, FakeBus]] = None, + config: Optional[Dict] = None): + config = config or Configuration().get("skills", {}).get("stop") or {} + super().__init__(config=config, bus=bus) self._voc_cache = {} self.load_resource_files() - super().__init__(config=Configuration().get("skills", {}).get("stop") or {}) def load_resource_files(self): base = f"{dirname(__file__)}/locale" @@ -52,17 +55,17 @@ def get_active_skills(message: Optional[Message] = None) -> List[str]: def _collect_stop_skills(self, message: Message) -> List[str]: """ Collect skills that can be stopped based on a ping-pong mechanism. - + This method determines which active skills can handle a stop request by sending a stop ping to each active skill and waiting for their acknowledgment. - + Parameters: message (Message): The original message triggering the stop request. - + Returns: List[str]: A list of skill IDs that can be stopped. If no skills explicitly indicate they can stop, returns all active skills. - + Notes: - Excludes skills that are blacklisted in the current session - Uses a non-blocking event mechanism to collect skill responses @@ -85,17 +88,17 @@ def _collect_stop_skills(self, message: Message) -> List[str]: def handle_ack(msg): """ Handle acknowledgment from skills during the stop process. - + This method is a nested function used in skill stopping negotiation. It validates and tracks skill responses to a stop request. - + Parameters: msg (Message): Message containing skill acknowledgment details. - + Side Effects: - Modifies the `want_stop` list with skills that can handle stopping - Updates the `skill_ids` list to track which skills have responded - Sets the threading event when all active skills have responded - + Notes: - Checks if a skill can handle stopping based on multiple conditions - Ensures all active skills provide a response before proceeding @@ -132,22 +135,22 @@ def handle_ack(msg): def stop_skill(self, skill_id: str, message: Message) -> bool: """ Stop a skill's ongoing activities and manage its session state. - + Sends a stop command to a specific skill and handles its response, ensuring that any active interactions or processes are terminated. The method checks for errors, verifies the skill's stopped status, and emits additional signals to forcibly abort ongoing actions like conversations, questions, or speech. - + Args: skill_id (str): Unique identifier of the skill to be stopped. message (Message): The original message context containing interaction details. - + Returns: bool: True if the skill was successfully stopped, False otherwise. - + Raises: Logs error if skill stop request encounters an issue. - + Notes: - Emits multiple bus messages to ensure complete skill termination - Checks and handles different skill interaction states @@ -179,27 +182,27 @@ def stop_skill(self, skill_id: str, message: Message) -> bool: return stopped - def match_stop_high(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_high(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """ Handles high-confidence stop requests by matching exact stop vocabulary and managing skill stopping. - + Attempts to stop skills when an exact "stop" or "global_stop" command is detected. Performs the following actions: - Identifies the closest language match for vocabulary - Checks for global stop command when no active skills exist - Emits a global stop message if applicable - Attempts to stop individual skills if a stop command is detected - Disables response mode for stopped skills - + Parameters: utterances (List[str]): List of user utterances to match against stop vocabulary lang (str): Four-letter ISO language code for language-specific matching message (Message): Message context for generating appropriate responses - + Returns: Optional[PipelineMatch]: Match result indicating whether stop was handled, with optional skill and session information - Returns None if no stop action could be performed - Returns PipelineMatch with handled=True for successful global or skill-specific stop - + Raises: No explicit exceptions raised, but may log debug/info messages during processing """ @@ -241,23 +244,23 @@ def match_stop_high(self, utterances: List[str], lang: str, message: Message) -> updated_session=sess) return None - def match_stop_medium(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_medium(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """ Handle stop intent with additional context beyond simple stop commands. - + This method processes utterances that contain "stop" or global stop vocabulary but may include additional words not explicitly defined in intent files. It performs a medium-confidence intent matching for stop requests. - + Parameters: utterances (List[str]): List of input utterances to analyze lang (str): Four-letter ISO language code for localization message (Message): Message context for generating appropriate responses - + Returns: Optional[PipelineMatch]: A pipeline match if the stop intent is successfully processed, otherwise None if no stop intent is detected - + Notes: - Attempts to match stop vocabulary with fuzzy matching - Falls back to low-confidence matching if medium-confidence match is inconclusive @@ -277,7 +280,7 @@ def match_stop_medium(self, utterances: List[str], lang: str, message: Message) if not is_global_stop: return None - return self.match_stop_low(utterances, lang, message) + return self.match_low(utterances, lang, message) def _get_closest_lang(self, lang: str) -> Optional[str]: if self._voc_cache: @@ -291,20 +294,20 @@ def _get_closest_lang(self, lang: str) -> Optional[str]: return closest return None - def match_stop_low(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: + def match_low(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """ Perform a low-confidence fuzzy match for stop intent before fallback processing. - + This method attempts to match stop-related vocabulary with low confidence and handle stopping of active skills. - + Parameters: utterances (List[str]): List of input utterances to match against stop vocabulary lang (str): Four-letter ISO language code for vocabulary matching message (Message): Message context used for generating replies and managing session - + Returns: Optional[PipelineMatch]: A pipeline match object if a stop action is handled, otherwise None - + Notes: - Increases confidence if active skills are present - Attempts to stop individual skills before emitting a global stop signal @@ -386,3 +389,15 @@ def voc_match(self, utt: str, voc_filename: str, lang: str, return any([re.match(r'.*\b' + i + r'\b.*', utt) for i in _vocs]) return False + + @deprecated("'match_stop_low' has been renamed to 'match_low'", "2.0.0") + def match_stop_low(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_low(utterances, lang, message) + + @deprecated("'match_stop_medium' has been renamed to 'match_medium'", "2.0.0") + def match_stop_medium(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_medium(utterances, lang, message) + + @deprecated("'match_stop_high' has been renamed to 'match_high'", "2.0.0") + def match_stop_high(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: + return self.match_high(utterances, lang, message) From 8939626c94f3ad5d46ba562e3b8e02f8cded94a6 Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 03:23:44 +0000 Subject: [PATCH 02/10] fix test --- test/unittests/test_intent_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unittests/test_intent_service.py b/test/unittests/test_intent_service.py index 6d4d062112ab..bb767b1191ad 100644 --- a/test/unittests/test_intent_service.py +++ b/test/unittests/test_intent_service.py @@ -94,11 +94,11 @@ def on_msg(m): msg = Message('register_vocab', {'entity_value': 'test', 'entity_type': 'testKeyword'}) - self.intent_service._adapt_service.handle_register_vocab(msg) + self.intent_service.bus.emit(msg) intent = IntentBuilder('skill:testIntent').require('testKeyword') msg = Message('register_intent', intent.__dict__) - self.intent_service._adapt_service.handle_register_intent(msg) + self.intent_service.bus.emit(msg) def test_get_intent_no_match(self): """Check that if the intent doesn't match at all None is returned.""" From 84925282bb468e812a1c27527fcbfa00a04eb042 Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 03:24:25 +0000 Subject: [PATCH 03/10] typo in log --- ovos_core/intent_services/fallback_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ovos_core/intent_services/fallback_service.py b/ovos_core/intent_services/fallback_service.py index 46862a382259..abe3cee881f0 100644 --- a/ovos_core/intent_services/fallback_service.py +++ b/ovos_core/intent_services/fallback_service.py @@ -227,7 +227,7 @@ def low_prio(self, utterances: List[str], lang: str, message: Message = None) -> def medium_prio(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: return self.match_medium(utterances, lang, message) - @deprecated("'high_prio' has been renamed to 'high_low'", "2.0.0") + @deprecated("'high_prio' has been renamed to 'match_high'", "2.0.0") def high_prio(self, utterances: List[str], lang: str, message: Message = None) -> Optional[PipelineMatch]: return self.match_high(utterances, lang, message) From d2b9696991aefa24f4359acac52e0b94e9b3a91f Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 03:37:48 +0000 Subject: [PATCH 04/10] . --- ovos_core/intent_services/__init__.py | 5 +---- test/end2end/minicroft.py | 16 +--------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 58e967240c64..ef7ba639e031 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -45,11 +45,8 @@ def __init__(self, bus, config=None): self.bus = bus self.config = config or Configuration().get("intents", {}) - # Dictionary for translating a skill id to a name - self.skill_names = {} - for p in OVOSPipelineFactory.get_installed_pipelines(): - LOG.debug(f"Found pipeline: {p}") + LOG.info(f"Found pipeline: {p}") OVOSPipelineFactory.create(use_cache=True, bus=self.bus) # pre-loa self.utterance_plugins = UtteranceTransformersService(bus) diff --git a/test/end2end/minicroft.py b/test/end2end/minicroft.py index 23eacd74a8f2..001da2680fd3 100644 --- a/test/end2end/minicroft.py +++ b/test/end2end/minicroft.py @@ -18,27 +18,13 @@ def __init__(self, skill_ids, *args, **kwargs): bus = FakeBus() super().__init__(bus, *args, **kwargs) self.skill_ids = skill_ids - self.intent_service = self._register_intent_services() + self.intent_service = IntentService(self.bus) self.scheduler = EventScheduler(bus, schedule_file="/tmp/schetest.json") def load_metadata_transformers(self, cfg): self.intent_service.metadata_plugins.config = cfg self.intent_service.metadata_plugins.load_plugins() - def _register_intent_services(self): - """Start up the all intent services and connect them as needed. - - Args: - bus: messagebus client to register the services on - """ - service = IntentService(self.bus) - # Register handler to trigger fallback system - self.bus.on( - 'mycroft.skills.fallback', - FallbackSkill.make_intent_failure_handler(self.bus) - ) - return service - def load_plugin_skills(self): LOG.info("loading skill plugins") plugins = find_skill_plugins() From e1488f3935562e3307b8e01e7a8f4eb8844bb010 Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 04:06:18 +0000 Subject: [PATCH 05/10] fix: pipeline filtering --- ovos_core/intent_services/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index ef7ba639e031..a57ea95f14a5 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -131,11 +131,13 @@ def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> L matchers = OVOSPipelineFactory.create(pipeline, use_cache=True, bus=self.bus, skip_stage_matchers=skip_stage_matchers) - if any(k[0] not in pipeline for k in matchers): + final_pipeline = [k[0] for k in matchers] + + if any(k not in pipeline for k in final_pipeline): LOG.warning(f"Requested some invalid pipeline components! " - f"filtered {[k for k in pipeline if k not in matchers]}") - pipeline = [k for k in pipeline if k in matchers] - LOG.debug(f"Session pipeline: {pipeline}") + f"filtered: {[k for k in pipeline if k not in final_pipeline]}") + + LOG.debug(f"Session final pipeline: {final_pipeline}") return matchers @staticmethod From c99aa1e7b1e30e0061eb599a7943b8530c48d9bd Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 04:55:08 +0000 Subject: [PATCH 06/10] fix: pipeline sort --- ovos_core/intent_services/__init__.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index a57ea95f14a5..e97fd5dbff0b 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -125,18 +125,19 @@ def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> L log_deprecation("'skips' kwarg has been deprecated!", "1.0.0") skips = [OVOSPipelineFactory._MAP.get(p, p) for p in skips] - pipeline = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline - if p not in skips] - - matchers = OVOSPipelineFactory.create(pipeline, use_cache=True, bus=self.bus, - skip_stage_matchers=skip_stage_matchers) - + pipeline: List[str] = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline + if p not in skips] + + matchers: List[Tuple[str, Callable]] = OVOSPipelineFactory.create(pipeline, use_cache=True, bus=self.bus, + skip_stage_matchers=skip_stage_matchers) + # Sort matchers to ensure the same order as in `pipeline` + matcher_dict = dict(matchers) + matchers = [(p, matcher_dict[p]) for p in pipeline if p in matcher_dict] final_pipeline = [k[0] for k in matchers] - if any(k not in pipeline for k in final_pipeline): + if pipeline != final_pipeline: LOG.warning(f"Requested some invalid pipeline components! " f"filtered: {[k for k in pipeline if k not in final_pipeline]}") - LOG.debug(f"Session final pipeline: {final_pipeline}") return matchers @@ -502,7 +503,7 @@ def padatious_service(self): @property def padacioso_service(self): warnings.warn( - "direct access to self.padatious_service is deprecated", + "direct access to self.padacioso_service is deprecated", DeprecationWarning, stacklevel=2, ) From e05f8a6b2eac75bdca132b2a95a9b92110d2a5d3 Mon Sep 17 00:00:00 2001 From: miro Date: Sat, 2 Nov 2024 05:11:14 +0000 Subject: [PATCH 07/10] preload pipeline plugs --- ovos_core/intent_services/__init__.py | 5 +++-- ovos_core/intent_services/stop_service.py | 24 +++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index e97fd5dbff0b..6bb56f2c4368 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -70,6 +70,7 @@ def __init__(self, bus, config=None): # internal, track skills that call self.deactivate to avoid reactivating them again self._deactivations = defaultdict(list) self.bus.on('intent.service.skills.deactivate', self._handle_deactivate) + self.get_pipeline() # trigger initial load of pipeline plugins (more may be lazy loaded on demand) def _handle_transformers(self, message): """ @@ -125,9 +126,9 @@ def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> L log_deprecation("'skips' kwarg has been deprecated!", "1.0.0") skips = [OVOSPipelineFactory._MAP.get(p, p) for p in skips] - pipeline: List[str] = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline + pipeline: List[str] = [OVOSPipelineFactory._MAP.get(p, p) + for p in session.pipeline if p not in skips] - matchers: List[Tuple[str, Callable]] = OVOSPipelineFactory.create(pipeline, use_cache=True, bus=self.bus, skip_stage_matchers=skip_stage_matchers) # Sort matchers to ensure the same order as in `pipeline` diff --git a/ovos_core/intent_services/stop_service.py b/ovos_core/intent_services/stop_service.py index f0466542ee60..7ea7b1473d84 100644 --- a/ovos_core/intent_services/stop_service.py +++ b/ovos_core/intent_services/stop_service.py @@ -282,18 +282,6 @@ def match_medium(self, utterances: List[str], lang: str, message: Message) -> Op return self.match_low(utterances, lang, message) - def _get_closest_lang(self, lang: str) -> Optional[str]: - if self._voc_cache: - lang = standardize_lang_tag(lang) - closest, score = closest_match(lang, list(self._voc_cache.keys())) - # https://langcodes-hickford.readthedocs.io/en/sphinx/index.html#distance-values - # 0 -> These codes represent the same language, possibly after filling in values and normalizing. - # 1- 3 -> These codes indicate a minor regional difference. - # 4 - 10 -> These codes indicate a significant but unproblematic regional difference. - if score < 10: - return closest - return None - def match_low(self, utterances: List[str], lang: str, message: Message) -> Optional[PipelineMatch]: """ Perform a low-confidence fuzzy match for stop intent before fallback processing. @@ -349,6 +337,18 @@ def match_low(self, utterances: List[str], lang: str, message: Message) -> Optio skill_id=None, utterance=utterance) + def _get_closest_lang(self, lang: str) -> Optional[str]: + if self._voc_cache: + lang = standardize_lang_tag(lang) + closest, score = closest_match(lang, list(self._voc_cache.keys())) + # https://langcodes-hickford.readthedocs.io/en/sphinx/index.html#distance-values + # 0 -> These codes represent the same language, possibly after filling in values and normalizing. + # 1- 3 -> These codes indicate a minor regional difference. + # 4 - 10 -> These codes indicate a significant but unproblematic regional difference. + if score < 10: + return closest + return None + def voc_match(self, utt: str, voc_filename: str, lang: str, exact: bool = False): """ From d8d667a64f0c1e916c523eb7accf95f1372e50ae Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 12 Nov 2024 07:10:27 +0000 Subject: [PATCH 08/10] . --- ovos_core/intent_services/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 6bb56f2c4368..4fb47293bfce 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -45,9 +45,7 @@ def __init__(self, bus, config=None): self.bus = bus self.config = config or Configuration().get("intents", {}) - for p in OVOSPipelineFactory.get_installed_pipelines(): - LOG.info(f"Found pipeline: {p}") - OVOSPipelineFactory.create(use_cache=True, bus=self.bus) # pre-loa + self.get_pipeline() # trigger initial load of pipeline plugins (more may be lazy loaded on demand) self.utterance_plugins = UtteranceTransformersService(bus) self.metadata_plugins = MetadataTransformersService(bus) @@ -70,7 +68,6 @@ def __init__(self, bus, config=None): # internal, track skills that call self.deactivate to avoid reactivating them again self._deactivations = defaultdict(list) self.bus.on('intent.service.skills.deactivate', self._handle_deactivate) - self.get_pipeline() # trigger initial load of pipeline plugins (more may be lazy loaded on demand) def _handle_transformers(self, message): """ @@ -126,6 +123,9 @@ def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> L log_deprecation("'skips' kwarg has been deprecated!", "1.0.0") skips = [OVOSPipelineFactory._MAP.get(p, p) for p in skips] + for p in OVOSPipelineFactory.get_installed_pipelines(): + LOG.info(f"Found pipeline: {p}") + pipeline: List[str] = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline if p not in skips] From 03f281b0b61af49d7078313cc0caef261157f137 Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 12 Nov 2024 07:20:01 +0000 Subject: [PATCH 09/10] fix:tests --- test/end2end/routing/test_session.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/end2end/routing/test_session.py b/test/end2end/routing/test_session.py index f1cd842e3d3a..d338251d9ab8 100644 --- a/test/end2end/routing/test_session.py +++ b/test/end2end/routing/test_session.py @@ -3,7 +3,7 @@ from unittest import TestCase from ovos_utils.ocp import PlayerState, MediaState from ocp_pipeline.opm import OCPPlayerProxy - +from ovos_plugin_manager.pipeline import OVOSPipelineFactory from ovos_bus_client.message import Message from ovos_bus_client.session import SessionManager, Session from ..minicroft import get_minicroft @@ -93,7 +93,7 @@ def tearDown(self) -> None: self.core.stop() def test_no_session(self): - self.assertIsNotNone(self.core.intent_service._ocp) + self.assertIsNotNone(OVOSPipelineFactory._CACHE.get("ovos-ocp-pipeline-plugin-high")) messages = [] def new_msg(msg): @@ -121,7 +121,8 @@ def wait_for_n_messages(n): "converse", "ocp_high" ]) - self.core.intent_service._ocp.ocp_sessions[sess.session_id] = OCPPlayerProxy( + + OVOSPipelineFactory._CACHE["ovos-ocp-pipeline-plugin-high"].ocp_sessions[sess.session_id] = OCPPlayerProxy( session_id=sess.session_id, available_extractors=[], ocp_available=True, player_state=PlayerState.STOPPED, media_state=MediaState.NO_MEDIA) utt = Message("recognizer_loop:utterance", From 1e92278735edbafb43b36987ccab202a1ffca4cd Mon Sep 17 00:00:00 2001 From: miro Date: Wed, 13 Nov 2024 11:41:20 +0000 Subject: [PATCH 10/10] logs --- ovos_core/intent_services/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ovos_core/intent_services/__init__.py b/ovos_core/intent_services/__init__.py index 4fb47293bfce..5f87d9deebf3 100644 --- a/ovos_core/intent_services/__init__.py +++ b/ovos_core/intent_services/__init__.py @@ -46,6 +46,8 @@ def __init__(self, bus, config=None): self.config = config or Configuration().get("intents", {}) self.get_pipeline() # trigger initial load of pipeline plugins (more may be lazy loaded on demand) + for p, c in OVOSPipelineFactory._CACHE.items(): + LOG.info(f"Loaded pipeline: {p} - {c.__class__.__name__}") self.utterance_plugins = UtteranceTransformersService(bus) self.metadata_plugins = MetadataTransformersService(bus) @@ -123,9 +125,6 @@ def get_pipeline(self, skips=None, session=None, skip_stage_matchers=False) -> L log_deprecation("'skips' kwarg has been deprecated!", "1.0.0") skips = [OVOSPipelineFactory._MAP.get(p, p) for p in skips] - for p in OVOSPipelineFactory.get_installed_pipelines(): - LOG.info(f"Found pipeline: {p}") - pipeline: List[str] = [OVOSPipelineFactory._MAP.get(p, p) for p in session.pipeline if p not in skips]