Skip to content

Transformer Plugins

In a nutshell

Your voice travels through the assistant: from raw sound, to written words, to a matched request, to the spoken reply. Transformer plugins are optional helpers that can tidy or tweak the information at each step. Think of them as filters on an assembly line. One might clean up background noise. Another might fix a misheard word before the system tries to understand it. They don't take over any step. They just polish what passes between steps. See the Glossary for related terms.

Formal specification

The transformer subsystem is specified by OVOS-TRANSFORM-1: Transformer Plugins (one of the formal architecture specs). It defines six ordered chains: audio, utterance, metadata, intent, dialog, tts. These run at fixed points in the utterance lifecycle. It also defines the per-type input/output contract for each, the per-session ordering and denylist overrides, and the utterance-cancellation signal. A transformer is identified by its (type, transformer_id) pair. Where this manual or the current code diverges from the spec, the spec is canonical.

Ordering. OVOS-TRANSFORM-1 §4 orders each chain by ascending priority. A lower number runs earlier, and the default is 50. The current OVOS code follows this: a plugin with priority=1 runs first, and later plugins see and may override its output. A legacy descending order is still available as an explicit opt-in (sort_ascending=False, marked deprecated in ovos_plugin_manager/transformer_services.py) for deployments that depend on the old behavior.

Transformer plugins let you intercept and modify data as it flows through the transformer chain. Each type is a small class with a transform() method that runs at a fixed stage. Examples are turning raw audio into cleaner audio, fixing transcribed text before intent matching, enriching a matched intent, or post-processing speech before playback.

A transformer never replaces a stage. It sits between two stages and reshapes what passes through. Several plugins of the same type can be active at once. They run in sequence, lowest priority first, so each one builds on the output of the previous.

Synchronous contract: keep transformers fast

transform() (and on_audio()/on_speech()) are plain synchronous methods. The chain runner calls each one inline, in order, on the thread that owns the chain. A slow transformer blocks the owning service for its full duration. There is no background execution or timeout. This matters most for AudioTransformer, which sits on the real-time audio path. Keep its work fast, and offload anything heavy (model inference, network calls) to a background thread/process instead of doing it inside transform().

There is no async return path back into the chain. transform() is called inline and must return before the chain can proceed, so it cannot await a background job's result mid-chain. "Offload the heavy work" only helps if transform() can return cached/previous data immediately on each call, while the background thread/process updates that cache for the next call to pick up. The current call never blocks on the background job finishing.

Transformer Types

All base classes live in ovos_plugin_manager.templates.transformers and share the same constructor: __init__(self, name, priority=50, config=None), plus bind(bus) and initialize(). The loader (TransformersService.load_plugins() in ovos_plugin_manager.transformer_services) only ever instantiates a plugin as plug(config=plugin_config). It does not pass name or priority. Since the base class has no default for name, every plugin must override __init__ to supply its own name (and usually a default priority). Each plugin must call super().__init__(name, priority, config) so the base class still gets them.

A "priority" key in a plugin's mycroft.conf block is not applied automatically. The plugin must read it back out of self.config itself if it wants deployments to override priority (see Utterance Transformers: Config-driven priority).

Config shape (the six chained types)

Six of the seven sections below are chains: every loaded plugin runs, one after another. The seventh, typed_slots_transformers, reads its section the same way but runs only one plugin; see Typed-slots transformers for where it differs.

Every chain's mycroft.conf section follows the same rules (TransformersService.load_plugins() in ovos_plugin_manager.transformer_services):

  • Each key in the section is an enabled plugin's entry-point name, except the two reserved keys order and blacklisted_skills, which are not plugin entries.
  • A plugin block with "active": false is still loaded but skipped when the chain runs.
  • An "order" key, if present, is a list of plugin names giving the run order explicitly. It wins over ascending-priority sorting; a loaded plugin absent from the list is not run. Plugins named in order are treated as enabled even with no config block of their own.

Two runtime behaviors from OVOS-TRANSFORM-1 apply in the shared service bases (transformer_services.py):

  • Provenance stamping (§1.3), utterance and metadata chains only: as each transformer runs, its name is appended to a list in the message context (utterance_transformer_ids, metadata_transformer_ids), so downstream consumers can see which plugins touched the data and in what order. The intent chain does not stamp provenance.
  • Wrong-shape rejection (§7): a transformer that returns the wrong tuple shape or a non-dict context is logged with a warning and its output discarded — the chain continues with the previous output. An intent transformer that mutates match_type or skill_id is discarded the same way (§3.4). If your transformer seems to have no effect, check the service log for a "returned wrong shape" warning.
{
  "utterance_transformers": {
    "order": ["ovos-utterance-cancel-plugin", "ovos-utterance-normalizer"],
    "ovos-utterance-cancel-plugin": {},
    "ovos-utterance-normalizer": {}
  }
}
Type Stage Base Class Entry-point group
Audio Before STT AudioTransformer opm.transformer.audio
Utterance After STT, before Intent UtteranceTransformer opm.transformer.text
Metadata After Utterance, before Intent MetadataTransformer opm.transformer.metadata
Intent After Intent match, before Skill IntentTransformer opm.transformer.intent
Dialog Before TTS DialogTransformer opm.transformer.dialog
TTS After TTS, before Playback TTSTransformer opm.transformer.tts
Typed slots After Metadata, before the first matcher TypedSlotsTransformer opm.transformer.typed_slots

ovos-plugin-manager also honors the deprecated Neon entry-point groups neon.plugin.text, neon.plugin.metadata and neon.plugin.audio as aliases for opm.transformer.text, opm.transformer.metadata and opm.transformer.audio (with a deprecation warning). There is no such alias for the intent, dialog, TTS or typed-slots groups — a plugin registered only under a legacy name for those four groups is not discovered.

The runner classes that load and chain these plugins live in ovos-plugin-manager (ovos_plugin_manager.transformer_services): UtteranceTransformersService, MetadataTransformersService, IntentTransformersService, AudioTransformersService, DialogTransformersService, TTSTransformersService. Each consumer imports the one it needs. ovos-core runs the utterance/metadata/intent chains, the listener runs the audio chain, and the audio/TTS stacks run the dialog/TTS chains. The typed-slots stage has its own runner, TypedSlotsTransformersService in ovos_core.transformers, because it selects rather than chains.


1. Audio Transformers

Entry point: opm.transformer.audio

Used to process or transform raw audio before it reaches the STT engine. Common use cases include noise reduction, volume normalization, or streaming language detection.

Template

from ovos_plugin_manager.templates.transformers import AudioTransformer

class MyAudioTransformer(AudioTransformer):
    def on_audio(self, audio_data):
        # Process non-speech chunks
        return audio_data

    def on_speech(self, audio_data):
        # Process speech chunks during recording
        return audio_data

    def transform(self, audio_data):
        # Final transformation and optional context injection
        return audio_data, {"extra_metadata": "value"}

2. Utterance Transformers

Entry point: opm.transformer.text

Used to modify the transcribed text (utterances) before they are sent to the intent service. Common use cases include spelling correction, filtering, or expansion. See Utterance Transformers for details.

Template

from ovos_plugin_manager.templates.transformers import UtteranceTransformer

class MyUtteranceTransformer(UtteranceTransformer):
    def transform(self, utterances, context=None):
        # utterances is a list of strings
        transformed = [u.upper() for u in utterances]
        return transformed, context

3. Metadata Transformers

Entry point: opm.transformer.metadata

Used to inject or modify metadata in the message context. This runs after utterance transformers but before intent matching. transform(context) returns a (possibly modified) context dict.


4. Intent Transformers

Entry point: opm.transformer.intent

Used to modify the IntentHandlerMatch object. This runs after a pipeline match is found but before the skill is triggered. See Intent Transformers for details.


5. Dialog Transformers

Entry point: opm.transformer.dialog

Used to modify the text that OVOS is about to speak, just before it is sent to the TTS engine.


6. TTS Transformers

Entry point: opm.transformer.tts

Used to process the generated WAV file after TTS synthesis but before it is played back.


7. Typed-slots Transformers

Entry point: opm.transformer.typed_slots

Computes the typed-slots map for an utterance: the number, date, duration and colour spans that intents ask for with the {type:name} syntax. It runs after the utterance and metadata chains and before the first matcher, so a matcher and a skill handler both see the same resolved values. Skill authors read the result with self.typed_slot(), described in Padatious Intents: Typed slots.

ovos-typed-slots-transformer is the plugin that ships in the bundled mycroft.conf, and it uses the OVOS parser libraries to do the work.

This stage reads its config section like the six chains above, but it does not chain. Four differences matter to a plugin author:

  • One plugin runs, not all of them. The stage produces a single map, so the runner picks one plugin and ignores the rest: the first name in an explicit order list, or otherwise the lowest priority number. Two plugins loaded at the same priority with no order list is a warning, and the choice between them is stable but unspecified.
  • A different transform() signature. It takes the candidate utterances, the set of types the registered intents declared, and the Session, and it returns a map of type name to a list of {"span": [start, end], "surface": str, "value": ...} entries. It must not touch utterances or Message.context; that is an utterance or metadata transformer's job.
  • supported_types describes, it does not gate. A plugin declares the types it can compute, and the runner reads that as documentation only. It never withholds the call from a plugin whose declared types look irrelevant. A transformer may compute every registered type where the deployment asks for that, so skipping it on the strength of its declaration would discard values it was entitled to produce.
  • No provenance stamping. Like the intent chain, this stage appends nothing to the message context.

A plugin that raises, or returns anything that is not a dictionary, is treated as having produced nothing. That is not the same as producing an empty map: an absent map means the value was never computed, while an empty one means the plugin looked and found nothing.


Standalone Usage

You can use transformers independently of the full OVOS stack. Here is an example with an UtteranceTransformer:

from ovos_plugin_manager.text_transformers import find_utterance_transformer_plugins

# Find and load the plugin (returns {plugin_name: class})
plugins = find_utterance_transformer_plugins()
transformer_class = plugins["ovos-utterance-normalizer"]
transformer = transformer_class()  # base __init__ supplies the name

# Transform an utterance (utterances is a list of strings)
utterances = ["hello world"]
transformed, context = transformer.transform(utterances)
print(f"Transformed: {transformed}")

The discovery helpers (find_*_transformer_plugins, load_*_transformer_plugin) live in ovos_plugin_manager.text_transformers, .intent_transformers, .metadata_transformers, .audio_transformers, and .dialog_transformers.

find_tts_transformer_plugins / load_tts_transformer_plugin live in their own ovos_plugin_manager.tts_transformers module. ovos_plugin_manager.dialog_transformers re-exports both names for backwards compatibility, but that module is not their canonical home.

Writing a plugin instead of choosing one? See Writing a Transformer Plugin, which covers inheriting from the base class, registering the entry point, packaging, and testing.

Transformer plugins Reference

Plugin Description Maturity
ovos-dialog-normalizer-plugin a dialog transformer plugins for OVOS Stable
ovos-bidirectional-translation-plugin This package includes a UtteranceTransformer plugin and a DialogTransformer plugin, they work together to allow OVOS to speak in ANY language Stable
ovos-audio-transformer-plugin-speechbrain-langdetect spoken language detector for ovos Stable
ovos-utterance-corrections-plugin This plugin provides tools to correct or adjust speech-to-text (STT) outputs for better intent matching or improved user experience. Stable
ovos-utterance-normalizer normalizes utterances before intent parsing Stable
ovos-utterance-plugin-cancel looks at the end of the transcribed phrase and ignores it if it ends with "nevermind that", "cancel it", or "ignore that". Stable
ovos-audio-transformer-plugin-ggwave plugin for ggwave Mature
ovos-tts-transformer-sox-plugin A Text-to-Speech (TTS) transformer that uses SoX (Sound eXchange) for audio processing. The transformer applies various effects to the generated audio before playback. Beta
ovos_tts_transformer_FlashSR ONNX-based audio super-resolution that upsamples synthesized TTS audio before playback (not yet on PyPI). Proof-of-concept
ovos_tts_transformer_NovaSR torch-based audio super-resolution upsampler for synthesized TTS audio (not yet on PyPI). Proof-of-concept

Maturity reflects repository health (age, activity, open issues/PRs, in-repo docs), not version. See the Maturity Scale.

ovos-dialog-normalizer-plugin


ovos-bidirectional-translation-plugin


ovos-audio-transformer-plugin-speechbrain-langdetect


ovos-utterance-corrections-plugin


ovos-utterance-normalizer


ovos-utterance-plugin-cancel


ovos-audio-transformer-plugin-ggwave


ovos-tts-transformer-sox-plugin

  • GitHub: OpenVoiceOS/ovos-tts-transformer-sox-plugin

  • Description: A Text-to-Speech (TTS) transformer that uses SoX (Sound eXchange) for audio processing. The transformer applies various effects to the generated audio before playback.


ovos_tts_transformer_FlashSR

  • GitHub: OpenVoiceOS/ovos_tts_transformer_FlashSR

  • Description: ONNX-based audio super-resolution TTS transformer (FlashSRTTSTransformer). It upsamples synthesized audio before playback, downloading its model from the Hugging Face Hub. Entry point ovos-tts-transformer-FlashSR under opm.transformer.tts. Upcoming: not yet published to PyPI.


ovos_tts_transformer_NovaSR

  • GitHub: OpenVoiceOS/ovos_tts_transformer_NovaSR

  • Description: torch-based super-resolution upsampler TTS transformer (NovaSRTTSTransformer). Entry point ovos-tts-transformer-NovaSR under opm.transformer.tts. Upcoming: not yet published to PyPI.


Read next: Utterance Transformers Related: Pipelines Overview · Dialog Transformers · LLM Transformers