From a829fce89385ee38c03d29fdeac538e48f17b401 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Fri, 6 Oct 2023 20:22:18 +0100 Subject: [PATCH] feat/dialog_transformers --- ovos_audio/service.py | 10 +++++ ovos_audio/transformers.py | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 ovos_audio/transformers.py diff --git a/ovos_audio/service.py b/ovos_audio/service.py index 65ceb3f..2ed3423 100644 --- a/ovos_audio/service.py +++ b/ovos_audio/service.py @@ -5,6 +5,7 @@ from ovos_audio.audio import AudioService from ovos_audio.playback import PlaybackThread from ovos_audio.tts import TTSFactory +from ovos_audio.transformers import DialogTransformersService from ovos_audio.utils import report_timing, validate_message_context from ovos_bus_client import Message, MessageBusClient from ovos_bus_client.session import SessionManager @@ -73,6 +74,7 @@ def __init__(self, ready_hook=on_ready, error_hook=on_error, self.bus = bus self.status.bind(self.bus) self.init_messagebus() + self.dialog_transform = DialogTransformersService(self.bus) try: self._maybe_reload_tts() @@ -269,6 +271,14 @@ def handle_speak(self, message): stopwatch.start() utterance = message.data['utterance'] + + # allow dialog transformers to rewrite speech + utt2 = self.dialog_transform.transform(utterance, sess) + if utterance != utt22: + LOG.debug(f"original dialog: {utterance}") + LOG.info(f"dialog transformed to: {utt2}") + utterance = utt2 + listen = message.data.get('expect_response', False) self.execute_tts(utterance, sess.session_id, listen, message) diff --git a/ovos_audio/transformers.py b/ovos_audio/transformers.py new file mode 100644 index 0000000..c6fb9dc --- /dev/null +++ b/ovos_audio/transformers.py @@ -0,0 +1,75 @@ +from ovos_plugin_manager.dialog_transformers import find_dialog_transformer_plugins +from ovos_utils.json_helper import merge_dict +from ovos_utils.log import LOG +from ovos_bus_client.session import Session, SessionManager + + +class DialogTransformersService: + """ transform dialogs before being sent to TTS """ + def __init__(self, bus, config=None): + self.loaded_plugins = {} + self.has_loaded = False + self.bus = bus + # to activate a plugin, just add an entry to mycroft.conf for it + self.config = config or Configuration().get("dialog_transformers", {}) + self.load_plugins() + + def load_plugins(self): + for plug_name, plug in find_dialog_transformer_plugins().items(): + if plug_name in self.config: + # if disabled skip it + if not self.config[plug_name].get("active", True): + continue + try: + self.loaded_plugins[plug_name] = plug() + self.loaded_plugins[plug_name].bind(self.bus) + LOG.info(f"loaded audio transformer plugin: {plug_name}") + except Exception as e: + LOG.exception(f"Failed to load audio transformer plugin: " + f"{plug_name}") + self.has_loaded = True + + @property + def plugins(self) -> list: + """ + Return loaded transformers in priority order, such that modules with a + higher `priority` rank are called first and changes from lower ranked + transformers are applied last. + + A plugin of `priority` 1 will override any existing context keys and + will be the last to modify `audio_data` + """ + return sorted(self.loaded_plugins.values(), + key=lambda k: k.priority, reverse=True) + + def shutdown(self): + """ + Shutdown all loaded plugins + """ + for module in self.plugins: + try: + module.shutdown() + except Exception as e: + LOG.warning(e) + + def transform(self, dialog: str, session: Session= None) -> str: + """ + Get transformed audio and context for the preceding audio + @param dialog: str to be spoken + @return: transformed dialog to be sent to TTS + """ + session = session or SessionManager.get() + + # TODO property not yet introduced in Session + # this will be set per Session/Persona + # active_transformers = session.dialog_transformers or self.plugins + active_transformers = self.plugins + + for module in active_transformers: + try: + LOG.debug(f"checking dialog transformer: {module}") + dialog = module.transform(dialog) + LOG.debug(f"{module.name}: {dialog}") + except Exception as e: + LOG.exception(e) + return dialog