unmute dev displays activity and values as they arrive, so you can inspect
finished work while a later step is still running.
This page is about reading the numbers. For the settings that move them,
see Optimizing your agent.
- Where the time goes - the parts of one turn
- What is making your agent slow - symptom by symptom
- How Unmute measures it - where the numbers come from
- What each number means - reading the dev page
- Reading a slow turn - a worked example
- What this does not measure - the honest limits
Where the time goes
Speech recognition, model generation and speech synthesis stream. Some work starts before an earlier stage has finished, and source measurements can cover more than one part of the wait. Their intervals can overlap. Do not add first-response values to reconstruct reply latency or total model time.Tool calls can add model requests
A common tool flow needs the model once to choose a tool and again to use its result:LLM 1, LLM 2, and later rows instead of
assuming every tool reply has exactly two model calls. The tool’s duration
covers its own work; separate model measurements cover the requests around it.
Two overlapping calls to the same tool keep separate rows and outcomes.
returned means the tool supplied a result. It does not prove the business
operation succeeded. Handoff and task controls are excluded from business
tool timing: their duration can include a whole sub-conversation.
What is making your agent slow
Compare the reported intervals and their source details to find a useful place to investigate.Turn detection is a wait you chose
A repeatedturn_detection value can point to a configured wait. Compare
several turns and the source’s definition before changing a model.
Three settings matter: the detector you bind, semantic_endpointing, and
pace. On LiveKit the detector also decides where its work happens:
turn-detector-mini runs on your machine, and turn-detector runs in LiveKit
Cloud. semantic_endpointing: off removes the turn model on LiveKit and the
end-of-turn analyzer on Pipecat; any other value keeps it running.
The floor and the ceiling are different numbers
endpointing_delay sets the silence floor. pace selects the endpointing
ceiling, taking snappy, balanced or patient on a turn binding and
defaulting to balanced.
If a turn is hitting the ceiling, lowering the floor alone will not shorten
that wait. Compare repeated low and high values, then check both settings.
Turn taking explains every legal value, what it
becomes on each target, and how to choose between them.
Or let the transcriber decide, and answer early
On Pipecat, a Deepgram Flux or Cartesia Turns listener can end the turn itself (turn: provider: listen), and with eager: true the reply is generated while
the transcriber is still confirming the caller stopped. The wait you see under
turn_detection then belongs to the transcriber, the ceiling is its own
end-of-turn timeout, and the early answer is dropped if the caller goes on.
Turn detection has the shape and what it refuses.
The model can think before it speaks
On a reasoning model,reasoning_effort controls how much reasoning happens
before the answer. The shared example profiles and unmute init write none.
Raising it trades more reasoning for more work before the answer is available.
A first-response measurement can precede visible words, so read its source
label alongside the streamed conversation.
The prompt is re-read every turn
Instructions go with each request, along with the conversation so far. A long instructions file can affect later requests as well as the first one. For tasks and handoffs you control how much is carried across withcontext.history, max_messages, and summarizer. Carrying the full history
into a task is a choice, not a default you have to keep.
How Unmute measures it
The generated agent observes native SDK activity, lifecycle events and timing reports. Each quantity keeps its source and scope. Model-call counts come from observed SDK invocations on both targets, not from the number of timing values. Hidden network retries are not counted as separate SDK invocations.1
The agent prints each update
The generated
utils/dev_metrics.py prints a marked JSON record when text,
activity or a measurement changes. Available values do not wait for a
completed-turn report.2
The dev server forwards the record
The local server reads the run’s output and forwards identified records to
the page, with a bounded replay buffer for reconnects.
3
The page updates the matching item
Call, operation and measurement identities keep late values on the right
item. Repeated records add no duplicate rows. Unknown associations remain
unassigned.
unmute dev enables UNMUTE_DEV_METRICS; the producer stays inert when it is
not enabled.
Raw updates, including recognized and generated transcript text, are kept in
build/<target>/dev.log. The browser’s measurement filter shows activity and
values with their call IDs, and omits repeated text fragments. To inspect raw
measurement records:
At a glance and debug details
Each reply shows its reported reply latency and observed model-call count. Under it, eachLLM N row shows its own first-response time and request
duration as soon as they arrive. TTS first-audio time and tool duration also
stay visible. There is no need to expand a reply to compare model requests.
Debug details holds turn detection, transcription delay, text aggregation,
node timings, playback delay, and speech duration. The Call diagnostics
inspector beside the conversation starts collapsed. It holds call first-speech
timing and activity or measurements with no proven exchange. These do not
interrupt the conversation.
It also holds model, provider, request IDs, and source names. Use these when the
main timings do not explain the wait. Definitions live here, rather than
repeating under every value in the conversation.
The Metrics view adds up the same values for the whole call. Its tiles show
reply latency, LLM first token and TTS first audio at p90. Each number is
colored against a limit: over the first limit it is slow (yellow), over the
second it is over the limit (orange, marked ▲). Red means a request failed. A
caller turn is green up to 900ms, slow above 900ms and over the limit above
1200ms. A turn where the model ran more than once, as it does around a tool
call, is not rated, because it cannot fit a one-request budget. Each tile names
its target, each bar marks its stage’s limit, and a legend under the turns
explains every color.
The view groups replies by caller turn. One caller turn can make several
replies, such as “Let me have a look.” and then the answer. The runtime reports
reply latency once, on the reply the caller heard first, so the turn carries it.
What each number means
Only captured measurements are shown. Missing and pending values have no
placeholder. A real zero is
0ms; positive values below one millisecond
show as <1ms. Values use milliseconds until rounding reaches one second,
then seconds with two decimal places.
Expand Debug details for model, provider, request and source names, even
when timing is unavailable. A reply-level or node-level value stays separate from a
request-level value. For example, LiveKit’s
llm_node_ttft is labelled LLM node
TTFT, and tts_node_ttfb is labelled TTS node TTFB. Neither
creates a model request or replaces that request’s own timing.
A known count says how many SDK model calls were observed for the reply, with
so far while it is open. A reply-level report alone shows no request count.
Partial source coverage or a lost slice of history labels the count as observed.
Where a reply’s time went, on Pipecat
Pipecat splits each measured reply into the parts that make it up, and the dev page shows them in time order in the Reply time section of the call diagnostics:
The first line says what the interval was measured from: the caller falling
silent, or the client connecting. A greeting is anchored on the second, so it is
never compared with a reply to someone who spoke.
This breakdown carries no reply identity, so it has its own section rather than
being attached to a reply by timing. The Metrics view lists every breakdown
in arrival order, one column per part. LiveKit reports its own per
request timings instead and has no equivalent split.
Reading a slow turn
A reply might show:failed with the kind
of error its handler raised, or timed_out when it ran past its deadline. A tool
or task cut off by a hang-up is not an error. It reads cancelled on LiveKit and
incomplete on Pipecat. A call
that ends because something broke names the service that stopped it. The messages
themselves are in build/<target>/dev.log, because an error’s text quotes what
was being worked on and these records carry no prompts or tool arguments.
Every model call after the first follows a tool call or a control, and the row
above it says which. A HANDOFF row is a call into a task, or a handoff to
another agent. It carries no duration, because it hands control over rather
than returning a result, and on LiveKit that call stays open until the task
finishes.
The three LLM rows describe three requests. Their first-response values do not
add up to reply latency. The later request-duration value also does not replace
the first-response value for LLM 1. Inspect each request and tool before
choosing which part to change.
A long speech duration can exceed reply latency without a problem: the caller
is listening during that speech. Keep it separate from the wait for the first
audio.
What this does not measure
- Delivery to the listener. Runtime timings exclude the trip to your browser or through a phone carrier. Provider request timings can still include the worker’s network trip to its model service. Verify the phone route on a deployed agent.
- Unavailable source detail. Targets and services expose different quantities and associations. A value without a proven reply stays unassigned, even when it arrives while another reply is visible.
- Complete history after a gap. Feed loss does not restart audio. If the replay cannot recover all records, the incomplete-history label and observed count remain until a new call, even after individual values become fresh.
Where to go next
Turn detection
The settings behind turn detection, which can be a large part of the wait.
Pre-fetch
Remove a round trip instead of waiting it out.
SLNG Execution Layer
What the speech models a scaffolded package binds run on.
Tools
Every kind of tool a turn can call, and what each costs you.