Observability
Deploy Timeline
Prompt and model versions on a timeline over the eval score they shipped with — regressions, rollbacks and the live canary.
1 variantMIT
Preview
Deployments
triage-l · prompt & model versions · 14 days
livev14 · o4-minicanary 1%v15eval0.931deploys6 · 1 regr
| Version | Kind | Deployed | Eval score | Change | Status |
|---|---|---|---|---|---|
| v11: few-shot refresh | prompt | Oct 27 | 0.904 | First | Replaced |
| v12: shorter system prompt | prompt | Oct 29 | 0.912 | +0.8 pts | Replaced |
| o4-mini: model swap | model | Oct 31 | 0.921 | +0.9 pts | Replaced |
| v13: stricter escalation rules | prompt | Nov 2 | 0.877 | −4.4 pts | Rolled back after 38 min |
| v14: escalation rules, take two | prompt | Nov 4 | 0.931 | +1.0 pts | Live |
| v15: tool-use hints | prompt | Nov 7 | 0.918 | −1.3 pts | Canary, 1% of traffic |
Installation
Copies the component into components/ui and installs its dependencies and theme tokens.
pnpm dlx shadcn@latest add https://tuffui.com/r/deploy-timeline.jsonUsage
Pass your own data and wire the callbacks to your agent runtime.
import { DeployTimeline } from "@/components/ui/deploy-timeline";
export function AgentDeploys({ agent }: { agent: Agent }) {
return (
<DeployTimeline
title="Deployments"
subtitle={`${agent.name} · prompt & model versions · 14 days`}
threshold={0.88} // minimum eval score
range={[daysAgo(14), new Date()]}
deploys={agent.releases.map((r) => ({
id: r.id,
version: r.kind === "model" ? r.model : `v${r.promptVersion}`,
kind: r.kind, // "prompt" | "model"
label: r.note,
at: r.deployedAt,
score: r.evalScore, // 0–1
rolledBackAfter: r.rolledBackAt
? (r.rolledBackAt - r.deployedAt) / 60_000 // minutes
: undefined,
canary: r.trafficShare < 1 ? r.trafficShare : undefined,
}))}
onCompare={(version, live) => openDiff(version.id, live.id)}
onRollback={(version) => confirmRollback(version.id)}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| deploys* | Deploy[] | — | Every prompt and model deploy in the window, in any order. The score steps at each one; a deploy with `rolledBackAfter` dips and comes back, the latest one with `canary` runs as a dashed line beside live. |
| threshold | number | — | Minimum eval score, drawn as a dashed line. Deploys under it count as regressions even without a rollback. |
| range | [Date | string | number, Date | string | number] | around the deploys | Time window of the axis. Day labels thin out to fit the width. |
| now | Date | string | number | end of range | Where the score line stops. Move it forward to grow the live and canary lines. |
| live | boolean | true | Pulses the canary while traffic is still being scored. |
| title | string | "Deployments" | Header title. |
| subtitle | string | — | Secondary line under the title. |
| stats | DeployStat[] | live, canary, eval, deploys | Chips under the chart. By default: the live version and its model, canary share, live eval score and the deploy count with regressions. Pass `[]` to hide them. |
| selectedId | string | null | — | Controlled selection. Left out, the component keeps its own. |
| onSelect | (deploy: Deploy | null) => void | — | Called when a marker (or its segment of the line) is clicked; null when the selection is cleared again or with Escape. |
| onCompare | (deploy: Deploy, live: Deploy) => void | — | Shows a Compare button for the selected deploy against live. With nothing selected it compares the last good version before live. |
| onRollback | (deploy: Deploy) => void | — | Shows a Roll back button to the selected deploy, or the last good version before live. Disabled for live, the canary and regressions. |
| aside | ReactNode | — | Extra content in the header, before the buttons. |
| intro | boolean | true | Draws the line in from the left on mount, then markers and the regression callout. Off renders the finished chart. Skipped with reduced motion. |
| formatScore | (score: number) => string | score.toFixed(3) | Formats scores in the callout, chips and the minimum label. |
| formatDate | (date: Date) => string | "Nov 2" in UTC | Formats the day axis and dates in the callout. |
| className | string | — | Extra classes for the root element. |
type Deploy = {
id: string;
version: string; // "v13", or a model id like "o4-mini"
kind: "prompt" | "model"; // dot or diamond on the rail
label?: string; // what changed, shown in the callout
at: Date | string | number;
score: number; // eval score it shipped with, 0–1
rolledBackAfter?: number; // minutes until rollback: a regression
canary?: number; // traffic share 0–1 while in canary
};
type DeployStat = {
label: string;
value: string;
tone?: "neutral" | "brand" | "negative";
};