Operator Plugins
A third-party operator plugin is a regular pip-installable Python package
that exposes one or more operators to VoxKitchen through the
voxkitchen.operators entry-point group. VoxKitchen discovers plugins
lazily at runtime — no source changes to the core are required.
1. What a plugin is
When VoxKitchen's operator registry is first accessed (on vkit operators,
vkit doctor, vkit run, etc.) it calls
importlib.metadata.entry_points(group="voxkitchen.operators") and loads
every advertised class into the same registry that holds the built-in
operators. Any operator name that is unique and satisfies the base contract
is usable in pipeline.yaml stages exactly like a built-in.
2. Write the operator
Subclass voxkitchen.operators.Operator and satisfy three requirements:
- Set
nameto a unique string (the key used inpipeline.yaml). - Set
config_clsto a subclass ofOperatorConfig(a Pydantic model describing the operator's YAML-configurable parameters). - Implement
process(self, cuts: CutSet) -> CutSet.
Declare the field contract with four ClassVar lists (all required; the
pre-flight validator checks them before a run starts):
| ClassVar | Meaning |
|---|---|
reads |
Fields that must be present on each cut. |
writes |
Fields this operator populates or updates. |
optional_reads |
Fields consumed when present; emits a warning if absent. |
clears |
Fields this operator removes. |
See Field Contracts for the full token
vocabulary (audio, supervisions.text, metrics.<name>, custom.<key>,
namespace wildcards, etc.).
from __future__ import annotations
from typing import ClassVar
from voxkitchen.operators.base import Operator, OperatorConfig
from voxkitchen.schema.cutset import CutSet
class WordCountConfig(OperatorConfig):
"""No parameters."""
class WordCountOperator(Operator):
"""Count words in each cut's first transcribed supervision.
Writes the count to ``metrics.word_count``.
"""
name = "word_count"
config_cls = WordCountConfig
# Execution ClassVars (base-class defaults shown; override as needed)
device = "cpu" # "cpu" | "gpu"
produces_audio = False # creates new audio files on disk?
reads_audio_bytes = True # default True; set False if your op ignores audio
required_extras: ClassVar[list[str]] = []
parallelizable: ClassVar[bool] = True
# Field contract
reads: ClassVar[list[str]] = ["supervisions.text"]
writes: ClassVar[list[str]] = ["metrics.word_count"]
optional_reads: ClassVar[list[str]] = []
clears: ClassVar[list[str]] = []
def process(self, cuts: CutSet) -> CutSet:
out = []
for cut in cuts:
text = next((s.text for s in cut.supervisions if s.text), "")
metrics = {**cut.metrics, "word_count": float(len(text.split()))}
out.append(cut.model_copy(update={"metrics": metrics}))
return CutSet(out)
Do NOT use @register_operator
The @register_operator decorator is the built-in registration mechanism
(used inside voxkitchen/operators/). For plugins the entry point is
the registration mechanism — applying the decorator as well would
double-register the operator and raise a ValueError ("already registered").
3. Package it
A minimal pyproject.toml:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "voxkitchen-my-plugin"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = ["voxkitchen"]
[project.entry-points."voxkitchen.operators"]
word_count = "voxkitchen_my_plugin.operator:WordCountOperator"
[tool.hatch.build.targets.wheel]
packages = ["voxkitchen_my_plugin"]
The entry-point key (word_count above) is only a label; the registry uses
the name ClassVar on the operator class. Use a descriptive label that
matches your operator name to avoid confusion.
You may register multiple operators from a single package by adding one entry
per operator under [project.entry-points."voxkitchen.operators"].
4. Install and verify
pip install ./voxkitchen-my-plugin # or any pip-installable path / URL
# The operator now appears in the list
vkit operators
# Third-party operators are listed under an "Other" category unless their
# module path matches a built-in category prefix.
vkit operators show word_count
# doctor reports the operator API version and the number of third-party
# operators discovered in the active environment
vkit doctor
5. Stable surface and compatibility
Plugins may depend on the following elements of the operator contract:
- Identity:
name,config_cls - Runtime method:
process(cuts: CutSet) -> CutSet - Execution ClassVars:
device,required_extras,produces_audio,reads_audio_bytes,parallelizable - Field-contract ClassVars:
reads,writes,optional_reads,clears - API version constant:
from voxkitchen.operators import OPERATOR_API_VERSION(currently1)
Compatibility policy:
- New optional ClassVars with safe defaults (e.g. a new execution hint)
are backward-compatible and do not bump
OPERATOR_API_VERSION. - Renaming or removing existing ClassVars, changing the signature of
process(), or any other breaking change bumps the major version of VoxKitchen and incrementsOPERATOR_API_VERSION.
If your package needs to guard against a future breaking change, check:
from voxkitchen.operators import OPERATOR_API_VERSION
assert OPERATOR_API_VERSION == 1, f"Unsupported API version: {OPERATOR_API_VERSION}"
6. Worked example
A complete, installable example is at
examples/plugin-operator/. It
implements the word_count operator shown above and is structured exactly
as a real third-party package would be.
7. Current limitation
A plugin runs in the Python environment where it is installed. The
multi-environment Docker images (asr, diarize, tts, etc.) do not yet
automatically map a third-party operator to a specific inner env — the
runner does not know which env satisfies the plugin's dependencies.
Today: single-environment local vkit run and container-internal
vkit run both work fine as long as the plugin is installed in the active
environment.
Workaround for multi-env images: install the plugin inside the target env
in your Dockerfile and set required_extras to an existing extras group that
maps to the desired env via EXTRA_TO_ENV in
voxkitchen/runtime/env_resolver.py. Full first-class multi-env dispatch for
third-party operators is planned.