Problem
In a streaming reply, the first audio does not start until a complete,
terminated sentence of at least 20 characters exists. Everything before that is
silence. The perceived difference is "talks as it thinks" versus "long pause, then
a wall of speech".
Evidence
autobot-frontend/src/utils/ttsSentences.ts:33-49 — extractCompleteSentences()
only ever emits on the terminator pattern (?<=[.!?])\s+, and only when
candidate.length >= minChars.
autobot-frontend/src/components/chat/ChatInterface.vue:1301,1325 — the caller
passes _MIN_TTS_SENTENCE_CHARS = 20 for every chunk. There is no lower bar
for the first one.
Proposed approach
Release the opening chunk of a reply on a weaker signal, then raise the bar
for everything after:
- first chunk: emit at an early clause boundary (
, ; : — –), or —
for a long opening sentence with no early pause — at a word boundary once a
minimum length is reached
- subsequent chunks: keep the current terminated-sentence +
minChars rule
The fast path must only fire when nothing is already held for merging, so the
opening never jumps ahead of an earlier short sentence waiting to be merged.
Risk — must be validated by listening, not just by test
A too-short opening fragment can render with a noticeably different timbre
from the rest of the reply, producing a "the voice changes mid-sentence" artifact.
The safe minimum is model-specific and must be measured against our own TTS worker
(autobot-tts-worker, Kani-TTS-2) — a threshold that works for one model does not
transfer to another.
If the artifact cannot be avoided at any useful length, close this as
not-worth-doing and record the measured finding, rather than shipping a latency
win that costs audio quality.
Acceptance criteria
Problem
In a streaming reply, the first audio does not start until a complete,
terminated sentence of at least 20 characters exists. Everything before that is
silence. The perceived difference is "talks as it thinks" versus "long pause, then
a wall of speech".
Evidence
autobot-frontend/src/utils/ttsSentences.ts:33-49—extractCompleteSentences()only ever emits on the terminator pattern
(?<=[.!?])\s+, and only whencandidate.length >= minChars.autobot-frontend/src/components/chat/ChatInterface.vue:1301,1325— the callerpasses
_MIN_TTS_SENTENCE_CHARS = 20for every chunk. There is no lower barfor the first one.
Proposed approach
Release the opening chunk of a reply on a weaker signal, then raise the bar
for everything after:
,;:—–), or —for a long opening sentence with no early pause — at a word boundary once a
minimum length is reached
minCharsruleThe fast path must only fire when nothing is already held for merging, so the
opening never jumps ahead of an earlier short sentence waiting to be merged.
Risk — must be validated by listening, not just by test
A too-short opening fragment can render with a noticeably different timbre
from the rest of the reply, producing a "the voice changes mid-sentence" artifact.
The safe minimum is model-specific and must be measured against our own TTS worker
(
autobot-tts-worker, Kani-TTS-2) — a threshold that works for one model does nottransfer to another.
If the artifact cannot be avoided at any useful length, close this as
not-worth-doing and record the measured finding, rather than shipping a latency
win that costs audio quality.
Acceptance criteria
the bug fixed in fix(voice): assistant text dropped/truncated — fallback aborts prior audio + cursor drift + tail not flushed #12502 must not regress)
chosen minimum length and its measured justification