/** * EvalRunCompare (#121) — side-by-side regression comparison of two dataset runs. * Pick a baseline + current run; shows pass-rate / avg-score deltas, the set of * cases that flipped (pass↔fail), the prompt/model variant per run, and a * dataset-version-mismatch warning. */ import { useState } from 'react'; import { useApi } from '../../hooks/useApi'; import { getEvalRuns, getRunComparison } from '../../api/eval'; import type { EvalRun, RegressionReport } from '../../api/eval'; const pct = (n: number): string => `${n >= 0 ? '+' : ''}${(n * 100).toFixed(1)}%`; const passRate = (r: EvalRun): number => (r.totalCases > 0 ? r.passedCases / r.totalCases : 0); const errMsg = (e: unknown): string => (e instanceof Error ? e.message : String(e)); function runLabel(r: EvalRun): string { const variant = [r.promptVersionId ? `prompt ${r.promptVersionId.slice(0, 8)}` : '', r.modelId ?? ''] .filter(Boolean) .join(' · '); return `${r.id.slice(0, 8)} · ${(passRate(r) * 100).toFixed(0)}% (${r.passedCases}/${r.totalCases})${variant ? ` · ${variant}` : ''}`; } function Stat({ label, value, good }: { label: string; value: string; good: boolean }) { return (
{label}
{value}
); } function ReportView({ report }: { report: RegressionReport }) { const tone = report.overallRegression ? 'bg-red-50 border-red-200 text-red-800' : 'bg-green-50 border-green-200 text-green-800'; return (
{report.overallRegression ? '✗ Regression detected' : '✓ No regression'} {report.datasetVersionMismatch && (
⚠ These runs are over different dataset versions — deltas may be misleading.
)}
= 0} /> = 0} />

Flipped cases ({report.flippedCases.length})

{report.flippedCases.length === 0 ? (

No cases changed pass/fail status.

) : ( )}
); } export function EvalRunCompare({ datasetId }: { datasetId: string }) { const { data, loading } = useApi(() => getEvalRuns({ datasetId, limit: 50 }), [datasetId]); const runs = (data?.runs ?? []).filter((r) => r.status === 'completed'); const [baselineId, setBaselineId] = useState(''); const [currentId, setCurrentId] = useState(''); const [report, setReport] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); async function compare() { if (!currentId || !baselineId) return; setBusy(true); setError(null); setReport(null); try { setReport(await getRunComparison(currentId, baselineId)); } catch (e) { setError(errMsg(e)); } finally { setBusy(false); } } return (

Compare runs

{loading ? (

Loading runs…

) : runs.length < 2 ? (

Run this dataset at least twice to compare runs.

) : ( <>
{error &&
{error}
} {report && } )}
); }