Stream Inspector
A model’s token stream event by event, with the timing between them. An average tokens-per-second hides both numbers that matter — time to first token, and the stalls after it.
src/components/ui/stream-inspector.tsxAbout
From the component's own source, where it was written.
A model's token stream, event by event, with the timing between them.
The devtool for the two numbers that decide whether a stream feels fast: **time to first token**, and the gaps after it. An average tokens-per-second hides both — a stream that stalls for two seconds mid-sentence and then catches up scores identically to one that never stalled, and only one of them is unpleasant to read.
So gaps above stallMs are marked inline, in the sequence, where they can be traced to the event that caused them — usually a tool call the stream paused for.
Text deltas collapse into runs. A stream is thousands of two-character events; rendering each as a row makes the tool-call boundaries impossible to find, which is the only reason to open a stream inspector at all.
Install
npx astralyx-ui add stream-inspector
Usage
import { StreamInspector } from '@/components/ui/stream-inspector'
<StreamInspector events={events} stallMs={400} />Composer
Change the props and the source below follows.
Stream
412ms to first token- 0msstartrequest sent
- stalled 412ms
- 412msthinkingThe customer is asking about a refund…
- stalled 478ms
- 890msYour refund of £340 is (6 deltas)
- 1.04stoolget_invoice({ id: "inv_881" })
- stalled 2.08s
- 3.12s pending approval. (3 deltas)
- 3.24sstopend_turn
<StreamInspector events={events} stallMs={400} />Examples
A stream that paused for a tool call
<StreamInspector events={events} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
StreamInspector
Extends Omit<ComponentProps<'div'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| events | StreamEvent[] | required | { id, at, kind, content? } where kind is 'start' | 'text' | 'tool' | 'thinking' | 'stop' | 'error'. at is ms from the start of the request. |
| stallMs | number | 400 | A gap longer than this is marked inline, in the sequence, where it can be traced to the event that caused it — usually a tool call the stream paused for. |
| collapseText | boolean | true | A stream is thousands of two-character deltas; a row each makes the tool-call boundaries impossible to find, which is the only reason to open this. |
| formatDuration | (ms: number) => string | defaultDuration | — |
| emptyLabel | string | 'No events captured.' | — |
| ttftLabel | string | 'to first token' | — |
| stallLabel | string | 'stalled' | — |
| label | string | 'Stream' | — |
| to | computed | — | From the first text or thinking event, shown in the header. |
Types
StreamEvent
A model's token stream, event by event, with the timing between them.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| at | number | Milliseconds from the start of the request. |
| kind | 'start' | 'text' | 'tool' | 'thinking' | 'stop' | 'error' | — |
| content? | string | Text delta, tool name, or an error message. |