/** * Calibration: map raw suite scores (mean grade, 0-1) onto the Artificial * Analysis 0-100 index the tier floors are defined against. * * A home-grown "0.72" is meaningless next to AA's "72" unless the two scales are * tied together. We do that empirically: run the SAME suite on models AA has * already scored (the anchors), fit raw -> AA per axis by least squares, then * apply that line to the unscored targets. Without enough anchors on an axis we * emit nothing for it — honest degradation, never an imputed number. */ import type { QualityAxis } from "../config/types.ts"; import type { FeedScore } from "../catalog/benchmark-feeds.ts"; import { normalizeModelKey } from "../catalog/benchmark-feeds.ts"; import type { AxisScore, EvalResult } from "./run.ts"; /** Minimum anchor models with a known AA score on an axis before we trust a fit. */ export const MIN_ANCHORS = 3; /** Minimum Pearson correlation between raw suite scores and AA before a fit is trusted. */ export const MIN_R = 0.5; /** * Anchor models for a run, chosen from the catalog and spread across its score range. * * A fit from three models that all score ~70 describes that cluster, not the scale: the * slope rests on a span of noise. Sampling the extremes and the quartiles gives the least * squares something to work with. Tool-capable only, since the suite calls tools, and never * the target itself. Fewer than `MIN_ANCHORS` scored models available ⇒ empty, and the * caller refuses rather than fitting a line through two points. */ export function pickAnchors(models: readonly { slug: string; quality: { coding?: number }; supportsTools: boolean }[], target: string): string[] { const scored = models .filter((m) => m.slug !== target && typeof m.quality.coding === "number" && m.supportsTools) .sort((a, b) => (a.quality.coding ?? 0) - (b.quality.coding ?? 0)); if (scored.length < MIN_ANCHORS) return []; const last = scored.length - 1; const picks = [0, Math.floor(last / 4), Math.floor(last / 2), Math.floor((3 * last) / 4), last]; return [...new Set(picks.map((i) => scored[i]!.slug))]; } export interface LineFit { slope: number; intercept: number; /** Pearson correlation of the anchor fit, 0-1. A quality signal on the calibration itself. */ r: number; /** Anchors used. */ n: number; } export type Calibration = Partial>; export interface AnchorPoint { /** Raw mean grade on the axis, 0-1. */ raw: number; /** Known AA index on the axis, 0-100. */ aa: number; } /** * The raw range the anchors must actually cover before a line through them means anything. * * Correlation alone does NOT catch a compressed fit: three anchors published 20/50/80 that * our suite scores 0.96/0.97/0.98 correlate at r = 1.0, and the line they define has a slope * of ~3000 index points per unit of raw score. That fit is arithmetically perfect and * completely useless — it is how a model published at 39.5 was calibrated to 22.8. If the * anchors barely differ on our suite, our suite cannot place anything between them. */ export const MIN_RAW_SPREAD = 0.15; /** * OLS fit, or null when the calibration cannot be trusted: too few points, too narrow a raw * range, no spread, a non-positive slope, or weak correlation. A suite that does not track * AA positively, with real correlation, over a real range would turn a target's score into * noise dressed as signal, so we refuse it and emit nothing for that axis. */ export function fitAxis(points: readonly AnchorPoint[]): LineFit | null { if (points.length < MIN_ANCHORS) return null; const raws = points.map((p) => p.raw); if (Math.max(...raws) - Math.min(...raws) < MIN_RAW_SPREAD) return null; const n = points.length; let sx = 0; let sy = 0; for (const p of points) { sx += p.raw; sy += p.aa; } const mx = sx / n; const my = sy / n; let sxx = 0; let syy = 0; let sxy = 0; for (const p of points) { const dx = p.raw - mx; const dy = p.aa - my; sxx += dx * dx; syy += dy * dy; sxy += dx * dy; } // No spread on either axis ⇒ undefined slope or correlation. if (sxx < 1e-9 || syy < 1e-9) return null; const slope = sxy / sxx; const r = sxy / Math.sqrt(sxx * syy); // The suite must rank models the same way AA does, and meaningfully so. if (slope <= 0 || r < MIN_R) return null; return { slope, intercept: my - slope * mx, r, n }; } export function applyFit(fit: LineFit, raw: number): number { const y = fit.slope * raw + fit.intercept; return Math.min(100, Math.max(0, y)); } const AXES: readonly QualityAxis[] = ["coding", "intelligence", "agentic"]; /** * The correlation a fit must reach before its numbers are published as scores. `MIN_R` (0.5) * is the bar for a fit being computable at all; this is the bar for TRUSTING one. The fit's * `r` and `n` were previously computed and then discarded, so an r of 0.51 and one of 0.99 * produced indistinguishable output — and a shallow fit silently compressed every target. */ export const PUBLISH_MIN_R = 0.8; /** * Fit every axis from the anchors' raw suite scores paired with their known AA scores. * `anchorAa` supplies the AA index per slug+axis (absent ⇒ that anchor is not used there). * * `rawOf` selects which observations the fit is built from. It defaults to every band, but * callers should pass the HARD band: easy and moderate sit at ~1.0 for every model worth * ranking, so including them leaves the regression almost no variation in x against a wide * spread in published y, and the slope collapses toward flat. */ export function fitCalibration( anchors: readonly EvalResult[], anchorAa: (slug: string, axis: QualityAxis) => number | undefined, rawOf: (result: EvalResult, axis: QualityAxis) => number | null = (r, axis) => axisMean(r.axes[axis]), ): Calibration { const cal: Calibration = {}; for (const axis of AXES) { const points: AnchorPoint[] = []; for (const r of anchors) { const raw = rawOf(r, axis); const aa = anchorAa(r.slug, axis); if (raw !== null && aa !== undefined) points.push({ raw, aa }); } const fit = fitAxis(points); if (fit !== null) cal[axis] = fit; } return cal; } export function axisMean(a: AxisScore | undefined): number | null { return a === undefined || a.n === 0 ? null : a.sum / a.n; } /** The hard band alone, for calibration. */ export const hardRaw = (r: EvalResult, axis: QualityAxis): number | null => axisMean(r.axesHard[axis]); /** * Calibrated local FeedScores for the targets, one axis at a time. An axis is skipped when it * has no fit, when the fit is weaker than `minR`, or when the target made no observation on * it — honest silence rather than a number nobody should act on. */ export function toLocalFeedScores( targets: readonly EvalResult[], cal: Calibration, authorOf: (slug: string) => string, rawOf: (result: EvalResult, axis: QualityAxis) => number | null = (r, axis) => axisMean(r.axes[axis]), minR = PUBLISH_MIN_R, ): FeedScore[] { const out: FeedScore[] = []; for (const r of targets) { const entry: FeedScore = { key: normalizeModelKey(r.slug), creator: authorOf(r.slug), source: "local" }; let any = false; for (const axis of AXES) { const fit = cal[axis]; const raw = rawOf(r, axis); if (fit === undefined || raw === null || fit.r < minR) continue; entry[axis] = applyFit(fit, raw); any = true; } if (any) out.push(entry); } return out; }