Spaces:
Running
Running
File size: 19,416 Bytes
aacebd7 2355bb3 aacebd7 2355bb3 aacebd7 2355bb3 aacebd7 2355bb3 aacebd7 6d05aa6 aacebd7 6d05aa6 aacebd7 2355bb3 aacebd7 6d05aa6 aacebd7 | 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 | /**
* Rule-based plain-language summaries for the policy-mode views.
*
* Pure templating β no live LLM calls. Each function takes a structured
* data object (model summary, eval summary, signal block) and returns
* either a single sentence or a small struct of paragraph fragments.
*
* Templating rules of thumb:
* - Lead with the headline (numbers / coverage), then the caveat.
* - Pick exactly one phrasing per branch β readers should never see
* two stitched-together fragments that mean the same thing.
* - "Not specified" sentinels collapse silently (caller decides whether
* to render the row at all).
*/
import type { ModelSummaryCore, BenchmarkEvaluation, MetricConfig } from "@/lib/benchmark-schema"
import type { BenchmarkEvalSummary } from "@/lib/eval-processing"
import type { ProvenanceSummary, ReproducibilitySummary, ComparabilitySummary } from "@/lib/backend-artifacts"
import { formatTagLabel } from "@/lib/benchmark-tags"
// ---------------------------------------------------------------------------
// Sentence-list helpers (kept tiny & pure β no JSX, no React)
// ---------------------------------------------------------------------------
/** Oxford-comma list with "and". `["a","b","c"]` β `"a, b, and c"`. */
export function listAnd(items: readonly string[]): string {
if (items.length === 0) return ""
if (items.length === 1) return items[0]
if (items.length === 2) return `${items[0]} and ${items[1]}`
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`
}
/** Plural-aware count phrase. `(1, "result")` β `"1 result"`. */
export function pluralize(count: number, singular: string, plural?: string): string {
return `${count.toLocaleString()} ${count === 1 ? singular : plural ?? `${singular}s`}`
}
/**
* Categories the EvalCards taxonomy can express. We compare against this
* canonical list to surface what *isn't* reported, not just what is.
*
* Sourced from data/benchmarks/categories.json β top-level tags that the
* derivedTag pipeline produces. We list the headliners only; obscure
* categories ("multilingual_general", "video_understanding") aren't
* useful as gap-callouts on a policy summary.
*/
export const HEADLINE_POLICY_CATEGORIES = [
"general",
"knowledge",
"logical_reasoning",
"applied_reasoning",
"mathematics",
"coding",
"agentic",
"safety",
"multilingual_general",
"multimodal",
] as const
/**
* Map a derivedTag category into a small bucket of "policy-relevant"
* groupings, so e.g. logical_reasoning + applied_reasoning collapse to
* "Reasoning" for a non-technical reader. Returns null when the input is
* neither headlinable nor in the policy bucket map.
*/
const POLICY_BUCKETS: Record<string, string> = {
general: "General capability",
knowledge: "Knowledge",
logical_reasoning: "Reasoning",
applied_reasoning: "Reasoning",
commonsense_reasoning: "Reasoning",
mathematics: "Math",
coding: "Coding",
software_engineering: "Coding",
agentic: "Agentic",
safety: "Safety",
multilingual_general: "Multilingual",
multimodal: "Multimodal",
}
/** Group categories into ~6 policy-readable buckets. */
export function bucketCategories(tags: readonly string[]): string[] {
const seen = new Set<string>()
const out: string[] = []
for (const tag of tags) {
const bucket = POLICY_BUCKETS[tag]
if (bucket && !seen.has(bucket)) {
seen.add(bucket)
out.push(bucket)
}
}
return out
}
const HEADLINE_BUCKET_LIST = ["General capability", "Knowledge", "Reasoning", "Math", "Coding", "Agentic", "Safety"] as const
/** Buckets we'd expect a frontier general-purpose model to report on. */
function expectedBuckets(): readonly string[] {
return HEADLINE_BUCKET_LIST
}
// ---------------------------------------------------------------------------
// MODEL view β produces the Measures / Caveat / Coverage / Reporting block.
// ---------------------------------------------------------------------------
export interface ModelPolicySummary {
/** "Reported across N benchmarks in K categories." */
scopeSentence: string
/** "Coverage spans Reasoning, Knowledge, and Agentic." (or null when there's only one category) */
coverageSentence: string | null
/** "No Safety or Math evaluations have been reported." Returns null when nothing material is missing. */
gapSentence: string | null
/** "Reported by Anthropic (the developer) and one independent third party." */
reportingSentence: string
/** "How this model was prompted is documented for X of Y reported scores." or null when fully documented / no data. */
reproducibilitySentence: string | null
/** "Comparing scores directly is limited because reporting setups differ." or null. */
comparabilitySentence: string | null
/** "Independently verified across N benchmarks." used as the optional headline tag. */
verificationLabel: string | null
}
interface ModelPolicyInputs {
/** Accepts any ModelSummaryCore-shaped object β the model page passes
* either the family summary or a selected variant. We only read the
* signal-summary blocks plus `total_evaluations`. */
summary: ModelSummaryCore
/** Pre-computed third-party tally from caller (cheap to compute, but
* caller already has it in benchmark-detail). */
thirdPartyEvaluations: number
/** Denominator for the third-party share. MUST be counted from the same
* population as `thirdPartyEvaluations` (the caller's flattened evaluation
* list), not the warehouse's distinct `total_evaluations` β those have
* different grains, which made the share exceed 100%. */
reportedEvaluationCount: number
organizationCount: number
organizationNames: string[]
/** Distinct benchmark count derived from group reduction. */
benchmarkCount: number
/** Categories actually reported, derived-tag form (snake_case). */
reportedCategories: readonly string[]
}
export function buildModelPolicySummary({
summary,
thirdPartyEvaluations,
reportedEvaluationCount,
organizationCount,
organizationNames,
benchmarkCount,
reportedCategories,
}: ModelPolicyInputs): ModelPolicySummary {
const totalEvals = summary.total_evaluations
// Denominator for the third-party share, counted from the same population as
// the numerator so the ratio stays within 0β100%. Falls back to totalEvals
// only if the caller passed nothing.
const thirdPartyBase = reportedEvaluationCount > 0 ? reportedEvaluationCount : totalEvals
const repro = summary.reproducibility_summary
const reproGap = repro?.has_reproducibility_gap_count ?? 0
const reproTotal = repro?.results_total ?? totalEvals
const provenance = summary.provenance_summary
const comparability = summary.comparability_summary
// ββ 1. Scope ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const scopeSentence =
benchmarkCount === 0
? "No benchmark evaluations have been reported for this model."
: `Reported across ${pluralize(benchmarkCount, "benchmark")}` +
(totalEvals > benchmarkCount
? ` (${pluralize(totalEvals, "result")} total).`
: ".")
// ββ 2. Coverage / Gap (the "missing categories" piece the user wanted) ββ
const reportedBuckets = bucketCategories(reportedCategories)
let coverageSentence: string | null = null
let gapSentence: string | null = null
if (reportedBuckets.length > 1) {
coverageSentence = `Coverage spans ${listAnd(reportedBuckets)}.`
} else if (reportedBuckets.length === 1) {
coverageSentence = `Coverage is concentrated in ${reportedBuckets[0]} only.`
}
if (reportedBuckets.length > 0) {
const reportedSet = new Set(reportedBuckets)
const missing = expectedBuckets().filter((b) => !reportedSet.has(b))
// Only flag a gap when there's a meaningful absence β at least one
// category reported AND at least one common bucket missing. We cap the
// list at three to stay readable.
if (missing.length > 0 && missing.length < expectedBuckets().length) {
const head = missing.slice(0, 3)
const trail = missing.length > 3 ? ` (and ${missing.length - 3} other categories)` : ""
gapSentence =
head.length === 1
? `No ${head[0]} evaluations have been reported.`
: `No ${listAnd(head)} evaluations have been reported${trail}.`
}
}
// ββ 3. Reporting (provenance) βββββββββββββββββββββββββββββββββββββββββββ
const firstPartyOnly =
provenance?.first_party_only_groups != null && provenance.total_groups > 0
? provenance.first_party_only_groups === provenance.total_groups
: null
const allThirdParty = thirdPartyBase > 0 && thirdPartyEvaluations === thirdPartyBase
const noThirdParty = thirdPartyEvaluations === 0 && thirdPartyBase > 0
const lead = organizationNames[0]
let reportingSentence: string
if (organizationCount === 0) {
reportingSentence = "No reporting organization is recorded."
} else if (organizationCount === 1 && lead) {
reportingSentence = allThirdParty
? `Tested independently by ${lead} (a third party, not the model's developer).`
: noThirdParty
? `Reported only by ${lead}; no independent third-party scores are available.`
: `Reported by ${lead}.`
} else if (lead) {
const others = organizationCount - 1
reportingSentence = allThirdParty
? `Tested independently by ${lead} and ${pluralize(others, "other organization")}.`
: noThirdParty
? `Reported by ${lead} and ${pluralize(others, "other organization")}, but no independent third-party scores are available.`
: `Reported by ${lead} and ${pluralize(others, "other organization")}.`
} else {
reportingSentence = `Reported by ${pluralize(organizationCount, "organization")}.`
}
// ββ 4. Reproducibility gap (plain language, no field names) ββββββββββββ
let reproducibilitySentence: string | null = null
if (reproTotal > 0) {
if (reproGap === 0) {
reproducibilitySentence = "How this model was prompted during testing is documented for every reported score."
} else if (reproGap === reproTotal) {
reproducibilitySentence =
"How this model was prompted during testing is not documented. Scores cannot be independently re-run as reported."
} else {
const documented = reproTotal - reproGap
const pct = Math.round((documented / reproTotal) * 100)
reproducibilitySentence = `Prompting setup is documented for ${pct}% of reported scores (${documented} of ${reproTotal}); the rest are missing enough detail to be re-run as-is.`
}
}
// ββ 5. Comparability caveat (no field names) ββββββββββββββββββββββββββ
let comparabilitySentence: string | null = null
if (comparability) {
const variantHits = comparability.variant_divergent_count
const crossPartyHits = comparability.cross_party_divergent_count
const noCrossPartyChecks = comparability.groups_with_cross_party_check === 0
if (variantHits === 0 && crossPartyHits === 0 && !noCrossPartyChecks) {
comparabilitySentence = "Where multiple reports are available, scores agree closely across setups and reporters."
} else if (variantHits > 0 && crossPartyHits > 0) {
comparabilitySentence = `Scores diverge across reporting setups in ${pluralize(variantHits, "case")} and across different reporters in ${pluralize(crossPartyHits, "case")}; some apparent score gaps may reflect setup choices rather than capability.`
} else if (variantHits > 0) {
comparabilitySentence = `Scores diverge across reporting setups in ${pluralize(variantHits, "case")}; apparent score gaps may partly reflect those setup choices.`
} else if (crossPartyHits > 0) {
comparabilitySentence = `Different reporters disagree on ${pluralize(crossPartyHits, "score")}; treat headline numbers as a range rather than a single value.`
} else if (noCrossPartyChecks) {
comparabilitySentence = "No third-party reports are available to cross-check the developer's numbers."
}
} else if (firstPartyOnly === true) {
comparabilitySentence = "Only the model's developer has reported these scores; cross-party comparison is not possible."
}
// ββ 6. Verification headline βββββββββββββββββββββββββββββββββββββββββ
let verificationLabel: string | null = null
if (allThirdParty && thirdPartyBase > 0) {
verificationLabel = "100% third party"
} else if (thirdPartyEvaluations > 0 && thirdPartyBase > 0) {
const pct = Math.min(100, Math.round((thirdPartyEvaluations / thirdPartyBase) * 100))
verificationLabel = `${pct}% third party`
} else if (noThirdParty) {
verificationLabel = "Developer-reported only"
}
return {
scopeSentence,
coverageSentence,
gapSentence,
reportingSentence,
reproducibilitySentence,
comparabilitySentence,
verificationLabel,
}
}
// ---------------------------------------------------------------------------
// EVAL view β produces single-sentence narrative for each of the four
// interpretive signals (used by the policy-mode panel renderers).
// ---------------------------------------------------------------------------
export interface EvalPolicySignals {
metricSentence: string
reproducibilitySentence: string | null
provenanceSentence: string | null
comparabilitySentence: string | null
}
export function buildEvalPolicySignals(summary: BenchmarkEvalSummary): EvalPolicySignals {
const cfg = summary.metric_config
const metricSentence = formatMetricSentence(cfg)
const reproducibilitySentence = formatReproducibilitySentence(summary.reproducibility_summary)
const provenanceSentence = formatProvenanceSentence(summary)
const comparabilitySentence = formatComparabilitySentence(summary.comparability_summary)
return {
metricSentence,
reproducibilitySentence,
provenanceSentence,
comparabilitySentence,
}
}
function formatMetricSentence(cfg: MetricConfig): string {
const lower = cfg.lower_is_better
const min = cfg.min_score
const max = cfg.max_score
const direction = lower
? "Lower scores indicate better performance"
: "Higher scores indicate better performance"
// Only mention the scale when both ends are documented and look like a
// tidy interval. Otherwise the sentence collapses to direction-only.
if (typeof min === "number" && typeof max === "number" && max > min) {
if (min === 0 && max === 1) return `${direction}, on a 0 to 1 scale.`
if (min === 0 && max === 100) return `${direction}, on a 0 to 100 scale.`
return `${direction}, on a ${min} to ${max} scale.`
}
return `${direction}.`
}
function formatReproducibilitySentence(repro?: ReproducibilitySummary): string | null {
if (!repro || repro.results_total === 0) return null
const total = repro.results_total
const gap = repro.has_reproducibility_gap_count
if (gap === 0) {
return "How models were prompted during testing is documented for every reported score."
}
if (gap === total) {
return "How models were prompted during testing is not documented. Scores cannot be independently re-run as reported."
}
const documented = total - gap
const pct = Math.round((documented / total) * 100)
return `Prompting setup is documented for ${pct}% of reported scores (${documented} of ${total}).`
}
function formatProvenanceSentence(summary: BenchmarkEvalSummary): string | null {
const prov: ProvenanceSummary | undefined = summary.provenance_summary
if (!prov) {
// Fall back to coarser ratio when the summary isn't attached.
if (summary.third_party_ratio === 0) {
return "These scores were reported only by the model developers themselves."
}
if (summary.third_party_ratio === 1) {
return "These scores were reported by independent third parties, not the model developers."
}
return null
}
const total = prov.total_groups
if (total === 0) return null
const firstPartyOnly = prov.first_party_only_groups
const multi = prov.multi_source_groups
if (firstPartyOnly === total) {
return "Every reported score on this benchmark comes only from the model's own developer; no independent third-party numbers are available."
}
if (firstPartyOnly === 0 && multi === 0) {
return "All reported scores come from independent third parties rather than the model developers."
}
if (multi > 0) {
const pctMulti = Math.round((multi / total) * 100)
return `${pctMulti}% of reported scores have been corroborated by more than one reporting organization.`
}
return null
}
function formatComparabilitySentence(comp?: ComparabilitySummary): string | null {
if (!comp || comp.total_groups === 0) return null
const variant = comp.variant_divergent_count
const crossParty = comp.cross_party_divergent_count
const noCrossPartyChecks = comp.groups_with_cross_party_check === 0
if (variant === 0 && crossParty === 0 && !noCrossPartyChecks) {
return "Where multiple reports exist, the scores agree closely; direct comparison across reports is reasonable."
}
if (variant > 0 && crossParty > 0) {
return "These scores have been reported under different setups and by different organizations, which may explain some of the variation seen across reports."
}
if (variant > 0) {
return "These scores have been reported under different evaluation setups, which may explain some of the variation across reports."
}
if (crossParty > 0) {
return "Different organizations have reported notably different numbers for the same model on this benchmark."
}
if (noCrossPartyChecks) {
return "No independent third-party reports are available to cross-check the developer's numbers."
}
return null
}
// ---------------------------------------------------------------------------
// Helpers used by the existing policySummary lede in benchmark-detail.tsx,
// re-exported so the inline copy can be replaced.
// ---------------------------------------------------------------------------
/**
* Pull derived-tag categories off a flat list of evaluations. Useful when the
* caller already has the per-result entries grouped in the page.
*/
export function collectReportedCategoriesFromEvals(
evaluations: readonly BenchmarkEvaluation[],
resolveCategory: (e: BenchmarkEvaluation) => string | null | undefined
): string[] {
const seen = new Set<string>()
const out: string[] = []
for (const evaluation of evaluations) {
const cat = resolveCategory(evaluation)
if (!cat) continue
if (!seen.has(cat)) {
seen.add(cat)
out.push(cat)
}
}
return out
}
/** "Reasoning, Knowledge, Agentic" β for compact category badge rows. */
export function formatPolicyBucketsCompact(tags: readonly string[]): string {
return bucketCategories(tags).join(" Β· ")
}
/** Pretty-print a derivedTag for the rare case the policy bucket falls
* through (we still want a friendly word, not snake_case). */
export function formatTagAsPolicyLabel(tag: string): string {
return formatTagLabel(tag)
}
|