ovos-media-classifier documentation

July 31, 2026 · View on GitHub

ovos-media-classifier provides media-type command/intent classification for OCP (OVOS Common Play). It is multi-task: it maps a spoken command onto a set of orthogonal axes at once, domain (play / control / not-OCP), modality (playback_type: audio / video / paged / interactive), structure (single / episodic / continuous / collection), explicitness (clean / adult), and the concrete mediavocab.MediaType leaf, plus the mediavocab descriptive axes: content_form (trailer / behind_scenes / …), programme_format (documentary / news / …), accessibility (subtitles / audio_description), variant (directors / remastered / …), the content_genres (⊆ KNOWN_GENRES) and the content-form genre tags. A detect-to-block content filter sits on top so OVOS can refuse sensitive requests by default.

It is a router, not a resolver. Its job is to gate (is this a media request at all?), route (media_type / playback_type decide which MediaProviders to call), apply content policy (explicitness / adult), and hand the providers a mediavocab.Signals as search context. It does not resolve a title to a stream, the providers do, so Signals.title carries the raw query by design. See routing-eval.md for the router framing and its honest eval.

This classifies voice commands. It is distinct from mediavocab.text.classify, which classifies catalog content, see taxonomy.md.

Quickstart

pip install ovos-media-classifier
from ovos_media_classifier import load_media_classifier

clf = load_media_classifier()                  # bundled .voc keyword classifier
clf.classify("play some music", "en-us")       # -> (<MediaType.MUSIC: 'music'>, 0.6)
clf.classify_full("play a podcast", "en-us").as_dict()
# {'media_type': 'podcast', 'playback_type': 'audio', 'structure': 'episodic',
#  'domain': 'ocp_play', 'genres': [], 'confidence': 0.6, 'control_intent': None}

Start here

You are…Read
New to the projectglossary.md (every term in one place), then this page's quickstart and stable-api.md
An operator tuning backendsbackends.md (selection + config keys), entity-lists.md, contextual-classification.md, metadatarr-routing.md (the gazetteer + the live-vs-offline latency rule)
Blocking contentcontent-filtering.md
A plugin author writing an external classifierexternal-plugins.md, then stable-api.md for the contract
A contributor training a modelmodel.md (the multi-task model + the ladder + limitations), dataset.md (the data + its generator), and benchmarks
An ML engineer extending itextending.md (add a backend, retrain, add a new axis/head), then model.md and hierarchical.md
Judging routing qualityrouting-eval.md, the harm-weighted, out-of-distribution eval that is the source of truth for how a backend routes real speech

New to the vocabulary (OCP, OPM, domain, axis, NER, …)? Read glossary.md first.

Contents

PageWhat it covers
glossary.mdEvery term and acronym, the 30-second mental model, and the command-vs-content distinction
classification-model.mdThe multi-axis model: the core axes + the mediavocab descriptive axes, why orthogonal axes rather than a strict tree, and the MediaType→(playback_type, structure) defaults
model.mdThe trained model for an ML engineer: the feature representation, the multi-task per-axis heads + soft-gating, the rules→context→context+NER ladder, the self-describing bundle/retrain contract, the benchmark table, and the honest limitations
extending.mdStep-by-step: implement a new AbstractMediaClassifier backend + register it under opm.media.classifier, retrain the ONNX bundle with train_sklearn.py, and add a new axis/head end-to-end
taxonomy.mdmediavocab.MediaType enforcement, the raw label→type/genre mapping, and the query-vs-content distinction
backends.mdThe keyword, NER, ONNX and embedding-router backends, how load_media_classifier selects between them, and adding an external classifier
embedding-router.mdThe learned guided-categorical-embeddings router: two-stream `[categorical
metadatarr-routing.mdThe open-vocab routing layers that fill keyword's abstains: the offline popularity gazetteer (default, bounded, ~1k/type) and the optional online metadatarr.resolve layer, with the live-vs-offline latency rule (small bounded set for live routing; large/online sets for offline tagging only)
routing-eval.mdThe harm-weighted, out-of-distribution routing eval, the source of truth: mis-route rate, GENERIC-is-safe, adult detect-to-block, and why the in-distribution benchmark is a false green
hierarchical.mdThe coarse-to-fine media_type masking/cascade experiment and why the flat multi-task head is kept (the taxonomy is a fallback, not a forward constraint)
entity-lists.mdEntity lists (label → list of strings): the source-agnostic store (runtime / .csv / .tsv / .jsonl / HuggingFace / media-server / inline) the NER backend consumes
dataset.mdThe canonical ocp-media-intents dataset and its on-demand generator: every column, the rebuild command, the .intent/.voc templates, confusables, and the content-filter slice
data-sources.mdEvery HF dataset + local scraper feeding the entity pools, the slot label each feeds, licenses, and how the training set is assembled
plots/dataset/Dataset characterization plots, rows per media type, the slot×media-type heatmap, axis distributions, entity-pool sizes (regenerate with python -m training.dataset_plots)
contextual-classification.mdHow the media you actually have biases prediction, via keyword feature slots
content-filtering.mdContentFilter: detect-to-block content moderation / parental control
external-plugins.mdRegistering a third-party classifier via the opm.media.classifier entry-point group
stable-api.mdThe AbstractMediaClassifier contract, the multi-axis methods, and return types

For the reproducible accuracy/latency harness, see benchmarks. For runnable, commented scripts that exercise the public API, see examples/.

Public API at a glance

from ovos_media_classifier import (
    load_media_classifier,          # factory: picks a backend from config
    load_media_classifier_plugin,   # load an external classifier by name
    AbstractMediaClassifier,        # the plugin / backend contract
    ContentFilter,                  # detect-to-block moderation
    MediaType,                      # re-exported mediavocab taxonomy (the leaf axis)
    Structure,                      # the structure axis (single/episodic/continuous/collection)
    MediaClassification,            # the full multi-axis result (classify_full)
    OCPDomain, OCPControlIntent, OCPEntityLabel,
)

See stable-api.md for the full method reference.