Spaces:
Running
Merge corpus dashboard into home as paper-aligned landing
Browse filesThe home page now renders the four interpretive signals (paper §4.2.1)
as a compact 4-tile strip alongside corpus stats from the rollout
hierarchy (paper §3.2). The standalone /corpus page redirects to home
and the Corpus tab is removed from navigation. Reproducibility headline
is inverted (% documented rather than % with gaps) for impact while the
paper-aligned gap rate stays in the detail line.
About page is rewritten to mirror the paper's structure: six-level
rollout hierarchy, four interpretive signals, two reader modes, and
composition with EEE + Auto-BenchmarkCards.
CorpusDashboard now accepts an `embedded` prop so callers can host the
full drill-down without its self-contained masthead. Manifest type is
extended with the count fields the upstream pipeline already emits so
the home page reads counts directly instead of pulling model-cards-lite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/about/page.tsx +217 -166
- app/corpus/page.tsx +5 -34
- app/page.tsx +162 -205
- components/navigation.tsx +0 -6
- components/signals/corpus-dashboard.tsx +69 -34
- components/signals/corpus-signals-strip.tsx +146 -0
- lib/backend-artifacts.ts +8 -0
|
@@ -1,5 +1,15 @@
|
|
| 1 |
import Link from "next/link"
|
| 2 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
import { Navigation } from "@/components/navigation"
|
| 5 |
import { PageHeader } from "@/components/page-header"
|
|
@@ -13,148 +23,212 @@ export default function AboutPage() {
|
|
| 13 |
<PageHeader
|
| 14 |
eyebrow="About"
|
| 15 |
title="About Eval Cards"
|
| 16 |
-
description="
|
| 17 |
size="wide"
|
| 18 |
metaItems={[
|
| 19 |
-
{ label: "
|
| 20 |
-
{ label: "
|
| 21 |
]}
|
| 22 |
/>
|
| 23 |
|
| 24 |
-
<main className="mx-auto w-full max-w-[
|
| 25 |
-
<div className="space-y-
|
|
|
|
| 26 |
<section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 27 |
<div className="space-y-3">
|
| 28 |
-
<SectionLabel label="
|
| 29 |
-
<h2 className="max-w-sm text-
|
| 30 |
-
Evaluation
|
| 31 |
</h2>
|
| 32 |
</div>
|
| 33 |
|
| 34 |
-
<div className="
|
| 35 |
-
<
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
Reading stance
|
| 47 |
-
</div>
|
| 48 |
-
<div className="mt-4 space-y-4">
|
| 49 |
-
<SignalLine icon={<Search className="h-4 w-4" />} label="Inspect provenance alongside rankings." tone="sky" />
|
| 50 |
-
<SignalLine icon={<Scale className="h-4 w-4" />} label="Surface comparability caveats early." tone="amber" />
|
| 51 |
-
<SignalLine icon={<BookOpenText className="h-4 w-4" />} label="Support research and policy use without duplicating the record." tone="emerald" />
|
| 52 |
-
</div>
|
| 53 |
-
</div>
|
| 54 |
</div>
|
| 55 |
</section>
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
<
|
| 65 |
-
|
| 66 |
-
|
| 67 |
</p>
|
| 68 |
</div>
|
| 69 |
|
| 70 |
-
<
|
| 71 |
-
<
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
body="A structured view of which evaluation details matter for interpretation, reproducibility, and public accountability."
|
| 76 |
/>
|
| 77 |
-
<
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
body="Evaluation outputs and benchmark metadata are shaped into a consistent layer that can be queried and compared across sources."
|
| 82 |
/>
|
| 83 |
-
<
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
body="The interface keeps setup differences, provenance, and evidence gaps visible so interpretation does not detach from reporting conditions."
|
| 88 |
/>
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
</section>
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
<
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
</
|
| 103 |
</div>
|
| 104 |
|
| 105 |
-
<div className="
|
| 106 |
-
<
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
</p>
|
| 120 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
</section>
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
<
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
</
|
| 134 |
</div>
|
| 135 |
|
| 136 |
-
<div className="space-y-
|
| 137 |
-
<
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
<
|
| 143 |
-
<
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
</
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
<ArrowRight className="h-4 w-4" />
|
| 153 |
-
</Button>
|
| 154 |
-
</Link>
|
| 155 |
-
</div>
|
| 156 |
</div>
|
| 157 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
</div>
|
| 159 |
</main>
|
| 160 |
</div>
|
|
@@ -169,83 +243,60 @@ function SectionLabel({ label }: { label: string }) {
|
|
| 169 |
)
|
| 170 |
}
|
| 171 |
|
| 172 |
-
function
|
| 173 |
-
title,
|
| 174 |
-
body,
|
| 175 |
-
}: {
|
| 176 |
-
title: string
|
| 177 |
-
body: string
|
| 178 |
-
}) {
|
| 179 |
return (
|
| 180 |
-
<
|
| 181 |
-
<div className="text-
|
| 182 |
-
|
| 183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
)
|
| 185 |
}
|
| 186 |
|
| 187 |
-
function
|
| 188 |
icon,
|
| 189 |
-
|
|
|
|
| 190 |
body,
|
| 191 |
-
tone,
|
| 192 |
}: {
|
| 193 |
icon: React.ReactNode
|
| 194 |
-
|
|
|
|
| 195 |
body: string
|
| 196 |
-
tone: "amber" | "emerald" | "sky"
|
| 197 |
}) {
|
| 198 |
-
const toneClasses = {
|
| 199 |
-
amber: "bg-amber-50/70 dark:bg-amber-950/20",
|
| 200 |
-
emerald: "bg-emerald-50/70 dark:bg-emerald-950/20",
|
| 201 |
-
sky: "bg-sky-50/70 dark:bg-sky-950/20",
|
| 202 |
-
}[tone]
|
| 203 |
-
|
| 204 |
return (
|
| 205 |
-
<div className=
|
| 206 |
-
<div className="flex items-center gap-2
|
| 207 |
-
<span className="rounded-full bg-
|
| 208 |
-
{
|
| 209 |
</div>
|
| 210 |
-
<
|
|
|
|
| 211 |
</div>
|
| 212 |
)
|
| 213 |
}
|
| 214 |
|
| 215 |
-
function
|
| 216 |
-
title,
|
| 217 |
-
body,
|
| 218 |
-
}: {
|
| 219 |
-
title: string
|
| 220 |
-
body: string
|
| 221 |
-
}) {
|
| 222 |
return (
|
| 223 |
-
<div className="rounded-
|
| 224 |
<div className="text-sm font-semibold text-foreground">{title}</div>
|
| 225 |
-
<p className="mt-2 text-sm leading-
|
| 226 |
</div>
|
| 227 |
)
|
| 228 |
}
|
| 229 |
|
| 230 |
-
function
|
| 231 |
-
icon,
|
| 232 |
-
label,
|
| 233 |
-
tone,
|
| 234 |
-
}: {
|
| 235 |
-
icon: React.ReactNode
|
| 236 |
-
label: string
|
| 237 |
-
tone: "amber" | "emerald" | "sky"
|
| 238 |
-
}) {
|
| 239 |
-
const toneClasses = {
|
| 240 |
-
amber: "bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300",
|
| 241 |
-
emerald: "bg-emerald-50 text-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-300",
|
| 242 |
-
sky: "bg-sky-50 text-sky-800 dark:bg-sky-950/30 dark:text-sky-300",
|
| 243 |
-
}[tone]
|
| 244 |
-
|
| 245 |
return (
|
| 246 |
-
<div className="flex items-start gap-3">
|
| 247 |
-
<
|
| 248 |
-
<div className="
|
|
|
|
|
|
|
|
|
|
| 249 |
</div>
|
| 250 |
)
|
| 251 |
}
|
|
|
|
| 1 |
import Link from "next/link"
|
| 2 |
+
import {
|
| 3 |
+
ArrowRight,
|
| 4 |
+
BookOpenText,
|
| 5 |
+
ClipboardCheck,
|
| 6 |
+
Database,
|
| 7 |
+
GitCompareArrows,
|
| 8 |
+
Layers,
|
| 9 |
+
Scale,
|
| 10 |
+
ShieldCheck,
|
| 11 |
+
Users,
|
| 12 |
+
} from "lucide-react"
|
| 13 |
|
| 14 |
import { Navigation } from "@/components/navigation"
|
| 15 |
import { PageHeader } from "@/components/page-header"
|
|
|
|
| 23 |
<PageHeader
|
| 24 |
eyebrow="About"
|
| 25 |
title="About Eval Cards"
|
| 26 |
+
description="An interpretative integration layer for AI evaluation reporting. Eval Cards composes existing evaluation infrastructure into a single reading surface, organizes reported scores through a six-level rollout hierarchy, and surfaces four interpretive signals that help readers decide whether to trust a reported result."
|
| 27 |
size="wide"
|
| 28 |
metaItems={[
|
| 29 |
+
{ label: "Reader modes", value: "Research + Policy" },
|
| 30 |
+
{ label: "Signal layers", value: "Reproducibility · Completeness · Provenance · Comparability" },
|
| 31 |
]}
|
| 32 |
/>
|
| 33 |
|
| 34 |
+
<main className="mx-auto w-full max-w-[88rem] px-4 py-12 sm:px-6 lg:px-8">
|
| 35 |
+
<div className="space-y-14">
|
| 36 |
+
{/* The framing problem */}
|
| 37 |
<section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 38 |
<div className="space-y-3">
|
| 39 |
+
<SectionLabel label="The problem" />
|
| 40 |
+
<h2 className="max-w-sm text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
|
| 41 |
+
Evaluation results travel faster than the context needed to interpret them.
|
| 42 |
</h2>
|
| 43 |
</div>
|
| 44 |
|
| 45 |
+
<div className="space-y-4 text-base leading-7 text-muted-foreground">
|
| 46 |
+
<p>
|
| 47 |
+
AI evaluations are produced at scale and reported through papers, leaderboards, model cards, and
|
| 48 |
+
framework outputs that rarely share conventions. The cost of fragmentation is paid at the point
|
| 49 |
+
of interpretation: provenance gets lost, configurations differ silently, and incomplete reporting
|
| 50 |
+
is treated indistinguishably from complete reporting.
|
| 51 |
+
</p>
|
| 52 |
+
<p>
|
| 53 |
+
Eval Cards composes three existing sources into a single record — Auto-BenchmarkCards for
|
| 54 |
+
benchmark metadata, the EEE schema for evaluation run data, and voluntary developer disclosure
|
| 55 |
+
for fields neither captures — then exposes interpretive signals over the result.
|
| 56 |
+
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
</div>
|
| 58 |
</section>
|
| 59 |
|
| 60 |
+
{/* Six-level rollout hierarchy */}
|
| 61 |
+
<section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 62 |
+
<div className="space-y-3">
|
| 63 |
+
<SectionLabel label="Rollout hierarchy" />
|
| 64 |
+
<h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
|
| 65 |
+
Six levels from a benchmark family down to a single metric.
|
| 66 |
+
</h2>
|
| 67 |
+
<p className="max-w-md text-sm leading-7 text-muted-foreground">
|
| 68 |
+
Reports are not flat (model, benchmark, score) triples. Every score resolves to an explicit
|
| 69 |
+
path through this hierarchy, which makes drill-down and apples-to-apples comparison possible.
|
| 70 |
</p>
|
| 71 |
</div>
|
| 72 |
|
| 73 |
+
<ol className="space-y-3">
|
| 74 |
+
<RollupRow
|
| 75 |
+
index="1"
|
| 76 |
+
title="Family"
|
| 77 |
+
body="A related collection sharing a common object of measurement or methodological lineage (e.g., the SWE-bench family, the MMLU family)."
|
|
|
|
| 78 |
/>
|
| 79 |
+
<RollupRow
|
| 80 |
+
index="2"
|
| 81 |
+
title="Suite"
|
| 82 |
+
body="A named composite reporting unit that aggregates multiple benchmarks under a unified presentation (e.g., Open LLM Leaderboard v2, HELM Instruct)."
|
|
|
|
| 83 |
/>
|
| 84 |
+
<RollupRow
|
| 85 |
+
index="3"
|
| 86 |
+
title="Single benchmark"
|
| 87 |
+
body="An individual evaluation with a defined dataset and scoring method (e.g., GSM8K, IFEval, MMLU-Pro)."
|
|
|
|
| 88 |
/>
|
| 89 |
+
<RollupRow
|
| 90 |
+
index="4"
|
| 91 |
+
title="Split"
|
| 92 |
+
body="A named partition of a benchmark's item set (e.g., test, validation, or a language-specific subset)."
|
| 93 |
+
/>
|
| 94 |
+
<RollupRow
|
| 95 |
+
index="5"
|
| 96 |
+
title="Subtask"
|
| 97 |
+
body="A capability- or construct-level decomposition within a benchmark (e.g., algebra within MATH)."
|
| 98 |
+
/>
|
| 99 |
+
<RollupRow
|
| 100 |
+
index="6"
|
| 101 |
+
title="Metric"
|
| 102 |
+
body="The specific scoring rule attached to a result (e.g., pass@1, accuracy, F1)."
|
| 103 |
+
/>
|
| 104 |
+
</ol>
|
| 105 |
</section>
|
| 106 |
|
| 107 |
+
{/* Four interpretive signals */}
|
| 108 |
+
<section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 109 |
+
<div className="space-y-3">
|
| 110 |
+
<SectionLabel label="Interpretive signals" />
|
| 111 |
+
<h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
|
| 112 |
+
Four signals that help readers decide whether to trust a reported score.
|
| 113 |
+
</h2>
|
| 114 |
+
<p className="max-w-md text-sm leading-7 text-muted-foreground">
|
| 115 |
+
Each signal is computed at the record level and rolled up at the corpus level. Per-record
|
| 116 |
+
instances appear on every model and benchmark page; rollups appear on the home page.
|
| 117 |
+
</p>
|
| 118 |
</div>
|
| 119 |
|
| 120 |
+
<div className="grid gap-3 sm:grid-cols-2">
|
| 121 |
+
<SignalCard
|
| 122 |
+
icon={<ShieldCheck className="h-4 w-4" />}
|
| 123 |
+
label="Reproducibility"
|
| 124 |
+
question="Can this be reproduced?"
|
| 125 |
+
body="Flags reported scores whose generation and prompting setup is too underspecified to re-run independently."
|
| 126 |
+
/>
|
| 127 |
+
<SignalCard
|
| 128 |
+
icon={<ClipboardCheck className="h-4 w-4" />}
|
| 129 |
+
label="Reporting completeness"
|
| 130 |
+
question="Is the documentation complete?"
|
| 131 |
+
body="Measures the fraction of operationalized framework fields that are populated for a benchmark."
|
| 132 |
+
/>
|
| 133 |
+
<SignalCard
|
| 134 |
+
icon={<Users className="h-4 w-4" />}
|
| 135 |
+
label="Provenance"
|
| 136 |
+
question="Who reported this, and what risks does the benchmark carry?"
|
| 137 |
+
body="Distinguishes first-party, third-party, and collaborative reporting and surfaces multi-source coverage."
|
| 138 |
+
/>
|
| 139 |
+
<SignalCard
|
| 140 |
+
icon={<GitCompareArrows className="h-4 w-4" />}
|
| 141 |
+
label="Comparability"
|
| 142 |
+
question="Are these scores really comparable?"
|
| 143 |
+
body="Flags score divergence across setup variants and across reporting parties for the same (model, benchmark, metric)."
|
| 144 |
+
/>
|
| 145 |
+
</div>
|
| 146 |
+
</section>
|
| 147 |
+
|
| 148 |
+
{/* Two reader modes */}
|
| 149 |
+
<section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 150 |
+
<div className="space-y-3">
|
| 151 |
+
<SectionLabel label="Reader modes" />
|
| 152 |
+
<h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
|
| 153 |
+
Two renderings of the same record.
|
| 154 |
+
</h2>
|
| 155 |
+
<p className="max-w-md text-sm leading-7 text-muted-foreground">
|
| 156 |
+
The two modes operate on the same underlying data. The difference is which fields are
|
| 157 |
+
surfaced, which are compressed, and which framing is used.
|
| 158 |
</p>
|
| 159 |
</div>
|
| 160 |
+
|
| 161 |
+
<div className="grid gap-3 sm:grid-cols-2">
|
| 162 |
+
<ModeCard
|
| 163 |
+
title="Research mode"
|
| 164 |
+
body="Foregrounds methodology and configuration. Reproducibility gaps list specific missing fields. Comparability surfaces the underlying setup differences. Default for technical evaluators, benchmark developers, and meta-analysis."
|
| 165 |
+
/>
|
| 166 |
+
<ModeCard
|
| 167 |
+
title="Policy mode"
|
| 168 |
+
body="Foregrounds accountability and plain-language interpretation. The same signals render with narrative caveats and compressed metric configuration. Default for regulators, standards bodies, and non-technical readers."
|
| 169 |
+
/>
|
| 170 |
+
</div>
|
| 171 |
</section>
|
| 172 |
|
| 173 |
+
{/* Composition + sources */}
|
| 174 |
+
<section className="grid gap-8 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 175 |
+
<div className="space-y-3">
|
| 176 |
+
<SectionLabel label="Composition" />
|
| 177 |
+
<h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
|
| 178 |
+
Eval Cards does not collect evaluation data directly.
|
| 179 |
+
</h2>
|
| 180 |
+
<p className="max-w-md text-sm leading-7 text-muted-foreground">
|
| 181 |
+
It composes three existing sources and applies a canonicalization layer that maps surface
|
| 182 |
+
identifiers to nodes in the rollout hierarchy.
|
| 183 |
+
</p>
|
| 184 |
</div>
|
| 185 |
|
| 186 |
+
<div className="space-y-3">
|
| 187 |
+
<SourceRow
|
| 188 |
+
icon={<BookOpenText className="h-4 w-4" />}
|
| 189 |
+
title="Auto-BenchmarkCards"
|
| 190 |
+
body="Benchmark metadata: design intent, scoring methodology, data licensing, risk annotations."
|
| 191 |
+
/>
|
| 192 |
+
<SourceRow
|
| 193 |
+
icon={<Database className="h-4 w-4" />}
|
| 194 |
+
title="EEE (evaluation run data)"
|
| 195 |
+
body="Generation configuration, evaluator relationships, and per-instance results from major frameworks."
|
| 196 |
+
/>
|
| 197 |
+
<SourceRow
|
| 198 |
+
icon={<Scale className="h-4 w-4" />}
|
| 199 |
+
title="Voluntary disclosure"
|
| 200 |
+
body="Two reserved fields accept developer-supplied disclosure: preregistration links and lifecycle status."
|
| 201 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
</div>
|
| 203 |
</section>
|
| 204 |
+
|
| 205 |
+
{/* CTAs */}
|
| 206 |
+
<section className="flex flex-wrap gap-3 border-t border-border/60 pt-10">
|
| 207 |
+
<Link href="/">
|
| 208 |
+
<Button className="gap-2 rounded-full px-5">
|
| 209 |
+
Back to home
|
| 210 |
+
<ArrowRight className="h-4 w-4" />
|
| 211 |
+
</Button>
|
| 212 |
+
</Link>
|
| 213 |
+
<Link href="/models">
|
| 214 |
+
<Button variant="outline" className="gap-2 rounded-full px-5">
|
| 215 |
+
Browse models
|
| 216 |
+
<ArrowRight className="h-4 w-4" />
|
| 217 |
+
</Button>
|
| 218 |
+
</Link>
|
| 219 |
+
<Link href="/evals">
|
| 220 |
+
<Button variant="outline" className="gap-2 rounded-full px-5">
|
| 221 |
+
Browse evaluations
|
| 222 |
+
<ArrowRight className="h-4 w-4" />
|
| 223 |
+
</Button>
|
| 224 |
+
</Link>
|
| 225 |
+
<Link href="/survey">
|
| 226 |
+
<Button variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
|
| 227 |
+
Leave feedback
|
| 228 |
+
<ArrowRight className="h-4 w-4" />
|
| 229 |
+
</Button>
|
| 230 |
+
</Link>
|
| 231 |
+
</section>
|
| 232 |
</div>
|
| 233 |
</main>
|
| 234 |
</div>
|
|
|
|
| 243 |
)
|
| 244 |
}
|
| 245 |
|
| 246 |
+
function RollupRow({ index, title, body }: { index: string; title: string; body: string }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
return (
|
| 248 |
+
<li className="flex gap-4 rounded-2xl border border-border/70 bg-card p-4">
|
| 249 |
+
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted/70 text-xs font-semibold text-muted-foreground">
|
| 250 |
+
{index}
|
| 251 |
+
</div>
|
| 252 |
+
<div className="min-w-0">
|
| 253 |
+
<div className="text-sm font-semibold text-foreground">{title}</div>
|
| 254 |
+
<p className="mt-1 text-sm leading-6 text-muted-foreground">{body}</p>
|
| 255 |
+
</div>
|
| 256 |
+
</li>
|
| 257 |
)
|
| 258 |
}
|
| 259 |
|
| 260 |
+
function SignalCard({
|
| 261 |
icon,
|
| 262 |
+
label,
|
| 263 |
+
question,
|
| 264 |
body,
|
|
|
|
| 265 |
}: {
|
| 266 |
icon: React.ReactNode
|
| 267 |
+
label: string
|
| 268 |
+
question: string
|
| 269 |
body: string
|
|
|
|
| 270 |
}) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
return (
|
| 272 |
+
<div className="flex flex-col gap-2 rounded-2xl border border-border/70 bg-card p-4">
|
| 273 |
+
<div className="flex items-center gap-2">
|
| 274 |
+
<span className="rounded-full bg-muted/60 p-1.5 text-muted-foreground">{icon}</span>
|
| 275 |
+
<div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">{label}</div>
|
| 276 |
</div>
|
| 277 |
+
<div className="text-sm font-semibold text-foreground">{question}</div>
|
| 278 |
+
<p className="text-sm leading-6 text-muted-foreground">{body}</p>
|
| 279 |
</div>
|
| 280 |
)
|
| 281 |
}
|
| 282 |
|
| 283 |
+
function ModeCard({ title, body }: { title: string; body: string }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
return (
|
| 285 |
+
<div className="rounded-2xl border border-border/70 bg-card p-4">
|
| 286 |
<div className="text-sm font-semibold text-foreground">{title}</div>
|
| 287 |
+
<p className="mt-2 text-sm leading-6 text-muted-foreground">{body}</p>
|
| 288 |
</div>
|
| 289 |
)
|
| 290 |
}
|
| 291 |
|
| 292 |
+
function SourceRow({ icon, title, body }: { icon: React.ReactNode; title: string; body: string }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
return (
|
| 294 |
+
<div className="flex items-start gap-3 rounded-2xl border border-border/70 bg-card p-4">
|
| 295 |
+
<span className="rounded-full bg-muted/60 p-1.5 text-muted-foreground">{icon}</span>
|
| 296 |
+
<div className="min-w-0">
|
| 297 |
+
<div className="text-sm font-semibold text-foreground">{title}</div>
|
| 298 |
+
<p className="mt-1 text-sm leading-6 text-muted-foreground">{body}</p>
|
| 299 |
+
</div>
|
| 300 |
</div>
|
| 301 |
)
|
| 302 |
}
|
|
@@ -1,36 +1,7 @@
|
|
| 1 |
-
import {
|
| 2 |
-
import { Navigation } from "@/components/navigation"
|
| 3 |
-
import { fetchCorpusAggregates, fetchEvalListLite } from "@/lib/hf-data"
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
])
|
| 10 |
-
|
| 11 |
-
const completenessScores = evalList.evals
|
| 12 |
-
.map((entry) => entry.evalcards?.annotations?.reporting_completeness?.completeness_score)
|
| 13 |
-
.filter((score): score is number => typeof score === "number" && Number.isFinite(score))
|
| 14 |
-
|
| 15 |
-
return (
|
| 16 |
-
<div className="min-h-screen bg-background">
|
| 17 |
-
<Navigation />
|
| 18 |
-
<main className="container mx-auto px-4 py-8">
|
| 19 |
-
{aggregates ? (
|
| 20 |
-
<CorpusDashboard aggregates={aggregates} completenessScores={completenessScores} />
|
| 21 |
-
) : (
|
| 22 |
-
<section className="rounded-2xl border border-dashed border-border/70 bg-card p-8 text-center">
|
| 23 |
-
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 24 |
-
Interpretive signals
|
| 25 |
-
</div>
|
| 26 |
-
<h1 className="mt-2 text-2xl font-semibold tracking-tight">Corpus aggregates are not available yet</h1>
|
| 27 |
-
<p className="mx-auto mt-3 max-w-2xl text-sm leading-6 text-muted-foreground">
|
| 28 |
-
The frontend is ready for `corpus-aggregates.json`, but this cached backend snapshot does not include it yet.
|
| 29 |
-
Once the dataset ships the file, this page will render reproducibility, completeness, provenance, and comparability rollups.
|
| 30 |
-
</p>
|
| 31 |
-
</section>
|
| 32 |
-
)}
|
| 33 |
-
</main>
|
| 34 |
-
</div>
|
| 35 |
-
)
|
| 36 |
}
|
|
|
|
| 1 |
+
import { redirect } from "next/navigation"
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
// Corpus signals were folded into the home page. Preserve any external links
|
| 4 |
+
// to /corpus by sending readers to the same content at the root.
|
| 5 |
+
export default function CorpusRedirect() {
|
| 6 |
+
redirect("/")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
}
|
|
@@ -1,256 +1,213 @@
|
|
| 1 |
import Link from "next/link"
|
|
|
|
|
|
|
| 2 |
import { Button } from "@/components/ui/button"
|
| 3 |
-
import {
|
| 4 |
import { HomeModeLabel } from "@/components/home-mode-label"
|
| 5 |
import { Navigation } from "@/components/navigation"
|
| 6 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
function formatGeneratedAt(value: string | null | undefined) {
|
| 9 |
-
if (!value) return
|
| 10 |
-
|
| 11 |
try {
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
| 14 |
day: "numeric",
|
| 15 |
-
hour: "numeric",
|
| 16 |
-
minute: "2-digit",
|
| 17 |
})
|
| 18 |
} catch {
|
| 19 |
return value
|
| 20 |
}
|
| 21 |
}
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
export default async function HomePage() {
|
| 24 |
-
const [
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
])
|
| 29 |
|
| 30 |
-
const
|
| 31 |
-
const
|
| 32 |
-
const
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
return (
|
| 39 |
<div className="min-h-screen bg-background">
|
| 40 |
<Navigation />
|
| 41 |
-
<main className="relative overflow-hidden">
|
| 42 |
-
<div className="absolute inset-x-0 top-0 -z-10 h-[24rem] bg-[radial-gradient(circle_at_top,rgba(196,167,96,0.12),transparent_60%)]" />
|
| 43 |
-
<div className="absolute inset-y-0 right-0 -z-10 hidden w-[36rem] bg-[radial-gradient(circle_at_center,rgba(71,129,177,0.08),transparent_66%)] lg:block" />
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
</Link>
|
| 70 |
-
<Link href="/evals">
|
| 71 |
-
<Button size="lg" variant="outline" className="gap-2 rounded-full px-6">
|
| 72 |
-
Browse evaluations
|
| 73 |
-
<ArrowRight className="h-4 w-4" />
|
| 74 |
-
</Button>
|
| 75 |
-
</Link>
|
| 76 |
-
<Link href="/survey">
|
| 77 |
-
<Button size="lg" variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
|
| 78 |
-
Leave feedback
|
| 79 |
-
<ArrowRight className="h-4 w-4" />
|
| 80 |
-
</Button>
|
| 81 |
-
</Link>
|
| 82 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
</div>
|
| 84 |
|
| 85 |
-
<
|
| 86 |
-
<
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
<
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
</div>
|
|
|
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
body="Research and policy views highlight different fields."
|
| 117 |
-
tone="emerald"
|
| 118 |
-
/>
|
| 119 |
</div>
|
| 120 |
|
| 121 |
-
|
| 122 |
-
<
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
href="/evals"
|
| 130 |
-
icon={<BookOpenText className="h-4 w-4" />}
|
| 131 |
-
title="Benchmark records"
|
| 132 |
-
body="How a benchmark is reported across models."
|
| 133 |
-
/>
|
| 134 |
-
<RoutePanel
|
| 135 |
-
href="/about"
|
| 136 |
-
icon={<Scale className="h-4 w-4" />}
|
| 137 |
-
title="Project notes"
|
| 138 |
-
body="Why this reporting format exists."
|
| 139 |
-
/>
|
| 140 |
-
<Link
|
| 141 |
-
href="/survey"
|
| 142 |
-
className="group flex h-full flex-col rounded-[1.5rem] border border-border/70 bg-muted/20 p-5 transition-colors hover:bg-muted/30"
|
| 143 |
-
>
|
| 144 |
-
<div className="flex items-center gap-2 text-sm font-semibold text-foreground">
|
| 145 |
-
<span className="rounded-full bg-rose-50 p-2 text-rose-600 dark:bg-rose-950/30">
|
| 146 |
-
<MessageSquare className="h-4 w-4" />
|
| 147 |
-
</span>
|
| 148 |
-
Feedback
|
| 149 |
-
</div>
|
| 150 |
-
<p className="mt-3 text-sm leading-7 text-muted-foreground">
|
| 151 |
-
Flag missing fields or unclear labels.
|
| 152 |
</p>
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
<ArrowRight className="h-4 w-4" />
|
| 156 |
-
</div>
|
| 157 |
-
</Link>
|
| 158 |
-
</div>
|
| 159 |
</section>
|
| 160 |
</main>
|
| 161 |
</div>
|
| 162 |
)
|
| 163 |
}
|
| 164 |
|
| 165 |
-
function
|
| 166 |
label,
|
| 167 |
value,
|
| 168 |
detail,
|
| 169 |
-
tone,
|
| 170 |
}: {
|
| 171 |
label: string
|
| 172 |
value: string
|
| 173 |
detail: string
|
| 174 |
-
tone: "amber" | "emerald" | "rose" | "sky" | "slate"
|
| 175 |
}) {
|
| 176 |
-
const toneClasses = {
|
| 177 |
-
amber: "border-amber-200/80 bg-amber-50/70 dark:border-amber-900/50 dark:bg-amber-950/20",
|
| 178 |
-
emerald: "border-emerald-200/80 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20",
|
| 179 |
-
rose: "border-rose-200/80 bg-rose-50/70 dark:border-rose-900/50 dark:bg-rose-950/20",
|
| 180 |
-
sky: "border-sky-200/80 bg-sky-50/70 dark:border-sky-900/50 dark:bg-sky-950/20",
|
| 181 |
-
slate: "border-border/70 bg-muted/25",
|
| 182 |
-
}[tone]
|
| 183 |
-
|
| 184 |
return (
|
| 185 |
-
<div className=
|
| 186 |
-
<div className="text-[10px] font-semibold uppercase tracking-[0.
|
| 187 |
-
|
| 188 |
-
<p className="text-sm leading-6 text-muted-foreground">{detail}</p>
|
| 189 |
-
</div>
|
| 190 |
-
)
|
| 191 |
-
}
|
| 192 |
-
|
| 193 |
-
function SignalCard({
|
| 194 |
-
icon,
|
| 195 |
-
title,
|
| 196 |
-
body,
|
| 197 |
-
tone,
|
| 198 |
-
}: {
|
| 199 |
-
icon: React.ReactNode
|
| 200 |
-
title: string
|
| 201 |
-
body: string
|
| 202 |
-
tone: "amber" | "emerald" | "sky"
|
| 203 |
-
}) {
|
| 204 |
-
const toneClasses = {
|
| 205 |
-
amber: "bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300",
|
| 206 |
-
emerald: "bg-emerald-50 text-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-300",
|
| 207 |
-
sky: "bg-sky-50 text-sky-800 dark:bg-sky-950/30 dark:text-sky-300",
|
| 208 |
-
}[tone]
|
| 209 |
-
|
| 210 |
-
const surfaceClasses = {
|
| 211 |
-
amber: "border-amber-200/70 bg-amber-50/40 dark:border-amber-900/40 dark:bg-amber-950/15",
|
| 212 |
-
emerald: "border-emerald-200/70 bg-emerald-50/40 dark:border-emerald-900/40 dark:bg-emerald-950/15",
|
| 213 |
-
sky: "border-sky-200/70 bg-sky-50/40 dark:border-sky-900/40 dark:bg-sky-950/15",
|
| 214 |
-
}[tone]
|
| 215 |
-
|
| 216 |
-
return (
|
| 217 |
-
<div className={`flex h-full flex-col gap-3 rounded-[1.5rem] border p-5 ${surfaceClasses}`}>
|
| 218 |
-
<div className="flex items-center gap-2 text-sm font-semibold text-foreground">
|
| 219 |
-
<span className={`rounded-full p-2 ${toneClasses}`}>{icon}</span>
|
| 220 |
-
{title}
|
| 221 |
</div>
|
| 222 |
-
<
|
| 223 |
-
|
| 224 |
-
)
|
| 225 |
-
}
|
| 226 |
-
|
| 227 |
-
function RoutePanel({
|
| 228 |
-
href,
|
| 229 |
-
icon,
|
| 230 |
-
title,
|
| 231 |
-
body,
|
| 232 |
-
}: {
|
| 233 |
-
href: string
|
| 234 |
-
icon: React.ReactNode
|
| 235 |
-
title: string
|
| 236 |
-
body: string
|
| 237 |
-
}) {
|
| 238 |
-
return (
|
| 239 |
-
<Link
|
| 240 |
-
href={href}
|
| 241 |
-
className="group flex h-full flex-col rounded-[1.5rem] border border-border/70 bg-background/80 p-5 transition-colors hover:bg-muted/20"
|
| 242 |
-
>
|
| 243 |
-
<div className="flex items-center gap-2 text-sm font-semibold text-foreground">
|
| 244 |
-
<span className="rounded-full bg-muted/60 p-2 text-muted-foreground transition-colors group-hover:text-foreground">
|
| 245 |
-
{icon}
|
| 246 |
-
</span>
|
| 247 |
-
{title}
|
| 248 |
</div>
|
| 249 |
-
<p className="mt-
|
| 250 |
-
|
| 251 |
-
Open
|
| 252 |
-
<ArrowRight className="h-4 w-4" />
|
| 253 |
-
</div>
|
| 254 |
-
</Link>
|
| 255 |
)
|
| 256 |
}
|
|
|
|
| 1 |
import Link from "next/link"
|
| 2 |
+
import { ArrowRight } from "lucide-react"
|
| 3 |
+
|
| 4 |
import { Button } from "@/components/ui/button"
|
| 5 |
+
import { Badge } from "@/components/ui/badge"
|
| 6 |
import { HomeModeLabel } from "@/components/home-mode-label"
|
| 7 |
import { Navigation } from "@/components/navigation"
|
| 8 |
+
import { CorpusSignalsStrip } from "@/components/signals/corpus-signals-strip"
|
| 9 |
+
import { getDeveloperList } from "@/lib/data-backend"
|
| 10 |
+
import {
|
| 11 |
+
fetchBackendManifest,
|
| 12 |
+
fetchCorpusAggregates,
|
| 13 |
+
fetchEvalHierarchy,
|
| 14 |
+
} from "@/lib/hf-data"
|
| 15 |
|
| 16 |
function formatGeneratedAt(value: string | null | undefined) {
|
| 17 |
+
if (!value) return null
|
|
|
|
| 18 |
try {
|
| 19 |
+
const date = new Date(value)
|
| 20 |
+
if (Number.isNaN(date.getTime())) return value
|
| 21 |
+
return date.toLocaleDateString("en-US", {
|
| 22 |
+
year: "numeric",
|
| 23 |
+
month: "long",
|
| 24 |
day: "numeric",
|
|
|
|
|
|
|
| 25 |
})
|
| 26 |
} catch {
|
| 27 |
return value
|
| 28 |
}
|
| 29 |
}
|
| 30 |
|
| 31 |
+
function formatNumber(value: number | undefined | null): string {
|
| 32 |
+
if (value == null || !Number.isFinite(value)) return "—"
|
| 33 |
+
return value.toLocaleString("en-US")
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
export default async function HomePage() {
|
| 37 |
+
const [aggregates, manifest, hierarchy, developers] = await Promise.all([
|
| 38 |
+
fetchCorpusAggregates(),
|
| 39 |
+
fetchBackendManifest(),
|
| 40 |
+
fetchEvalHierarchy(),
|
| 41 |
+
getDeveloperList().catch(() => []),
|
| 42 |
])
|
| 43 |
|
| 44 |
+
const stats = hierarchy.stats
|
| 45 |
+
const familyCount = stats?.family_count ?? hierarchy.families.length
|
| 46 |
+
const compositeCount = stats?.composite_count ?? 0
|
| 47 |
+
const singleBenchmarkCount = stats?.single_benchmark_count ?? 0
|
| 48 |
+
const standaloneBenchmarkCount = stats?.standalone_benchmark_count ?? 0
|
| 49 |
+
const benchmarkLeafCount = singleBenchmarkCount + standaloneBenchmarkCount
|
| 50 |
+
const sliceCount = stats?.slice_count ?? 0
|
| 51 |
+
const metricCount = stats?.metric_count ?? 0
|
| 52 |
+
const tripleCount = stats?.metric_rows_scanned ?? 0
|
| 53 |
+
const modelCount = manifest?.model_count ?? 0
|
| 54 |
+
const developerCount = developers.length
|
| 55 |
+
const generatedAt = formatGeneratedAt(manifest?.generated_at)
|
| 56 |
|
| 57 |
return (
|
| 58 |
<div className="min-h-screen bg-background">
|
| 59 |
<Navigation />
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
+
<main className="mx-auto w-full max-w-[88rem] px-4 py-12 sm:px-6 lg:px-8 lg:py-16">
|
| 62 |
+
{/* Masthead */}
|
| 63 |
+
<section className="border-b border-border/60 pb-10">
|
| 64 |
+
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-[11px] font-semibold uppercase tracking-[0.24em] text-muted-foreground">
|
| 65 |
+
<HomeModeLabel />
|
| 66 |
+
{generatedAt && <span>Snapshot · {generatedAt}</span>}
|
| 67 |
+
{aggregates && (
|
| 68 |
+
<Badge variant="outline" className="font-normal tracking-normal">
|
| 69 |
+
Signals v{aggregates.signal_version}
|
| 70 |
+
</Badge>
|
| 71 |
+
)}
|
| 72 |
+
</div>
|
| 73 |
|
| 74 |
+
<h1 className="mt-5 max-w-4xl text-balance text-4xl font-semibold tracking-[-0.04em] text-foreground sm:text-5xl lg:text-[3.75rem] lg:leading-[1.05]">
|
| 75 |
+
Eval Cards
|
| 76 |
+
</h1>
|
| 77 |
+
<p className="mt-3 max-w-3xl text-base leading-7 text-muted-foreground sm:text-lg sm:leading-8">
|
| 78 |
+
An interpretative integration layer for AI evaluation reporting. Eval Cards composes
|
| 79 |
+
benchmark metadata, evaluation run data, and provenance into a single reading surface,
|
| 80 |
+
and surfaces four interpretive signals — reproducibility, reporting completeness,
|
| 81 |
+
provenance, and comparability — over a public corpus of reported scores.
|
| 82 |
+
</p>
|
| 83 |
+
|
| 84 |
+
<div className="mt-6 flex flex-wrap gap-3">
|
| 85 |
+
<Link href="/models">
|
| 86 |
+
<Button size="lg" className="gap-2 rounded-full px-6">
|
| 87 |
+
Browse models
|
| 88 |
+
<ArrowRight className="h-4 w-4" />
|
| 89 |
+
</Button>
|
| 90 |
+
</Link>
|
| 91 |
+
<Link href="/evals">
|
| 92 |
+
<Button size="lg" variant="outline" className="gap-2 rounded-full px-6">
|
| 93 |
+
Browse evaluations
|
| 94 |
+
<ArrowRight className="h-4 w-4" />
|
| 95 |
+
</Button>
|
| 96 |
+
</Link>
|
| 97 |
+
<Link href="/about">
|
| 98 |
+
<Button size="lg" variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
|
| 99 |
+
About this project
|
| 100 |
+
<ArrowRight className="h-4 w-4" />
|
| 101 |
+
</Button>
|
| 102 |
+
</Link>
|
| 103 |
+
</div>
|
| 104 |
+
</section>
|
| 105 |
|
| 106 |
+
{/* Corpus stats — paper §5.1 */}
|
| 107 |
+
<section className="border-b border-border/60 py-10">
|
| 108 |
+
<div className="grid gap-6 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 109 |
+
<div>
|
| 110 |
+
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 111 |
+
Corpus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
</div>
|
| 113 |
+
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-foreground sm:text-[1.75rem]">
|
| 114 |
+
What this snapshot covers
|
| 115 |
+
</h2>
|
| 116 |
+
<p className="mt-3 max-w-md text-sm leading-7 text-muted-foreground">
|
| 117 |
+
Reports are organized through a six-level rollout hierarchy
|
| 118 |
+
(family → suite → single benchmark → split → subtask → metric) so that
|
| 119 |
+
aggregate claims can be drilled down to the evidence supporting them.
|
| 120 |
+
</p>
|
| 121 |
</div>
|
| 122 |
|
| 123 |
+
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
| 124 |
+
<CorpusStat
|
| 125 |
+
label="Models"
|
| 126 |
+
value={formatNumber(modelCount)}
|
| 127 |
+
detail="Tracked across reporting sources"
|
| 128 |
+
/>
|
| 129 |
+
<CorpusStat
|
| 130 |
+
label="Reported results"
|
| 131 |
+
value={formatNumber(tripleCount)}
|
| 132 |
+
detail="(model, benchmark, metric) triples"
|
| 133 |
+
/>
|
| 134 |
+
<CorpusStat
|
| 135 |
+
label="Reporting organizations"
|
| 136 |
+
value={formatNumber(developerCount)}
|
| 137 |
+
detail="Developers and third-party evaluators"
|
| 138 |
+
/>
|
| 139 |
+
<CorpusStat
|
| 140 |
+
label="Benchmark families"
|
| 141 |
+
value={formatNumber(familyCount)}
|
| 142 |
+
detail="Top of the rollout hierarchy"
|
| 143 |
+
/>
|
| 144 |
+
<CorpusStat
|
| 145 |
+
label="Suites"
|
| 146 |
+
value={formatNumber(compositeCount)}
|
| 147 |
+
detail="Composite reporting units"
|
| 148 |
+
/>
|
| 149 |
+
<CorpusStat
|
| 150 |
+
label="Single benchmarks"
|
| 151 |
+
value={formatNumber(benchmarkLeafCount)}
|
| 152 |
+
detail={`${formatNumber(sliceCount)} slices · ${formatNumber(metricCount)} metrics`}
|
| 153 |
+
/>
|
| 154 |
+
</div>
|
| 155 |
</div>
|
| 156 |
+
</section>
|
| 157 |
|
| 158 |
+
{/* Four interpretive signals */}
|
| 159 |
+
<section className="py-10">
|
| 160 |
+
<div className="mb-6 grid gap-4 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
|
| 161 |
+
<div>
|
| 162 |
+
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 163 |
+
Interpretive signals
|
| 164 |
+
</div>
|
| 165 |
+
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-foreground sm:text-[1.75rem]">
|
| 166 |
+
Reproducibility, completeness, provenance, comparability
|
| 167 |
+
</h2>
|
| 168 |
+
</div>
|
| 169 |
+
<p className="max-w-3xl text-sm leading-7 text-muted-foreground">
|
| 170 |
+
Each signal answers a question a reader brings to a reported score. Per-record
|
| 171 |
+
instances appear on every model and benchmark page. Corpus-level rollups below
|
| 172 |
+
show how reporting practice looks across the public record as a whole.
|
| 173 |
+
</p>
|
|
|
|
|
|
|
|
|
|
| 174 |
</div>
|
| 175 |
|
| 176 |
+
{aggregates ? (
|
| 177 |
+
<CorpusSignalsStrip aggregates={aggregates} />
|
| 178 |
+
) : (
|
| 179 |
+
<div className="rounded-2xl border border-dashed border-border/70 bg-muted/10 p-8 text-center">
|
| 180 |
+
<h3 className="text-lg font-semibold tracking-tight">Corpus aggregates unavailable</h3>
|
| 181 |
+
<p className="mx-auto mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
|
| 182 |
+
The current backend snapshot does not include <code>corpus-aggregates.json</code>.
|
| 183 |
+
When it does, this section will render the four corpus-level rollups.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
</p>
|
| 185 |
+
</div>
|
| 186 |
+
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
</section>
|
| 188 |
</main>
|
| 189 |
</div>
|
| 190 |
)
|
| 191 |
}
|
| 192 |
|
| 193 |
+
function CorpusStat({
|
| 194 |
label,
|
| 195 |
value,
|
| 196 |
detail,
|
|
|
|
| 197 |
}: {
|
| 198 |
label: string
|
| 199 |
value: string
|
| 200 |
detail: string
|
|
|
|
| 201 |
}) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
return (
|
| 203 |
+
<div className="rounded-2xl border border-border/70 bg-card p-4">
|
| 204 |
+
<div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 205 |
+
{label}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
</div>
|
| 207 |
+
<div className="mt-2 text-2xl font-semibold tracking-tight text-foreground tabular-nums sm:text-[1.625rem]">
|
| 208 |
+
{value}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
</div>
|
| 210 |
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{detail}</p>
|
| 211 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
)
|
| 213 |
}
|
|
@@ -38,12 +38,6 @@ export function Navigation() {
|
|
| 38 |
icon: BarChart3,
|
| 39 |
isActive: pathname === "/evals" || pathname?.startsWith("/evals/")
|
| 40 |
},
|
| 41 |
-
{
|
| 42 |
-
href: "/corpus",
|
| 43 |
-
label: "Corpus",
|
| 44 |
-
icon: FlaskConical,
|
| 45 |
-
isActive: pathname === "/corpus" || pathname?.startsWith("/corpus/")
|
| 46 |
-
},
|
| 47 |
{
|
| 48 |
href: "/survey",
|
| 49 |
label: "Survey",
|
|
|
|
| 38 |
icon: BarChart3,
|
| 39 |
isActive: pathname === "/evals" || pathname?.startsWith("/evals/")
|
| 40 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
{
|
| 42 |
href: "/survey",
|
| 43 |
label: "Survey",
|
|
@@ -32,9 +32,16 @@ const SOURCE_COLORS: Record<string, string> = {
|
|
| 32 |
export function CorpusDashboard({
|
| 33 |
aggregates,
|
| 34 |
completenessScores,
|
|
|
|
| 35 |
}: {
|
| 36 |
aggregates: CorpusAggregates
|
| 37 |
completenessScores: number[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}) {
|
| 39 |
const { mode } = useAudienceMode()
|
| 40 |
const [view, setView] = useState<"overall" | "category">("overall")
|
|
@@ -56,44 +63,72 @@ export function CorpusDashboard({
|
|
| 56 |
|
| 57 |
return (
|
| 58 |
<div className="space-y-6">
|
| 59 |
-
|
| 60 |
-
<div className="flex flex-
|
| 61 |
-
<div>
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
<
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
|
|
|
| 93 |
</div>
|
| 94 |
</div>
|
| 95 |
-
</
|
| 96 |
-
|
| 97 |
|
| 98 |
{view === "overall" ? (
|
| 99 |
<div className="grid gap-6">
|
|
|
|
| 32 |
export function CorpusDashboard({
|
| 33 |
aggregates,
|
| 34 |
completenessScores,
|
| 35 |
+
embedded = false,
|
| 36 |
}: {
|
| 37 |
aggregates: CorpusAggregates
|
| 38 |
completenessScores: number[]
|
| 39 |
+
/**
|
| 40 |
+
* When true, hides the dashboard's own masthead card. The host page is
|
| 41 |
+
* expected to provide its own title and generated-date badge. The view
|
| 42 |
+
* toggle is preserved inline above the signal sections.
|
| 43 |
+
*/
|
| 44 |
+
embedded?: boolean
|
| 45 |
}) {
|
| 46 |
const { mode } = useAudienceMode()
|
| 47 |
const [view, setView] = useState<"overall" | "category">("overall")
|
|
|
|
| 63 |
|
| 64 |
return (
|
| 65 |
<div className="space-y-6">
|
| 66 |
+
{embedded ? (
|
| 67 |
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
| 68 |
+
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 69 |
+
Interpretive signals
|
| 70 |
+
</div>
|
| 71 |
+
<div className="inline-flex rounded-full border bg-muted/20 p-1">
|
| 72 |
+
<Button
|
| 73 |
+
type="button"
|
| 74 |
+
size="sm"
|
| 75 |
+
variant={view === "overall" ? "default" : "ghost"}
|
| 76 |
+
className="h-8 rounded-full"
|
| 77 |
+
onClick={() => setView("overall")}
|
| 78 |
+
>
|
| 79 |
+
Overall
|
| 80 |
+
</Button>
|
| 81 |
+
<Button
|
| 82 |
+
type="button"
|
| 83 |
+
size="sm"
|
| 84 |
+
variant={view === "category" ? "default" : "ghost"}
|
| 85 |
+
className="h-8 rounded-full"
|
| 86 |
+
onClick={() => setView("category")}
|
| 87 |
+
>
|
| 88 |
+
By category
|
| 89 |
+
</Button>
|
| 90 |
</div>
|
| 91 |
+
</div>
|
| 92 |
+
) : (
|
| 93 |
+
<section className="rounded-2xl border border-border/70 bg-card p-5 shadow-sm">
|
| 94 |
+
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
| 95 |
+
<div>
|
| 96 |
+
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 97 |
+
Interpretive signals
|
| 98 |
+
</div>
|
| 99 |
+
<h1 className="mt-2 text-3xl font-semibold tracking-tight">Corpus Dashboard</h1>
|
| 100 |
+
<p className="mt-2 max-w-3xl text-sm leading-6 text-muted-foreground">
|
| 101 |
+
Corpus-level rollups for reproducibility, reporting completeness, provenance, and comparability.
|
| 102 |
+
</p>
|
| 103 |
+
</div>
|
| 104 |
|
| 105 |
+
<div className="flex flex-wrap items-center gap-2">
|
| 106 |
+
<Badge variant="outline">Signals v{aggregates.signal_version}</Badge>
|
| 107 |
+
<Badge variant="outline">Generated {formatGeneratedDate(aggregates.generated_at)}</Badge>
|
| 108 |
+
<div className="inline-flex rounded-full border bg-muted/20 p-1">
|
| 109 |
+
<Button
|
| 110 |
+
type="button"
|
| 111 |
+
size="sm"
|
| 112 |
+
variant={view === "overall" ? "default" : "ghost"}
|
| 113 |
+
className="h-8 rounded-full"
|
| 114 |
+
onClick={() => setView("overall")}
|
| 115 |
+
>
|
| 116 |
+
Overall
|
| 117 |
+
</Button>
|
| 118 |
+
<Button
|
| 119 |
+
type="button"
|
| 120 |
+
size="sm"
|
| 121 |
+
variant={view === "category" ? "default" : "ghost"}
|
| 122 |
+
className="h-8 rounded-full"
|
| 123 |
+
onClick={() => setView("category")}
|
| 124 |
+
>
|
| 125 |
+
By category
|
| 126 |
+
</Button>
|
| 127 |
+
</div>
|
| 128 |
</div>
|
| 129 |
</div>
|
| 130 |
+
</section>
|
| 131 |
+
)}
|
| 132 |
|
| 133 |
{view === "overall" ? (
|
| 134 |
<div className="grid gap-6">
|
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { CorpusAggregates } from "@/lib/backend-artifacts"
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Compact 4-tile rollup of the four interpretive signals (paper §4.2.1).
|
| 5 |
+
* Designed for the home page: one headline per signal, one caption, one
|
| 6 |
+
* sub-detail line. Drill-down lives on per-record pages.
|
| 7 |
+
*/
|
| 8 |
+
export function CorpusSignalsStrip({
|
| 9 |
+
aggregates,
|
| 10 |
+
}: {
|
| 11 |
+
aggregates: CorpusAggregates
|
| 12 |
+
}) {
|
| 13 |
+
const repro = aggregates.reproducibility.overall
|
| 14 |
+
const comp = aggregates.completeness.overall
|
| 15 |
+
const prov = aggregates.provenance.overall
|
| 16 |
+
const cmp = aggregates.comparability.overall
|
| 17 |
+
|
| 18 |
+
// Invert the gap rate so the headline reads as "how many are documented",
|
| 19 |
+
// not "how many have gaps". The inverse is more impactful and lower-is-worse,
|
| 20 |
+
// which matches the user's intuition. The gap rate stays in the detail line.
|
| 21 |
+
const reproDocumentedRate =
|
| 22 |
+
repro.reproducibility_gap_rate == null
|
| 23 |
+
? null
|
| 24 |
+
: Math.max(0, 1 - repro.reproducibility_gap_rate)
|
| 25 |
+
const reproDocumentedRateText = formatPct(reproDocumentedRate)
|
| 26 |
+
const reproGapRateText = formatPct(repro.reproducibility_gap_rate)
|
| 27 |
+
const reproDetail = topMissingFields(repro.per_field_missingness, 2)
|
| 28 |
+
|
| 29 |
+
const compMean = formatPct(comp.completeness_score_mean)
|
| 30 |
+
const compMedian = formatPct(comp.completeness_score_median)
|
| 31 |
+
|
| 32 |
+
const provMulti = formatPct(prov.multi_source_rate)
|
| 33 |
+
const totalReports = prov.total_triples
|
| 34 |
+
const tpShare = totalReports > 0 ? prov.source_type_distribution.third_party / totalReports : 0
|
| 35 |
+
const fpShare = totalReports > 0 ? prov.source_type_distribution.first_party / totalReports : 0
|
| 36 |
+
|
| 37 |
+
const cmpRate = formatPct(cmp.variant_divergence_rate)
|
| 38 |
+
const crossPartyAvailable = cmp.cross_party_eligible_groups > 0
|
| 39 |
+
|
| 40 |
+
return (
|
| 41 |
+
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
| 42 |
+
<SignalTile
|
| 43 |
+
label="Reproducibility"
|
| 44 |
+
value={reproDocumentedRateText}
|
| 45 |
+
caption="of reported scores have a complete setup recorded — the rest cannot be independently re-run"
|
| 46 |
+
detail={
|
| 47 |
+
reproDetail
|
| 48 |
+
? `${reproGapRateText} have at least one undocumented field. Most often missing: ${reproDetail}`
|
| 49 |
+
: `${reproGapRateText} have at least one undocumented field`
|
| 50 |
+
}
|
| 51 |
+
/>
|
| 52 |
+
|
| 53 |
+
<SignalTile
|
| 54 |
+
label="Reporting completeness"
|
| 55 |
+
value={compMean}
|
| 56 |
+
caption={`mean across ${comp.total_benchmarks.toLocaleString()} benchmarks (median ${compMedian})`}
|
| 57 |
+
detail="Source-provenance fields populate fully; preregistration fields are unmet"
|
| 58 |
+
/>
|
| 59 |
+
|
| 60 |
+
<SignalTile
|
| 61 |
+
label="Provenance"
|
| 62 |
+
value={provMulti}
|
| 63 |
+
caption="of (model, benchmark) groups have reports from more than one party"
|
| 64 |
+
detail={`${formatPct(tpShare)} third-party, ${formatPct(fpShare)} first-party of ${totalReports.toLocaleString()} results`}
|
| 65 |
+
/>
|
| 66 |
+
|
| 67 |
+
<SignalTile
|
| 68 |
+
label="Comparability"
|
| 69 |
+
value={cmpRate}
|
| 70 |
+
caption={`of setup-eligible groups diverge across variants (${cmp.variant_divergent_groups.toLocaleString()} of ${cmp.variant_eligible_groups.toLocaleString()})`}
|
| 71 |
+
detail={
|
| 72 |
+
crossPartyAvailable
|
| 73 |
+
? `Cross-party divergence: ${formatPct(cmp.cross_party_divergence_rate)}`
|
| 74 |
+
: "Cross-party divergence not yet computable — too few multi-org reports"
|
| 75 |
+
}
|
| 76 |
+
/>
|
| 77 |
+
</div>
|
| 78 |
+
)
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function SignalTile({
|
| 82 |
+
label,
|
| 83 |
+
value,
|
| 84 |
+
caption,
|
| 85 |
+
detail,
|
| 86 |
+
}: {
|
| 87 |
+
label: string
|
| 88 |
+
value: string
|
| 89 |
+
caption: string
|
| 90 |
+
detail: string
|
| 91 |
+
}) {
|
| 92 |
+
return (
|
| 93 |
+
<div className="flex flex-col gap-2 rounded-2xl border border-border/70 bg-card p-4">
|
| 94 |
+
<div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
| 95 |
+
{label}
|
| 96 |
+
</div>
|
| 97 |
+
<div className="flex items-baseline gap-2">
|
| 98 |
+
<span className="text-3xl font-semibold tabular-nums tracking-tight text-foreground">
|
| 99 |
+
{value}
|
| 100 |
+
</span>
|
| 101 |
+
</div>
|
| 102 |
+
<p className="text-sm leading-snug text-muted-foreground">{caption}.</p>
|
| 103 |
+
<p className="mt-auto text-xs leading-snug text-muted-foreground/80">{detail}.</p>
|
| 104 |
+
</div>
|
| 105 |
+
)
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
function formatPct(value: number | null | undefined): string {
|
| 109 |
+
if (value == null || !Number.isFinite(value)) return "—"
|
| 110 |
+
if (value === 0) return "0%"
|
| 111 |
+
if (value > 0 && value < 0.01) return "<1%"
|
| 112 |
+
return `${Math.round(value * 100)}%`
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
const FIELD_LABELS: Record<string, string> = {
|
| 116 |
+
temperature: "temperature",
|
| 117 |
+
max_tokens: "max tokens",
|
| 118 |
+
top_p: "top-p",
|
| 119 |
+
prompt_template: "prompt template",
|
| 120 |
+
eval_plan: "eval plan",
|
| 121 |
+
eval_limits: "eval limits",
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
/**
|
| 125 |
+
* Returns a short comma-separated list of the top-N missing fields, ranked by
|
| 126 |
+
* missing count. We rank by count rather than rate so cross-denominator fields
|
| 127 |
+
* (agentic-only vs all-triples) don't get artificially boosted.
|
| 128 |
+
*/
|
| 129 |
+
function topMissingFields(
|
| 130 |
+
perField: Record<
|
| 131 |
+
string,
|
| 132 |
+
{ missing_count: number; missing_rate: number | null; denominator: string }
|
| 133 |
+
>,
|
| 134 |
+
n: number,
|
| 135 |
+
): string {
|
| 136 |
+
const entries = Object.entries(perField)
|
| 137 |
+
.filter(([, v]) => v.missing_count > 0)
|
| 138 |
+
.sort((a, b) => b[1].missing_count - a[1].missing_count)
|
| 139 |
+
.slice(0, n)
|
| 140 |
+
|
| 141 |
+
if (entries.length === 0) return ""
|
| 142 |
+
|
| 143 |
+
return entries
|
| 144 |
+
.map(([key, v]) => `${FIELD_LABELS[key] ?? key.replace(/_/g, " ")} (${formatPct(v.missing_rate)})`)
|
| 145 |
+
.join(", ")
|
| 146 |
+
}
|
|
@@ -2,6 +2,14 @@ export interface BackendManifest {
|
|
| 2 |
generated_at: string
|
| 3 |
config_version: number
|
| 4 |
skipped_configs: string[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
summary_artifacts?: {
|
| 6 |
corpus_aggregates?: string
|
| 7 |
[key: string]: string | undefined
|
|
|
|
| 2 |
generated_at: string
|
| 3 |
config_version: number
|
| 4 |
skipped_configs: string[]
|
| 5 |
+
// Counts the upstream pipeline already records in manifest.json. The home
|
| 6 |
+
// page reads these directly so it does not have to load model-cards-lite
|
| 7 |
+
// (~20MB) just to display headline numbers.
|
| 8 |
+
model_count?: number
|
| 9 |
+
eval_count?: number
|
| 10 |
+
metric_eval_count?: number
|
| 11 |
+
source_config_count?: number
|
| 12 |
+
skipped_config_count?: number
|
| 13 |
summary_artifacts?: {
|
| 14 |
corpus_aggregates?: string
|
| 15 |
[key: string]: string | undefined
|