Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/pipeline #336

Merged
merged 5 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions mycroft/skills/intent_services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from ovos_core.intent_services import AdaptService,\
PadatiousService, PadatiousMatcher, \
ConverseService,\
CommonQAService, \
FallbackService
FallbackService, \
PadaciosoService, \
PadatiousService
from ovos_core.intent_services import IntentMatch
from mycroft.skills.intent_services.adapt_service import AdaptIntent, IntentBuilder, Intent

try: # TODO -remove backwards compat import, before 0.0.8, ovos_core module didnt make it into a stable release yet!
from ovos_core.intent_services import PadatiousMatcher
except ImportError:
from ovos_utils.log import LOG
LOG.warning("padatious not installed")
2 changes: 1 addition & 1 deletion mycroft/skills/intent_services/padatious_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
# limitations under the License.
#
"""Intent service wrapping padatious."""
from ovos_core.intent_services.padatious_service import PadatiousMatcher, PadatiousService, PadatiousIntent, FallbackIntentContainer
from ovos_core.intent_services.padatious_service import PadatiousMatcher, PadatiousService, PadatiousIntent

122 changes: 77 additions & 45 deletions ovos_core/intent_services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,24 @@

from ovos_config.config import Configuration
from ovos_config.locale import setup_locale

from ovos_core.transformers import MetadataTransformersService, UtteranceTransformersService
from ovos_core.intent_services.adapt_service import AdaptService
from ovos_core.intent_services.commonqa_service import CommonQAService
from ovos_core.intent_services.converse_service import ConverseService
from ovos_core.intent_services.fallback_service import FallbackService
from ovos_core.intent_services.padatious_service import PadatiousService, PadatiousMatcher
from ovos_utils.intents.intent_service_interface import open_intent_envelope
from ovos_utils.log import LOG
from ovos_utils.messagebus import get_message_lang
from ovos_utils.metrics import Stopwatch
from ovos_utils.sound import play_error_sound

from ovos_core.intent_services.adapt_service import AdaptService
from ovos_core.intent_services.commonqa_service import CommonQAService
from ovos_core.intent_services.converse_service import ConverseService
from ovos_core.intent_services.fallback_service import FallbackService
from ovos_core.intent_services.padacioso_service import PadaciosoService
from ovos_core.transformers import MetadataTransformersService, UtteranceTransformersService

try:
from ovos_core.intent_services.padatious_service import PadatiousService, PadatiousMatcher
except ImportError:
from ovos_core.intent_services.padacioso_service import PadaciosoService as PadatiousService

# Intent match response tuple containing
# intent_service: Name of the service that matched the intent
# intent_type: intent name (used to call intent handler over the message bus)
Expand Down Expand Up @@ -56,10 +61,12 @@ def __init__(self, bus):

# TODO - replace with plugins
self.adapt_service = AdaptService(config.get('context', {}))
try:
if PadaciosoService is not PadatiousService:
self.padatious_service = PadatiousService(bus, config['padatious'])
except Exception as err:
LOG.exception(f'Failed to create padatious handlers ({err})')
else:
LOG.error(f'Failed to create padatious handlers, padatious not installed')
self.padatious_service = None
self.padacioso_service = PadaciosoService(bus, config['padatious'])
self.fallback = FallbackService(bus)
self.converse = ConverseService(bus)
self.common_qa = CommonQAService(bus)
Expand Down Expand Up @@ -105,6 +112,22 @@ def __init__(self, bus):
self.bus.on('intent.service.padatious.entities.manifest.get',
self.handle_entity_manifest)

@property
def pipeline(self):
# List of functions to use to match the utterance with intent, listed in priority order.
config = Configuration().get("intents") or {}
return config.get("pipeline", [
"converse",
"padacioso_high",
"adapt",
"common_qa",
"fallback_high",
"padacioso_medium",
"fallback_medium",
"padacioso_low",
"fallback_low"
])

@property
def registered_intents(self):
lang = get_message_lang()
Expand Down Expand Up @@ -199,6 +222,40 @@ def disambiguate_lang(message):

return default_lang

def get_pipeline(self, skips=None):
"""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"""

# Create matchers
# TODO - from plugins
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will solve this TODO in #310

for now just want to make it configurable to clean up padatious/padacioso and allow more control for users

if self.padatious_service is None:
if any("padatious" in p for p in self.pipeline):
LOG.warning("padatious is not available! using padacioso in it's place")
padatious_matcher = self.padacioso_service
else:
from ovos_core.intent_services.padatious_service import PadatiousMatcher
padatious_matcher = PadatiousMatcher(self.padatious_service)

matchers = {
"converse": self.converse.converse_with_skills,
"padatious_high": padatious_matcher.match_high,
"padacioso_high": self.padacioso_service.match_high,
"adapt": self.adapt_service.match_intent,
"common_qa": self.common_qa.match,
"fallback_high": self.fallback.high_prio,
"padatious_medium": padatious_matcher.match_medium,
"padacioso_medium": self.padacioso_service.match_medium,
"fallback_medium": self.fallback.medium_prio,
"padatious_low": padatious_matcher.match_low,
"padacioso_low": self.padacioso_service.match_low,
"fallback_low": self.fallback.low_prio
}
skips = skips or []
pipeline = [k for k in self.pipeline if k not in skips]
return [matchers[k] for k in pipeline]

def handle_utterance(self, message):
"""Main entrypoint for handling user utterances

Expand Down Expand Up @@ -242,24 +299,11 @@ def handle_utterance(self, message):

stopwatch = Stopwatch()

# Create matchers
padatious_matcher = PadatiousMatcher(self.padatious_service)

# List of functions to use to match the utterance with intent.
# These are listed in priority order.
match_funcs = [
self.converse.converse_with_skills, padatious_matcher.match_high,
self.adapt_service.match_intent, self.common_qa.match,
self.fallback.high_prio, padatious_matcher.match_medium,
self.fallback.medium_prio, padatious_matcher.match_low,
self.fallback.low_prio
]

# match
match = None
with stopwatch:
# Loop through the matching functions until a match is found.
for match_func in match_funcs:
for match_func in self.get_pipeline():
match = match_func(utterances, lang, message)
if match:
break
Expand All @@ -269,6 +313,7 @@ def handle_utterance(self, message):

if match.skill_id:
self.converse.activate_skill(match.skill_id)
message.context["skill_id"] = match.skill_id
# If the service didn't report back the skill_id it
# takes on the responsibility of making the skill "active"

Expand Down Expand Up @@ -392,24 +437,11 @@ def handle_get_intent(self, message):
utterance = message.data["utterance"]
lang = get_message_lang(message)

# Create matchers
padatious_matcher = PadatiousMatcher(self.padatious_service)

# List of functions to use to match the utterance with intent.
# These are listed in priority order.
# TODO once we have a mechanism for checking if a fallback will
# trigger without actually triggering it, those should be added here
match_funcs = [
padatious_matcher.match_high,
self.adapt_service.match_intent,
# self.fallback.high_prio,
padatious_matcher.match_medium,
# self.fallback.medium_prio,
padatious_matcher.match_low,
# self.fallback.low_prio
]
# Loop through the matching functions until a match is found.
for match_func in match_funcs:
for match_func in self.get_pipeline(skips=["converse",
"fallback_high",
"fallback_medium",
"fallback_low"]):
match = match_func([utterance], lang, message)
if match:
if match.intent_type:
Expand Down Expand Up @@ -483,9 +515,9 @@ def handle_get_padatious(self, message):
"""
utterance = message.data["utterance"]
norm = message.data.get('norm_utt', utterance)
intent = self.padatious_service.calc_intent(utterance)
intent = self.padacioso_service.calc_intent(utterance)
if not intent and norm != utterance:
intent = self.padatious_service.calc_intent(norm)
intent = self.padacioso_service.calc_intent(norm)
if intent:
intent = intent.__dict__
self.bus.emit(message.reply("intent.service.padatious.reply",
Expand All @@ -499,7 +531,7 @@ def handle_padatious_manifest(self, message):
"""
self.bus.emit(message.reply(
"intent.service.padatious.manifest",
{"intents": self.padatious_service.registered_intents}))
{"intents": self.padacioso_service.registered_intents}))

def handle_entity_manifest(self, message):
"""Messagebus handler returning the registered padatious entities.
Expand All @@ -509,7 +541,7 @@ def handle_entity_manifest(self, message):
"""
self.bus.emit(message.reply(
"intent.service.padatious.entities.manifest",
{"entities": self.padatious_service.registered_entities}))
{"entities": self.padacioso_service.registered_entities}))


def _is_old_style_keyword_message(message):
Expand Down
6 changes: 4 additions & 2 deletions ovos_core/intent_services/converse_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def deactivate_skill(self, skill_id, source_skill=None):
self.active_skills.pop(idx)
self.bus.emit(
Message("intent.service.skills.deactivated",
{"skill_id": skill_id}))
data={"skill_id": skill_id},
context={"skill_id": skill_id}))
if skill_id in self._consecutive_activations:
self._consecutive_activations[skill_id] = 0

Expand All @@ -75,7 +76,8 @@ def activate_skill(self, skill_id, source_skill=None):
self.active_skills.insert(0, [skill_id, time.time()])
self.bus.emit(
Message("intent.service.skills.activated",
{"skill_id": skill_id}))
data={"skill_id": skill_id},
context={"skill_id": skill_id}))

self._consecutive_activations[skill_id] += 1

Expand Down
Loading
Loading