memory: TTS streaming play-as-it-arrives shipped (7856ec5) + autoplay unlock

This commit is contained in:
vh
2026-08-02 00:00:11 -07:00
parent 7856ec5438
commit aecec87919
+12 -5
View File
@@ -92,11 +92,18 @@ Contract `docs/contracts/donut_voiced_interview.contract.md`. **NOT pushed** (op
#382/#383 both closed. **Native tool is strictly better than the bridge** (in-turn, provenance packets, server-side
grounding rule, fail-soft, no separate consult). FOOT-GUN (#381): freshly-ingested content needs the
restart-after-ingest runbook to be Tier-3-recallable.
- **✅ Zonos WAV-header fix (`6c3c08b`) — operator-reported "browser wants wav, zonos sends pcm."** The gateway
emits a STREAMING wav header (RIFF size @4 + data size both `0xFFFFFFFF` = unknown length); a browser `<audio>`
can't play a finite blob with an unknown-length header. `tts.py:_finalize_wav_header` rewrites both size fields
with the real byte counts (clip is buffered server-side). Live-verified: `wave.open` decodes it (1ch/16bit/44.1kHz).
Booth: `http://10.100.10.50:8090/b/donut-voiced/` (donut-wav-fixed.wav).
- **✅ TTS is now STREAMING play-as-it-arrives (`7856ec5`, supersedes the buffered `6c3c08b`).** operator wanted
no wait-for-the-whole-clip; infra-ops confirmed the Zonos gateway ALREADY streams (chunked int16 WAV, TTFB
~0.44s, placeholder `0xFFFFFFFF` sizes DESIGNED for progressive `<audio src>`) — the buffering was all in OUR
proxy, and `_finalize_wav_header` FORCED it. Fix: `tts.py` `tts_stream` (async-gen over the gateway's chunked
resp, pass-through, no buffer/rewrite) + `gateway_body`; `/api/tts` is now **GET** → chunked `StreamingResponse`
(peek-first-chunk for a 503-on-bad-open; serialize lock across the stream; PAD via p/a query floats); `index.html`
`speakOnDone` sets `<audio src="/api/tts?...">`. **AUTOPLAY was the real "no audio" cause** — `play()` fires ~15s
after the keypress (past the transient-activation window) → blocked; `_unlockTtsAudio()` plays a silent WAV within
the toggle/submit gesture → persistent element may-play flag. Live-verified: GET is `transfer-encoding: chunked`
TTFB 0.46s; Playwright w/ restrictive autoplay → streaming `<audio src>` plays progressively (currentTime advances,
no decode error, no MSE) 6.5s post-gesture. FOOT-GUN: text rides the GET query → capped ~2000 chars (truncate at
word boundary); `_TTS_MAX_TEXT_CHARS=2000`. TTS is GET now, not POST.
**✅ SHIPPED + PUSHED since v0.22.0 (origin at `14bbc2b`):** the worldtree-sdk cutover (#20, **v0.22.0**, 7 slices,
494 green — the big one) + **bifrost 1.1.5** (`3ef3a5e`, **v0.22.1**) + **worldtree-sdk 1.0.0→1.1.1→1.1.2**