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
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):
def translate_partial(
self, text, source_lang="en", target_lang="zh",
committed_prefix="", glossary_context=None,
target=None, source=None,
) -> Translation: ...ASR
def transcribe(self, audio, sample_rate=16000, language="auto", stream_id="default",
with_alignment=False, on_token=None) -> Transcript: ...with_alignmentdefaults toFalseand must be present in the signature. Engines without a word-level aligner accept the parameter and ignore it; in that caseTranscript.wordsisNone(the built-in Qwen3 has had its ForcedAligner removed, so this parameter never triggers alignment work).- Callers must not forward
with_alignment=to an underlyingtranscribethat takes fewer parameters.
Optional streaming interface (default implementations provided):
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) -> NoneCapability declaration:
@dataclass
class ASRCapabilities:
has_native_vad: bool = False
supports_streaming: bool = False
supports_word_timestamps: bool = False
supports_language_detection: bool = TrueTTS
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: ...| Item | Contract |
|---|---|
synthesize | Returns (float32 mono PCM, sample rate); voice takes precedence over voice_id (the host may pass only one of them) |
stream_tts | Optional. 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 passesspeed=(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.0andsupports_speedis not declared, the host will not callstream_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
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_errorSee interpretation.
Data types
@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)
| Hook | When it is called | What 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 config | Hot-sync; restart the service only when it is running |
check_ready() | Readiness gate | Decide "can it be used"; a stopped service is not "not ready" |
shutdown() | Exit / release VRAM / disable / config change | Must kill the child process |
wait_async_init() | Around the host's ensure_loaded call | Provided by the base class; waits for the async initialize to settle |
Details in Local-service engine contract.
