Spaces:
Running
Running
File size: 37,534 Bytes
6978d97 d249d5b 6978d97 04b4cff 6978d97 c4756f2 d249d5b 0fd9809 3a12290 c1f2130 3a12290 d249d5b 6978d97 e59af24 c4756f2 04b4cff 6978d97 b597ca9 c4756f2 e59af24 c4756f2 e59af24 c4756f2 e59af24 3a12290 c1f2130 599471d 3a12290 478ae6c b7439ff 3a12290 9b4cdbb da4309d 731462e cfd41ff 68d591a e59af24 c4756f2 e59af24 04b4cff 3a12290 bca888a 3a12290 e59af24 3a12290 c5c4016 415ac43 03e2430 d249d5b 3a12290 478ae6c 3a12290 e59af24 04b4cff e59af24 04b4cff c1f2130 11542d9 2ed4959 11542d9 2ed4959 11542d9 c1f2130 d249d5b 415ac43 e59af24 415ac43 bca888a 59c7b6c 0d81559 59c7b6c c4756f2 59c7b6c 415ac43 e59af24 415ac43 599471d 415ac43 cfd41ff 478ae6c bca888a e59af24 415ac43 3a12290 0fd9809 4010530 0fd9809 c4756f2 0fd9809 c4756f2 0fd9809 b7439ff b597ca9 e59af24 c4756f2 e59af24 c685464 cfd41ff 0fd9809 e59af24 c5c4016 c4756f2 0fd9809 7a54021 e59af24 7a54021 e59af24 7a54021 e59af24 7a54021 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | /**
* Benchmark-first evaluation types, shared between the v2 view-layer reader
* (lib/view-data.ts) and its UI consumers. The data-shaping functions that
* used to live here were part of the legacy v1 (HF-JSON / duckdb) backend and
* were removed once the producer pipeline took over aggregation.
*/
import type {
BenchmarkCard,
BenchmarkEvaluation,
EvalLibrary,
EvalTag,
GenerationConfig,
ModelInfo,
SourceMetadata,
SourceData,
ScoreDetails,
MetricConfig,
EvaluationResult,
ModelEvaluationSummary,
} from './benchmark-schema'
import type {
ComparabilityStatus,
EvalcardsAnnotations,
RowAnnotations,
SignalSummaries,
} from './backend-artifacts'
import type {
CollectionAttachment,
CollectionsSidecarEntry,
ProtocolAxesByCollection,
StudyRef,
} from './collections'
export type { BenchmarkCard }
export type { ModelEvaluationSummary }
/**
* True when a result ran under the oracle answer-feedback arm of a
* protocol-varied collection (the model was told when its answer was
* correct). Assisted rows are shown but excluded from client-side ranking
* by default β mirroring the backend, which never serves them a rank.
*/
export function isAssistedResult(protocolCondition: string | null | undefined): boolean {
if (!protocolCondition) return false
try {
const parsed = JSON.parse(protocolCondition) as { feedback?: unknown } | null
return parsed != null && typeof parsed === "object" && parsed.feedback === "answer_feedback"
} catch {
return false
}
}
export interface ScoreSpread {
/** How many scores the range spans. */
n: number
min: number
max: number
}
/**
* Where a model's runs on one page landed: the smallest and the largest,
* and how many there were.
*
* Descriptive only, and deliberately so. The runs are different
* CONFIGURATIONS (token budget, thinking tokens, effort, answer oracle),
* not repeated draws of one quantity, so there is no population to
* estimate and no sampling error to bound. An interval computed from
* them would narrow as the study added design points while the observed
* range stayed as wide, and would then be read as a claim about the
* model's ability. The observed endpoints make no such claim.
*/
export function summariseScoreSpread(values: readonly number[]): ScoreSpread | null {
const finite = values.filter((value) => Number.isFinite(value))
if (finite.length === 0) return null
return { n: finite.length, min: Math.min(...finite), max: Math.max(...finite) }
}
/** A parsed `judge_condition`. `judges` holds the canonical
* model ids of the LLM judges behind the number, `label` the source's own
* name for the channel. */
export interface JudgeCondition {
judges: string[]
label: string | null
}
/**
* Parse the view's canonical `judge_condition` JSON
* (`{"judges":[...],"label":"..."}`). NULL/absent means the source did not
* disclose a judge β never "no judge" β so it parses to null and the row
* is labelled as an ordinary result. Orthogonal to `protocol_condition`,
* whose semantics (isAssistedResult above) are unchanged.
*/
export function parseJudgeCondition(
raw: string | null | undefined,
): JudgeCondition | null {
if (!raw) return null
try {
const parsed = JSON.parse(raw) as { judges?: unknown; label?: unknown } | null
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) return null
// Trim, drop empties, dedupe. A blank id would render "judged by "
// and a repeated one would inflate "mean of N judges" β both claim
// something about the panel that isn't in the data.
const judges = Array.isArray(parsed.judges)
? [
...new Set(
parsed.judges
.filter((judge): judge is string => typeof judge === "string")
.map((judge) => judge.trim())
.filter((judge) => judge.length > 0),
),
]
: []
const label =
typeof parsed.label === "string" && parsed.label.trim().length > 0
? parsed.label.trim()
: null
if (judges.length === 0 && label == null) return null
return { judges, label }
} catch {
return null
}
}
/**
* Row label for a judged result: the judge when a single model graded it,
* the panel size when several did, plus the panel's display names for the
* row's tooltip. `displayName` resolves a judge's canonical model id to its
* registry display name (server-built, see BenchmarkEvalSummary
* .judge_display_names) and falls back to the raw id. Returns null when the
* source disclosed no judge at all.
*/
export function judgeConditionSummary(
raw: string | null | undefined,
displayName: (modelId: string) => string,
): { label: string; names: string[]; judges: string[] } | null {
const condition = parseJudgeCondition(raw)
if (!condition || condition.judges.length === 0) return null
const names = condition.judges.map(displayName)
return {
label: names.length === 1 ? `judged by ${names[0]}` : `mean of ${names.length} judges`,
names,
// The raw ids behind the names. Two dated variants that folded into
// one survivor resolve to the SAME display name, so the id is the
// only thing that tells a reader which reading a row is.
judges: condition.judges,
}
}
/** One non-headline judge reading of a (model, metric) cell: the panel the
* producer did not pick, and the number it published. */
export interface JudgeReading {
judge_condition: string
score: number | null
}
/**
* Judge treatment for one cell of the multi-metric matrix, which is a pivot
* of headline readings only. `label` is the same "judged by β¦" / "mean of N
* judges" wording the per-row leaderboard uses, and `tooltip` names the
* panel's members and then lists every OTHER judge reading of the same
* (model, metric) with its value β the pivot has no row to put those on, so
* dropping them would hide a disagreement the page is meant to show.
* `formatScore` renders an alternate's number the way the column renders its
* cells. Returns null when nothing about the cell names a judge.
*/
export function judgeCellSummary(
condition: string | null | undefined,
alternates: JudgeReading[] | undefined,
displayName: (modelId: string) => string,
formatScore: (score: number) => string,
): { label: string; tooltip: string } | null {
const headline = judgeConditionSummary(condition, displayName)
const others = (alternates ?? [])
.map((reading) => ({
summary: judgeConditionSummary(reading.judge_condition, displayName),
score: reading.score,
}))
.filter(
(reading): reading is { summary: NonNullable<typeof reading.summary>; score: number | null } =>
reading.summary !== null,
)
if (!headline && others.length === 0) return null
const lines: string[] = []
if (headline) {
lines.push(
headline.names.length > 1
? `Judges: ${headline.names.join(", ")}`
: `Judge model id: ${headline.judges[0]}`,
)
}
if (others.length > 0) {
lines.push("Other judge readings, not ranked:")
for (const other of others) {
const value =
typeof other.score === "number" && Number.isFinite(other.score)
? formatScore(other.score)
: "β"
lines.push(`${other.summary.label}: ${value}`)
}
}
return {
// No headline judge but alternates exist (the producer's pick was an
// undisclosed-judge row): say so rather than showing nothing.
label:
headline?.label
?? `${others.length} other judge reading${others.length === 1 ? "" : "s"}`,
tooltip: lines.join("\n"),
}
}
/**
* The matrix column the page ranks on: the eval's `primary_metric_id`, which
* is the metric every other surface on the page summarises. A payload that
* declares no primary metric, or names one no column carries, falls back to
* the first root metric and then to the first column.
*/
export function primaryMetricColumnKey(
metrics: Array<{ column_key: string; metric_id?: string; scope?: string }> | undefined,
primaryMetricId: string | null | undefined,
): string | undefined {
const all = metrics ?? []
const roots = all.filter((metric) => metric.scope !== "subtask")
const primary = primaryMetricId?.trim()
const declared = primary
? roots.find((metric) => metric.column_key === primary || metric.metric_id === primary)
: undefined
return declared?.column_key ?? roots[0]?.column_key ?? all[0]?.column_key
}
/** A row the backend did not pick as its model's headline reading (a losing
* judge panel or protocol arm) β shown beneath the headline, never ranked.
* An absent field reads as "headline", which is what a payload assembled
* outside the view layer (a fixture, an embed) gets. */
export function isHeadlineResult(result: {
is_headline?: boolean | null
}): boolean {
return result.is_headline !== false
}
/**
* Score standings for rows already in score order: one rank per ranked
* row, ties sharing a rank, and 0 for every row the caller says takes no
* rank (an assisted run, a losing judge panel, a secondary protocol
* point). The standing is the model's position in the field, so it is
* assigned once here and never recomputed from a row's display position.
*/
export function scoreStandings<T>(
rows: T[],
score: (row: T) => number,
ranked: (row: T) => boolean,
): number[] {
let currentRank = 0
let previousScore: number | null = null
let rankedCount = 0
return rows.map((row) => {
if (!ranked(row)) return 0
rankedCount += 1
const value = score(row)
if (previousScore === null || Math.abs(value - previousScore) > 1e-9) {
currentRank = rankedCount
previousScore = value
}
return currentRank
})
}
/** The identity a model's rows group under: the producer's route id when
* it resolved one, else whatever identifies the model at all. */
export function modelGroupKey(result: {
model_route_id?: string
model_info?: { id?: string; name?: string }
}): string {
return result.model_route_id ?? result.model_info?.id ?? result.model_info?.name ?? ""
}
/**
* A stable identity for one observation: the model, the source it came
* from, the protocol point it ran at, the judge panel that scored it and
* the source's own label for the number. Everything the page can use to
* tell two readings of the same model apart, and nothing that depends on
* where the reading currently sits in the table.
*/
export function observationKey(result: ModelResultForBenchmark): string {
return [
result.model_route_id ?? result.model_info?.id ?? result.model_info?.name ?? "",
result.merged_source_slug ?? result.source_metadata?.source_name ?? "",
result.collection_id ?? "",
result.protocol_condition ?? "",
result.judge_condition ?? "",
result.metric_source_label ?? "",
].join("|")
}
/**
* Group rows so that a model's non-headline readings (its judge panels and
* protocol arms) stay attached beneath its headline row.
*
* Sorting a leaderboard must reorder these GROUPS, never the flattened row
* list: reversing a flat list alone puts every secondary row above its own
* headline, and an arbitrary key sort separates them from their model
* entirely. A non-headline row whose model has no group yet opens one of
* its own rather than attaching to an unrelated model above it.
*/
export function groupByHeadlineModel<T>(
rows: readonly T[],
resultOf: (row: T) => { is_headline?: boolean | null; model_route_id?: string; model_info?: { id?: string; name?: string } },
): T[][] {
const groups: T[][] = []
const openByModel = new Map<string, T[]>()
for (const row of rows) {
const result = resultOf(row)
const key = modelGroupKey(result)
const open = openByModel.get(key)
if (open && !isHeadlineResult(result)) {
open.push(row)
continue
}
const group = [row]
groups.push(group)
openByModel.set(key, group)
}
return groups
}
/**
* The direction the ranker's own row order already reads in: best first,
* which is ASCENDING when the metric's better end is the low one. A score
* sort that assumes "best first == descending" shows a lower-is-better
* board upside down and then reverses the wrong way on the toggle.
*/
export function scoreSortBaseDirection(lowerIsBetter?: boolean | null): "asc" | "desc" {
return lowerIsBetter ? "asc" : "desc"
}
/**
* Score-ordered rows for a user-chosen direction. Takes the GROUPS the
* ranker produced (each a headline row plus the judge / protocol readings
* beneath it) so a reversal moves whole models β reversing the flat row
* list would put every secondary reading above its own headline.
*/
export function orderGroupsByScore<T>(
groups: readonly T[][],
direction: "asc" | "desc",
lowerIsBetter?: boolean | null,
): T[] {
return direction === scoreSortBaseDirection(lowerIsBetter)
? groups.flat()
: [...groups].reverse().flat()
}
/**
* Composite comparison view: one score per (model, sub-eval) across a
* suite's child evaluations, for the models x sub-evals matrix.
*
* Headline rows only. The cell is written once per matching row, so
* without the filter a later judge panel or protocol arm overwrites the
* model's summary reading β and that overwritten number is then averaged
* across the suite and ranked as the model's standing.
*/
export function compositeScoresByModel(
subSummaries: ReadonlyArray<{
evaluation_name: string
model_results: ModelResultForBenchmark[]
}>,
): Map<string, { name: string; developer: string; scores: Map<string, number | null> }> {
const modelScores = new Map<
string,
{ name: string; developer: string; scores: Map<string, number | null> }
>()
for (const sub of subSummaries) {
for (const result of sub.model_results) {
if (!isHeadlineResult(result)) continue
const id = result.model_info.id
const existing = modelScores.get(id) ?? {
name: result.model_info.name,
developer: result.model_info.developer ?? "",
scores: new Map<string, number | null>(),
}
existing.scores.set(sub.evaluation_name, result.score)
modelScores.set(id, existing)
}
}
return modelScores
}
export interface ModelResultForBenchmark {
model_info: ModelInfo
model_route_id?: string
/**
* model-resolution-rework: server-provided group canonical id. Used as
* the routing fallback when `model_route_id` is absent β replaces the
* old client-side family-route computation (since removed).
*/
model_group_id?: string
score: number
score_details: ScoreDetails
evaluation_timestamp: string
source_metadata: SourceMetadata
source_data: BenchmarkEvaluation['source_data']
/** Per-result verification flag; mirrors `result.is_verified_evaluator`. */
is_verified_evaluator?: boolean
/** De-aliased evaluator identity (registry canonical display name when
* the org resolves, raw string otherwise). Preferred for the Source
* label so upstream spelling drift never leaks into display; the raw
* source_metadata strings stay available for provenance. */
evaluator_display_name?: string
result: EvaluationResult
/** URL to the underlying record JSON in the upstream HF dataset, when known. */
source_record_url?: string
/** Deep-link to the raw EEE_datastore source record this score came from, when known. */
eee_record_url?: string
/** Merged-page rows only (set by lib/merged-adapter): the observation's
* source composite slug, used for the ?source= row pre-highlight. */
merged_source_slug?: string
/** Merged-page rows only: how `score` was rescaled to sit on the page's
* scale (see MergedObservationRow.scale_harmonized). */
scale_harmonized?: "mul100" | "div100" | "of_total"
scale_harmonized_total?: number
/** Collections (see the backend's collections spec, warehouse-outputs section): submission-channel id of
* the row's representative fact row β key into the snapshot's
* `collections.json` sidecar. */
collection_id?: string
/** Protocol point for protocol-varied collections: canonical sorted-key
* JSON typed by the collection's declared `protocol_axes`; absent/null
* for ordinary rows. The view ships one row per protocol point; rows
* whose reserved `feedback` key is `answer_feedback` are
* shown-but-not-ranked (backend already emits NULL position for them). */
protocol_condition?: string | null
/** Canonical `{"judges":[...],"label":"..."}` JSON for a
* judged result; absent/null when the source disclosed no judge. */
judge_condition?: string | null
/** True on the one row per (composite, benchmark, metric, model) the
* producer picked as the page's summary reading. The view layer always
* projects it, deriving it from the producer's ranking on a snapshot
* that predates the column β see isHeadlineResult. */
is_headline?: boolean | null
/** The source's own label for the published number (`gpt_score`).
* Display and provenance only β never a key. */
metric_source_label?: string | null
/** The harness that produced the run, and its version when the source
* named one. Carried on the row because the reproducibility slots ask
* which harness a re-runner would have to obtain and pin. */
eval_library?: EvalLibrary | null
/** How the score was produced: `generative` when the model wrote the
* answer, `log_prob` when the harness scored likelihoods over fixed
* choices. The producer's own classification, so it outranks anything
* inferred from a row's raw fields; absent on a snapshot predating the
* column, and null when the producer could not classify the row. */
scoring_mode?: string | null
/** The row's comparability verdict; only `ok` groups were assessed. */
comparability_status?: ComparabilityStatus | null
/** The number the source published, before any canonical-scale
* conversion applied to `score`. */
score_published?: number
aggregate_components?: Array<{
evaluation_id: string
composite_benchmark_key: string
composite_benchmark_name: string
score: number
normalized_score: number
evaluation_timestamp: string
source_name?: string
source_type: SourceMetadata["source_type"]
source_organization_name: string
evaluator_relationship: SourceMetadata["evaluator_relationship"]
}>
}
export interface BenchmarkEvalSummary extends SignalSummaries {
evaluation_name: string
/** Registry display names for the judge model ids named in this page's
* judge conditions, keyed by the raw id the condition carries (which
* can be a dated variant that folded into the named model). Built
* server-side from models_view; absent when no row on the page names a
* judge, and missing an entry when models_view has no matching row β
* the label then falls back to the raw id. */
judge_display_names?: Record<string, string>
/** URL-safe slug derived from evaluation_name */
evaluation_id: string
/** True when this summary was adapted from a merged all-sources payload
* (lib/merged-adapter) β lets shared UI phrase observation-grain rows
* correctly ("N results Β· M models", not "N of M"). */
merged_view?: true
canonical_display_name?: string
composite_benchmark_key: string
composite_benchmark_name: string
derived_tags?: EvalTag[]
metric_config: MetricConfig
model_results: ModelResultForBenchmark[]
models_count: number
/** Unique evaluator organisation names */
evaluator_names: string[]
/** Subset of `evaluator_names` that are validated submitters (badge). */
verified_evaluator_names?: string[]
source_types: SourceMetadata["source_type"][]
latest_source_name?: string
third_party_ratio: number
missing_generation_config_count: number
best_model: { name: string; score: number } | null
worst_model: { name: string; score: number } | null
avg_score: number
/** avg_score normalised to 0-1 using metric_config.min/max_score */
avg_score_norm: number | null
/** Rich benchmark card from the metadata/ folder, when available */
benchmark_card?: BenchmarkCard
is_aggregated?: boolean
aggregate_sources?: Array<{
evaluation_id: string
composite_benchmark_key: string
composite_benchmark_name: string
models_count: number
avg_score_norm: number | null
}>
/** Tags from the pipeline (domains, languages, tasks) */
tags?: { domains: string[]; languages: string[]; tasks: string[] }
/** Number of distinct metrics for this benchmark */
metrics_count?: number
/** Names of all metrics */
metric_names?: string[]
/** Instance-level data availability */
instance_data?: { available: boolean; url_count: number; sample_urls: string[]; models_with_loaded_instances: number }
/** Canonical benchmark id (the registry-resolved benchmark). Drives
* benchmark-card lookups regardless of slice/composite axis. */
benchmark_id?: string
/** Family display name. */
benchmark_family_name?: string
/** Composite (leaderboard) slug β e.g. "wasp", "helm-classic". */
composite_slug?: string
/** Composite display name β e.g. "WASP", "HELM Classic". */
composite_display_name?: string
/** Curated multi-benchmark family slug (e.g. "mmlu"), defaults to
* benchmark id for singletons. */
family_id?: string
/** Family display, post-cutover canonical name. */
family_display_name?: string
/** Parent benchmark id β populated when this row is a slice of a
* root benchmark; null for non-slice rows. */
parent_benchmark_id?: string
/** True when this row is a within-benchmark slice cut. */
is_slice?: boolean
/** Source dataset metadata from the pipeline */
source_data?: SourceData
/** Best raw score reported in the eval summary list */
top_score?: number
/** Count of nested subtasks reported for the benchmark */
subtasks_count?: number
/** Whether this row is a summary/rollup score for a composite */
is_summary_score?: boolean
/** Evaluation_ids this benchmark summary is composed of. */
constituent_evaluation_ids?: string[]
/** Canonical benchmark-level metrics from root metrics[] */
root_metrics?: BenchmarkSummaryMetric[]
/** Canonical benchmark subdivisions from subtasks[] */
subtasks?: BenchmarkSummarySubtask[]
/** The metric the page ranks on (`evals_view.primary_metric_id`) β the one
* every other surface here summarises. The multi-metric matrix sorts and
* charts this column rather than whichever measure happens to come first.
* Absent on payloads assembled outside the view layer. */
primary_metric_id?: string
/** Matrix columns for multi-metric benchmark leaderboards */
leaderboard_metrics?: BenchmarkLeaderboardMetric[]
/** Matrix rows for multi-metric benchmark leaderboards */
leaderboard_rows?: BenchmarkLeaderboardRow[]
evalcards?: { annotations?: EvalcardsAnnotations }
/** Curated protocol-varied collection attachment. Per-source-only:
* built server-side from the
* collections.json sidecar; the merged adapter never sets it, which
* gates every collection surface off merged summaries. Per-source
* embeds carry it and render the study surfaces deliberately. */
collection?: CollectionAttachment
/** Curated studies the visible rows come from. Attribution only, and
* the one study surface a merged summary carries. */
study_refs?: StudyRef[]
/** Declared protocol axes keyed by collection id, for a page whose rows
* span several collections. Descriptor lookup only: it gives a row's
* numbers their unit and says which axes apply to which row. */
protocol_axes_by_collection?: ProtocolAxesByCollection
/** Source β merged switcher data for per-source pages.
* Absent when the page has neither a merged page nor sibling sources,
* and on old snapshots. */
source_options?: EvalSourceOptions
}
export interface EvalSourceOption {
/** Per-source eval page id (URL-encoded, straight off evals_view). */
evaluation_id: string
composite_slug?: string
composite_display_name?: string
models_count?: number
}
export interface EvalSourceOptions {
/** Merged-page id to navigate to, or null when NO merged page exists β
* the switcher must never navigate to a nonexistent merged page. */
merged_evaluation_id: string | null
sources: EvalSourceOption[]
}
export interface BenchmarkSummaryMetric {
metric_summary_id: string
metric_name: string
display_name: string
canonical_display_name?: string
metric_key?: string
lower_is_better: boolean
models_count: number
top_score?: number
unit?: string
}
export interface BenchmarkSummarySubtask {
subtask_key: string
subtask_name: string
display_name: string
canonical_display_name?: string
metrics: BenchmarkSummaryMetric[]
}
export interface BenchmarkLeaderboardMetric {
column_key: string
/** Registry metric id. Equal to `column_key` on root metrics; a subtask
* entry keys its column "<metric_id>::<slice>" and carries the base id
* here. Matches `evals_view.primary_metric_id`. */
metric_id?: string
metric_summary_id: string
metric_name: string
display_name: string
canonical_display_name?: string
lower_is_better: boolean
unit?: string
scope: "root" | "subtask"
subtask_key?: string
subtask_name?: string
}
export interface BenchmarkLeaderboardRow {
model_info: ModelInfo
model_route_id?: string
/** model-resolution-rework: server group id, routing fallback. */
model_group_id?: string
evaluation_timestamp: string
source_metadata: SourceMetadata
source_data: BenchmarkEvaluation["source_data"]
values: Record<string, number | null>
/** Merged-page rows only: hover text for a score that was rescaled to
* sit on the page's scale (see scaleHarmonizedTitle). */
rescaled_title?: string
/** Per-column verified-evaluator flag, keyed identically to `values`. */
verified?: Record<string, boolean>
annotations_by_metric?: Record<string, RowAnnotations | null | undefined>
/** Per-column comparability verdict, keyed identically to `values`.
* Prebaked matrix cells carry no annotation struct, so this is what
* lets a matrix cell render "not assessable" instead of silence. */
comparability_status_by_metric?: Record<string, ComparabilityStatus | null | undefined>
/** Per-column `judge_condition` of the headline reading the cell shows,
* keyed identically to `values`. */
judge_condition_by_metric?: Record<string, string | null | undefined>
/** Per-column non-headline judge readings of the same (model, metric).
* The pivot keeps one row per model, so these ride the cell rather than
* becoming rows of their own. */
judge_alternates_by_metric?: Record<string, JudgeReading[] | undefined>
metrics_present: number
}
export type BenchmarkEvalListItem = Omit<BenchmarkEvalSummary, "model_results">
// ---------------------------------------------------------------------------
// Merged benchmark view (merged-benchmark-view spec F1) β one page per
// resolved canonical benchmark, merging every publishing source at
// observation grain. Backed by `merged_evals_view.parquet` plus a live
// query over `eval_results_view`.
// ---------------------------------------------------------------------------
/** 'flagged' rows have no canonical-scale score (never guessed); 'no_bounds'
* rows pass the raw score through unconverted; 'curated' rows were
* multiplied by a registry-declared factor (e.g. raw 1-10 onto 0-1). */
export type MergedScaleConversion = "none" | "div100" | "mul100" | "curated" | "flagged" | "no_bounds"
export interface MergedAggregateSource {
/** Per-source eval page id; null for slice-only sources (no top-level page target). */
evaluation_id: string | null
composite_slug: string
composite_display_name: string
models_count: number
results_count: number
/** False = excluded from the default view (source reports only other metrics). */
reports_preferred: boolean
/** True = source reports slice-level results only (disclosure note). */
slice_only: boolean
}
export interface MergedMetricOption {
metric_id: string
display_name: string
results_count: number
models_count: number
sources_count: number
lower_is_better: boolean | null
}
export interface MergedSliceOption {
slice_id: string
display_name: string
}
export interface MergedBestResult {
model_name: string | null
model_key: string | null
score: number | null
score_canonical: number | null
composite_slug: string | null
evaluation_id: string | null
}
/** One (model, source) observation row. Echo republications stay visible β
* the merged accessor never dedupes by model identity (spec design pt 3). */
export interface MergedObservationRow {
model_info: ModelInfo
model_route_id?: string
model_key?: string
/** The observation's per-source evaluation_id (two-segment) β Source link target. */
evaluation_id: string
composite_slug: string
composite_display_name?: string
score: number
/** Score on the metric's registry canonical scale; null for flagged rows. */
score_canonical: number | null
scale_conversion: MergedScaleConversion | null
evaluation_timestamp: string
source_metadata: SourceMetadata
/** The row's own upstream dataset provenance (repo, url, version,
* sample count). Absent when the fact row carries none. */
source_data?: SourceData
generation_config?: GenerationConfig
/** Same meaning as on ModelResultForBenchmark: the harness the
* reproducibility slots ask a re-runner to obtain and pin. */
eval_library?: EvalLibrary | null
is_verified_evaluator?: boolean
/** De-aliased evaluator identity (canonical display when resolvable). */
evaluator_display_name?: string
/** Collections: submission-channel id of the observation's source row. */
collection_id?: string
/** Protocol point (canonical sorted-key JSON) for protocol-varied
* collections; absent/null for ordinary observations. */
protocol_condition?: string | null
/** Same meanings as on ModelResultForBenchmark. Merged pages
* pool one observation per (model, source), so only headline rows
* belong in the pool (see lib/merged-adapter). */
judge_condition?: string | null
is_headline?: boolean | null
metric_source_label?: string | null
comparability_status?: ComparabilityStatus | null
/** Same meaning as on ModelResultForBenchmark. */
scoring_mode?: string | null
/** The number the source published, before any canonical-scale
* conversion applied to `score`. */
score_published?: number
/** Set when the merged accessor moved this row's `score_canonical`
* between the fraction and percent scales so it sits on the same scale
* as the page's other sources (see harmonizeUnboundedScales). Only
* 'no_bounds' rows are ever moved. 'of_total' rows were published as
* raw totals and divided by the benchmark's published maximum. */
scale_harmonized?: "mul100" | "div100" | "of_total"
/** The published maximum an 'of_total' row was divided by. */
scale_harmonized_total?: number
}
export interface MergedBenchmarkSummary {
/** Discriminator vs the per-source BenchmarkEvalSummary payload. */
merged: true
/** Single-segment percent-encoded benchmark id (never contains %2F). */
evaluation_id: string
benchmark_id: string
display_name: string
family_id?: string | null
family_display_name?: string | null
/** 'slice' = benchmark has no top-level data; page shows a slice selector. */
grain: "benchmark" | "slice"
preferred_metric_id: string
preferred_metric_display_name: string
preferred_from_registry: boolean
lower_is_better: boolean
/** Counts at default-metric grain (spec P5). */
sources_count: number
all_sources_count: number
results_count: number
models_count: number
best_result: MergedBestResult | null
aggregate_sources: MergedAggregateSource[]
metrics: MergedMetricOption[]
/** Non-null only for grain='slice'. */
slices: MergedSliceOption[] | null
/** Metric actually queried for `results` (defaults to preferred). */
selected_metric_id: string
selected_lower_is_better: boolean
/** For grain='slice': the slice actually queried (defaults to the first). */
selected_slice_id: string | null
results: MergedObservationRow[]
/** Registry display names for the judge ids named by these rows, keyed
* by the raw id β same map and same fallback rule as
* BenchmarkEvalSummary.judge_display_names. */
judge_display_names?: Record<string, string>
/** The benchmark's card, sourced from a per-source instantiation that
* authored one (preferring a source that reports the preferred metric). */
benchmark_card?: BenchmarkCard | null
/** `collections.json` entries for the curated collections these rows
* belong to, keyed by collection_id. Lets the reader name the study a
* row comes from and give its protocol numbers their declared units
* without a second fetch. */
collections?: Record<string, CollectionsSidecarEntry>
}
/**
* Collapse leaderboard rows that describe the same model under two
* source attributions (typical: one record with `developer: "OpenAI"`,
* another with `developer: "unknown"` from a source that didn't carry
* the developer field, both pointing at the same physical model). We
* only merge when every shared score column is byte-equal across the
* duplicates β that guarantees we never mask a legitimate second run
* that happens to share a name. When merging, we keep the attribution
* that actually identifies a developer.
*/
function devAttributionScore(developer: string | undefined | null): number {
if (!developer) return 0
const lower = developer.trim().toLowerCase()
if (!lower || lower === "unknown") return 0
return 1
}
function routeAttributionScore(routeId: string | undefined | null): number {
if (!routeId) return 0
const lower = routeId.toLowerCase()
return lower.startsWith("unknown%2f") || lower.startsWith("unknown/") ? 0 : 1
}
export function dedupeLeaderboardRowsByModelIdentity(
rows: BenchmarkLeaderboardRow[],
): BenchmarkLeaderboardRow[] {
if (rows.length < 2) return rows
const groups = new Map<string, BenchmarkLeaderboardRow[]>()
for (const row of rows) {
const name = (row.model_info?.name ?? "").trim().toLowerCase()
if (!name) continue
const bucket = groups.get(name)
if (bucket) bucket.push(row)
else groups.set(name, [row])
}
const result: BenchmarkLeaderboardRow[] = []
const consumed = new WeakSet<BenchmarkLeaderboardRow>()
for (const row of rows) {
if (consumed.has(row)) continue
const name = (row.model_info?.name ?? "").trim().toLowerCase()
const bucket = name ? groups.get(name) : null
if (!bucket || bucket.length < 2) {
result.push(row)
continue
}
// Verify per-column score agreement before merging. Any conflict
// (two different numbers for the same column key) means the rows
// are distinct runs that happen to share a model name β leave them.
let conflict = false
const valuesByKey: Record<string, number> = {}
outer: for (const candidate of bucket) {
for (const [key, raw] of Object.entries(candidate.values ?? {})) {
if (typeof raw !== "number" || !Number.isFinite(raw)) continue
if (key in valuesByKey) {
if (valuesByKey[key] !== raw) {
conflict = true
break outer
}
} else {
valuesByKey[key] = raw
}
}
}
if (conflict) {
result.push(row)
continue
}
// Pick the canonical row: best developer attribution, then best
// route id, then most populated values map as a tiebreaker.
const canonical = [...bucket].sort((a, b) => {
const devDelta = devAttributionScore(b.model_info?.developer) - devAttributionScore(a.model_info?.developer)
if (devDelta !== 0) return devDelta
const routeDelta = routeAttributionScore(b.model_route_id) - routeAttributionScore(a.model_route_id)
if (routeDelta !== 0) return routeDelta
return Object.keys(b.values ?? {}).length - Object.keys(a.values ?? {}).length
})[0]
const mergedValues: Record<string, number | null> = { ...(canonical.values ?? {}) }
const mergedAnnotations: Record<string, unknown> = { ...(canonical.annotations_by_metric ?? {}) }
const mergedStatus: Record<string, unknown> = {
...(canonical.comparability_status_by_metric ?? {}),
}
for (const candidate of bucket) {
if (candidate === canonical) continue
for (const [key, raw] of Object.entries(candidate.values ?? {})) {
if (mergedValues[key] == null && raw != null) mergedValues[key] = raw
}
for (const [key, ann] of Object.entries(candidate.annotations_by_metric ?? {})) {
if (mergedAnnotations[key] == null && ann != null) mergedAnnotations[key] = ann
}
for (const [key, status] of Object.entries(candidate.comparability_status_by_metric ?? {})) {
if (mergedStatus[key] == null && status != null) mergedStatus[key] = status
}
consumed.add(candidate)
}
result.push({
...canonical,
values: mergedValues,
annotations_by_metric: mergedAnnotations as typeof canonical.annotations_by_metric,
comparability_status_by_metric:
mergedStatus as typeof canonical.comparability_status_by_metric,
})
consumed.add(canonical)
}
return result
}
|