Eval results
The results of an eval run: pass rate, average score, cost and duration against a baseline, and every case with its score, latency and pass or fail. Filter, sort, and expand a case for the full input, output and the grader's reasoning. Rows fill in while the run is going.
On this page
Installation
Usage
import { EvalResults } from "@/components/ui/chatcn/eval-results"
<EvalResults
title="docs-qa"
meta="claude-sonnet-5-5, 12 cases"
cases={cases}
baseline={{ label: "main, run 212", passRate: 0.6, avgScore: 0.71, cost: 0.045 }}
threshold={0.7}
/>Each case has an input, the expected reference, the output, a score from 0 to 1, a status and its latencyMs. Pass rate, average score and cost are computed from the cases; pass metrics to override them, and to give the run duration, which is never guessed. With a baseline, every figure gets a delta: green and an up arrow when it got better, red and a down arrow when it got worse, whichever direction is good for that figure.
While running, pass cases as queued or running and update them as results arrive. Their rows keep their place and fill in, the summary updates in place, and a 2 px bar under the header tracks progress against total.
Examples
Running
Failures, worst first
Filter, sort and expanded rows are all controllable. Sorting by score starts with the lowest, and by latency with the slowest: the cases worth reading come first. Cases without the figure always sort last.
States
Props
| Prop | Type | Notes |
|---|---|---|
cases | EvalCase[] | { id, input, expected?, output?, score?, status, latencyMs?, cost?, grader?, reasoning?, errorText? } |
title, meta | ReactNode | Run name and quiet context such as dataset and model |
metrics | EvalMetrics | { passRate?, avgScore?, cost?, durationMs? }. Overrides the computed figures |
baseline | EvalBaseline | { label, ...metrics }. Turns on deltas |
running, total | boolean, number | Progress and fill-in while cases finish |
loading | boolean | Skeleton summary and rows in the final layout |
error, onRetry | ReactNode, () => void | A banner above the cases with Resume run |
threshold | number | The pass mark, shown in the toolbar and in each case's reasoning |
filter, defaultFilter, onFilterChange | "all" | "passed" | "failed" | Failed includes errored cases |
sort, defaultSort, onSortChange | { key, direction } | Keys index, score, latency |
expanded, defaultExpanded, onExpandedChange | string[] | Ids of the open cases |
emptyState | ReactNode | Replaces the no cases message |
currency, locale | string | Cost formatting. Defaults to USD |
maxHeight | number | string | Height of the table scroll area. The header row stays put |
Keyboard
| Key | Action |
|---|---|
Tab | Filter buttons, sort headers, then one stop per case |
ArrowDown, ArrowUp | Next or previous case |
Home, End | First or last case |
Enter, Space | Expand or collapse the focused case |
Accessibility
- A real table with column headers. Sortable headers are buttons and the header cell carries
aria-sort. - Each case row has one button with
aria-expanded; the whole row is its hit area, so a click anywhere opens it. - Pass, fail and error badges pair the hue with an icon and a word. Deltas add an arrow and a hidden Better or Worse.
- Progress is a
progressbarwhile running, and the final counts are announced once through a polite live region. - On narrow containers, expected and output columns fold away and stay readable in the expanded case.
Motion
- Expanding a case is instant: reviewing a run is a high-frequency action.
- When a case finishes, its badge swaps in with the kit's 150 ms blur crossfade. Figures update in place and never count up.
- The progress bar scales rather than resizes. Under reduced motion the swap is skipped.