Trace Waterfall
An agent run as a waterfall of spans. Bars are positioned against the whole run rather than each row’s own width, which is what makes concurrency visible — two tools that ran in parallel look identical to two that ran in sequence until you share the axis.
src/components/ui/trace-waterfall.tsxAbout
From the component's own source, where it was written.
An agent run as a waterfall of spans.
The question a trace has to answer is "where did the time go", and a list of steps cannot answer it — steps tell you the order, a waterfall tells you the shape. Two tool calls that ran in parallel look identical to two that ran in sequence until you put them on a shared time axis.
**Bars are positioned against the root's window, not each row's own.** Every span is offset by start and sized by duration as fractions of the whole run, which is what makes overlap visible. Scaling each row to its own width — the easy version — draws every span as a full bar and hides exactly the concurrency you opened the trace to see.
A span that is a rounding error still gets a visible sliver: minWidth keeps a 2ms call from vanishing, because "it ran and was instant" and "it never ran" must not look the same.
Children collapse. A run with forty retrieval spans under one node is unreadable open, and the parent's own bar already says how long the subtree took.
Install
npx astralyx-ui add trace-waterfall
Usage
import { TraceWaterfall } from '@/components/ui/trace-waterfall'
<TraceWaterfall spans={spans} />Composer
Change the props and the source below follows.
- run4.82s
- guard: pii_scan60ms
- model: plan940ms
- tools (parallel)2.18s
- search_docs2.12s
- get_customer240ms
- get_invoice1.88s
- model: answer1.48s
- guard: tone60ms
<TraceWaterfall
spans={spans}
defaultDepth={2}
nameWidth={200}
/>Examples
A run with a parallel fan-out and a failure
<TraceWaterfall spans={spans} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
TraceWaterfall
Extends Omit<ComponentProps<'div'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| spans | TraceSpan[] | required | { id, name, start, duration, kind?, error?, meta?, children? }. start and duration are milliseconds from the beginning of the run. |
| total | number | — | The run window. Defaults to the furthest span end, including nested ones. |
| defaultDepth | number | 2 | Levels open on first render. Forty retrieval spans under one node are unreadable expanded. |
| formatDuration | (ms: number) => string | defaultFormat | Defaults to ms under a second, then seconds. |
| nameWidth | number | string | 200 | Width of the name column. |
| emptyLabel | string | 'No spans recorded.' | — |
| label | string | 'Run trace' | Accessible name for the whole trace. |
Functions
| Function | Signature | Description |
|---|---|---|
| traceEnd | (spans: TraceSpan[]) => number | The furthest point any span reaches, including nested ones. |
Types
TraceSpan
An agent run as a waterfall of spans.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| name | string | — |
| start | number | Milliseconds from the start of the run. |
| duration | number | Milliseconds. |
| kind? | 'model' | 'tool' | 'retrieval' | 'guard' | string | Drives the bar colour. An unknown kind falls back to neutral rather than throwing. |
| error? | boolean | Draws the bar as failed, whatever its kind. |
| meta? | ReactNode | Trailing detail — tokens, cost, a status code. |
| children? | TraceSpan[] | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| minimum bar | 2px | A span too short to draw still gets a sliver — “it ran and was instant” and “it never ran” must not look the same. |