turn-detection / backend /types.py
amanetize's picture
Upload folder using huggingface_hub
dc98a34 verified
Raw
History Blame Contribute Delete
2.72 kB
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Literal, Optional
Provenance = Literal['real_checkpoint', 'trained_by_us', 'rule', 'architecture_reimplemented', 'unavailable']
GateChoice = Literal['webrtcvad', 'silero_vad', 'none']
EncoderChoice = Literal['whisper_tiny', 'whisper_base', 'wav2vec2', 'smart_turn_onnx', 'none']
PoolingChoice = Literal['mean', 'cross_attention']
HeadChoice = Literal['linear', 'mlp']
SemanticChoice = Literal['off', 'qwen_local', 'qwen_local_streaming', 'livekit_eou', 'groq_api', 'openrouter_api']
FusionChoice = Literal['off', 'weighted_vote', 'easy_turn']
OutputClasses = Literal['binary', '3class', '4class']
FullDuplexChoice = Literal['off', 'moshi', 'human1']
class StageUnavailableError(RuntimeError):
pass
@dataclass
class StageResult:
stage: str
timing_ms: float
output: Any = None
available: bool = True
reason: Optional[str] = None
provenance: Optional[Provenance] = None
@dataclass
class PipelineConfig:
gate: GateChoice = 'silero_vad'
encoder: EncoderChoice = 'whisper_tiny'
pooling: PoolingChoice = 'mean'
head: HeadChoice = 'linear'
semantic: SemanticChoice = 'off'
fusion: FusionChoice = 'off'
output_classes: OutputClasses = 'binary'
full_duplex: FullDuplexChoice = 'off'
vad_aggressiveness: int = 2
silence_trigger_ms: int = 400
semantic_temperature: float = 0.2
acoustic_weight: float = 0.6
def __post_init__(self) -> None:
if self.encoder == 'none':
if self.semantic == 'off':
raise ValueError("encoder='none' requires the semantic branch to be on (nothing would decide).")
if self.fusion != 'off':
raise ValueError("encoder='none' has no acoustic score to fuse with - set fusion='off'.")
if self.encoder == 'smart_turn_onnx':
if self.pooling != 'mean' or self.head != 'linear':
raise ValueError("encoder='smart_turn_onnx' is a single opaque preset and cannot be combined with a separate pooling/head choice (see docs/decision-log.md #18).")
if self.fusion != 'off' and self.semantic == 'off':
raise ValueError('fusion requires the semantic branch to be on.')
if self.full_duplex != 'off':
pass
@dataclass
class PipelineResult:
config: PipelineConfig
decision: Optional[str] = None
probability: Optional[float] = None
transcript: Optional[str] = None
semantic_verdict: Optional[str] = None
stage_results: list[StageResult] = field(default_factory=list)
total_latency_ms: float = 0.0
provenance: Optional[Provenance] = None
preset_label: Optional[str] = None