evijit HF Staff Claude Opus 4.7 (1M context) commited on
Commit
8ef4cbc
·
1 Parent(s): cb0db40

Merge cross-source benchmark families; tidy leaderboard panel + table chrome

Browse files

clean-hierarchy:
* New `groupSameBenchAcrossSources` pass collapses N≥2 single-bench
families that share a bench key but ship disjoint eval_summary_ids
(i.e. independent sources publishing the same conceptual benchmark)
into a single family card. Survivor = most models; other benches
are moved in as siblings with display_name " · <Source>" suffixed
from metrics[0].sources, and a unique key suffix
(mmlu-pro__arcadia-impact) so siblings don't collide. Runs AFTER
decorateHierarchyDerivedTags so the sanitizer's shareToken /
humanizeKey passes don't clobber the suffixed names. Net effect:
one MMLU-Pro card with two source-tagged benches.

eval-detail:
* Drop the standalone bar column (header repeated the eval name, the
bar repeated the score). Inline a thin perf bar under the score
cell with the setup / dataset caption right-aligned beneath.
* Drop the duplicate ApplesToApplesBanner from inside
MultiMetricLeaderboard — the parent already renders it.
* Render the multi-metric SplitPicker INSIDE MultiMetricLeaderboard
(after the section heading + description) so it matches the
position of the single-metric branch's Split picker. Both fibble
and global-mmlu now render the dropdown in the same spot.
* Restore the subtask Split picker for evals like Global MMLU Lite
(1 root metric + N subtasks) but keep it gated by !splitConfig so
fibble (with page-level Split) doesn't double up.
* Wording: rename remaining "Slice" UI strings to "Split" (Slice
breakdown → Split breakdown, "metric spec · ... · slices" →
"splits") to match the rest of the app.
* getMetricChipLabel walks display_name → metric_name → metric_id →
column_key tail and humanises underscores so AgentHarm's null-
display-name metrics surface as "avg full score" etc. instead of
four chips literally labelled "Metric".

family-table:
* "Reported results" column now divides total_results by the family's
bench-level metric count, giving a unique-models-tested
approximation. AgentHarm's 72 metric-rows / 4 metrics → 18 unique
models surfaced instead of the misleading raw 72.
* Drop the dead totalModels prop and unused visibleLeaves local.

score-distribution:
* View toggle (Distribution / Frontier) reverts to ec-pill chips —
same chip style as the metric row below; row labels (VIEW vs
METRIC) carry the differentiation.

models page:
* Default sort changes from Released ↓ to Coverage ↓ (and the Reset
Filters button resets to Coverage ↓).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

app/evals/page.tsx CHANGED
@@ -312,7 +312,6 @@ function EvalsPageInner() {
312
  ) : (
313
  <FamilyTable
314
  families={visibleFamilies}
315
- totalModels={totalModels}
316
  evalItems={evalItems}
317
  benchmarkCards={benchmarkCards}
318
  categoryFilter={new Set(selectedCategories)}
 
312
  ) : (
313
  <FamilyTable
314
  families={visibleFamilies}
 
315
  evalItems={evalItems}
316
  benchmarkCards={benchmarkCards}
317
  categoryFilter={new Set(selectedCategories)}
app/models/page.tsx CHANGED
@@ -58,7 +58,7 @@ export default function ModelsPage() {
58
  const [loadingDevelopers, setLoadingDevelopers] = useState(false)
59
  const [developersReady, setDevelopersReady] = useState(false)
60
  const [groupByDeveloper, setGroupByDeveloper] = useState(false)
61
- const [modelSortBy, setModelSortBy] = useState<ModelSort>("released")
62
  const [modelSortDir, setModelSortDir] = useState<SortDir>("desc")
63
  const [developerSortBy, setDeveloperSortBy] = useState<DevSort>("models")
64
  const [developerSortDir, setDeveloperSortDir] = useState<SortDir>("desc")
@@ -383,7 +383,7 @@ export default function ModelsPage() {
383
  className="btn-ec outline"
384
  onClick={() => {
385
  setSearchQuery("")
386
- setModelSortBy("released")
387
  setModelSortDir("desc")
388
  setDeveloperSortBy("models")
389
  setDeveloperSortDir("desc")
 
58
  const [loadingDevelopers, setLoadingDevelopers] = useState(false)
59
  const [developersReady, setDevelopersReady] = useState(false)
60
  const [groupByDeveloper, setGroupByDeveloper] = useState(false)
61
+ const [modelSortBy, setModelSortBy] = useState<ModelSort>("coverage")
62
  const [modelSortDir, setModelSortDir] = useState<SortDir>("desc")
63
  const [developerSortBy, setDeveloperSortBy] = useState<DevSort>("models")
64
  const [developerSortDir, setDeveloperSortDir] = useState<SortDir>("desc")
 
383
  className="btn-ec outline"
384
  onClick={() => {
385
  setSearchQuery("")
386
+ setModelSortBy("coverage")
387
  setModelSortDir("desc")
388
  setDeveloperSortBy("models")
389
  setDeveloperSortDir("desc")
components/eval-detail.tsx CHANGED
@@ -624,7 +624,14 @@ export function EvalDetail({
624
  return Array.from(seen, ([key, name]) => ({ key, label: name }))
625
  }, [lb.leaderboard_metrics])
626
 
627
- const hasSlicePicker = !hasMultiMetricLeaderboard && subtaskSlices.length > 1
 
 
 
 
 
 
 
628
 
629
  const ALL_SLICE_KEY = "__all__"
630
  const [activeSlice, setActiveSlice] = useState<string>(ALL_SLICE_KEY)
@@ -870,7 +877,7 @@ export function EvalDetail({
870
  className="font-mono text-[10px] uppercase tracking-[0.12em]"
871
  style={{ color: "var(--fg-subtle)" }}
872
  >
873
- metric spec · completeness · comparability{summary.subtasks?.length ? " · slices" : ""}
874
  </span>
875
  </div>
876
  {overviewOpen ? (
@@ -992,7 +999,7 @@ export function EvalDetail({
992
  className="font-mono uppercase mb-1"
993
  style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
994
  >
995
- Slice breakdown · {summary.subtasks.length}
996
  </div>
997
  <ul
998
  className="flex flex-col"
@@ -1050,10 +1057,11 @@ export function EvalDetail({
1050
  summary={lb.comparability_summary}
1051
  detailsAnchorId="comparability-panel"
1052
  />
1053
- {splitConfig && (
1054
- <SplitPicker config={splitConfig} className="mb-4" />
1055
- )}
1056
- <MultiMetricLeaderboard summary={lb} isResearchView={isResearchView} />
 
1057
  </section>
1058
  ) : (
1059
  <section>
@@ -1090,11 +1098,16 @@ export function EvalDetail({
1090
  <SplitPicker config={splitConfig} className="mb-4" />
1091
  )}
1092
 
 
 
 
 
 
1093
  {hasSlicePicker && (
1094
  <SplitPicker
1095
  className="mb-4"
1096
  config={{
1097
- label: "Slice",
1098
  activeId: activeSlice,
1099
  onChange: setActiveSlice,
1100
  options: [
@@ -1156,12 +1169,7 @@ export function EvalDetail({
1156
  <th className="hidden lg:table-cell" style={{ minWidth: 160 }}>
1157
  {isResearchView ? "Developer" : "Provider"}
1158
  </th>
1159
- <th className="hidden md:table-cell" style={{ minWidth: 220 }}>
1160
- {lb.composite_benchmark_name && lb.composite_benchmark_name !== lb.evaluation_name
1161
- ? `${lb.composite_benchmark_name} · ${lb.evaluation_name}`
1162
- : lb.evaluation_name}
1163
- </th>
1164
- <th className="num" style={{ width: 130 }}>
1165
  {lb.metric_config.unit ?? "Score"}
1166
  </th>
1167
  <th className="hidden lg:table-cell" style={{ width: 110 }}>Evaluator</th>
@@ -1289,51 +1297,54 @@ export function EvalDetail({
1289
  </div>
1290
  </td>
1291
 
1292
- <td className="hidden md:table-cell align-top">
1293
- {/* Performance bar with shot/setup caption */}
1294
- <div className="min-w-[200px] py-0.5">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1295
  <div
1296
  style={{
1297
- position: "relative",
1298
- height: 6,
1299
- background: "var(--bg-surface)",
1300
- overflow: "hidden",
 
1301
  }}
 
 
 
 
 
 
1302
  >
1303
- <div
1304
- style={{
1305
- position: "absolute",
1306
- inset: 0,
1307
- width: `${Math.max(2, normalizedScore * 100)}%`,
1308
- background: isTopRank ? "var(--accent)" : "var(--fg-muted)",
1309
- opacity: isTopRank ? 1 : 0.55,
1310
- }}
1311
- />
1312
  </div>
1313
- {setupLabel && (
 
 
 
 
1314
  <div
1315
- className="mt-1 font-mono uppercase truncate"
1316
- style={{ fontSize: 10, letterSpacing: "0.06em", color: "var(--fg-subtle)" }}
1317
  >
1318
- {setupLabel}
1319
  </div>
1320
  )}
1321
- {!setupLabel &&
1322
- datasetName &&
1323
- !isResearchView &&
1324
- datasetName !== lb.evaluation_name && (
1325
- <div
1326
- className="mt-1 font-mono truncate"
1327
- style={{ fontSize: 10, color: "var(--fg-subtle)" }}
1328
- >
1329
- {datasetName}
1330
- </div>
1331
- )}
1332
- </div>
1333
- </td>
1334
-
1335
- <td className="num align-top tabular-nums" style={{ fontSize: 15, fontWeight: 600 }}>
1336
- {formatRawScore(modelResult.score, undefined)}
1337
  </td>
1338
 
1339
  <td className="hidden lg:table-cell align-top">
@@ -1387,7 +1398,7 @@ export function EvalDetail({
1387
 
1388
  {isExpanded && (
1389
  <tr>
1390
- <td colSpan={hasAnyUpdatedTimestamp ? 8 : 7} style={{ background: "var(--bg-warm)", padding: 0 }}>
1391
  <div className="space-y-5 px-4 py-5 sm:px-6">
1392
  <div className="grid gap-4 xl:grid-cols-3">
1393
  <DetailPanel
@@ -1530,13 +1541,13 @@ export function EvalDetail({
1530
  className="font-mono uppercase"
1531
  style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
1532
  >
1533
- Slice breakdown
1534
  </div>
1535
  <div className="overflow-x-auto" style={{ border: "1px solid var(--border-soft)" }}>
1536
  <table className="ec-htable">
1537
  <thead>
1538
  <tr>
1539
- <th>Slice</th>
1540
  <th className="num">Raw</th>
1541
  </tr>
1542
  </thead>
@@ -1669,7 +1680,7 @@ export function EvalDetail({
1669
  })}
1670
  {leaderboardRows.length === 0 && (
1671
  <tr>
1672
- <td colSpan={hasAnyUpdatedTimestamp ? 8 : 7} style={{ padding: "32px 16px", textAlign: "center", color: "var(--fg-muted)" }}>
1673
  No leaderboard entries match the selected parameter range.
1674
  </td>
1675
  </tr>
@@ -1706,9 +1717,11 @@ export function EvalDetail({
1706
  function MultiMetricLeaderboard({
1707
  summary,
1708
  isResearchView,
 
1709
  }: {
1710
  summary: BenchmarkEvalSummary
1711
  isResearchView: boolean
 
1712
  }) {
1713
  const [page, setPage] = useState(1)
1714
  // Default sort: the first root-scope metric (the benchmark's overall
@@ -1984,10 +1997,9 @@ function MultiMetricLeaderboard({
1984
 
1985
  return (
1986
  <section>
1987
- <ApplesToApplesBanner
1988
- summary={summary.comparability_summary}
1989
- detailsAnchorId="comparability-panel"
1990
- />
1991
  <div className="section-head">
1992
  <h2>{isResearchView ? "Leaderboard" : "Reporting Comparison"}</h2>
1993
  <span
@@ -2049,6 +2061,10 @@ function MultiMetricLeaderboard({
2049
  </DropdownMenu>
2050
  </div>
2051
 
 
 
 
 
2052
  {/* Distribution panel — one curve, dropdown swaps between metrics */}
2053
  {(() => {
2054
  const distSeries = visibleMetrics
@@ -2086,27 +2102,6 @@ function MultiMetricLeaderboard({
2086
  })()}
2087
 
2088
  <div className="ec-card" style={{ padding: 0, overflow: "hidden" }}>
2089
- {hasSliceTabs && (
2090
- <div className="border-b bg-background px-5 py-3 sm:px-6 flex items-center gap-3">
2091
- <span
2092
- className="font-mono uppercase tracking-[0.14em] shrink-0"
2093
- style={{ fontSize: 10, color: "var(--fg-subtle)" }}
2094
- >
2095
- Slice
2096
- </span>
2097
- <select
2098
- className="ec-select"
2099
- value={activeSliceTab}
2100
- onChange={(e) => setActiveSliceTab(e.target.value)}
2101
- >
2102
- <option value="all">Overall</option>
2103
- {sliceTabs.map((tab) => (
2104
- <option key={tab.key} value={tab.key}>{tab.label}</option>
2105
- ))}
2106
- </select>
2107
- </div>
2108
- )}
2109
-
2110
  {hasParameterData && (
2111
  <div className="border-b bg-background px-5 py-4 sm:px-6">
2112
  <ParamRangePicker
 
624
  return Array.from(seen, ([key, name]) => ({ key, label: name }))
625
  }, [lb.leaderboard_metrics])
626
 
627
+ // Suppress the slice picker when a split picker is already in play.
628
+ // For evals like Fibble Arena both pickers partition the same axis
629
+ // (each split is one of the per-lie variants; each slice is the
630
+ // matrix-backfilled subtask for the same per-lie variant), so showing
631
+ // both reads as a redundant control. The page-level split is more
632
+ // authoritative — it loads richer per-eval data — so it wins.
633
+ const hasSlicePicker =
634
+ !hasMultiMetricLeaderboard && subtaskSlices.length > 1 && !splitConfig
635
 
636
  const ALL_SLICE_KEY = "__all__"
637
  const [activeSlice, setActiveSlice] = useState<string>(ALL_SLICE_KEY)
 
877
  className="font-mono text-[10px] uppercase tracking-[0.12em]"
878
  style={{ color: "var(--fg-subtle)" }}
879
  >
880
+ metric spec · completeness · comparability{summary.subtasks?.length ? " · splits" : ""}
881
  </span>
882
  </div>
883
  {overviewOpen ? (
 
999
  className="font-mono uppercase mb-1"
1000
  style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
1001
  >
1002
+ Split breakdown · {summary.subtasks.length}
1003
  </div>
1004
  <ul
1005
  className="flex flex-col"
 
1057
  summary={lb.comparability_summary}
1058
  detailsAnchorId="comparability-panel"
1059
  />
1060
+ <MultiMetricLeaderboard
1061
+ summary={lb}
1062
+ isResearchView={isResearchView}
1063
+ splitConfig={splitConfig}
1064
+ />
1065
  </section>
1066
  ) : (
1067
  <section>
 
1098
  <SplitPicker config={splitConfig} className="mb-4" />
1099
  )}
1100
 
1101
+ {/* Subtask split picker for evals like Global MMLU Lite where
1102
+ the splits live as subtasks of a single eval (not as
1103
+ separate eval IDs the page-level SplitPicker can swap to).
1104
+ Suppressed when a page-level split is already in play —
1105
+ two pickers would partition the same axis (see fibble). */}
1106
  {hasSlicePicker && (
1107
  <SplitPicker
1108
  className="mb-4"
1109
  config={{
1110
+ label: "Split",
1111
  activeId: activeSlice,
1112
  onChange: setActiveSlice,
1113
  options: [
 
1169
  <th className="hidden lg:table-cell" style={{ minWidth: 160 }}>
1170
  {isResearchView ? "Developer" : "Provider"}
1171
  </th>
1172
+ <th className="num" style={{ minWidth: 200 }}>
 
 
 
 
 
1173
  {lb.metric_config.unit ?? "Score"}
1174
  </th>
1175
  <th className="hidden lg:table-cell" style={{ width: 110 }}>Evaluator</th>
 
1297
  </div>
1298
  </td>
1299
 
1300
+ <td className="num align-top">
1301
+ {/* Score with inline performance bar so the
1302
+ previously-dedicated bar column can be
1303
+ dropped — its only purpose was visualising
1304
+ this same number. Caption shows shot/CoT
1305
+ setup or a differing dataset name when
1306
+ available; otherwise it's omitted. */}
1307
+ <div className="flex items-baseline justify-end gap-2 tabular-nums" style={{ fontSize: 15, fontWeight: 600 }}>
1308
+ <span>{formatRawScore(modelResult.score, undefined)}</span>
1309
+ </div>
1310
+ <div
1311
+ className="mt-1 hidden md:block"
1312
+ style={{
1313
+ position: "relative",
1314
+ height: 4,
1315
+ background: "var(--bg-surface)",
1316
+ overflow: "hidden",
1317
+ }}
1318
+ >
1319
  <div
1320
  style={{
1321
+ position: "absolute",
1322
+ inset: 0,
1323
+ width: `${Math.max(2, normalizedScore * 100)}%`,
1324
+ background: isTopRank ? "var(--accent)" : "var(--fg-muted)",
1325
+ opacity: isTopRank ? 1 : 0.55,
1326
  }}
1327
+ />
1328
+ </div>
1329
+ {setupLabel && (
1330
+ <div
1331
+ className="mt-1 font-mono uppercase truncate text-right"
1332
+ style={{ fontSize: 10, letterSpacing: "0.06em", color: "var(--fg-subtle)" }}
1333
  >
1334
+ {setupLabel}
 
 
 
 
 
 
 
 
1335
  </div>
1336
+ )}
1337
+ {!setupLabel &&
1338
+ datasetName &&
1339
+ !isResearchView &&
1340
+ datasetName !== lb.evaluation_name && (
1341
  <div
1342
+ className="mt-1 font-mono truncate text-right"
1343
+ style={{ fontSize: 10, color: "var(--fg-subtle)" }}
1344
  >
1345
+ {datasetName}
1346
  </div>
1347
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1348
  </td>
1349
 
1350
  <td className="hidden lg:table-cell align-top">
 
1398
 
1399
  {isExpanded && (
1400
  <tr>
1401
+ <td colSpan={hasAnyUpdatedTimestamp ? 7 : 6} style={{ background: "var(--bg-warm)", padding: 0 }}>
1402
  <div className="space-y-5 px-4 py-5 sm:px-6">
1403
  <div className="grid gap-4 xl:grid-cols-3">
1404
  <DetailPanel
 
1541
  className="font-mono uppercase"
1542
  style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
1543
  >
1544
+ Split breakdown
1545
  </div>
1546
  <div className="overflow-x-auto" style={{ border: "1px solid var(--border-soft)" }}>
1547
  <table className="ec-htable">
1548
  <thead>
1549
  <tr>
1550
+ <th>Split</th>
1551
  <th className="num">Raw</th>
1552
  </tr>
1553
  </thead>
 
1680
  })}
1681
  {leaderboardRows.length === 0 && (
1682
  <tr>
1683
+ <td colSpan={hasAnyUpdatedTimestamp ? 7 : 6} style={{ padding: "32px 16px", textAlign: "center", color: "var(--fg-muted)" }}>
1684
  No leaderboard entries match the selected parameter range.
1685
  </td>
1686
  </tr>
 
1717
  function MultiMetricLeaderboard({
1718
  summary,
1719
  isResearchView,
1720
+ splitConfig,
1721
  }: {
1722
  summary: BenchmarkEvalSummary
1723
  isResearchView: boolean
1724
+ splitConfig?: SplitConfig
1725
  }) {
1726
  const [page, setPage] = useState(1)
1727
  // Default sort: the first root-scope metric (the benchmark's overall
 
1997
 
1998
  return (
1999
  <section>
2000
+ {/* The parent EvalDetail already renders the apples-to-apples
2001
+ banner before this leaderboard section — duplicating it here
2002
+ made the box appear twice on multi-metric evals like fibble. */}
 
2003
  <div className="section-head">
2004
  <h2>{isResearchView ? "Leaderboard" : "Reporting Comparison"}</h2>
2005
  <span
 
2061
  </DropdownMenu>
2062
  </div>
2063
 
2064
+ {splitConfig && (
2065
+ <SplitPicker config={splitConfig} className="mb-4" />
2066
+ )}
2067
+
2068
  {/* Distribution panel — one curve, dropdown swaps between metrics */}
2069
  {(() => {
2070
  const distSeries = visibleMetrics
 
2102
  })()}
2103
 
2104
  <div className="ec-card" style={{ padding: 0, overflow: "hidden" }}>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2105
  {hasParameterData && (
2106
  <div className="border-b bg-background px-5 py-4 sm:px-6">
2107
  <ParamRangePicker
components/family-table.tsx CHANGED
@@ -16,7 +16,6 @@ export type FamilySortCol = "name" | "benchmarks" | "results"
16
 
17
  interface FamilyTableProps {
18
  families: HierarchyFamily[]
19
- totalModels: number
20
  evalItems?: Map<string, BenchmarkEvalListItem>
21
  benchmarkCards?: Record<string, BenchmarkCard>
22
  domainFilter?: Set<string> | null
@@ -197,7 +196,6 @@ interface RowData {
197
 
198
  export function FamilyTable({
199
  families,
200
- totalModels,
201
  evalItems,
202
  benchmarkCards,
203
  domainFilter,
@@ -299,13 +297,38 @@ export function FamilyTable({
299
  if (overview) description = overview.length > 140 ? overview.slice(0, 137) + "…" : overview
300
  }
301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  out.push({
303
  key: fam.key,
304
  name: displayName,
305
  keySlug: fam.key,
306
  tags: fam.derivedTags ?? [],
307
  benchmarks: benchmarkCount,
308
- evalsCount: fam.evals_count ?? metricCount,
309
  leaves: visibleLeafEntries,
310
  sections,
311
  description,
@@ -372,9 +395,6 @@ export function FamilyTable({
372
  ? expanded[row.key] ?? true
373
  : expanded[row.key] ?? false
374
  const allLeaves = row.sections.flatMap((s) => s.leaves)
375
- const visibleLeaves = isExpanded
376
- ? allLeaves.slice(0, LEAVES_INLINE_MAX)
377
- : []
378
  const hiddenLeafCount = isExpanded
379
  ? Math.max(allLeaves.length - LEAVES_INLINE_MAX, 0)
380
  : 0
@@ -461,9 +481,6 @@ export function FamilyTable({
461
  <td className="num font-mono text-[13px]">{row.benchmarks.toLocaleString()}</td>
462
  <td className="num font-mono text-[13px]">
463
  {row.evalsCount.toLocaleString()}
464
- {totalModels > 0 && (
465
- <span className="text-[color:var(--fg-subtle)]"> / {totalModels.toLocaleString()}</span>
466
- )}
467
  </td>
468
  <td>
469
  <span className="font-mono text-[10px] tracking-[0.12em] uppercase text-[color:var(--accent)] inline-flex items-center gap-1">
 
16
 
17
  interface FamilyTableProps {
18
  families: HierarchyFamily[]
 
19
  evalItems?: Map<string, BenchmarkEvalListItem>
20
  benchmarkCards?: Record<string, BenchmarkCard>
21
  domainFilter?: Set<string> | null
 
196
 
197
  export function FamilyTable({
198
  families,
 
199
  evalItems,
200
  benchmarkCards,
201
  domainFilter,
 
297
  if (overview) description = overview.length > 140 ? overview.slice(0, 137) + "…" : overview
298
  }
299
 
300
+ // "Reported results" approximates the unique models tested in the
301
+ // family. provenance_summary.total_results counts every (model,
302
+ // metric) report row, so families that publish many metrics inflate
303
+ // the number — agentharm's 4 declared metrics turned 18 unique
304
+ // models into 72 result rows. Normalise by the family's metric
305
+ // count (sum of bench-level metric entries; clamp to 1 to avoid
306
+ // div-by-zero on families whose metrics array is empty).
307
+ const totalResults =
308
+ fam.provenance_summary?.total_results ??
309
+ fam.reproducibility_summary?.results_total ??
310
+ fam.evals_count ??
311
+ metricCount
312
+ const allBenchesForMetricCount: HierarchyBenchmark[] = [
313
+ ...(fam.benchmarks ?? []),
314
+ ...(fam.standalone_benchmarks ?? []),
315
+ ...(fam.composites ?? []).flatMap((c) => c.benchmarks ?? []),
316
+ ]
317
+ const familyMetricCount = allBenchesForMetricCount.reduce(
318
+ (n, b) => n + Math.max((b.metrics ?? []).length, 1),
319
+ 0,
320
+ )
321
+ const reportedResults =
322
+ familyMetricCount > 0
323
+ ? Math.round(totalResults / familyMetricCount)
324
+ : totalResults
325
  out.push({
326
  key: fam.key,
327
  name: displayName,
328
  keySlug: fam.key,
329
  tags: fam.derivedTags ?? [],
330
  benchmarks: benchmarkCount,
331
+ evalsCount: reportedResults,
332
  leaves: visibleLeafEntries,
333
  sections,
334
  description,
 
395
  ? expanded[row.key] ?? true
396
  : expanded[row.key] ?? false
397
  const allLeaves = row.sections.flatMap((s) => s.leaves)
 
 
 
398
  const hiddenLeafCount = isExpanded
399
  ? Math.max(allLeaves.length - LEAVES_INLINE_MAX, 0)
400
  : 0
 
481
  <td className="num font-mono text-[13px]">{row.benchmarks.toLocaleString()}</td>
482
  <td className="num font-mono text-[13px]">
483
  {row.evalsCount.toLocaleString()}
 
 
 
484
  </td>
485
  <td>
486
  <span className="font-mono text-[10px] tracking-[0.12em] uppercase text-[color:var(--accent)] inline-flex items-center gap-1">
components/score-distribution.tsx CHANGED
@@ -284,22 +284,32 @@ export function ScoreDistribution({
284
  {canShowFrontier ? "View" : "Score distribution"}
285
  </span>
286
  {canShowFrontier && (
287
- <div className="flex items-center gap-1">
288
- <button
289
- type="button"
290
- className={`ec-pill${effectiveView === "distribution" ? " on" : ""}`}
291
- onClick={() => setView("distribution")}
292
- >
293
- Distribution
294
- </button>
295
- <button
296
- type="button"
297
- className={`ec-pill${effectiveView === "frontier" ? " on" : ""}`}
298
- onClick={() => setView("frontier")}
299
- title="Frontier score over model release dates (cumulative best)."
300
- >
301
- Frontier
302
- </button>
 
 
 
 
 
 
 
 
 
 
303
  </div>
304
  )}
305
  </div>
 
284
  {canShowFrontier ? "View" : "Score distribution"}
285
  </span>
286
  {canShowFrontier && (
287
+ <div
288
+ role="tablist"
289
+ aria-label="Chart view"
290
+ className="inline-flex items-center gap-1"
291
+ >
292
+ {(["distribution", "frontier"] as const).map((view) => {
293
+ const on = effectiveView === view
294
+ const label = view === "distribution" ? "Distribution" : "Frontier"
295
+ return (
296
+ <button
297
+ key={view}
298
+ type="button"
299
+ role="tab"
300
+ aria-selected={on}
301
+ onClick={() => setView(view)}
302
+ title={
303
+ view === "frontier"
304
+ ? "Frontier score over model release dates (cumulative best)."
305
+ : "Kernel-density distribution of model scores."
306
+ }
307
+ className={`ec-pill${on ? " on" : ""}`}
308
+ >
309
+ {label}
310
+ </button>
311
+ )
312
+ })}
313
  </div>
314
  )}
315
  </div>
lib/clean-hierarchy.ts CHANGED
@@ -214,6 +214,12 @@ export function cleanHierarchy(
214
  dedupAggregatorBenchesByScore(h, comparisonIndex)
215
  }
216
  decorateHierarchyDerivedTags(h)
 
 
 
 
 
 
217
  if (h.benchmark_index) {
218
  const survivingFamilyKeys = new Set<string>(
219
  (h.families ?? []).map((f) => f.key),
@@ -891,6 +897,101 @@ function consolidateDedicatedHomeBenchmarks(h: CleanableHierarchy) {
891
  h.families = allFamilies.filter((fam) => !dropped.has(fam))
892
  }
893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
  /**
895
  * AIR-Bench 2024 is a single safety benchmark with a 4-tier taxonomy
896
  * (314 leaf risk categories per the spec, ~30 of which the HELM
 
214
  dedupAggregatorBenchesByScore(h, comparisonIndex)
215
  }
216
  decorateHierarchyDerivedTags(h)
217
+ // Run AFTER the sanitizer so it can't clobber our suffixed bench
218
+ // display_names (`MMLU-Pro · Arcadia Impact` etc.) — the merged-in
219
+ // bench keys deliberately use a non-shareToken-matching form to
220
+ // keep them distinct, which would otherwise trip the humanizeKey
221
+ // fallback in benchmark-tags.sanitizeName.
222
+ groupSameBenchAcrossSources(h)
223
  if (h.benchmark_index) {
224
  const survivingFamilyKeys = new Set<string>(
225
  (h.families ?? []).map((f) => f.key),
 
897
  h.families = allFamilies.filter((fam) => !dropped.has(fam))
898
  }
899
 
900
+ /**
901
+ * Group single-bench families that publish the same conceptual benchmark
902
+ * from different upstream sources into one merged family card.
903
+ *
904
+ * Triggered when ≥2 single-bench families share a bench key but their
905
+ * bench rows have non-overlapping eval_summary_ids — i.e. independent
906
+ * sources publishing the same benchmark. The richest family (most
907
+ * models) keeps its slot; other families contribute their bench under
908
+ * the survivor as siblings, with each bench's display_name suffixed
909
+ * with " · <Source>" so the user can tell which run a row came from.
910
+ *
911
+ * Runs AFTER decorateHierarchyDerivedTags so the sanitizer's
912
+ * shareToken / humanizeKey passes don't clobber the suffixed names
913
+ * (the merged-in bench keys like `mmlu-pro__arcadia` deliberately
914
+ * don't share tokens with "MMLU-Pro · Arcadia Impact").
915
+ */
916
+ function groupSameBenchAcrossSources(h: CleanableHierarchy) {
917
+ const sourceLabel = (
918
+ bench: HierarchyBenchmark,
919
+ fallback: string,
920
+ ): string => {
921
+ const sources = (bench.metrics ?? []).flatMap((m) => m.sources ?? [])
922
+ for (const s of sources) {
923
+ const trimmed = String(s ?? "").trim()
924
+ if (trimmed) return trimmed
925
+ }
926
+ return fallback
927
+ }
928
+ const slugifyShort = (s: string) =>
929
+ s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "")
930
+
931
+ type Handle = { fam: HierarchyFamily; bench: HierarchyBenchmark }
932
+ const collectBenches = (fam: HierarchyFamily): HierarchyBenchmark[] => [
933
+ ...(fam.benchmarks ?? []),
934
+ ...(fam.standalone_benchmarks ?? []),
935
+ ...(fam.composites ?? []).flatMap((c) => c.benchmarks ?? []),
936
+ ]
937
+ const candidatesByKey = new Map<string, Handle[]>()
938
+ for (const fam of h.families ?? []) {
939
+ const benches = collectBenches(fam)
940
+ if (benches.length !== 1) continue
941
+ const sole = benches[0]
942
+ const list = candidatesByKey.get(sole.key) ?? []
943
+ list.push({ fam, bench: sole })
944
+ candidatesByKey.set(sole.key, list)
945
+ }
946
+
947
+ const dropped = new Set<HierarchyFamily>()
948
+ for (const [, group] of candidatesByKey) {
949
+ if (group.length < 2) continue
950
+ // eval_ids must be disjoint (otherwise an earlier rule should have
951
+ // caught them as aliases of the same row).
952
+ const seenIds = new Set<string>()
953
+ let disjoint = true
954
+ for (const entry of group) {
955
+ for (const id of entry.bench.summary_eval_ids ?? []) {
956
+ if (seenIds.has(id)) { disjoint = false; break }
957
+ seenIds.add(id)
958
+ }
959
+ if (!disjoint) break
960
+ }
961
+ if (!disjoint) continue
962
+
963
+ const sortedGroup = [...group].sort((x, y) => {
964
+ const xModels = x.bench.metrics?.[0]?.models_count ?? 0
965
+ const yModels = y.bench.metrics?.[0]?.models_count ?? 0
966
+ if (xModels !== yModels) return yModels - xModels
967
+ return x.fam.key.localeCompare(y.fam.key)
968
+ })
969
+ const survivor = sortedGroup[0]
970
+ const baseDisplay =
971
+ survivor.bench.display_name?.trim() ||
972
+ survivor.fam.display_name?.trim() ||
973
+ survivor.bench.key
974
+
975
+ for (const entry of sortedGroup) {
976
+ const src = sourceLabel(
977
+ entry.bench,
978
+ entry.fam.display_name || entry.fam.key,
979
+ )
980
+ entry.bench.display_name = `${baseDisplay} · ${src}`
981
+ if (entry !== survivor) {
982
+ entry.bench.key = `${survivor.bench.key}__${slugifyShort(src) || slugifyShort(entry.fam.key)}`
983
+ survivor.fam.benchmarks = survivor.fam.benchmarks ?? []
984
+ survivor.fam.benchmarks.push(entry.bench)
985
+ dropped.add(entry.fam)
986
+ }
987
+ }
988
+ survivor.fam.display_name = baseDisplay
989
+ }
990
+
991
+ if (dropped.size === 0) return
992
+ h.families = (h.families ?? []).filter((fam) => !dropped.has(fam))
993
+ }
994
+
995
  /**
996
  * AIR-Bench 2024 is a single safety benchmark with a 4-tier taxonomy
997
  * (314 leaf risk categories per the spec, ~30 of which the HELM