Debug Cloudflare Voice Agent Latency

Trace slow and silent Cloudflare voice turns with stage timings and outcomes, then separate transcription, model, speech, and interruption problems.

Saturday, September 12, 2026Omid Saffari
Debug Cloudflare Voice Agent Latency

You can now prove where a slow or silent Cloudflare voice turn stopped before changing models, rewriting prompts, or paying for faster speech. @cloudflare/voice 0.4.0 gives every speech and text turn a typed outcome plus stage timings, so the first debugging question becomes "which stage failed?" instead of "which vendor should we replace?"

The short answer

Install @cloudflare/voice@^0.4.0 with agents@^0.22.0, listen for turnmetrics, and group each turn by its turnId, source, outcome, and the timing fields that are actually present. Cloudflare's September 11 release covers completed speech, text, empty output, model limits, content filtering, model errors, speech-generation errors, and aborted turns.

That is a meaningful change from the current Voice guide, last updated June 16, which still shows four compatibility metrics: llm_ms, tts_ms, first_audio_ms, and total_ms. Those four describe successful, non-empty speech turns. They do not tell you why a text turn, empty answer, interruption, or failed turn ended without audio.

Think of the old view as a delivery receipt. It tells you how long a successful parcel took. VoiceTurnMetrics is the scan history: one identifier follows the parcel through intake, sorting, dispatch, and completion, including the point where a failed parcel stopped.

TypeScript
client.addEventListener("turnmetrics", (turn) => {
  console.log(turn.outcome, turn.turnTotalMs);
});

The same latest summary is available through VoiceClient, useVoiceAgent(), and useVoiceInput(). The last of those is speech-to-text only, so it exposes only the speech and transcription measurements it can make.

Architectural infographic mapping speech, model, and TTS timing rails to one total voice turn
Read the timing rails as overlapping landmarks inside one turn, not numbers to add together.

What each timing can actually tell you

The useful unit is one turn. turnId is the correlation handle, source says whether the input was speech or text, and outcome says how the turn ended. Every other field is a duration in milliseconds.

Part of the turnFields to inspectWhat the measurement isolates
Speech becomes textspeechStartToFirstInterimMs, speechStartToFinalMsTime from provider speech start to the first partial transcript, then to the final transcript
Your transcript hookafterTranscribeMsTime spent in your server-side afterTranscribe hook
Model responsemodelToFirstTextMs, modelStreamConsumptionMsTime to the first non-whitespace model text, then time through normalized stream consumption
Exposed reasoningexposedReasoningMsCumulative time in reasoning blocks that the model stream exposes
First spoken responsefinalInputToFirstAudioMs, ttsToFirstAudioMsTime from finalized input, and from the first TTS call, to the first server audio send
Speech-generation workttsWallMs, ttsWorkMsWall time through all sentence work, and cumulative work across overlapping TTS jobs
Whole turnturnTotalMsTime from turn allocation to its terminal summary

Do not add these values. Cloudflare says the timings share one server clock and can overlap. Sentence chunking is the obvious example: the model can keep streaming while completed sentences are already being synthesized. Adding model and TTS durations would count some wall time twice.

An absent timing is also evidence. It means that lifecycle landmark was not reached. A text turn should not have speech-to-transcript fields. A no_output turn should not tempt you into tuning TTS because the model produced nothing for TTS to receive. A turn with no ttsToFirstAudioMs never reached the first server audio send from TTS.

There is one important boundary: browser playback is not part of VoiceTurnMetrics. Cloudflare excludes it because the Worker and browser use independent clocks. If the server reports a quick first audio send but the caller hears a pause, move your investigation to transport, decoding, device routing, or playback on the client.

Run three controlled tests before touching production

These are controlled SDK test observations, not production latency receipts. Their purpose is to prove that your instrumentation classifies known paths correctly before you trust it on customer calls. Use neutral prompts and keep conversation text out of logs.

Test 1: a completed speech turn

Start a call, speak one short fixed phrase, and let the agent finish its reply without interruption. The upstream Cloudflare test for this path receives source: "speech", outcome: "completed", a turnId, transcription measurements, model-stream consumption, TTS work, and a total turn duration.

Your check is structural, not competitive. Confirm that the same turnId appears with the terminal summary and that the stage fields you expect are present. Do not publish the resulting milliseconds as a speed claim from a single local run.

Test 2: a completed text turn

Send a fixed message with sendText(). This bypasses speech-to-text and goes straight to onTurn(). Cloudflare's controlled test receives source: "text", outcome: "completed", and model-stream timing. It does not receive speechStartToFirstInterimMs, speechStartToFinalMs, or afterTranscribeMs.

That makes text a useful control. If speech turns feel slow but comparable text turns reach first model text quickly, the model is a weaker suspect than transcription, turn detection, or the handoff into onTurn().

Test 3: a controlled empty response

In a test-only branch, make onTurn() return an empty stream for one known input. Cloudflare's upstream test classifies that speech turn as no_output. It produces no assistant transcript events, no compatibility metrics message, and no speaking state.

This is the cleanest proof that silence is not automatically a TTS problem. The turn never had response text to synthesize. Remove the test branch after the assertion and never key it from user-controlled conversation text.

Architectural test matrix comparing completed speech, completed text, and no-output voice turns
Three controlled paths establish which fields should appear before you interpret production turns.

Seven outcomes, seven different diagnoses

The outcome is the routing label. Treating every silent turn as one generic failure throws away the main value of the release.

OutcomeWhat it means for the next check
completedThe pipeline reached its ordinary terminal result. Inspect the slow timing landmark, then check client playback if server audio was sent quickly.
no_outputThe model completed without visible response text. Check prompt logic, tool branches, empty streams, and response normalization before TTS.
output_limitThe supported model stream ended for length. Inspect output limits and whether partial output is acceptable for speech.
content_filteredThe supported model stream reported content filtering. Review the request path and safety policy without logging the conversation itself.
model_errorModel streaming failed. Check the model event and provider-safe error metadata, not the voice renderer.
tts_errorResponse text existed, but one or more TTS attempts failed. Now the speech provider, audio format, and synthesis hooks are the right suspects.
abortedThe turn was interrupted, replaced, or disconnected before completion. Check barge-in behavior, disconnects, and cancellation handling.

The distinction between no_output, output_limit, content_filtered, and model_error matters because all four can look like "the agent said nothing" to a caller. Only one points first toward prompt or empty-stream logic. None points first toward buying a faster voice.

Where this pays first

The best uses are the ones where a wrong diagnosis creates repeat work or pushes a team into an unnecessary vendor change.

1. Customer-support agents with silent turns

A support engineering team can record content-free turn summaries beside a case identifier, group silent turns by outcome, and route each group to the model, safety, TTS, or connection owner. The payoff is fewer handoffs built on guesswork. A no_output cluster goes to response logic; a tts_error cluster goes to the speech path.

2. Appointment and reservation agents

A clinic or restaurant automation team can test a fixed booking flow, correlate every turn, and compare where delays enter before and after a release. The business value is not a prettier latency chart. It is knowing whether a caller waited on transcription, model text, speech generation, or local playback before changing the workflow that books revenue.

3. Voice-agent regression testing

A product team can keep a small suite of known speech, text, empty-output, and interruption cases. Each build can assert the terminal outcome and field presence, then compare stage distributions across releases. That catches a changed failure path before a broad end-to-end score hides it.

4. Provider comparisons without blaming the wrong layer

A team evaluating speech vendors can hold the prompt and model constant, then compare ttsToFirstAudioMs and TTS work across controlled runs. If the delay sits before model text appears, the TTS comparison is irrelevant. If TTS is the measured bottleneck, the low-latency TTS API comparison becomes useful instead of premature.

5. Multilingual transcription tuning

A multilingual service can run the same task with known utterances in each supported language and inspect speech-to-interim and speech-to-final timing separately from model time. This can expose a transcription or turn-detection problem that a single whole-turn number would hide. Accuracy still needs its own evaluation because fast transcription can be wrong.

6. Mixed text and voice interfaces

A field-service app can compare a typed control turn with a spoken turn through the same agent logic. Since text bypasses STT, a gap between the two paths narrows the search to speech intake and turn finalization. Shared turnId, source, and outcome fields let the team keep one diagnostic schema across both channels.

7. Interruption-heavy phone flows

An IVR replacement team can deliberately interrupt long replies and confirm aborted rather than counting those turns as unexplained failures. The payoff is cleaner failure reporting and safer cancellation work. It does not prove that callers liked the interruption behavior, so the team still needs audio review and user testing.

The budget decision this changes

The new event can cover first-pass stage triage inside a Cloudflare test app. It does not replace a full voice QA platform.

That distinction matters because current specialist products price a much broader job. Coval lists a $100 monthly Starter plan and a $500 monthly Growth plan, with simulation, monitoring, trace retention, and evaluation features. Roark lists $50 in starting credit, a $500 monthly Team tier spent as usage, and Enterprise pricing from $4,000 per month.

If your immediate problem is "which stage made this Cloudflare turn slow or silent?", instrument turnmetrics before buying that broader stack. If you need synthetic callers, scoring, alerts, long-term traces, human review, compliance workflows, or cross-platform comparisons, the SDK event is only raw material. The honest budget split is instrumentation for diagnosis, a QA product for an operating system around that diagnosis.

Three products worth building

1. A Cloudflare-native turn triage console

This is the strongest opportunity. The product would ingest content-free VoiceTurnMetrics, group outcomes, show stage distributions, and link related events by turnId. Voice-agent buyers are commercially valuable: DataForSEO reports 6,600 US searches a month for ai voice agent, with commercial intent and a $51.22 CPC. The narrow phrase voice agent latency is only 10 searches a month, which says this is a specialist wedge, not a broad consumer product.

The smallest sellable version needs an event collector, retention controls, filters for source and outcome, before-and-after release comparisons, and the decision routing in the final table below. Price pressure is visible in the current market: Coval starts at $100 a month, while broader team tiers from Coval and Roark sit at $500 a month.

The catch is platform concentration. Cloudflare can expand its own UI, and browser playback is outside the stable turn summary. The moat has to become workflow: release comparisons, regression evidence, privacy controls, and a fast route from a bad cluster to the responsible owner.

2. A latency regression gate for pull requests

This product would run fixed speech, text, empty-output, and interruption cases against a preview deployment, then fail a release when the wrong outcome appears or a measured stage regresses against the team's own baseline. DataForSEO reports 880 US searches a month for voice ai agent at a $36.64 CPC. The more specific low latency voice agent has only 10 searches a month but a $29.34 CPC, another small signal with expensive clicks.

The MVP is a test runner, a baseline store, outcome assertions, percentile comparisons, and a concise CI report. It should compare like with like and never add overlapping timings.

The catch is test fidelity. A synthetic microphone path does not reproduce every caller network, accent, browser, device, or telephony hop. Sell it as release protection, not proof of production experience.

3. A stage-aware provider benchmark lab

This product would let a team hold most of its pipeline constant, switch one provider at a time, and compare the stage that provider can actually affect. DataForSEO reports 40 US searches a month for voice agent platform, with commercial intent and a $44.12 CPC. That is modest volume, but the price of the click suggests vendors compete for a small pool of serious buyers.

The MVP needs repeatable prompts and audio fixtures, provider configuration, per-stage summaries, outcome rates, and an exportable decision report. Its value is stopping a fast TTS vendor from receiving credit for a model improvement, or blame for a transcription delay.

The catch is attribution. VoiceTurnMetrics measures SDK lifecycle landmarks, not a complete provider trace. Network placement, browser playback, input quality, and provider-side queues still need separate evidence.

What this does not solve

Turn metrics tell you where to look. They do not tell you whether the transcript was correct, the answer was useful, the voice sounded natural, the caller completed the job, or client playback felt smooth.

The browser-console diagnostic stream can help locally because it combines server lifecycle events with microphone, connection, first-audio, and playback events. Keep it as a temporary debugging aid. Cloudflare says it is off by default and its event names and fields can change, so it is not a stable analytics contract.

The stable summary is intentionally content-free, but your own messages can undo that protection. Cloudflare removes known content fields and does not inspect arbitrary provider bodies. Keep custom error strings free of transcripts, prompts, tool arguments, customer identifiers, and other conversation content.

Finally, the Voice guide is still marked Beta. Treat version pins, a controlled test suite, and release-by-release review as part of the implementation, not administrative cleanup.

Questions people search around this topic

What are Cloudflare Realtime Agents?

Cloudflare Realtime Agents are an earlier realtime voice runtime built around WebRTC, pipeline orchestration, and configurable speech and model components. The @cloudflare/voice package discussed here is the Agents SDK voice path over WebSocket. They are related Cloudflare voice offerings, but the September 11 turn-metrics release is specifically for @cloudflare/voice.

How do realtime voice agents work?

A typical turn captures speech, converts it to text, passes that text through application and model logic, synthesizes the response into speech, and plays audio to the caller. Cloudflare's package streams microphone audio over WebSocket, runs onTurn(), sentence-chunks streamed model text, and sends speech audio back.

Does Cloudflare offer AI agents?

Yes. Cloudflare's Agents SDK provides stateful agents built on Durable Objects, and @cloudflare/voice adds full voice and speech-input paths. The voice package remains Beta according to the current guide.

Where is the Cloudflare Agents GitHub repository?

The official repository is cloudflare/agents on GitHub. Its voice types and tests show the stable turn schema and controlled outcome behavior behind the release.

The Monday move: route the evidence

Next week, add the event listener to a test build and run the three controlled paths above. Keep only content-free summaries. Then use this table instead of changing a model on instinct.

Measured signalFirst investigationDo not change first
Slow speechStartToFirstInterimMs or speechStartToFinalMsMicrophone input, transcriber, turn detection, speech provider pathTTS voice
Slow afterTranscribeMsYour transcript hook and its dependenciesModel or TTS vendor
Slow modelToFirstTextMsModel choice, prompt path, tools, provider latencySpeech voice
Slow modelStreamConsumptionMs after quick first textStream handling, tool work, long responses, consumer waitsTranscriber
Slow ttsToFirstAudioMsTTS provider, sentence boundary, synthesis hook, audio formatModel prompt
Quick server first audio but late audible playbackBrowser transport, decoding, output device, playback queueServer model
no_outputEmpty model stream, prompt branch, response normalizationTTS provider
output_limitModel output limit and spoken-response lengthTranscriber
content_filteredSafety policy and request pathAudio transport
model_errorModel event and provider-safe error metadataTTS voice
tts_errorSpeech provider and synthesis pathModel choice
abortedInterruption, replacement, disconnect, cancellationAny latency provider before reproducing the abort

If you want a voice support system with this diagnostic loop built in, I can help you design and ship it.

Last Updated
Sep 12, 2026
Category
Build

Prefer this site in Google

Add omidsaffari.com as a preferred source in Google Search

Mark omidsaffari.com as preferred and Google lifts it in Top Stories, AI Overviews and AI Mode for you.

Related Articles
How to Test Claude Code Plugins With Evals

How to Test Claude Code Plugins With Evals

Run Claude Code plugin evals, compare results with a no-plugin baseline, and budget the repeated agent and judge calls before adding CI.Sep 12, 2026Build
Burn SRT Subtitles Into a Video With Rendi

Burn SRT Subtitles Into a Video With Rendi

Turn a video and SRT file into a captioned MP4 with Rendi, covering API submission, completion checks, subtitle styling, and output review.Sep 11, 2026Build
OpenAI Agents API vs Agents SDK

OpenAI Agents API vs Agents SDK

Compare the managed OpenAI Agents API with Agents SDK on session ownership, runtime control, sandbox costs, and migration work.Sep 11, 2026Build
Rendi Pricing (2026): Pick by Bytes, Not Video Length

Rendi Pricing (2026): Pick by Bytes, Not Video Length

Decode Rendi's FFmpeg API plans, byte-based processing, storage, runtime caps, and the smallest tier an automated video pipeline needs.Sep 11, 2026Build
How to Use Codex CLI Worktrees

How to Use Codex CLI Worktrees

Use Codex CLI worktrees for isolated coding sessions, then check branches, dependencies, resume behavior, and cleanup before keeping the change.Sep 10, 2026Build
How to Cap Claude Code Reasoning Effort

How to Cap Claude Code Reasoning Effort

Set Claude Code effort caps, understand which setting wins, and check routine-task quality while measuring token spend separately.Sep 10, 2026Build
agent-browser Video Recording FPS

agent-browser Video Recording FPS

Choose recording frame rates in agent-browser, check ffmpeg, and save readable QA videos with the new 30 fps default in v0.37.0.Sep 8, 2026Build
UltaHost VPS Renewal Pricing

UltaHost VPS Renewal Pricing

Decode UltaHost VPS renewal rates after the August price change, including introductory discounts, legacy plans, billing terms, and management limits.Sep 7, 2026Build
Newsletter

One letter, every Sunday.Working systems, not hot takes.

Weekly. No spam. Unsubscribe anytime.