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:fallback_plugins #263

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 8 additions & 11 deletions ovos_plugin_manager/g2p.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@ def get_g2p_config(config: Optional[dict] = None) -> dict:


class OVOSG2PFactory:
""" replicates the base mycroft class, but uses only OPM enabled plugins"""
MAPPINGS = {
"dummy": "ovos-g2p-plugin-dummy",
"phoneme_guesser": "neon-g2p-plugin-phoneme-guesser",
"gruut": "neon-g2p-plugin-gruut"
}

@staticmethod
def get_class(config=None):
Expand All @@ -97,15 +91,13 @@ def get_class(config=None):
"""
config = get_g2p_config(config)
g2p_module = config.get("module") or 'dummy'
if g2p_module in OVOSG2PFactory.MAPPINGS:
g2p_module = OVOSG2PFactory.MAPPINGS[g2p_module]
if g2p_module == 'ovos-g2p-plugin-dummy':
if g2p_module == 'dummy':
return Grapheme2PhonemePlugin

return load_g2p_plugin(g2p_module)

@staticmethod
def create(config=None):
@classmethod
def create(cls, config=None):
"""Factory method to create a G2P engine based on configuration.

The configuration file ``mycroft.conf`` contains a ``g2p`` section with
Expand All @@ -117,11 +109,16 @@ def create(config=None):
"""
g2p_config = get_g2p_config(config)
g2p_module = g2p_config.get('module', 'dummy')
fallback = g2p_config.get("fallback_module")
try:
clazz = OVOSG2PFactory.get_class(g2p_config)
g2p = clazz(g2p_config)
LOG.debug(f'Loaded plugin {g2p_module}')
except Exception:
LOG.exception('The selected G2P plugin could not be loaded.')
if fallback in config and fallback != g2p_module:
LOG.info(f"Attempting to load fallback plugin instead: {fallback}")
config["module"] = fallback
return cls.create(config)
raise
return g2p
74 changes: 18 additions & 56 deletions ovos_plugin_manager/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,7 @@ def get_lang_detect_module_configs(module_name: str):
return load_plugin_configs(module_name, PluginConfigTypes.LANG_DETECT)


_fallback_lang_detect_plugin = "ovos-lang-detect-ngram-lm"
_fallback_translate_plugin = "ovos-translate-plugin-server"


class OVOSLangDetectionFactory:
"""
replicates the base neon class, but uses only OPM enabled plugins
"""
MAPPINGS = {
"libretranslate": "libretranslate_detection_plug",
"google": "googletranslate_detection_plug",
"amazon": "amazontranslate_detection_plug",
"cld2": "cld2_plug",
"cld3": "cld3_plug",
"langdetect": "langdetect_plug",
"fastlang": "fastlang_plug",
"lingua_podre": "lingua_podre_plug"
}

@staticmethod
def get_class(config=None):
Expand All @@ -120,12 +103,10 @@ def get_class(config=None):
lang_module = config.get("detection_module", config.get("module"))
if not lang_module:
raise ValueError("`language.detection_module` not configured")
if lang_module in OVOSLangDetectionFactory.MAPPINGS:
lang_module = OVOSLangDetectionFactory.MAPPINGS[lang_module]
return load_lang_detect_plugin(lang_module)

@staticmethod
def create(config=None) -> LanguageDetector:
@classmethod
def create(cls, config=None) -> LanguageDetector:
"""
Factory method to create a LangDetection engine based on configuration

Expand All @@ -140,37 +121,25 @@ def create(config=None) -> LanguageDetector:
if "language" in config:
config = config["language"]
lang_module = config.get("detection_module", config.get("module"))
cfg = config.get(lang_module, {})
fallback = cfg.get("fallback_module")
try:
clazz = OVOSLangDetectionFactory.get_class(config)
if clazz is None:
raise ValueError(f"Failed to load module: {lang_module}")
LOG.info(f'Loaded the Language Detection plugin {lang_module}')
if lang_module in OVOSLangDetectionFactory.MAPPINGS:
lang_module = OVOSLangDetectionFactory.MAPPINGS[lang_module]
return clazz(config=get_plugin_config(config, "language",
lang_module))
except Exception:
# The Language Detection backend failed to start, fall back if appropriate.
if lang_module != _fallback_lang_detect_plugin:
lang_module = _fallback_lang_detect_plugin
LOG.error(f'Language Detection plugin {lang_module} not found. '
f'Falling back to {_fallback_lang_detect_plugin}')
clazz = load_lang_detect_plugin(_fallback_lang_detect_plugin)
if clazz:
return clazz(config=get_plugin_config(config, "language",
lang_module))

LOG.exception(f'Language Detection plugin {lang_module} could not be loaded!')
if fallback in config and fallback != lang_module:
LOG.info(f"Attempting to load fallback plugin instead: {fallback}")
config["detection_module"] = fallback
return cls.create(config)
raise


class OVOSLangTranslationFactory:
""" replicates the base neon class, but uses only OPM enabled plugins"""
MAPPINGS = {
"libretranslate": "libretranslate_plug",
"google": "googletranslate_plug",
"amazon": "amazontranslate_plug",
"apertium": "apertium_plug"
}

@staticmethod
def get_class(config=None):
Expand All @@ -190,12 +159,10 @@ def get_class(config=None):
lang_module = config.get("translation_module", config.get("module"))
if not lang_module:
raise ValueError("`language.translation_module` not configured")
if lang_module in OVOSLangTranslationFactory.MAPPINGS:
lang_module = OVOSLangTranslationFactory.MAPPINGS[lang_module]
return load_tx_plugin(lang_module)

@staticmethod
def create(config=None) -> LanguageTranslator:
@classmethod
def create(cls, config=None) -> LanguageTranslator:
"""
Factory method to create a LangTranslation engine based on configuration

Expand All @@ -210,24 +177,19 @@ def create(config=None) -> LanguageTranslator:
if "language" in config:
config = config["language"]
lang_module = config.get("translation_module", config.get("module"))
cfg = config.get(lang_module, {})
fallback = cfg.get("fallback_module")
try:
clazz = OVOSLangTranslationFactory.get_class(config)
if clazz is None:
raise ValueError(f"Failed to load module: {lang_module}")
LOG.info(f'Loaded the Language Translation plugin {lang_module}')
if lang_module in OVOSLangTranslationFactory.MAPPINGS:
lang_module = OVOSLangTranslationFactory.MAPPINGS[lang_module]
return clazz(config=get_plugin_config(config, "language",
lang_module))
except Exception:
# The Language Translation backend failed to start, fall back if appropriate.
if lang_module != _fallback_translate_plugin:
lang_module = _fallback_translate_plugin
LOG.error(f'Language Translation plugin {lang_module} '
f'not found. Falling back to {_fallback_translate_plugin}')
clazz = load_tx_plugin(_fallback_translate_plugin)
if clazz:
return clazz(config=get_plugin_config(config, "language",
lang_module))

LOG.exception(f'Language Translation plugin {lang_module} could not be loaded!')
if fallback in config and fallback != lang_module:
LOG.info(f"Attempting to load fallback plugin instead: {fallback}")
config["translation_module"] = fallback
return cls.create(config)
raise
10 changes: 8 additions & 2 deletions ovos_plugin_manager/microphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ def get_class(config=None):
microphone_module = config.get("module")
return load_microphone_plugin(microphone_module)

@staticmethod
def create(config=None):
@classmethod
def create(cls, config=None):
"""Factory method to create a microphone engine based on configuration.

The configuration file ``mycroft.conf`` contains a ``microphone`` section with
Expand All @@ -62,16 +62,22 @@ def create(config=None):
"""
microphone_config = get_microphone_config(config)
microphone_module = microphone_config.get('module')
fallback = microphone_config.get("fallback_module")
try:
clazz = OVOSMicrophoneFactory.get_class(microphone_config)
# Note that configuration is expanded for this class of plugins
# since they are dataclasses and don't have the same init signature
# as other plugin types
microphone_config.pop('lang')
microphone_config.pop('module')
microphone_config.pop('fallback_module')
microphone = clazz(**microphone_config)
LOG.debug(f'Loaded microphone plugin {microphone_module}')
except Exception:
LOG.exception('The selected microphone plugin could not be loaded.')
if fallback in config and fallback != microphone_module:
LOG.info(f"Attempting to load fallback plugin instead: {fallback}")
config["module"] = fallback
return cls.create(config)
raise
return microphone
16 changes: 7 additions & 9 deletions ovos_plugin_manager/vad.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ def get_vad_config(config: dict = None) -> dict:


class OVOSVADFactory:
""" replicates the base mycroft class, but uses only OPM enabled plugins"""
MAPPINGS = {
"silero": "ovos-vad-plugin-silero",
"webrtcvad": "ovos-vad-plugin-webrtcvad"
}

@staticmethod
def get_class(config=None):
Expand All @@ -84,12 +79,10 @@ def get_class(config=None):
raise ValueError(f"VAD Plugin not configured in: {config}")
if vad_module == "dummy":
return VADEngine
if vad_module in OVOSVADFactory.MAPPINGS:
vad_module = OVOSVADFactory.MAPPINGS[vad_module]
return load_vad_plugin(vad_module)

@staticmethod
def create(config=None):
@classmethod
def create(cls, config=None):
"""Factory method to create a VAD engine based on configuration.

The configuration file ``mycroft.conf`` contains a ``VAD`` section with
Expand All @@ -101,6 +94,7 @@ def create(config=None):
"""
vad_config = get_vad_config(config)
plugin = vad_config.get("module")
fallback = vad_config.get("fallback_module")
if not plugin:
raise ValueError(f"VAD Plugin not configured in: {vad_config}")
try:
Expand All @@ -111,4 +105,8 @@ def create(config=None):
return clazz(plugin_config)
except Exception:
LOG.exception(f'VAD plugin {plugin} could not be loaded!')
if fallback in config and fallback != plugin:
LOG.info(f"Attempting to load fallback plugin instead: {fallback}")
config["module"] = fallback
return cls.create(config)
raise
10 changes: 0 additions & 10 deletions ovos_plugin_manager/wakewords.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,6 @@ def get_wws(scan=False):


class OVOSWakeWordFactory:
""" replicates the base mycroft class, but uses only OPM enabled plugins"""
MAPPINGS = {
"dummy": "ovos-ww-plugin-dummy",
"pocketsphinx": "ovos-ww-plugin-pocketsphinx",
"precise": "ovos-ww-plugin-precise",
"snowboy": "ovos-ww-plugin-snowboy",
"porcupine": "porcupine_wakeword_plug"
}

@staticmethod
def get_class(hotword: str, config: Optional[dict] = None) -> type:
Expand All @@ -128,8 +120,6 @@ def get_class(hotword: str, config: Optional[dict] = None) -> type:
f"Returning base HotWordEngine")
return HotWordEngine
ww_module = hotword_config[hotword]["module"]
if ww_module in OVOSWakeWordFactory.MAPPINGS:
ww_module = OVOSWakeWordFactory.MAPPINGS[ww_module]
return load_wake_word_plugin(ww_module)

@staticmethod
Expand Down
Loading