Skip to content

Commit

Permalink
companion to OpenVoiceOS/ovos-utils#193 - part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
JarbasAl committed Sep 21, 2023
1 parent f19fb3f commit 19ad4c6
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 104 deletions.
45 changes: 2 additions & 43 deletions ovos_workshop/decorators/compat.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,3 @@
from functools import wraps
# TODO - remove import next alpha
from ovos_utils import backwards_compat


def backwards_compat(classic_core=None, pre_008=None, no_core=None):
"""
Decorator to run a different method if specific ovos-core versions are detected
"""

def backwards_compat_decorator(func):
is_classic = False
is_old = False
is_standalone = True
try:
from mycroft.version import CORE_VERSION_STR # all classic mycroft and ovos versions
is_classic = True
is_standalone = False

try:
from ovos_core.version import OVOS_VERSION_MINOR # ovos-core >= 0.0.8
is_classic = False
except ImportError:
is_old = True
try:
from mycroft.version import OVOS_VERSION_MINOR # ovos-core <= 0.0.7
is_classic = False
except:
is_standalone = True

except:
is_standalone = True

@wraps(func)
def func_wrapper(*args, **kwargs):
if is_classic and callable(classic_core):
return classic_core(*args, **kwargs)
if is_old and callable(pre_008):
return pre_008(*args, **kwargs)
if is_standalone and callable(no_core):
return no_core(*args, **kwargs)
return func(*args, **kwargs)

return func_wrapper

return backwards_compat_decorator
3 changes: 3 additions & 0 deletions ovos_workshop/resource_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ def _read(self) -> str:
continue
yield line

def __iter__(self):
return iter(self._read())


class QmlFile(ResourceFile):
def _locate(self):
Expand Down
131 changes: 74 additions & 57 deletions ovos_workshop/skills/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from ovos_bus_client import MessageBusClient
from ovos_bus_client.message import Message, dig_for_message
from ovos_bus_client.session import SessionManager
from ovos_utils import backwards_compat
from ovos_utils import camel_case_split
from ovos_utils import classproperty
from ovos_utils.dialog import get_dialog, MustacheDialogRenderer
Expand All @@ -54,7 +55,6 @@
from ovos_utils.parse import match_one
from ovos_utils.process_utils import RuntimeRequirements
from ovos_utils.skills import get_non_properties
from ovos_workshop.decorators.compat import backwards_compat
from ovos_workshop.decorators.killable import AbortEvent
from ovos_workshop.decorators.killable import killable_event, \
AbortQuestion
Expand Down Expand Up @@ -846,9 +846,8 @@ def detach(self):
"""
Detach all intents for this skill from the intent_service.
"""
for (name, _) in self.intent_service:
name = f'{self.skill_id}:{name}'
self.intent_service.detach_intent(name)
for name in self.intent_service.intent_names:
self.intent_service.remove_intent(name)

def initialize(self):
"""
Expand Down Expand Up @@ -1540,16 +1539,7 @@ def remove_event(self, name: str) -> bool:
"""
return self.events.remove(name)

def _register_adapt_intent(self,
intent_parser: Union[IntentBuilder, Intent, str],
handler: callable):
"""
Register an adapt intent.
Args:
intent_parser: Intent object to parse utterance for the handler.
handler (func): function to register with intent
"""
def _validate_intent_name(self, intent_parser: Union[IntentBuilder, Intent], handler: callable):
# Default to the handler's function name if none given
is_anonymous = not intent_parser.name
name = intent_parser.name or handler.__name__
Expand All @@ -1560,15 +1550,48 @@ def _register_adapt_intent(self,
while name in self.intent_service.intent_names:
nbr += 1
name = f'{original_name}{nbr}'

elif name in self.intent_service.intent_names and \
not self.intent_service.intent_is_detached(name):
raise ValueError(f'The intent name {name} is already taken')
return name

def _register_adapt_intent_classic(self,
intent_parser: Union[IntentBuilder, Intent, str],
handler: callable):
"""
< ovos-core 0.0.8 munging happened skill side,
since 0.0.8 pipeline plugins are skill_id aware out of the box
any needed munging happens plugin side
"""
name = self._validate_intent_name(intent_parser, handler)

# old mycroft needs things munged before registering
munge_intent_parser(intent_parser, name, self.skill_id)
self.intent_service.register_adapt_intent(name, intent_parser)
if handler:
self.add_event(intent_parser.name, handler,
'mycroft.skill.handler')
self.add_event(intent_parser.name, handler, 'mycroft.skill.handler')

@backwards_compat(classic_core=_register_adapt_intent_classic,
pre_008=_register_adapt_intent_classic)
def _register_adapt_intent(self,
intent_parser: Union[IntentBuilder, Intent, str],
handler: callable):
"""
< ovos-core 0.0.8 munging happened skill side,
since 0.0.8 pipeline plugins are skill_id aware out of the box
any needed munging happens plugin side
"""
name = self._validate_intent_name(intent_parser, handler)

intent = self.intent_service.register_keyword_intent(name=name,
required=intent_parser.requires,
at_least_one=intent_parser.at_least_one,
optional=intent_parser.optional,
lang=self.lang,
skill_id=self.skill_id)
if handler:
self.add_event(intent.intent_message, handler, 'mycroft.skill.handler')

def register_intent(self, intent_parser: Union[IntentBuilder, Intent, str],
handler: callable):
Expand Down Expand Up @@ -1616,16 +1639,21 @@ def register_intent_file(self, intent_file: str, handler: callable):
handler: function to register with intent
"""
for lang in self._native_langs:
name = f'{self.skill_id}:{intent_file}'
resources = self._load_lang(self.res_dir, lang)
resource_file = ResourceFile(resources.types.intent, intent_file)
if resource_file.file_path is None:
self.log.error(f'Unable to find "{intent_file}"')
continue
filename = str(resource_file.file_path)
self.intent_service.register_padatious_intent(name, filename, lang)

samples = list(resource_file)

name = intent_file.replace(".intent", "") # not munged!
intent = self.intent_service.register_intent(name=name,
samples=samples,
lang=lang,
skill_id=self.skill_id)
if handler:
self.add_event(name, handler, 'mycroft.skill.handler')
self.add_event(intent.intent_message, handler, 'mycroft.skill.handler')

def register_entity_file(self, entity_file: str):
"""
Expand All @@ -1651,19 +1679,19 @@ def register_entity_file(self, entity_file: str):
if entity.file_path is None:
self.log.error(f'Unable to find "{entity_file}"')
continue
filename = str(entity.file_path)
name = f"{self.skill_id}:{basename(entity_file)}_" \
f"{md5(entity_file.encode('utf-8')).hexdigest()}"
self.intent_service.register_padatious_entity(name, filename, lang)
samples = list(entity)
name = basename(entity_file)
self.intent_service.register_entity(name=name, samples=samples,
lang=lang, skill_id=self.skill_id)

def handle_enable_intent(self, message: Message):
"""
Listener to enable a registered intent if it belongs to this skill.
@param message: `mycroft.skill.enable_intent` Message
"""
intent_name = message.data['intent_name']
for (name, _) in self.intent_service.detached_intents:
if name == intent_name:
for intent in self.intent_service.intents:
if intent.name == intent_name and intent.detached:
return self.enable_intent(intent_name)

def handle_disable_intent(self, message: Message):
Expand All @@ -1672,8 +1700,8 @@ def handle_disable_intent(self, message: Message):
@param message: `mycroft.skill.disable_intent` Message
"""
intent_name = message.data['intent_name']
for (name, _) in self.intent_service.registered_intents:
if name == intent_name:
for intent in self.intent_service.intents:
if intent.name == intent_name and not intent.detached:
return self.disable_intent(intent_name)

def disable_intent(self, intent_name: str) -> bool:
Expand All @@ -1686,16 +1714,9 @@ def disable_intent(self, intent_name: str) -> bool:
Returns:
bool: True if disabled, False if it wasn't registered
"""
if intent_name in self.intent_service:
if intent_name in self.intent_service.intent_names:
self.log.info('Disabling intent ' + intent_name)
name = f'{self.skill_id}:{intent_name}'
self.intent_service.detach_intent(name)

langs = [self._core_lang] + self._secondary_langs
for lang in langs:
lang_intent_name = f'{name}_{lang}'
self.intent_service.detach_intent(lang_intent_name)
return True
return self.intent_service.disable_intent(intent_name)
else:
self.log.error(f'Could not disable {intent_name}, it hasn\'t been registered.')
return False
Expand All @@ -1710,15 +1731,9 @@ def enable_intent(self, intent_name: str) -> bool:
Returns:
bool: True if enabled, False if it wasn't registered
"""
intent = self.intent_service.get_intent(intent_name)
if intent:
if ".intent" in intent_name:
self.register_intent_file(intent_name, None)
else:
intent.name = intent_name
self.register_intent(intent, None)
if intent_name in self.intent_service.intent_names:
self.log.debug(f'Enabling intent {intent_name}')
return True
return self.intent_service.enable_intent(intent_name)
else:
self.log.error(f'Could not enable {intent_name}, it hasn\'t been registered.')
return False
Expand All @@ -1738,7 +1753,7 @@ def set_context(self, context: str, word: str = '', origin: str = ''):
raise ValueError('Word should be a string')

context = self._alphanumeric_skill_id + context
self.intent_service.set_adapt_context(context, word, origin)
self.intent_service.set_context(context, word, origin)

def remove_context(self, context: str):
"""
Expand All @@ -1747,7 +1762,7 @@ def remove_context(self, context: str):
if not isinstance(context, str):
raise ValueError('context should be a string')
context = self._alphanumeric_skill_id + context
self.intent_service.remove_adapt_context(context)
self.intent_service.remove_context(context)

def handle_set_cross_context(self, message: Message):
"""
Expand Down Expand Up @@ -1803,10 +1818,10 @@ def register_vocabulary(self, entity: str, entity_type: str,
@param entity_type: Intent handler entity name to associate entity to
@param lang: language of `entity` (default self.lang)
"""
keyword_type = self._alphanumeric_skill_id + entity_type
lang = lang or self.lang
self.intent_service.register_adapt_keyword(keyword_type, entity,
lang=lang)
self.intent_service.register_keyword(name=entity_type,
samples=[entity],
lang=lang or self.lang,
skill_id=self.skill_id)

def register_regex(self, regex_str: str, lang: Optional[str] = None):
"""
Expand All @@ -1815,9 +1830,9 @@ def register_regex(self, regex_str: str, lang: Optional[str] = None):
@param lang: language of regex_str (default self.lang)
"""
self.log.debug('registering regex string: ' + regex_str)
regex = munge_regex(regex_str, self.skill_id)
re.compile(regex) # validate regex
self.intent_service.register_adapt_regex(regex, lang=lang or self.lang)
name = "" # TODO - get from regex_str
self.intent_service.register_regex_entity(name="", samples=[regex_str],
lang=lang or self.lang, skill_id=self.skill_id)

def speak(self, utterance: str, expect_response: bool = False,
wait: bool = False, meta: Optional[dict] = None):
Expand Down Expand Up @@ -1939,8 +1954,8 @@ def load_vocab_files(self, root_directory: Optional[str] = None):
for line in skill_vocabulary[vocab_type]:
entity = line[0]
aliases = line[1:]
self.intent_service.register_adapt_keyword(
vocab_type, entity, aliases, lang)
self.intent_service.register_keyword(name=vocab_type, samples=[entity] + aliases,
lang=lang, skill_id=self.skill_id)

def load_regex_files(self, root_directory=None):
""" Load regex files found under the skill directory."""
Expand All @@ -1950,7 +1965,9 @@ def load_regex_files(self, root_directory=None):
if resources.types.regex.base_directory is not None:
regexes = resources.load_skill_regex(self._alphanumeric_skill_id)
for regex in regexes:
self.intent_service.register_adapt_regex(regex, lang)
name = "" # TODO extract from regex
self.intent_service.register_regex_entity(name=name, samples=[regex],
lang=lang, skill_id=self.skill_id)

def __handle_stop(self, message):
"""Handler for the "mycroft.stop" signal. Runs the user defined
Expand Down
2 changes: 1 addition & 1 deletion ovos_workshop/skills/common_query_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from ovos_utils.file_utils import resolve_resource_file
from ovos_utils.log import LOG
from ovos_workshop.skills.ovos import OVOSSkill
from ovos_workshop.decorators.compat import backwards_compat
from ovos_utils import backwards_compat


class CQSMatchLevel(IntEnum):
Expand Down
2 changes: 1 addition & 1 deletion ovos_workshop/skills/fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from ovos_utils.messagebus import get_handler_name, Message
from ovos_utils.metrics import Stopwatch
from ovos_utils.skills import get_non_properties
from ovos_workshop.decorators.compat import backwards_compat
from ovos_utils import backwards_compat
from ovos_workshop.permissions import FallbackMode
from ovos_workshop.skills.ovos import OVOSSkill

Expand Down
2 changes: 1 addition & 1 deletion ovos_workshop/skills/mycroft_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from ovos_bus_client import MessageBusClient, Message
from ovos_utils.log import LOG, log_deprecation, deprecated
from ovos_workshop.decorators.compat import backwards_compat
from ovos_utils import backwards_compat
from ovos_workshop.skills.base import BaseSkill, is_classic_core


Expand Down
2 changes: 1 addition & 1 deletion ovos_workshop/skills/ovos.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ovos_utils.skills.audioservice import OCPInterface
from ovos_utils.skills.settings import PrivateSettings
from ovos_utils.sound import play_audio
from ovos_workshop.decorators.compat import backwards_compat
from ovos_utils import backwards_compat
from ovos_workshop.decorators.layers import IntentLayers
from ovos_workshop.resource_files import SkillResources
from ovos_workshop.skills.base import BaseSkill
Expand Down

0 comments on commit 19ad4c6

Please sign in to comment.