Skip to content

v1.20.16 — Voice no longer reads formatting aloud in conversation mode

Choose a tag to compare

@jgouviergmail jgouviergmail released this 30 May 01:42

🔊 Fixed — Voice/TTS no longer reads HTML/Markdown formatting aloud in conversation mode

With text-to-speech on and the HTML display mode, conversational replies were rendered as full HTML (with an inline <style> CSS block) and streamed verbatim to the TTS engine, so the voice spoke tags and CSS aloud. Tool/action turns were unaffected — they voice a separate clean commentary via the Voice Comment LLM built from the data registry.

Root cause, fixed at the source: the rich-HTML response directive is now injected only for tool/data turns — gated on the router's route_to == "planner" (the exact signal RouterOutput.intention derives from, and the one the voice path's progressive-TTS trigger uses, so display and voice can never desync). Conversational turns stay plain Markdown, rendered identically by the frontend (ReactMarkdown + rehypeRaw) in every display mode — no display regression, no voice-pipeline change (the progressive low-latency TTS of ADR-082 is preserved). The route_to lookup reuses the canonical get_qi_attr helper (object/dict-form safe).

Defense-in-depth: the synchronous TTS entry points (stream_direct_tts and the stream_voice_comment fallbacks) strip HTML to speakable text via html_to_text, but only when real markup is present — plain prose with bare angle brackets (x < 5 and y > 3) and Markdown symbols are left untouched.

Tests & quality

20 new unit tests (directive gating per display-mode × route + TTS sanitization incl. the angle-bracket guard). Full host suite green: Ruff / Black / MyPy (858 files), 8344 unit tests, ESLint / tsc, i18n parity across the 6 locales. No DB migration, no new env var, no endpoint change.

Applicative FAQ changelog updated in all 6 languages; docs/technical/VOICE.md gains a Plain-text input guarantee section. Full details in CHANGELOG.md.