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 factory #15

Merged
merged 11 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
147 changes: 120 additions & 27 deletions ovos_padatious/opm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,30 @@
from functools import lru_cache
from os.path import expanduser, isfile
from threading import Event
from typing import List, Optional
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, Session
from ovos_config.config import Configuration
from ovos_config.meta import get_xdg_base
from ovos_padatious import IntentContainer as PadatiousIntentContainer
from ovos_padatious.match_data import MatchData as PadatiousIntent
from ovos_plugin_manager.templates.pipeline import ConfidenceMatcherPipeline, IntentHandlerMatch, IntentMatch
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_utils.xdg_utils import xdg_data_home
from ovos_plugin_manager.templates.pipeline import PipelinePlugin, IntentMatch
from langcodes import closest_match

from ovos_padatious import IntentContainer as PadatiousIntentContainer
from ovos_padatious.match_data import MatchData as PadatiousIntent


class PadatiousMatcher:
"""Matcher class to avoid redundancy in padatious intent matching."""

def __init__(self, service):
@deprecated("PadatiousMatcher class is deprecated!", "2.0.0")
def __init__(self, service: 'PadatiousPipeline'):
self.service = service

def _match_level(self, utterances, limit, lang=None, message: Optional[Message] = None) -> Optional[IntentMatch]:
Expand All @@ -46,16 +50,8 @@ def _match_level(self, utterances, limit, lang=None, message: Optional[Message]
with optional normalized version.
limit (float): required confidence level.
"""
LOG.debug(f'Padatious Matching confidence > {limit}')
# call flatten in case someone is sending the old style list of tuples
utterances = flatten_list(utterances)
lang = standardize_lang_tag(lang or self.service.lang)
padatious_intent = self.service.calc_intent(utterances, lang, message)
if padatious_intent is not None and padatious_intent.conf > limit:
skill_id = padatious_intent.name.split(':')[0]
return IntentMatch(
'Padatious', padatious_intent.name,
padatious_intent.matches, skill_id, padatious_intent.sent)
m: IntentHandlerMatch = self.service._match_level(utterances, limit, lang, message)
return IntentMatch("Padatious", m.match_type, m.match_data, m.skill_id, m.utterance)

def match_high(self, utterances, lang=None, message=None) -> Optional[IntentMatch]:
"""Intent matcher for high confidence.
Expand Down Expand Up @@ -85,13 +81,13 @@ def match_low(self, utterances, lang=None, message=None) -> Optional[IntentMatch
return self._match_level(utterances, self.service.conf_low, lang, message)


class PadatiousPipeline(PipelinePlugin):
class PadatiousPipeline(ConfidenceMatcherPipeline):
"""Service class for padatious intent matching."""

def __init__(self, bus, config):
super().__init__(config)
self.padatious_config = config
self.bus = bus
def __init__(self, bus: Optional[Union[MessageBusClient, FakeBus]] = None,
config: Optional[Dict] = None):

super().__init__(bus, config)

core_config = Configuration()
self.lang = standardize_lang_tag(core_config.get("lang", "en-US"))
Expand All @@ -100,11 +96,11 @@ def __init__(self, bus, config):
if self.lang not in langs:
langs.append(self.lang)

self.conf_high = self.padatious_config.get("conf_high") or 0.95
self.conf_med = self.padatious_config.get("conf_med") or 0.8
self.conf_low = self.padatious_config.get("conf_low") or 0.5
self.conf_high = self.config.get("conf_high") or 0.95
self.conf_med = self.config.get("conf_med") or 0.8
self.conf_low = self.config.get("conf_low") or 0.5
JarbasAl marked this conversation as resolved.
Show resolved Hide resolved

intent_cache = expanduser(self.padatious_config.get('intent_cache') or
intent_cache = expanduser(self.config.get('intent_cache') or
f"{xdg_data_home()}/{get_xdg_base()}/intent_cache")
self.containers = {lang: PadatiousIntentContainer(f"{intent_cache}/{lang}")
for lang in langs}
Expand All @@ -114,6 +110,9 @@ def __init__(self, bus, config):
self.bus.on('detach_intent', self.handle_detach_intent)
self.bus.on('detach_skill', self.handle_detach_skill)
self.bus.on('mycroft.skills.initialized', self.train)
self.bus.on('intent.service.padatious.get', self.handle_get_padatious)
self.bus.on('intent.service.padatious.manifest.get', self.handle_padatious_manifest)
self.bus.on('intent.service.padatious.entities.manifest.get', self.handle_entity_manifest)
JarbasAl marked this conversation as resolved.
Show resolved Hide resolved

self.finished_training_event = Event()
self.finished_initial_train = False
Expand All @@ -123,14 +122,72 @@ def __init__(self, bus, config):
self.max_words = 50 # if an utterance contains more words than this, don't attempt to match
LOG.debug('Loaded Padatious intent parser.')

@property
def padatious_config(self) -> Dict:
log_deprecation("self.padatious_config is deprecated, access self.config directly instead", "2.0.0")
return self.config

@padatious_config.setter
def padatious_config(self, val):
log_deprecation("self.padatious_config is deprecated, access self.config directly instead", "2.0.0")
self.config = val

JarbasAl marked this conversation as resolved.
Show resolved Hide resolved
def _match_level(self, utterances, limit, lang=None, message: Optional[Message] = None) -> Optional[IntentHandlerMatch]:
"""Match intent and make sure a certain level of confidence is reached.

Args:
utterances (list of tuples): Utterances to parse, originals paired
with optional normalized version.
limit (float): required confidence level.
"""
LOG.debug(f'Padatious Matching confidence > {limit}')
# call flatten in case someone is sending the old style list of tuples
utterances = flatten_list(utterances)
lang = standardize_lang_tag(lang or self.lang)
padatious_intent = self.calc_intent(utterances, lang, message)
if padatious_intent is not None and padatious_intent.conf > limit:
skill_id = padatious_intent.name.split(':')[0]
return IntentHandlerMatch(
match_type=padatious_intent.name,
match_data=padatious_intent.matches,
skill_id=skill_id,
utterance=padatious_intent.sent)

def match_high(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentHandlerMatch]:
"""Intent matcher for high confidence.

Args:
utterances (list of tuples): Utterances to parse, originals paired
with optional normalized version.
"""
return self._match_level(utterances, self.conf_high, lang, message)

def match_medium(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentHandlerMatch]:
"""Intent matcher for medium confidence.

Args:
utterances (list of tuples): Utterances to parse, originals paired
with optional normalized version.
"""
return self._match_level(utterances, self.conf_med, lang, message)

def match_low(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentHandlerMatch]:
"""Intent matcher for low confidence.

Args:
utterances (list of tuples): Utterances to parse, originals paired
with optional normalized version.
"""
return self._match_level(utterances, self.conf_low, lang, message)

def train(self, message=None):
"""Perform padatious training.

Args:
message (Message): optional triggering message
"""
self.finished_training_event.clear()
padatious_single_thread = self.padatious_config.get('single_thread', False)
padatious_single_thread = self.config.get('single_thread', False)
if message is None:
single_thread = padatious_single_thread
else:
Expand Down Expand Up @@ -284,6 +341,42 @@ def shutdown(self):
self.bus.remove('detach_skill', self.handle_detach_skill)
self.bus.remove('mycroft.skills.initialized', self.train)

def handle_get_padatious(self, message):
"""messagebus handler for perfoming padatious parsing.

Args:
message (Message): message triggering the method
"""
utterance = message.data["utterance"]
norm = message.data.get('norm_utt', utterance)
intent = self.calc_intent(utterance)
if not intent and norm != utterance:
intent = self.calc_intent(norm)
if intent:
intent = intent.__dict__
self.bus.emit(message.reply("intent.service.padatious.reply",
{"intent": intent}))

JarbasAl marked this conversation as resolved.
Show resolved Hide resolved
def handle_padatious_manifest(self, message):
"""Messagebus handler returning the registered padatious intents.

Args:
message (Message): message triggering the method
"""
self.bus.emit(message.reply(
"intent.service.padatious.manifest",
{"intents": self.registered_intents}))

def handle_entity_manifest(self, message):
"""Messagebus handler returning the registered padatious entities.

Args:
message (Message): message triggering the method
"""
self.bus.emit(message.reply(
"intent.service.padatious.entities.manifest",
{"entities": self.registered_entities}))


@lru_cache(maxsize=3) # repeat calls under different conf levels wont re-run code
def _calc_padatious_intent(utt: str,
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
fann2>=1.0.7, < 1.1.0
xxhash
ovos-plugin-manager>=0.0.26
ovos-plugin-manager>=0.5.0,<1.0.0
ovos-workshop>=0.1.7,<2.0.0
ovos-utils>=0.3.4,<1.0.0
langcodes
Loading