Skip to content
Sign in
VoiceTurn latency barMenu

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.

ProA pharmacy refill call's responses arriving as it runs. The p50 and p90 lines move with each turn, the slow turn after a tool timeout runs off the scale, and any row opens its stages.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/turn-latency-bar

Usage

tsx
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

ProA single response with the call's p50, p90 and budget marked, and the compact bar beside transcript lines.

Loading, empty, outliers, failures and RTL

ProLoading, no turns, an outlier past the scale, a failed turn, a missing stage, a cut off reply and no budget.

Props

TurnLatencyChart

PropTypeNotes
turnsTurnLatency[]{ id, label?, stages: { transcriber?, endpointing?, llm?, voice?, transport? }, status?, failedAt? }, ms per stage
budgetnumber | falseResponse budget in ms. Default 800. Turns past it are flagged
scalenumberMs at full width. Default 2.5 times the median turn
onSelectTurn, activeTurnId(turn) => void, stringRows become toggle buttons
loadingbooleanSkeleton rows that mirror the layout
emptyReactNodeShown while there are no turns
hideSummarybooleanHide the stat line and legend
labelstringAccessible name. Default "Response latency by turn"

TurnLatencyBar

PropTypeNotes
turnTurnLatencyOne turn
size"md" | "sm"md with a header and stage values; sm inline. Default md
p50, p90numberThe call's percentiles, drawn as markers
budgetnumber | falseDefault 800
scalenumberMs at full width. Default fits the turn, budget and p90
onSelect, selected() => void, booleanMakes the bar a toggle button

TurnLatencyBreakdown

PropTypeNotes
turnsTurnLatency[]p50, p90 and share per stage; the slowest stage is set in ink and named for screen readers
labelstringTable caption. Default "Latency by stage"

Keyboard

KeyAction
TabMoves between turn rows when they are selectable, and to sm bars with onSelect
Enter, SpaceSelects 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 sm bars 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.