/ws/tts/stream is one logical TTS request per turn, regardless of how many
send calls you make. The server’s text buffer accumulates tokens and hands a
complete chunk to the model the moment it sees a natural boundary (sentence
punctuation, or the configured chunk_length_schedule threshold). Inside a
single turn, model state (KV cache, voice conditioning) is preserved across
chunks so prosody stays natural.
Calling flush=true mid-turn breaks that flow: the server treats the flush as
a hard segment boundary, runs another full model prefill on whatever has been
buffered, and only then emits audio. The cost of that prefill is the full
model time-to-first-audio (see Latency) — the same cost you pay on
the very first chunk of a turn. Do it on every word and you pay model TTFA on
every word.
Chunk-size ordering — pick the largest you can
If you’re driving the session from a layer above raw LLM tokens (for example, a translation pipeline that emits clauses, or a router that batches output before sending), use the largest chunks you can. The ordering, from best to worst time-to-first-audio per emitted segment, is:
Two important nuances:
- Raw LLM tokens are fine as long as you
sendthem withoutflush=true— the server’s text buffer reassembles them and only hands sentence-sized work to the model. The “word-level is bad” row above applies when you flush after each word, not when you send one word at a time without flushing. - We deliberately don’t publish exact ms figures here — they depend on
region, voice, and deployment. The ordering is stable; the absolute
numbers aren’t. To reproduce the comparison for your own deployment, run
TTFABench.chunkingStrategyBenchagainst your endpoint — see Measuring TTFA correctly.
Tuning auto-chunking
You rarely need this, but two config parameters let you trade prosody context for lower first-chunk latency, without any client-side flushing:
Use the defaults unless you’ve measured a problem.
Per-segment latency
For real-time voice agents, per-segment latency (time from sentence boundary to first audio of the next sentence) matters as much as initial TTFA. Two parameters let you trade audio quality for speed:optimize_streaming_latency typically reduces per-segment latency by
~40-50% with a modest quality trade-off that is acceptable for real-time
voice conversations. For maximum quality (narration, podcasts), leave it
disabled.Handle backpressure
If audio arrives faster than you can play it, bound your buffer instead of letting it grow:Common mistakes
- Per-segment
flush=true. Every flush is a fresh TTS request that pays the full model TTFA. If you flush after every sentence, you pay it N times per turn instead of once. - One session per sentence. A new WebSocket handshake plus a fresh model prefill, every sentence. Keep the same session open for the whole assistant turn; only end it when the turn ends — see Turn lifecycle.
- Client-side sentence buffering before
send. Unnecessary — the server already buffers tokens and chunks at sentence boundaries. Pre-buffering on the client just adds latency. - Calling
send(text, flush=true)per word “for lower latency.” It is the opposite: each flush is a separate model call. Word-granular flushing produces the worst possible TTFA.
Next steps
Latency
The numbers: what to expect, and how to measure TTFA correctly
Turn lifecycle
Flush semantics, the 5 s idle auto-flush, session reuse