Turn latency barPro
Where a voice agent's response time goes. Each turn splits into transcriber, endpointing, LLM, voice and transport, with the call's p50 and p90 marked and the budget dashed, so the slow turn and the stage to fix are obvious.
On this page
Installation
Usage
import { TurnLatencyChart } from "@/components/ui/chatcn/pro/turn-latency-bar"
<TurnLatencyChart turns={call.latency} budget={800} onSelectTurn={(t) => setTurn(t.id)} />A turn is the time from the caller going quiet to the first audio they hear. Pass the milliseconds each stage took; leave a stage out when your stack does not report it and the bar says so instead of guessing. Four parts share one data shape: TurnLatencyChart puts every turn on one scale, TurnLatencyBar shows one turn (with a compact sm size for transcript rows), TurnLatencyBreakdown gives p50 and p90 per stage, and TurnLatencyLegend names the colors.
The axis stops at 2.5 times the median turn, so one slow turn after a tool timeout runs off the edge with its real value and a chevron instead of squeezing every other bar into a sliver; a p90 past the edge is marked at the end. Failed turns are left out of the percentiles and drawn with a hatched end where the stage failed.
Examples
One turn, with markers and inline
Loading, empty, outliers, failures and RTL
Props
TurnLatencyChart
| Prop | Type | Notes |
|---|---|---|
turns | TurnLatency[] | { id, label?, stages: { transcriber?, endpointing?, llm?, voice?, transport? }, status?, failedAt? }, ms per stage |
budget | number | false | Response budget in ms. Default 800. Turns past it are flagged |
scale | number | Ms at full width. Default 2.5 times the median turn |
onSelectTurn, activeTurnId | (turn) => void, string | Rows become toggle buttons |
loading | boolean | Skeleton rows that mirror the layout |
empty | ReactNode | Shown while there are no turns |
hideSummary | boolean | Hide the stat line and legend |
label | string | Accessible name. Default "Response latency by turn" |
TurnLatencyBar
| Prop | Type | Notes |
|---|---|---|
turn | TurnLatency | One turn |
size | "md" | "sm" | md with a header and stage values; sm inline. Default md |
p50, p90 | number | The call's percentiles, drawn as markers |
budget | number | false | Default 800 |
scale | number | Ms at full width. Default fits the turn, budget and p90 |
onSelect, selected | () => void, boolean | Makes the bar a toggle button |
TurnLatencyBreakdown
| Prop | Type | Notes |
|---|---|---|
turns | TurnLatency[] | p50, p90 and share per stage; the slowest stage is set in ink and named for screen readers |
label | string | Table caption. Default "Latency by stage" |
Keyboard
| Key | Action |
|---|---|
Tab | Moves between turn rows when they are selectable, and to sm bars with onSelect |
Enter, Space | Selects the focused turn |
Accessibility
- Every bar has a text twin read in stage order: "1.04 seconds total, Transcriber 110 milliseconds, Endpointing 180 milliseconds, ..., over budget". Missing stages are named, never skipped silently.
- Selectable rows and
smbars are toggle buttons whose names start with the visible label or total, so voice control can say what it sees. - Stage colors are the chart tokens in a fixed order with labels in the legend and the tooltip. Over budget is amber with a warning glyph and words; failed is red with a cross and the word Failed.
- The breakdown is a real table with a caption, column headers and row headers.
Motion
- When turns arrive, the p50, p90 and budget markers glide to their new place with a 280 ms transform, so you can see the median move. Under reduced motion they jump.
- Bars never grow or count up. Row highlights change color in 150 ms.