"use client" // Merged all-sources benchmark page (merged-benchmark-view spec F3/F4). // // One page per resolved canonical benchmark, at observation grain: one // row per (model, source) score, flat-interleaved and sorted by // score_canonical in the metric's direction (spec Q6). Echo // republications stay visible (Q3). // // The page renders the SAME full EvalDetail experience as a per-source // eval page — hero, benchmark signals, metric spec, score distribution, // leaderboard, embed links — fed with the merged payload adapted onto // the BenchmarkEvalSummary surface (lib/merged-adapter). Merged-specific // controls layer on top: // - Source narrower: NAVIGATES to the per-source eval page (Q5 — // unlike the state-swap SplitPicker on per-source pages). // - Metric switcher: mounts in EvalDetail's split-picker slot; // re-queries via ?metric= without navigation. // - Slice selector (grain='slice' pages only): ?slice=. // ?source= pre-highlights the clicked browse-tree leaf's source rows. import { useEffect, useMemo, useState } from "react" import Link from "next/link" import { usePathname, useRouter, useSearchParams } from "next/navigation" import { EvalDetail } from "@/components/eval-detail" import { fetchMergedBenchmarkSummary } from "@/lib/dashboard-data-client" import { isMergedBenchmarkSummary, mergedSummaryToEvalSummary } from "@/lib/merged-adapter" import type { MergedBenchmarkSummary, ModelResultForBenchmark } from "@/lib/eval-processing" import type { ComparisonIndex, EvalHierarchy } from "@/lib/backend-artifacts" import { routeIdToPath } from "@/lib/utils" export function MergedBenchmarkView({ benchmarkId, evalHierarchy, comparisonIndex, }: { benchmarkId: string /** Cross-suite comparability inputs, lazily loaded by the route page — * same wiring the per-source path gives EvalDetail. */ evalHierarchy?: EvalHierarchy | null comparisonIndex?: ComparisonIndex | null }) { const router = useRouter() const pathname = usePathname() const searchParams = useSearchParams() const sourceParam = searchParams.get("source") const metricParam = searchParams.get("metric") const sliceParam = searchParams.get("slice") const [summary, setSummary] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { let cancelled = false setLoading(true) fetchMergedBenchmarkSummary(benchmarkId, { metricId: metricParam ?? undefined, sliceId: sliceParam ?? undefined, }) .then((payload) => { if (cancelled) return if (!isMergedBenchmarkSummary(payload)) { setError("This snapshot has no merged page for this benchmark.") return } setSummary(payload) setError(null) document.title = `${payload.display_name} | Benchmark` }) .catch((err) => { console.error(err) if (!cancelled) setError("Benchmark not found") }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [benchmarkId, metricParam, sliceParam]) // Update a query param in place (no navigation) so metric/slice // selections survive reload and back. const setQueryParam = (key: string, value: string | null) => { const params = new URLSearchParams(searchParams.toString()) if (value) params.set(key, value) else params.delete(key) const qs = params.toString() router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false }) } // Reshape onto the per-source BenchmarkEvalSummary surface so the page // can mount the full EvalDetail experience at merged grain: pooled // all-sources observation rows on the metric's canonical scale. const adapted = useMemo( () => (summary ? mergedSummaryToEvalSummary(summary) : null), [summary], ) const selectedMetricId = summary?.selected_metric_id const selectedMetric = useMemo( () => summary?.metrics.find((m) => m.metric_id === selectedMetricId) ?? null, [summary, selectedMetricId], ) // Counts at the SELECTED metric's grain (hero scalar counts are at the // default metric's). const resultsCount = selectedMetric?.results_count ?? summary?.results_count ?? 0 const sourcesCount = selectedMetric?.sources_count ?? summary?.sources_count ?? 0 const modelsCount = selectedMetric?.models_count ?? summary?.models_count ?? 0 const sourceSlugSet = useMemo( () => new Set((summary?.aggregate_sources ?? []).map((s) => s.composite_slug)), [summary], ) const disclosureSources = (summary?.aggregate_sources ?? []).filter( (s) => !s.reports_preferred || s.slice_only, ) // Rows the adapter dropped from the pool: flagged observations whose // raw score could not be converted to the metric's canonical scale. const excludedCount = summary && adapted ? summary.results.length - adapted.model_results.length : 0 if (loading) { return (
Loading merged benchmark…
) } if (error || !summary || !adapted) { return (
{error ?? "Benchmark not found"}
) } const preselectedSource = sourceParam && sourceSlugSet.has(sourceParam) ? sourceParam : "" // Metric switcher rides EvalDetail's split-picker slot (rendered just // above the score distribution, exactly where per-source pages mount // their split pickers). Switching re-queries ?metric= and re-adapts. const metricSplitConfig = summary.metrics.length > 1 ? { label: "Metric", activeId: selectedMetricId ?? summary.preferred_metric_id, onChange: (metricId: string) => setQueryParam("metric", metricId === summary.preferred_metric_id ? null : metricId), options: summary.metrics.map((metric) => ({ id: metric.metric_id, label: `${metric.display_name} (${metric.sources_count} ${ metric.sources_count === 1 ? "source" : "sources" }, ${metric.results_count} ${metric.results_count === 1 ? "result" : "results"})`, })), } : undefined const rowHighlight = preselectedSource ? (row: ModelResultForBenchmark) => row.merged_source_slug === preselectedSource : undefined // Study-protocol banner link target: the shared EvalDetail // has no per-source evaluation_id in scope on merged pages, so resolve // the study source here — the protocol-carrying observation rows name // their per-source page directly. const studyRow = summary.results.find( (row) => row.protocol_condition != null && row.evaluation_id, ) const studySourceHref = studyRow ? `/evals/${routeIdToPath(studyRow.evaluation_id)}` : undefined return (
{/* MERGED SCOPE BAR — merged-specific controls above the shared EvalDetail chrome. ------------------------------------------- */}
Merged benchmark · all sources
{resultsCount.toLocaleString()} {resultsCount === 1 ? "result" : "results"} from{" "} {sourcesCount.toLocaleString()} {sourcesCount === 1 ? "source" : "sources"} ·{" "} {modelsCount.toLocaleString()} {modelsCount === 1 ? "model" : "models"}
{summary.grain === "slice" && (summary.slices?.length ?? 0) > 0 && ( )}
{summary.grain === "slice" && (

This benchmark reports slice-level results only; each slice merges across sources.

)} {/* FULL EVAL PAGE at merged grain -------------------------------- */} {/* DISCLOSURE NOTES -------------------------------------------------- */} {(disclosureSources.length > 0 || excludedCount > 0) && (
{excludedCount > 0 && (

{excludedCount.toLocaleString()} {excludedCount === 1 ? "result is" : "results are"} not shown: {excludedCount === 1 ? "its score" : "their scores"} could not be converted to this metric's common scale.

)} {disclosureSources.map((source) => { const name = source.composite_display_name || source.composite_slug const note = source.slice_only ? "reports slice-level results only." : "reports only other metrics for this benchmark (see metric switcher)." return (

{source.evaluation_id ? ( {name} ) : ( {name} )}{" "} {note}

) })}
)}
) }