From 19b499ab500faf0ad57b21b43c69f6884020ab2f Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Fri, 7 Aug 2026 10:23:13 -0700 Subject: [PATCH] feat(tts): migrate off Zonos to chatterbox-fast; drop affect, hold English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repoint the TTS client from the Zonos gateway (:8890 /v1/audio/speech) to chatterbox-fast (:8197 /tts β€” bespoke non-OpenAI {text,voice,format,stream} schema, no auth, 24kHz, infra-ops-verified). tts.py stays the single swap seam. Dropped, no backward-compat (pre-v1): - Affect (DEC-7): the Turbo checkpoint has no emotion knob, so PadState, EmotionDials, pad_to_dials, the /api/tts p/a fields, and the browser pad argument are deleted. Voice is now flat. - Client-side chunking (DEC-10): chatterbox has no per-synth cap and chunks internally, so chunk_text/tts_stream_long/_pcm_after_header are deleted; a single tts_stream call voices a whole turn, the mid-stream yielded_any degrade folded into it. - Language pin (DEC-9): no language field; re-purposed to sampling curbs (below). Fixed / added: - Browser Web Audio sample rate 44100 -> 24000 (the chatterbox rate). - Default voice Cora -> glados_25s; donut registered lowercase at /refs/donut.wav. - English-drift curb: Turbo is multilingual-leaky and wanders off English on a long generation (the gateway scheduler ratchets chunk size unbounded). Tighten sampling in gateway_body: top_k 1000->80, top_p 0.95->0.85, temperature 0.8->0.5. These reduce drift probability; the guaranteed fix is a server-side max-chunk cap (infra-ops, greenlit). - OOM guard (DEC-9a): a long generation can OOM the shared 3090, returning 200 with a 0-byte body; /api/tts surfaces an empty 200 as 503 rather than committing silent audio. Contract donut_voiced_interview.contract.md amended: migration banner, DEC-1/3/8 amended, DEC-7/9/10 retired with historical notes, DEC-9a added. Tests rewritten to the new wire; 520 green. Live-smoked against the gateway (24kHz synth + endpoint proxy + web console). persistent-memory.md committed alongside (commit-along). --- .../donut_voiced_interview.contract.md | 211 +++++++--- persistent-memory.md | 31 +- src/ratatoskr/tts.py | 389 ++++-------------- src/ratatoskr/web/entrypoint.py | 6 +- src/ratatoskr/web/server.py | 69 ++-- src/ratatoskr/web/static/index.html | 15 +- tests/test_tts.py | 341 +++------------ tests/test_web_server.py | 91 ++-- 8 files changed, 393 insertions(+), 760 deletions(-) diff --git a/docs/contracts/donut_voiced_interview.contract.md b/docs/contracts/donut_voiced_interview.contract.md index 56866ba..f6c3a83 100644 --- a/docs/contracts/donut_voiced_interview.contract.md +++ b/docs/contracts/donut_voiced_interview.contract.md @@ -3,9 +3,10 @@ contract_version: "2.1" module: "ratatoskr.web.tts_kb" purpose: > A voiced, corpus-grounded Tier-3 interview character in the ratatoskr web - console. Two capabilities plus one character: (a) auto-TTS via the Zonos - gateway, spoken on SSE `done`, with emotion driven by the character's LIVE - PAD affect; (b) a consumer-side KB-retrieval + `memory_context` pinning + console. Two capabilities plus one character: (a) auto-TTS via the + chatterbox-fast gateway, spoken on SSE `done` (migrated off Zonos 2026-08-07; + no affect modulation β€” chatterbox Turbo has no emotion knob); (b) a + consumer-side KB-retrieval + `memory_context` pinning BRIDGE that grounds the character's recall in the ingested corpus while she stays in-voice; (c) Princess Donut (Dungeon Crawler Carl) as the first instance. The bridge is a deliberate, retire-ready workaround for a Worldtree @@ -21,12 +22,12 @@ touches: - src/ratatoskr/web/server.py # /api/tts route + the retrieval-pinning seam on the turn path - src/ratatoskr/web/static/index.html # speak-on-done playback, πŸ”Š toggle,