Back to DevLog

Interruptible TTS and short spoken replies in voice mode

2 min read

Voice mode blocked on long text-to-speech replies and read raw markdown verbatim. I fixed both.

Interruptible speech

The speak tool blocked in sd.wait(), so long replies played to completion no matter what. I added a poll loop that checks every 50ms while the output stream is active. Ctrl+space stops playback and returns "interrupted", Esc stops playback and returns "exit", and normal completion returns None.

The keyboard library is imported lazily with try/except. If it's unavailable, the code falls back to sd.wait() exactly as before. Keys are checked separately (ctrl and space as two is_pressed() calls) to match the existing listen tool.

When say() returns "interrupted", the voice loop naturally falls to record and transcribe. The user is still holding the keys, so stopping playback is the whole job. Only "exit" unwinds the loop.

Short spoken replies, full printed text

The router returned screen-formatted markdown that TTS read verbatim. The weather denial ran about 15 seconds.

I added strip_for_speech() to remove asterisks, backticks, and leading bullet markers. It turns each source line into its own sentence, drops trailing list-intro punctuation, and caps at two sentences. The full reply prints to the terminal, but speech gets the stripped version.

The router system prompt now ends with an instruction: "Replies will be read aloud. Answer in at most two short plain sentences. No markdown, no bullet points, no offers to help further." The live weather denial arrived as two plain sentences.

A stripped line ending in a colon read awkwardly as "report on:." so strip_for_speech() now strips trailing colons and semicolons before appending the sentence period.

Both fixes shipped in commit 563bca3. Live test passed: barge-in cut speech and recorded immediately, the weather denial spoke as two plain sentences, and Esc mid-speech exited clean.

Share this post