External plugins
July 31, 2026 · View on GitHub
Third-party media classifiers integrate through OPM (the OVOS Plugin Manager).
A classifier is any subclass of
AbstractMediaClassifier registered under the
opm.media.classifier entry-point group. AbstractMediaClassifier is the contract
the host relies on.
Registering a classifier
# my_pkg/__init__.py
from ovos_media_classifier import AbstractMediaClassifier, MediaType
class MyMediaClassifier(AbstractMediaClassifier):
def __init__(self, config=None):
self.config = config or {}
def classify(self, query, lang, valid_labels=None):
...
return MediaType.MUSIC, 0.9
# my_pkg's pyproject.toml
[project.entry-points."opm.media.classifier"]
my-classifier = "my_pkg:MyMediaClassifier"
The entry-point name (my-classifier) is how the host selects the plugin. A
companion group opm.media.classifier.config mirrors other OPM types for shipping
default per-plugin config.
Loading a classifier
Via the factory, by setting media_classifier_plugin in config:
from ovos_media_classifier import load_media_classifier
clf = load_media_classifier(config={"media_classifier_plugin": "my-classifier"})
The whole config dict is forwarded to the plugin constructor as config=…. If the
plugin fails to load, the factory logs a warning and falls through to the built-in
backends, an external plugin never hard-fails the pipeline.
Or load one directly:
from ovos_media_classifier import (
load_media_classifier_plugin,
find_media_classifier_plugins,
)
find_media_classifier_plugins() # {name: class} for installed plugins
clf = load_media_classifier_plugin("my-classifier", config={})
load_media_classifier_plugin raises ValueError if no plugin with that name is
installed (the message lists the available names). It tolerates classifiers whose
__init__ takes no config kwarg. find_media_classifier_plugins() never raises, it returns {} when the plugin manager is unavailable.
Implementing the contract
A plugin must implement classify(). It may override classify_domain(),
is_ocp_query(), and classify_genres() when it has cheaper or richer signal, e.g. a dedicated domain head, control-intent support, or genre detection so the
content filter can block on it. See
stable-api.md for the full contract and the default
implementations.
A trained plugin SHOULD also override the coarse-axis methods
(classify_playback_type(), classify_structure(), classify_full()) to predict
each axis with its own head and soft-gate the leaf, rather than relying on the
derive-from-leaf defaults, that is the whole point of the multi-axis model and is
what the TigreGotico/ocp-media-intents dataset's per-axis columns are for. See
classification-model.md.