# backend/voice/tts_router.py # §0.4 — Locked TTS Engine Routing Rule (Single Source of Truth) # select_engine(context) is the ONLY place that decides which engine is used. # No other file in OMEGA decides this. All callers pass a ResponseContext. from dataclasses import dataclass from enum import Enum class TTSEngine(Enum): KOKORO = "kokoro" # Fast system notifications & alerts XTTS = "xtts" # Conversational JARVIS/FRIDAY speaking CHATTERBOX = "chatterbox" # Cinematic / pre-rendered / scripted content @dataclass class ResponseContext: type: str = "conversation" # Default: real JARVIS/FRIDAY speaking # ── Locked routing rule ──────────────────────────────────────────────────────── def select_engine(context: ResponseContext) -> TTSEngine: """ Final, locked routing rule. Cinematic → Chatterbox System notifications & alerts → Kokoro (fast, functional) Everything else (JARVIS/FRIDAY actually speaking) → XTTS (tuned profile) """ # Cinematic / scripted — never in the live wake-word loop if context.type in ("scripted_intro", "demo_narration", "storytelling"): return TTSEngine.CHATTERBOX # System notifications & alerts — fast, functional, breaks immersion intentionally # These are system events, not JARVIS "speaking" in character if context.type in ( "usb_device_alert", # device connected/ejected "vault_backup_complete", # vault build finished "vault_backup_started", "automation_status", # automation running/paused/failed/completed "github_commit_status", # commit success/failure "system_warning", # low disk, connection lost, etc. "upgrade_notification", # "Feature implemented" toast-paired speech "research_alert", # safety/critical research alert "quick_command_ack", # "Done." "Lights on." style acks ): return TTSEngine.KOKORO # Everything else: actual JARVIS/FRIDAY speaking — conversation, AR/automation # speak() actions with personality, space-change acknowledgements, OSINT results, # internet summaries read aloud, etc. return TTSEngine.XTTS