- Architecture
Sentence boundaries for streaming TTS
Build a conservative streaming text segmenter with abbreviation, decimal, revision, and end-of-stream fixtures.
- Author
Udit Goenka- Editorial date
- Reading time
- 25 min

TL;DR: Keep an unsent text tail, emit only boundaries your policy can defend, and flush the remaining text only when the producer confirms completion. Test lossless reconstruction separately from pronunciation and prosody, especially around abbreviations, decimals, revisions, and canceled responses.
A period arrives in the model stream. The speech adapter immediately submits the prefix to TTS. The next delta turns that period into part of a decimal, an abbreviation, or an address, but the first fragment has already been spoken.
Streaming sentence boundaries are commit decisions. Before a prefix reaches synthesis, you can revise it. After its audio reaches the caller, you need an explicit conversational repair rather than a silent replacement in a text buffer.
Begin with a conservative rule: preserve the producer's exact text, retain the uncertain suffix, and make every emission traceable to its source interval. This guide includes a runnable JavaScript segmenter and synthetic fixtures. It does not claim a universal natural-language parser or a measured latency result for 60db Agent.
As checked on November 9, 2026, Unicode Text Segmentation distinguishes default boundaries from language-specific tailoring and explains why punctuation alone can be ambiguous. That makes a boundary library useful evidence, while the application's speech policy still owns when text is committed.
Table of contents
- Define a speech-unit contract
- Separate transport deltas from language boundaries
- Implement a conservative streaming adapter
- Design fixtures around meaning
- Measure latency and listening quality
- Handle revisions and cancellation
- Frequently asked questions
- Ship a boundary policy you can test
Define a speech-unit contract
A speech unit is the text interval your application submits as one committed synthesis input. It can be a sentence, an approved short acknowledgment, or a deliberately bounded clause. The name should describe its role without implying that every unit is a complete grammatical sentence.
The contract needs an input model. An append-only producer sends new text after earlier text. A revision producer can replace text that appeared before. These models need different adapters; treating a replacement as a new delta duplicates words and can send contradictory speech.
For append-only input, maintain an emitted prefix and a pending suffix. Their concatenation must equal the exact producer text received so far. Trimming whitespace, normalizing punctuation, or expanding abbreviations inside this accounting step breaks that invariant and makes later diagnosis harder.
Normalization belongs in a separate stage with its own source mapping. A speech renderer may choose a spoken form for a URL or abbreviation, but the original canonical value should remain available for validation and tool use. Sentence detection should not quietly rewrite the business value it is trying to speak.
State what commitment means
A prefix can be accepted by a synthesis service, generated as audio, queued for playback, or heard by the caller. Those are separate commitment boundaries. Name which boundary your retry and cancellation logic observes.
A provider can accept a text segment while the network response is lost. Retrying the same segment can generate duplicate audio if you append both results later. Keep a segment identifier and response epoch through text submission and output admission, even when the external service has no idempotency mechanism.
At the caller boundary, the conversation has advanced once words are heard. If a later model delta contradicts those words, replace the remaining response and speak a repair where appropriate. A text UI that edits history does not undo speech already delivered.
This relationship is one reason the interruption-safe voice guide treats generated work and playback ownership separately. A sentence segmenter should expose enough identity to participate in that policy without controlling the entire conversation itself.
Choose the first supported language scope
Start with the languages and scripts your product supports. Include the punctuation, abbreviations, quotation conventions, and mixed-language identifiers that appear in that scope. An English abbreviation list is not a multilingual boundary model.
For browser or JavaScript runtimes, Intl.Segmenter provides locale-sensitive segmentation, including sentence granularity. Feature-detect it and test the runtime versions you ship because available data and behavior belong to the actual runtime, not to a README promise.
A library's output is still a candidate boundary when its input is incomplete. If you segment the text received so far, its last span may be an unfinished sentence. Retaining that final span is a useful streaming policy, but it does not solve all ambiguity at earlier punctuation.
Document unsupported cases. You can ship a bounded English-oriented adapter for a specific corpus if it refuses ambiguous input and preserves text. Claiming universal support makes a narrow implementation harder to test and harder for integrators to use safely.
Separate transport deltas from language boundaries
A model delta is a transport or generation event. It can end inside a word, a number, a Unicode sequence as represented by an upstream protocol, or a punctuation run. A network callback is not evidence that a sentence is ready for speech.
Decode the upstream stream according to its protocol before applying language logic. If it delivers UTF-8 bytes, use a streaming decoder so a multibyte character split across reads is reconstructed correctly. If it delivers JSON text strings, follow the event protocol and distinguish content deltas from control events.
The TextDecoder.decode() documentation describes the streaming option for continued input. Keep that byte-decoding boundary separate from the sentence state, so tests can identify whether a broken character came from transport decoding or segmentation.
Decimal values
The synthetic input "The price is 12.50 credits." contains a period inside a value and another at the end. Emitting at the first period can turn one value into disconnected speech, even if the remaining digits eventually arrive in order.
A digit on both sides of a period is a straightforward guard for that fixture. It is not a complete numeric grammar: version strings, grouped values, localized decimal separators, and dates need additional domain rules. Preserve the value until its intended spoken form is resolved.
A lookahead delay prevents one immediate failure: receiving "12." before receiving "50". The adapter can wait for a following character rather than treating the current end of the buffer as a confirmed boundary. End-of-stream is a different signal and should arrive explicitly from the producer.
Abbreviations and initials
"Dr. Rao can help." should not become a complete speech unit at "Dr." in a policy meant to preserve the phrase. A local abbreviation guard can hold that boundary until more context arrives.
Initials and dotted abbreviations require their own fixtures. "A. B. Rao" and "e.g." create several plausible period positions. A finite list can handle a bounded corpus, but adding more entries indefinitely is a poor substitute for a language-aware library and listening tests.
The spelling context matters. Matching an abbreviation substring inside another word can corrupt text even before TTS sees it. Anchor any normalization rule to its intended lexical boundary, and prove with negative fixtures that unrelated words remain unchanged.
Quotes and punctuation runs
Closing quotation marks and brackets often belong with the sentence they close. If you emit before those characters arrive, the next unit can begin with detached punctuation and receive awkward prosody.
Retain a small lookahead and include closing marks in the emitted interval when they are present. Test a punctuation run such as "Really?!" and a quoted statement separately. The exact spoken pause is a listening property; source preservation is a deterministic property.
An ellipsis can express hesitation or an unfinished thought. Decide whether your supported content treats it as a boundary, a pause within a unit, or a reason to wait. Do not let a generic period rule make that product decision accidentally.
Clauses and incomplete commitments
A comma can be a useful low-latency speech boundary in some systems, but a clause can end before the words that qualify it. "You can cancel" followed by "only before dispatch" is a different message from the short prefix alone.
For a consequential answer, prefer a complete qualified statement or an approved short acknowledgment while the answer forms. A segmenter should not be asked to infer whether an unfinished clause is safe business advice from punctuation alone.
The truthful waiting-speech guide gives a separate policy for approved acknowledgments. Keeping those short messages explicit reduces pressure to make an unsafe prefix sound responsive.


A transport delta can stop where punctuation or meaning is still incomplete.
Implement a conservative streaming adapter
The following example is intentionally bounded. It handles selected English punctuation, a small abbreviation set, digit-period-digit decimals, and closing quotes. It accepts append-only strings, preserves every character, and uses explicit completion to flush an unresolved tail.
Save it as sentence-stream.mjs and run it with a modern Node.js runtime. The assertions are synthetic fixtures. They prove the listed accounting and boundary behaviors, not language coverage or acceptable prosody for every TTS voice.
import assert from "node:assert/strict";
const ABBREVIATIONS = new Set([
"dr.", "mr.", "mrs.", "ms.", "prof.", "e.g.", "i.e."
]);
const END = new Set([".", "!", "?", "。", "!", "?"]);
const CLOSE = new Set(['"', "'", ")", "]", "”", "’"]);
export class SentenceStream {
constructor() {
this.pending = "";
this.emitted = "";
this.source = "";
this.closed = false;
}
push(delta, { final = false } = {}) {
if (this.closed) throw new Error("stream already complete");
if (typeof delta !== "string") throw new TypeError("text required");
this.source += delta;
this.pending += delta;
const output = [];
let i = 0;
while (i < this.pending.length) {
const ch = this.pending[i];
if (!END.has(ch)) { i++; continue; }
if (ch === ".") {
const before = this.pending[i - 1] ?? "";
const after = this.pending[i + 1] ?? "";
if (/\d/.test(before) && /\d/.test(after)) { i++; continue; }
const token = this.pending.slice(0, i + 1)
.match(/[A-Za-z.]+$/)?.[0].toLowerCase() ?? "";
if (ABBREVIATIONS.has(token) || /^[a-z]\.$/.test(token)) {
i++; continue;
}
}
let end = i + 1;
while (END.has(this.pending[end]) || CLOSE.has(this.pending[end])) end++;
if (end === this.pending.length && !final) break;
if (end < this.pending.length && !/\s/.test(this.pending[end])) {
i++; continue;
}
while (/\s/.test(this.pending[end] ?? "") && end < this.pending.length) end++;
const unit = this.pending.slice(0, end);
output.push(unit);
this.emitted += unit;
this.pending = this.pending.slice(end);
i = 0;
}
if (final) {
if (this.pending) {
output.push(this.pending);
this.emitted += this.pending;
this.pending = "";
}
this.closed = true;
}
assert.equal(this.emitted + this.pending, this.source);
return output;
}
}
const stream = new SentenceStream();
assert.deepEqual(stream.push("Dr."), []);
assert.deepEqual(stream.push(" Rao owes 12."), []);
assert.deepEqual(stream.push("50 credits. Next"), ["Dr. Rao owes 12.50 credits. "]);
assert.deepEqual(stream.push(" step?"), []);
assert.deepEqual(stream.push("", { final: true }), ["Next step?"]);
assert.equal(stream.emitted, "Dr. Rao owes 12.50 credits. Next step?");
assert.throws(() => stream.push("late"), /complete/);
for (const text of [
'She said "Go." Then wait.',
"A. B. Rao can help. Please wait.",
"Version 2.4 is ready! Continue?",
"Short unfinished tail",
"你好。 下一步。"
]) {
for (let split = 0; split <= text.length; split++) {
const s = new SentenceStream();
const parts = [...s.push(text.slice(0, split)),
...s.push(text.slice(split)), ...s.push("", { final: true })];
assert.equal(parts.join(""), text);
}
}
console.log("Synthetic segmentation fixtures passed");
The implementation preserves whitespace instead of trimming each unit. That choice makes exact reconstruction simple. A downstream renderer can decide how to treat leading or trailing space, but it should retain the mapping to the original source interval.
The abbreviation and initial guards deliberately favor waiting. At final completion, an unresolved tail is emitted as text without claiming the tail is a complete grammatical sentence. If your product requires a complete sentence, validate that separately before synthesis rather than pretending every flush is a sentence boundary.
Known limits deserve a test plan
This sample does not recognize arbitrary URLs, email addresses, markdown, code, escaped quotations, or every numeric format. It also does not guarantee natural sentence decisions for every script. The multilingual fixture checks lossless reconstruction only; it is not a multilingual quality certification.
A URL can contain punctuation that resembles a sentence boundary. If the application speaks URLs, identify or render that structured span before applying general speech boundaries. Keep the canonical URL intact for display or tools and test the spoken representation separately.
For production, use an existing language-aware facility where it meets the contract, then add the smallest application policy around its incomplete final span. The sample is useful as a transparent baseline and a fixture harness, not an argument for maintaining a large home-grown linguistic engine.
Keep markup outside the plain-text adapter
The sample accepts plain text. If your application sends speech markup, validate it as markup and preserve its structural boundaries before applying text segmentation. Cutting a serialized tag in half creates invalid input even when the visible text looks like a complete sentence.
The W3C SSML specification defines speech-synthesis markup, including elements that affect pronunciation and pauses. Provider support is a separate question. Check the documented subset for the service and voice you use instead of assuming that any valid SSML document is accepted identically everywhere.
Build a small fixture with nested emphasis, a pronunciation element, and a pause if your supported interface exposes those features. Verify well-formedness at every submitted unit and keep the canonical text mapping available. A plain string splitter should reject this input class or receive already extracted text, rather than guessing which angle brackets are safe to move.
Escaping is another trust boundary. Model-produced text containing markup characters should not acquire speech-control authority merely because it passes through a string concatenation. Use the receiving interface's plain-text field where possible, or escape text with the platform's established XML tooling before adding approved markup.
Make fixture failures explain the boundary
Use assertion messages that identify the source text, upstream split position, and expected unit when a fixture fails. The Node.js assertion API supplies strict comparisons suitable for a small deterministic harness. A test runner can provide better grouping when the corpus grows, but a new framework is unnecessary for this example.
Keep raw fixture text synthetic and small enough to inspect. When an assertion catches a duplicate trailing space or lost quote, determine whether the difference violates the source contract before updating an expectation. A speech renderer may ignore that character, while the source-mapping contract still depends on it.
Test producer errors too. A non-string delta, a delta after explicit completion, and a completion event repeated after closure should have deliberate outcomes. Silent coercion hides protocol errors, while a clear failure lets the caller replace the response or enter a controlled recovery state.
For a revision-capable integration, add separate tests for replacing only the pending tail, revising the committed prefix, and receiving an old epoch after a new response starts. Do not extend the append-only sample by quietly mutating its source string; define the revision contract and its observable outcomes first.
Keep the deterministic harness distinct from the listening report. Passing string assertions establishes preservation and selected boundary behavior. It cannot certify pronunciation, voice quality, cultural appropriateness, or the business truth of the answer. Those questions need evidence appropriate to their own boundary.
Bound memory without silently deleting text
A producer can send a long sequence without punctuation. Set a maximum pending-text budget and define what happens when it is reached. Possible policies include an explicitly approved clause boundary, asking the producer for a complete unit, or failing the response with a controlled repair.
Do not truncate the pending suffix to protect memory and continue as if nothing happened. That changes the spoken answer while preserving the appearance of success. Surface a policy outcome that downstream code and evaluation can count.
The budget should account for representation. JavaScript string length counts UTF-16 code units, while a service limit can count bytes or characters under its own definition. Validate against the actual API limit after encoding, and retain a separate application budget for memory and waiting.
Design fixtures around meaning
A segmentation suite needs both boundary assertions and preservation assertions. Preservation proves that text survived. Boundary assertions prove that chosen intervals match the intended policy. Listening review determines whether those intervals produce acceptable speech.
Make every fixture name a behavior. "Decimal remains in one unit" is more useful than a numeric test identifier because it tells a future maintainer why a seemingly harmless period rule cannot change without review.
Use synthetic text for deterministic tests and mark it as such. You do not need customer transcripts to cover abbreviations, decimals, punctuation runs, cancellation, or malformed upstream events. Real consented speech can be added later to evaluate the wider conversation experience.
Chunk-invariance tests
The same completed text should reconstruct identically regardless of upstream delta boundaries. Split a fixture at every code-unit position for a basic harness, then add actual byte-stream splits around multibyte characters at the decoder layer.
Do not require identical emission timing for every split. A stream that delivers a full sentence in one event exposes different available context from a stream that pauses after an abbreviation. Require identical preserved text and policy-valid emissions under the context available at each point.
Keep an assertion that no emission contains text absent from the source. Another should ensure a source interval is admitted once per response epoch. These checks catch accidental re-expansion, repeated tails, and duplicate retries before a listening test has to find them.
Semantic fixture matrix
The table describes what each test can establish. A check means that test directly checks the criterion; a cross means another method is required. It is a test-design comparison, not a vendor feature ranking.
| Test | Exact preservation | Boundary policy | Spoken quality | Late-output rejection |
|---|---|---|---|---|
| Rejoin emitted units and tail | ✓ | ✗ | ✗ | ✗ |
| Expected-unit fixtures | ✓ | ✓ | ✗ | ✗ |
| Consented listening review | ✗ | ✓ | ✓ | ✗ |
| Canceled-epoch replay | ✓ | ✓ | ✗ | ✓ |
Build a negative fixture beside each special rule. If "Dr." is protected, test a different word ending with similar characters. If decimal periods are protected, test a sentence ending in a number. If closing quotes are absorbed, test an unmatched quote and record the intended fallback.
Ambiguity should remain visible. A test can say that a phrase requires domain handling instead of forcing a single natural-language answer. The useful property is that the application stops or waits consistently, with a reason an engineer can diagnose.
Structured values need separate treatment
Identifiers, money, dates, and addresses should retain typed source values through the speech path. A period in a version number is different from a sentence mark, and a decimal value can be misread even when it stays inside one segment.
A structured renderer can generate approved speech from a canonical value while exposing its provenance. The segmenter then receives a span with an explicit no-split boundary or an already approved speech unit. Keep this interface small and avoid making the segmenter a general business parser.
The workflow-enforcement guide gives the larger authority boundary: spoken text is one presentation of application state. Segmenting that presentation cannot validate whether the underlying value or promised operation is correct.
Preserve disagreement examples
When human reviewers disagree about a boundary, retain the text, locale, chosen voice, and reason under approved privacy rules. Some disagreements concern grammar; others concern pacing or the action implied by a prematurely spoken prefix.
Separate those causes in the review notes. A grammar library update may repair the first without changing the second. A speech policy change may need business review even when all deterministic segmentation fixtures still pass.
Use regression-testing practices to freeze a reviewed subset and keep a held-out set for release decisions. Rewriting fixtures to match a changed library erases the evidence needed to decide whether the change improved the product.
Measure latency and listening quality
Boundary selection contributes waiting between text arrival and synthesis submission. Record the first relevant text delta, the selected boundary, submission, first audio received, and playback evidence. Keep the timestamps in their own clock domains.
The latency benchmark guide distinguishes first received audio from caller-side playback. The segmenter report should preserve that distinction rather than claiming an end-to-end win from an earlier submission event alone.
Record why a unit was emitted: sentence boundary, explicit producer completion, approved acknowledgment, or a budget fallback. If different reasons are pooled, a latency improvement can hide increased use of incomplete clauses or a more aggressive timeout policy.
Submission APIs do not settle language policy
A service's streaming interface determines how you send configuration and text, not whether each fragment is semantically safe. Google Cloud's bidirectional TTS quickstart documents its streaming input contract. Check the current voice and model support before building an adapter around it.
Read the interface's completion and flushing semantics. A client closing its writer, an explicit final text event, and a connection interruption can have different meanings. Do not treat transport closure after an error as proof that the producer completed its answer.
If your provider buffers text internally, measure the observed result instead of assuming every submitted fragment triggers immediate audio. A segmenter can expose text earlier while the service retains it for its own generation policy. That behavior belongs in the tested environment notes.
Compare answer cohorts
Use several content types: short factual answers, qualified instructions, structured values, multi-sentence explanations, and clarification questions. Include very short responses so a conservative policy does not accidentally wait forever for punctuation the producer never emits.
Keep language cohorts visible. A segmentation rule optimized on English punctuation can behave differently with other scripts or code switching. Use the multilingual evaluation guide to define supported language mixtures and listening coverage.
Measure response completion as well as first sound. Overly fragmented synthesis can introduce repeated pauses, inconsistent intonation, or awkward joins. A fast first segment is only useful if the remaining speech arrives coherently and the caller can understand the answer.
Listen for the consequence of a boundary
Ask reviewers whether a split changes meaning, creates a misleading pause, clips a word, or makes a value hard to understand. Give them the canonical text and the actual audio under a consistent review protocol. Record disagreements rather than converting every opinion into a numeric score.
A sentence can remain grammatically intact while its speech sounds poor. Conversely, a deliberate short clause can sound natural and preserve meaning under a reviewed policy. The acceptance rule should describe what the caller needs to understand, with deterministic text tests covering what machines can prove.
Use audio chunk measurements to diagnose downstream gaps before changing sentence rules. A pause caused by a playback underrun should not be repaired by making the language segmenter emit unsafe fragments.


Revising unsent text and repairing already committed speech require different paths.
Handle revisions and cancellation
The sample accepts append-only input. If the upstream system revises earlier text, normalize that behavior into a replaceable pending tail before it reaches this adapter, or use a separate adapter with explicit revision intervals.
Track the longest committed source prefix. A revision wholly inside the pending suffix can be applied before segmentation resumes. A revision that overlaps emitted text is a conversational repair event, because downstream synthesis or playback may already have made that text observable.
Do not append a full revised snapshot as if it were a delta. The result often repeats the beginning of the answer and can produce duplicate actions if spoken content is also used to drive state. The partial-transcript revision guide shows the related input-side distinction.
Invalidate the whole response path
On cancellation, close the text producer, discard the pending suffix under an explicit invalidation outcome, and reject late deltas from the old epoch. Also cancel or invalidate synthesis results and queued audio through the controls your integration provides.
A segmenter reset is insufficient once text has left the process. Carry response and segment identity into the audio path so a late successful synthesis response cannot repopulate a new turn's queue. Test this race with a delayed callback after the cancellation event.
Keep invalidation separate from final completion. A normal final event flushes the pending suffix. A cancellation discards it. Calling the same generic cleanup function for both can accidentally speak a half-finished answer precisely when the user has interrupted it.
Recover from errors without duplicate speech
When synthesis fails for one segment, decide whether to retry that segment, replace the response, or give a controlled failure message. Base the choice on what has already been admitted to playback and what the provider exposes about the request outcome.
An unknown synthesis outcome is different from a confirmed rejection. If you cannot determine whether audio was produced or accepted downstream, the safe replay boundary may be a new response epoch rather than blindly resending the same bytes into an existing queue.
Preserve enough state to explain the outcome without retaining unnecessary text. A content-free event can include source offsets, segment length, emission reason, epoch, and disposition. Keep actual text only where approved review or reproduction requires it.
Bound the downstream speech queue
A correct segmenter can still produce speech faster than the caller can hear it. Track how many committed units are waiting for synthesis and playback, and retain their response epoch. A long queue increases both answer delay and the amount of content that needs invalidation after interruption.
Use the downstream interface's supported backpressure mechanism rather than adding an unbounded array of pending requests. If synthesis runs concurrently, preserve the original segment order when admitting audio. A later short segment can finish first without becoming the first sentence the caller should hear.
Test this with synthetic service completions delivered in reverse order. The expected output remains the source order, with canceled epochs omitted under the chosen policy. Include a failure in the earlier segment so the queue cannot wait forever for a result that will never arrive.
Keep that recovery decision explicit. The application can replace the response, report a controlled error, or retry under its outcome policy. It should not skip an arbitrary middle sentence and continue as though the answer were intact, especially when that sentence contains a qualification or a business condition.
The segmenter needs only enough identity and ordering metadata to support this handoff. It does not need a new general scheduler. Reuse the application's existing response queue and add the missing source interval or emission reason when the contract requires it.
Record the producer completion contract
Keep the event name and conditions that authorize a normal tail flush in the integration notes. A completed generation, an output limit, a moderation stop, and a transport error can all stop new deltas while requiring different speech behavior. Map each source event to a deliberate completion or invalidation outcome.
Add one fixture per supported stop reason and assert whether the tail is emitted or discarded. This prevents a future SDK update from routing every closed stream through the normal flush path. The resulting tests protect the caller from hearing text that was never approved as a complete response.
Release and rollback
Version the boundary policy, locale data, normalization rules, and tested runtime together in the release record. A library update can change segmentation even when your adapter code remains unchanged. Freeze expected fixtures so that change becomes visible.
Run deterministic tests first, then the approved listening corpus and latency trial. Reject a release that improves submission timing by losing text, misreading structured values, or allowing stale speech after cancellation. Those are correctness failures, not acceptable costs of responsiveness.
A rollback should restore the earlier policy and its compatible fixture set. Keep new disagreement cases as evidence rather than deleting them. They can reveal a gap in both versions that deserves a separate bounded change.

Normal completion flushes a tail; cancellation invalidates it.
Frequently asked questions
Should I send every model token to TTS?
Only when the service contract and your speech policy support that input pattern. Tokens can split words, values, and qualifiers. Preserve a pending tail and measure the provider's actual buffering behavior. Earlier submission alone does not establish earlier useful speech or acceptable pronunciation.
Is splitting on periods enough?
Periods also appear in abbreviations, decimal values, addresses, initials, and version strings. A period-only splitter needs explicit scope and negative fixtures. A language-aware segmenter can improve candidate boundaries, while streaming completion and semantic commitment remain application decisions.
Can Intl.Segmenter solve streaming segmentation?
It provides locale-sensitive candidate segmentation for the text you supply. It does not know whether more text is still coming or whether a prefix is safe to speak. Retain the unfinished final span, test actual runtime behavior, and add only the application policy your supported corpus requires.
Why preserve whitespace exactly?
Exact preservation gives you a simple accounting invariant: emitted text plus the pending suffix equals the source received so far. A separate rendering stage can normalize presentation with a recorded mapping. Trimming inside segmentation makes loss and duplication harder to distinguish from deliberate edits.
What should end-of-stream mean?
It should mean the producer explicitly completed the intended response under its protocol. A network disconnect, timeout, or cancellation can also end transport without completing the answer. Give those outcomes separate events so normal flushing does not speak an invalid partial response.
How long should the pending tail wait?
Set a product-specific waiting and memory budget using your response workload. A fixed timeout cannot prove that a clause is complete. When the budget expires, use an approved fallback, request completion, or fail visibly rather than silently treating arbitrary text as a safe sentence.
How do I handle Dr. and similar abbreviations?
Use locale-aware rules or a bounded reviewed abbreviation set, with fixtures for both matching and unrelated words. Delay emission when more context is needed. Do not expand abbreviations by unanchored substring replacement, because that can alter unrelated source words before synthesis.
How do I protect decimal values?
A digit-period-digit guard handles a narrow decimal fixture, but structured numeric parsing belongs outside a generic sentence splitter. Preserve the canonical value and its intended spoken form. Test localized formats, sentence-final numbers, and deltas that stop immediately after the decimal mark.
Can commas be speech boundaries?
They can be under a reviewed clause policy, but a comma does not guarantee a complete or safe commitment. Qualifiers may arrive later. Test whether each emitted prefix preserves meaning, especially for prices, availability, permission, and instructions that can affect a caller's decisions.
Why test every upstream split position?
It checks that transport event boundaries do not lose or duplicate text. A fixture may arrive as one string or several partial deltas. Require exact reconstruction and policy-valid emissions across those deliveries, then test byte decoding separately where multibyte input can be split.
Are identical emission boundaries required for all splits?
No. Different delivery patterns expose different context at a given time. A conservative adapter can wait longer when the next character is unavailable. Require preserved text and defensible boundaries, rather than forcing an adapter to guess context it has not received.
How should I handle a URL?
Treat it as a structured span with a canonical value and an explicit spoken rendering. URLs contain punctuation that can resemble sentence boundaries. Protect the span or submit an approved speech unit, and keep the original value available for display, validation, or tool use.
What happens when the model revises spoken text?
A revision inside the pending suffix can replace unsent text. A revision overlapping emitted or heard text requires a controlled repair, because changing a buffer cannot retract speech. Track committed source intervals and response epochs so the application can identify that boundary.
Does the sample support every language?
No. It is a bounded example with selected punctuation and English-oriented guards. Its multilingual fixture checks reconstruction, not natural boundary quality. Use supported locale data and a reviewed language corpus before making a production claim about multilingual speech behavior.
How do I measure segmentation latency?
Measure from relevant text availability to the selected boundary and synthesis submission within the owning process. Keep first audio and playback observations separate. Record the emission reason so a change in fallback frequency does not masquerade as a faster sentence detector.
Why can fast first audio sound worse overall?
Very short segments can produce repeated pauses, inconsistent intonation, or unnatural joins. Measure completion and listening quality alongside first sound. Diagnose downstream playback gaps separately, because changing language boundaries cannot reliably repair a queue or scheduling failure.
Should cancellation flush the remaining text?
No. Normal completion and cancellation have different semantics. Completion can flush a valid pending suffix; cancellation invalidates it. Test that a late producer or synthesis callback from the canceled response cannot add text or audio to the next response's queue.
Can I retry a failed segment automatically?
Only under a policy that accounts for the observed request and playback outcome. A lost response can leave an unknown result rather than a confirmed failure. Preserve segment identity and reject duplicate output admission so a retry does not cause the same words to play twice.
What belongs in a production regression set?
Include reviewed abbreviations, decimals, identifiers, quotes, punctuation runs, unfinished tails, long unpunctuated text, locale cases, and canceled epochs. Pair exact-preservation tests with expected-boundary assertions and consented listening checks. Keep disagreement cases so later changes can be assessed against evidence.
How does this guide relate to 60db Agent?
It supplies a generic contract for evaluating streamed speech around documented interfaces. Apply the fixtures and measurements to your own integration. The article does not describe a private implementation, promise a particular boundary policy, or report measured latency or quality for 60db Agent.
Ship a boundary policy you can test
Start with exact preservation, explicit completion, and a small supported language scope. Run the fixture harness before listening tests, then compare useful-answer timing under a fixed workload and a documented playback boundary.
Add the reviewed policy and cancellation races to your voice regression suite. If the pending tail becomes a real source of caller delay, collect that evidence before adding more aggressive clause emission or a new linguistic dependency.
