Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions apps/web/app/benchmarks/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { loadBenchmarkById, loadBenchmarkPairsById } from "../../../lib/data";
import {
loadBenchmarkById,
loadBenchmarkPairsById,
loadAnalysisIndex,
} from "../../../lib/data";
import {
buildBenchmarkRunRoster,
sortBenchmarkRunRoster,
Expand All @@ -14,6 +18,11 @@ import { ResponsiveTable } from "../../../components/ResponsiveTable";
import { TruncateText } from "../../../components/ResponsiveTable";
import { findMostSimilarPeerRunId } from "../../../lib/benchmarkPeers";
import { inferModeLabel } from "../../../lib/modeLabeler";
import {
buildBenchmarkIndexLookup,
resolveIndexedModeLabel,
} from "../../../lib/benchmarkIndexLookup";
import { buildBenchmarkOutlierLookup } from "../../../lib/outlierLookup";
import { questionHubHref } from "../../../lib/questionGroups";
import {
extractBenchmarkSummaryDisplay,
Expand Down Expand Up @@ -46,12 +55,21 @@ export default async function BenchmarkDetailPage({
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const benchmark = await loadBenchmarkById(id);
const [benchmark, index] = await Promise.all([
loadBenchmarkById(id),
loadAnalysisIndex(),
]);

if (!benchmark) {
notFound();
}

const benchmarkIndex = index
? buildBenchmarkIndexLookup(index).get(benchmark.id)
: null;
const outlierLookup = buildBenchmarkOutlierLookup(index, benchmark.id);
const outlierCount = outlierLookup.size;

const runIds = benchmark.payload.runIds ?? [];
const pairsData = await loadBenchmarkPairsById(id);
const pairs =
Expand All @@ -78,6 +96,7 @@ export default async function BenchmarkDetailPage({
peerCompareHref: peerRunId
? `/runs/compare?left=${row.runId}&right=${peerRunId}`
: null,
outlier: outlierLookup.get(row.runId) ?? null,
};
});
const thresholdCounts = [
Expand Down Expand Up @@ -140,6 +159,20 @@ export default async function BenchmarkDetailPage({
>
Compare as right
</Link>
<Link
href={`/pairs?benchmark=${benchmark.id}`}
className="button secondary"
>
Pairwise explorer
</Link>
{outlierCount > 0 ? (
<Link
href={`/outliers?q=${encodeURIComponent(benchmark.question)}`}
className="button secondary"
>
Outlier runs ({outlierCount})
</Link>
) : null}
</div>
</div>

Expand Down Expand Up @@ -351,6 +384,7 @@ export default async function BenchmarkDetailPage({
thresholdCounts={thresholdCounts}
similarityPairs={pairs}
runs={benchmark.payload.runs}
runIds={rosterRunIds}
/>

<div className="card">
Expand Down Expand Up @@ -405,6 +439,29 @@ export default async function BenchmarkDetailPage({
: value.toFixed(3);
},
},
{
key: "outlier",
label: "Outlier",
helpKey: "zScore",
hideOnMobile: true,
render: (row) => {
const outlier = (
row as {
outlier: {
zScore: number;
} | null;
}
).outlier;
if (!outlier) return "—";
return (
<span
title={`z-score ${outlier.zScore.toFixed(2)}`}
>
Yes
</span>
);
},
},
{
key: "open",
label: "Open",
Expand Down Expand Up @@ -567,7 +624,11 @@ export default async function BenchmarkDetailPage({
]}
data={modes.map((mode, idx) => ({
modeIndex: idx,
label: inferModeLabel(mode.exemplarPreview),
label: resolveIndexedModeLabel(
benchmarkIndex?.modeLabels ?? [],
idx,
inferModeLabel(mode.exemplarPreview),
),
size: mode.size,
exemplarPreview: mode.exemplarPreview,
memberRunIds: mode.members
Expand Down
15 changes: 15 additions & 0 deletions apps/web/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,21 @@ pre {
transform: scale(1.05);
}

.benchmark-heatmap-run-link,
.benchmark-heatmap-cell-link {
color: inherit;
text-decoration: none;
}

.benchmark-heatmap-cell-link {
display: block;
}

.benchmark-heatmap-run-link:hover,
.benchmark-heatmap-cell-link:hover {
text-decoration: underline;
}

@media (max-width: 600px) {
.trace-timeline::before {
left: 0.95rem;
Expand Down
46 changes: 26 additions & 20 deletions apps/web/app/questions/view/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import {
loadRunsByQuestion,
} from "../../../lib/data";
import { buildQueryString } from "../../../lib/listPagination";
import { questionHubHref } from "../../../lib/questionGroups";
import {
questionHubHref,
listQuestionInsightLinks,
} from "../../../lib/questionGroups";
import {
buildQuestionExperimentMatrix,
lookupMatrixCell,
Expand Down Expand Up @@ -410,7 +413,28 @@ export default async function QuestionHubPage({
critique rollups for this question.
</p>
</div>
) : null}
) : (
<div className="card">
<h2 style={{ marginTop: 0 }}>Research insights</h2>
<p className="small muted" style={{ marginBottom: 12 }}>
Open filtered insight explorers scoped to this question.
</p>
<div
className="page-actions"
style={{ display: "flex", gap: 10, flexWrap: "wrap" }}
>
{listQuestionInsightLinks(question).map((link) => (
<Link
key={link.page}
href={link.href}
className="button secondary"
>
{link.label}
</Link>
))}
</div>
</div>
)}

{experimentMatrix.models.length > 0 &&
experimentMatrix.presets.length > 0 ? (
Expand Down Expand Up @@ -495,24 +519,6 @@ export default async function QuestionHubPage({
</tbody>
</table>
</div>
{indexMetrics?.runsWithQualityScores ? (
<div
className="page-actions"
style={{
display: "flex",
gap: 10,
flexWrap: "wrap",
marginTop: 12,
}}
>
<Link
href={`/quality?q=${encodeURIComponent(question)}`}
className="button secondary"
>
Quality insights for this question
</Link>
</div>
) : null}
</div>
) : null}

Expand Down
107 changes: 83 additions & 24 deletions apps/web/components/charts/BenchmarkDetailCharts.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import {
Bar,
Expand All @@ -18,8 +19,13 @@ type BenchmarkDetailChartsProps = {
thresholdCounts: Array<{ threshold: string; modeCount: number }>;
similarityPairs?: Array<{ i: number; j: number; similarity: number }>;
runs: number;
runIds?: string[];
};

function buildPairCompareHref(runIdA: string, runIdB: string): string {
return `/runs/compare?left=${runIdA}&right=${runIdB}`;
}

function similarityColor(value: number) {
if (value >= 0.95) return "rgba(34, 197, 94, 0.5)";
if (value >= 0.9) return "rgba(34, 197, 94, 0.4)";
Expand All @@ -35,6 +41,7 @@ export function BenchmarkDetailCharts({
thresholdCounts,
similarityPairs = [],
runs,
runIds = [],
}: BenchmarkDetailChartsProps) {
const [pairs, setPairs] = useState(similarityPairs);
const [pairsSource, setPairsSource] = useState<"artifact" | "chunk">(
Expand Down Expand Up @@ -176,41 +183,93 @@ export function BenchmarkDetailCharts({
<InfoTooltip helpKey="pairwiseSimilarityHeatmap" />
</h3>
<p className="small muted">
source: {pairsSource} ({pairs.length} pairwise entries)
source: {pairsSource} ({pairs.length} pairwise entries).
Click off-diagonal cells to compare runs.
</p>
<div className="benchmark-heatmap-wrap">
<table>
<thead>
<tr>
<th />
{Array.from({ length: runs }, (_, idx) => (
<th key={`head-${idx}`}>r{idx}</th>
))}
{Array.from({ length: runs }, (_, idx) => {
const runId = runIds[idx];
const label = `r${idx}`;
return (
<th key={`head-${idx}`}>
{runId ? (
<Link
href={`/runs/${runId}`}
title={runId}
className="benchmark-heatmap-run-link"
>
{label}
</Link>
) : (
label
)}
</th>
);
})}
</tr>
</thead>
<tbody>
{matrix.map((row, i) => (
<tr key={`row-${i}`}>
<th>r{i}</th>
{row.map((value, j) => (
<td
key={`cell-${i}-${j}`}
className="benchmark-heatmap-cell"
style={{
background:
i === j
? "var(--color-bg-elevated)"
: similarityColor(
value,
),
color: "var(--color-text-primary)",
minWidth: 44,
}}
title={`r${i} vs r${j}: ${value.toFixed(3)}`}
>
{value.toFixed(3)}
</td>
))}
<th>
{runIds[i] ? (
<Link
href={`/runs/${runIds[i]}`}
title={runIds[i]}
className="benchmark-heatmap-run-link"
>
r{i}
</Link>
) : (
`r${i}`
)}
</th>
{row.map((value, j) => {
const runIdA = runIds[i];
const runIdB = runIds[j];
const compareHref =
i !== j && runIdA && runIdB
? buildPairCompareHref(
runIdA,
runIdB,
)
: null;
const cellContent = value.toFixed(3);
const cellStyle = {
background:
i === j
? "var(--color-bg-elevated)"
: similarityColor(value),
color: "var(--color-text-primary)",
minWidth: 44,
};
const title = `r${i} vs r${j}: ${value.toFixed(3)}`;

return (
<td
key={`cell-${i}-${j}`}
className="benchmark-heatmap-cell"
style={cellStyle}
title={title}
>
{compareHref ? (
<Link
href={compareHref}
className="benchmark-heatmap-cell-link"
title={`${title} — compare runs`}
>
{cellContent}
</Link>
) : (
cellContent
)}
</td>
);
})}
</tr>
))}
</tbody>
Expand Down
10 changes: 10 additions & 0 deletions apps/web/lib/benchmarkIndexLookup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AnalysisIndex } from "./data";
import {
buildBenchmarkIndexLookup,
formatTopModeLabel,
resolveIndexedModeLabel,
} from "./benchmarkIndexLookup";

function sampleIndex(): AnalysisIndex {
Expand Down Expand Up @@ -65,4 +66,13 @@ describe("benchmarkIndexLookup", () => {
const labels = lookup.get("bench-1")!.modeLabels;
expect(formatTopModeLabel(labels)).toBe("Yes with safeguards");
});

it("prefers indexed mode labels over fallback text", () => {
const lookup = buildBenchmarkIndexLookup(sampleIndex());
const labels = lookup.get("bench-1")!.modeLabels;
expect(resolveIndexedModeLabel(labels, 0, "fallback")).toBe(
"Yes with safeguards",
);
expect(resolveIndexedModeLabel(labels, 9, "fallback")).toBe("fallback");
});
});
Loading
Loading