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

fix:standardize_lang #568

Merged
merged 13 commits into from
Oct 16, 2024
2 changes: 2 additions & 0 deletions ovos_core/intent_services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from ovos_core.intent_services.stop_service import StopService
from ovos_core.transformers import MetadataTransformersService, UtteranceTransformersService
from ovos_plugin_manager.templates.pipeline import IntentMatch
from ovos_utils.lang import standardize_lang_tag
from ovos_utils.log import LOG, deprecated, log_deprecation
from ovos_utils.metrics import Stopwatch
from padacioso.opm import PadaciosoPipeline as PadaciosoService
Expand Down Expand Up @@ -352,6 +353,7 @@ def get_pipeline(self, skips=None, session=None) -> Tuple[str, Callable]:

def _validate_session(self, message, lang):
# get session
lang = standardize_lang_tag(lang)
sess = SessionManager.get(message)
if sess.session_id == "default":
updated = False
Expand Down
60 changes: 32 additions & 28 deletions ovos_core/intent_services/converse_service.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import time
from threading import Event
from typing import Optional
from typing import Optional, List

from ovos_bus_client.message import Message
from ovos_bus_client.session import SessionManager, UtteranceState
from ovos_bus_client.session import SessionManager, UtteranceState, Session
from ovos_bus_client.util import get_message_lang
from ovos_workshop.permissions import ConverseMode, ConverseActivationMode

from ovos_config.config import Configuration
from ovos_config.locale import setup_locale
from ovos_plugin_manager.templates.pipeline import IntentMatch, PipelinePlugin
from ovos_utils import flatten_list
from ovos_utils.lang import standardize_lang_tag
from ovos_utils.log import LOG
from ovos_workshop.permissions import ConverseMode, ConverseActivationMode


class ConverseService(PipelinePlugin):
Expand All @@ -26,14 +28,7 @@ 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)

@property
def config(self):
"""
Returns:
converse_config (dict): config for converse handling options
"""
return Configuration().get("skills", {}).get("converse") or {}
super().__init__(config=Configuration().get("skills", {}).get("converse") or {})

@property
def active_skills(self):
Expand All @@ -47,7 +42,8 @@ def active_skills(self, val):
for skill_id, ts in val:
session.activate_skill(skill_id)

def get_active_skills(self, message=None):
@staticmethod
def get_active_skills(message: Optional[Message] = None) -> List[str]:
"""Active skill ids ordered by converse priority
this represents the order in which converse will be called

Expand All @@ -57,12 +53,14 @@ def get_active_skills(self, message=None):
session = SessionManager.get(message)
return [skill[0] for skill in session.active_skills]

def deactivate_skill(self, skill_id, source_skill=None, message=None):
def deactivate_skill(self, skill_id: str, source_skill: Optional[str] = None,
message: Optional[Message] = None):
"""Remove a skill from being targetable by converse.

Args:
skill_id (str): skill to remove
source_skill (str): skill requesting the removal
message (Message): the bus message that requested deactivation
"""
source_skill = source_skill or skill_id
if self._deactivate_allowed(skill_id, source_skill):
Expand All @@ -81,7 +79,8 @@ def deactivate_skill(self, skill_id, source_skill=None, message=None):
if skill_id in self._consecutive_activations:
self._consecutive_activations[skill_id] = 0

def activate_skill(self, skill_id, source_skill=None, message=None):
def activate_skill(self, skill_id: str, source_skill: Optional[str] = None,
message: Optional[Message] = None) -> Optional[Session]:
"""Add a skill or update the position of an active skill.

The skill is added to the front of the list, if it's already in the
Expand All @@ -90,6 +89,7 @@ def activate_skill(self, skill_id, source_skill=None, message=None):
Args:
skill_id (str): identifier of skill to be added.
source_skill (str): skill requesting the removal
message (Message): the bus message that requested activation
"""
source_skill = source_skill or skill_id
if self._activate_allowed(skill_id, source_skill):
Expand All @@ -108,7 +108,7 @@ def activate_skill(self, skill_id, source_skill=None, message=None):
self._consecutive_activations[skill_id] += 1
return session

def _activate_allowed(self, skill_id, source_skill=None):
def _activate_allowed(self, skill_id: str, source_skill: Optional[str] = None) -> bool:
"""Checks if a skill_id is allowed to jump to the front of active skills list

- can a skill activate a different skill
Expand Down Expand Up @@ -166,7 +166,7 @@ def _activate_allowed(self, skill_id, source_skill=None):
return False # skill exceeded authorized consecutive number of activations
return True

def _deactivate_allowed(self, skill_id, source_skill=None):
def _deactivate_allowed(self, skill_id: str, source_skill: Optional[str] = None) -> bool:
"""Checks if a skill_id is allowed to be removed from active skills list

- can a skill deactivate a different skill
Expand All @@ -186,7 +186,7 @@ def _deactivate_allowed(self, skill_id, source_skill=None):
return False
return True

def _converse_allowed(self, skill_id):
def _converse_allowed(self, skill_id: str) -> bool:
"""Checks if a skill_id is allowed to converse

- is the skill blacklisted from conversing
Expand All @@ -208,7 +208,7 @@ def _converse_allowed(self, skill_id):
return False
return True

def _collect_converse_skills(self, message):
def _collect_converse_skills(self, message: Message) -> List[str]:
"""use the messagebus api to determine which skills want to converse
This includes all skills and external applications"""
session = SessionManager.get(message)
Expand Down Expand Up @@ -256,7 +256,7 @@ def handle_ack(msg):
self.bus.remove("skill.converse.pong", handle_ack)
return want_converse

def _check_converse_timeout(self, message):
def _check_converse_timeout(self, message: Message):
""" filter active skill list based on timestamps """
timeouts = self.config.get("skill_timeouts") or {}
def_timeout = self.config.get("timeout", 300)
Expand All @@ -265,7 +265,7 @@ def _check_converse_timeout(self, message):
skill for skill in session.active_skills
if time.time() - skill[1] <= timeouts.get(skill[0], def_timeout)]

def converse(self, utterances, skill_id, lang, message):
def converse(self, utterances: List[str], skill_id: str, lang: str, message: Message) -> bool:
"""Call skill and ask if they want to process the utterance.

Args:
Expand All @@ -278,6 +278,7 @@ def converse(self, utterances, skill_id, lang, message):
Returns:
handled (bool): True if handled otherwise False.
"""
lang = standardize_lang_tag(lang)
session = SessionManager.get(message)
session.lang = lang

Expand Down Expand Up @@ -312,7 +313,7 @@ def converse(self, utterances, skill_id, lang, message):
f'increasing "max_skill_runtime" in mycroft.conf might help alleviate this issue')
return False

def converse_with_skills(self, utterances, lang, message) -> Optional[IntentMatch]:
def converse_with_skills(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentMatch]:
"""Give active skills a chance at the utterance

Args:
Expand All @@ -323,6 +324,7 @@ def converse_with_skills(self, utterances, lang, message) -> Optional[IntentMatc
Returns:
IntentMatch if handled otherwise None.
"""
lang = standardize_lang_tag(lang)
session = SessionManager.get(message)

# we call flatten in case someone is sending the old style list of tuples
Expand All @@ -344,21 +346,23 @@ def converse_with_skills(self, utterances, lang, message) -> Optional[IntentMatc
utterance=utterances[0])
return None

def handle_get_response_enable(self, message):
@staticmethod
def handle_get_response_enable(message: Message):
skill_id = message.data["skill_id"]
session = SessionManager.get(message)
session.enable_response_mode(skill_id)
if session.session_id == "default":
SessionManager.sync(message)

def handle_get_response_disable(self, message):
@staticmethod
def handle_get_response_disable(message: Message):
skill_id = message.data["skill_id"]
session = SessionManager.get(message)
session.disable_response_mode(skill_id)
if session.session_id == "default":
SessionManager.sync(message)

def handle_activate_skill_request(self, message):
def handle_activate_skill_request(self, message: Message):
# TODO imperfect solution - only a skill can activate itself
# someone can forge this message and emit it raw, but in OpenVoiceOS all
# skill messages should have skill_id in context, so let's make sure
Expand All @@ -370,7 +374,7 @@ def handle_activate_skill_request(self, message):
if sess.session_id == "default":
SessionManager.sync(message)

def handle_deactivate_skill_request(self, message):
def handle_deactivate_skill_request(self, message: Message):
# TODO imperfect solution - only a skill can deactivate itself
# someone can forge this message and emit it raw, but in ovos-core all
# skill message should have skill_id in context, so let's make sure
Expand All @@ -382,17 +386,17 @@ def handle_deactivate_skill_request(self, message):
if sess.session_id == "default":
SessionManager.sync(message)

def reset_converse(self, message):
def reset_converse(self, message: Message):
"""Let skills know there was a problem with speech recognition"""
lang = get_message_lang(message)
lang = get_message_lang()
try:
setup_locale(lang) # restore default lang
except Exception as e:
LOG.exception(f"Failed to set lingua_franca default lang to {lang}")

self.converse_with_skills([], lang, message)

def handle_get_active_skills(self, message):
def handle_get_active_skills(self, message: Message):
"""Send active skills to caller.

Argument:
Expand Down
28 changes: 17 additions & 11 deletions ovos_core/intent_services/fallback_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
import operator
import time
from collections import namedtuple
from typing import Optional
from typing import Optional, List

from ovos_bus_client.message import Message
from ovos_bus_client.session import SessionManager
from ovos_workshop.permissions import FallbackMode

from ovos_config import Configuration
from ovos_plugin_manager.templates.pipeline import IntentMatch, PipelinePlugin
from ovos_utils import flatten_list
from ovos_utils.lang import standardize_lang_tag
from ovos_utils.log import LOG
from ovos_workshop.permissions import FallbackMode

FallbackRange = namedtuple('FallbackRange', ['start', 'stop'])

Expand All @@ -39,7 +42,7 @@ def __init__(self, bus):
self.bus.on("ovos.skills.fallback.deregister", self.handle_deregister_fallback)
super().__init__(self.fallback_config)

def handle_register_fallback(self, message):
def handle_register_fallback(self, message: Message):
skill_id = message.data.get("skill_id")
priority = message.data.get("priority") or 101

Expand All @@ -52,12 +55,12 @@ def handle_register_fallback(self, message):
else:
self.registered_fallbacks[skill_id] = priority

def handle_deregister_fallback(self, message):
def handle_deregister_fallback(self, message: Message):
skill_id = message.data.get("skill_id")
if skill_id in self.registered_fallbacks:
self.registered_fallbacks.pop(skill_id)

def _fallback_allowed(self, skill_id):
def _fallback_allowed(self, skill_id: str) -> bool:
"""Checks if a skill_id is allowed to fallback

- is the skill blacklisted from fallback
Expand All @@ -78,7 +81,8 @@ def _fallback_allowed(self, skill_id):
return False
return True

def _collect_fallback_skills(self, message, fb_range=FallbackRange(0, 100)):
def _collect_fallback_skills(self, message: Message,
fb_range: FallbackRange = FallbackRange(0, 100)) -> List[str]:
"""use the messagebus api to determine which skills have registered fallback handlers
This includes all skills and external applications"""
skill_ids = [] # skill_ids that already answered to ping
Expand Down Expand Up @@ -116,7 +120,7 @@ def handle_ack(msg):
self.bus.remove("ovos.skills.fallback.pong", handle_ack)
return fallback_skills

def attempt_fallback(self, utterances, skill_id, lang, message):
def attempt_fallback(self, utterances: List[str], skill_id: str, lang: str, message: Message) -> bool:
"""Call skill and ask if they want to process the utterance.

Args:
Expand Down Expand Up @@ -158,7 +162,8 @@ def attempt_fallback(self, utterances, skill_id, lang, message):
f'increasing "max_skill_runtime" in mycroft.conf might help alleviate this issue')
return False

def _fallback_range(self, utterances, lang, message, fb_range) -> Optional[IntentMatch]:
def _fallback_range(self, utterances: List[str], lang: str,
message: Message, fb_range: FallbackRange) -> Optional[IntentMatch]:
"""Send fallback request for a specified priority range.

Args:
Expand All @@ -171,6 +176,7 @@ def _fallback_range(self, utterances, lang, message, fb_range) -> Optional[Inten
Returns:
IntentMatch or None
"""
lang = standardize_lang_tag(lang)
# we call flatten in case someone is sending the old style list of tuples
utterances = flatten_list(utterances)
message.data["utterances"] = utterances # all transcripts
Expand All @@ -195,17 +201,17 @@ def _fallback_range(self, utterances, lang, message, fb_range) -> Optional[Inten
utterance=utterances[0])
return None

def high_prio(self, utterances, lang, message) -> Optional[IntentMatch]:
def high_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentMatch]:
"""Pre-padatious fallbacks."""
return self._fallback_range(utterances, lang, message,
FallbackRange(0, 5))

def medium_prio(self, utterances, lang, message) -> Optional[IntentMatch]:
def medium_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentMatch]:
"""General fallbacks."""
return self._fallback_range(utterances, lang, message,
FallbackRange(5, 90))

def low_prio(self, utterances, lang, message) -> Optional[IntentMatch]:
def low_prio(self, utterances: List[str], lang: str, message: Message) -> Optional[IntentMatch]:
"""Low prio fallbacks with general matching such as chat-bot."""
return self._fallback_range(utterances, lang, message,
FallbackRange(90, 101))
Expand Down
Loading
Loading