Skip to content

Provider signatures & data types ​

The host and every caller may only pass base-class parameters. When an underlying client takes fewer parameters, adapt inside your implementation; never forward extra kwargs down the stack.


MT ​

python
def translate(
    self, text: str,
    source_lang: str = "en", target_lang: str = "zh",
    glossary_context: str | None = None,
    target: str | None = None, source: str | None = None,
) -> Translation: ...

The host facade host.translate(text, source, target, glossary_context=None) and host.translate_batch(...) pass glossary_context to the currently active MT engine.

Optional incremental interface (falls back to whole sentences by default):

python
def translate_partial(
    self, text, source_lang="en", target_lang="zh",
    committed_prefix="", glossary_context=None,
    target=None, source=None,
) -> Translation: ...

ASR ​

python
def transcribe(self, audio, sample_rate=16000, language="auto", stream_id="default",
               with_alignment=False, on_token=None) -> Transcript: ...
  • with_alignment defaults to False and must be present in the signature. Engines without a word-level aligner accept the parameter and ignore it; in that case Transcript.words is None (the built-in Qwen3 has had its ForcedAligner removed, so this parameter never triggers alignment work).
  • Callers must not forward with_alignment= to an underlying transcribe that takes fewer parameters.

Optional streaming interface (default implementations provided):

python
def stream_open(self, stream_id: str, language: str = "auto") -> Any
def stream_feed(self, stream_id: str, pcm_chunk) -> Any
def stream_reopen(self, stream_id: str, language: str = "auto") -> Any
def stream_events(self, stream_id: str) -> Iterator[Tuple[str, bool]]
def stream_close(self, stream_id: str) -> str
def has_active_stream(self, stream_id: str) -> bool
def close_all_streams(self) -> None

Capability declaration:

python
@dataclass
class ASRCapabilities:
    has_native_vad: bool = False
    supports_streaming: bool = False
    supports_word_timestamps: bool = False
    supports_language_detection: bool = True

TTS ​

python
class BaseTTSProvider(BasePlugin):
    supports_voice_clone: bool = False   # supports registering cloned voices
    supports_speed: bool = False         # native speed support

    @property
    def sample_rate(self) -> int: ...            # defaults to 24000
    @property
    def is_streaming_capable(self) -> bool: ...  # defaults to False

    def synthesize(self, text, voice_id="", voice=None, language="zh"
                   ) -> tuple[np.ndarray, int]: ...
    def stream_tts(self, text, voice_id="", voice=None, language="zh"
                   ) -> Iterator[np.ndarray]: ...
    def list_voices(self) -> list[dict]: ...
    def register_voice(self, voice_name, audio_samples, reference_text="",
                       sample_rate=24000) -> bool: ...
    def remove_voice(self, voice_name: str) -> bool: ...
ItemContract
synthesizeReturns (float32 mono PCM, sample rate); voice takes precedence over voice_id (the host may pass only one of them)
stream_ttsOptional. If implemented, yield chunk by chunk (the host plays chunks after a pre-buffer, so playback starts earlier); if not implemented the host degrades to a single synthesize call
list_voices()→ [{"id", "name", "lang", "kind"}], kind ∈ speaker (bundled with the model) / registered (user clone, the only kind that can be deleted). Return genuinely usable entries only; when the service is not running, return the cache / an empty list
register_voice(voice_name, audio_samples, reference_text, sample_rate)audio_samples = float32 mono numpy (1~30 seconds). The host reads the audio (the user picks a file, or records it inside the app on the settings page). The plugin only transcodes / uploads / persists, and returns True/False
remove_voice(name)Deletes a registered cloned voice, returns True/False

Speed:

  • Declaring supports_speed = True → the host passes speed= (1.0 = original speed, range 0.5~2.0) to you and you implement it inside the model.
  • Not declared → the host performs a pitch-preserving time stretch after synthesis, and you do nothing.
  • When speed != 1.0 and supports_speed is not declared, the host will not call stream_tts.

Voice config key: the host writes the voice the user selected into the plugin config key voice (with voice_en / voice_other when configured per language, falling back to voice). Read it in your implementation with self._config.get("voice").

Cloned-voice persistence (mandatory): if the server keeps them in memory only, you must persist them to disk yourself and re-register automatically once the service is ready — see tts § Cloned-voice persistence.


interpretation ​

python
def start_session(self, session_id="peer", source_lang="zh", target_lang="en",
                  voice=None, tts_enabled=False) -> bool
def feed_audio(self, session_id: str, pcm_chunk) -> None
def stop_session(self, session_id: str = "peer") -> None
def is_active(self, session_id: str = "peer") -> bool
# emit results upward (base-class implementations): emit_transcript / emit_translation / emit_tts_audio / emit_status / emit_error

See interpretation.


Data types ​

python
@dataclass
class WordTime:
    word: str
    start: float
    end: float

@dataclass
class Transcript:
    text: str
    language: str | None = None
    words: list[WordTime] | None = None

@dataclass
class Translation:
    text: str
    source_prefix: str | None = None
    complete: bool = True     # False means an incremental fragment

@dataclass
class PipelineContext:        # context of a middleware hook
    session_id: str = ""
    sample_rate: int = 16000
    source: str = "peer"      # 'peer' | 'self'
    source_lang: str = "auto"
    target_lang: str = "zh"
    extra: dict = {}

Lifecycle hooks (common to every Provider) ​

HookWhen it is calledWhat you should do
initialize(config)When the instance is created (external plugins run on a background thread)Preparation only: parse config, check files; do not start a service
ensure_loaded()When the host really wants to use the engine (enabling a track, starting the pipeline, clicking Test Connection)Start the local service and wait for readiness (may block)
update_config(delta)The user changes configHot-sync; restart the service only when it is running
check_ready()Readiness gateDecide "can it be used"; a stopped service is not "not ready"
shutdown()Exit / release VRAM / disable / config changeMust kill the child process
wait_async_init()Around the host's ensure_loaded callProvided by the base class; waits for the async initialize to settle

Details in Local-service engine contract.


TransBox — empowering borderless cross-language communication for everyone