Costi
remove stat-pills
083261d
Raw
History Blame Contribute Delete
36.9 kB
(() => {
"use strict";
// ---------------------------------------------------------------------
// State
// ---------------------------------------------------------------------
const state = {
data: null,
gen: null, // "7-2026" | "6-2026" | "all"; default = data.latest_generation
search: "",
sort: "avg-desc",
expanded: null, // "modelId" whose detail row is open
};
const $ = (sel) => document.querySelector(sel);
// ---------------------------------------------------------------------
// Data helpers
// ---------------------------------------------------------------------
const numeric = (v) => typeof v === "number" && isFinite(v);
// Category score resolution. `score` (v2 uniform key) wins; the legacy
// metric-name keys keep 6-2026 entries rendering unchanged.
const catVal = (c) =>
[c.score, c.hybrid_score, c.exact_match, c.soft_score_norm, c.acc_norm, c.soft_score]
.find(numeric);
function benchesFor(gen) {
const all = state.data.benchmarks.filter((b) => !b.unavailable);
const list = gen === "all" ? all : all.filter((b) => (b.generation || "6-2026") === gen);
return list.slice().sort((a, b) => (a.tier - b.tier) || String(a.generation).localeCompare(String(b.generation)));
}
function runScore(model, benchId) {
const run = model.runs && model.runs[benchId];
return run && numeric(run.score) ? run.score : null;
}
function avgScore(model, benches) {
const vals = benches.map((b) => runScore(model, b.id)).filter(numeric);
return vals.length ? vals.reduce((s, v) => s + v, 0) / vals.length : null;
}
function visibleModels(benches) {
let models = state.data.models.filter((m) =>
benches.some((b) => runScore(m, b.id) !== null)
);
if (state.search) {
const q = state.search.toLowerCase();
models = models.filter(
(m) => (m.name || "").toLowerCase().includes(q) || (m.org || "").toLowerCase().includes(q)
);
}
const key = state.sort;
models.sort((a, b) => {
if (key === "name-asc") return (a.name || "").localeCompare(b.name || "");
if (key.startsWith("params")) {
const pa = numeric(a.params_b) ? a.params_b : Infinity;
const pb = numeric(b.params_b) ? b.params_b : Infinity;
return key === "params-asc" ? pa - pb : (pb === Infinity ? -1 : pb) - (pa === Infinity ? -1 : pa);
}
const aa = avgScore(a, benches), ab = avgScore(b, benches);
const va = aa === null ? -1 : aa, vb = ab === null ? -1 : ab;
return key === "avg-asc" ? va - vb : vb - va;
});
return models;
}
// ---------------------------------------------------------------------
// Presentation helpers
// ---------------------------------------------------------------------
const esc = (s) =>
String(s).replace(/[&<>"']/g, (ch) =>
({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[ch]));
function cssRgb(varName) {
return getComputedStyle(document.documentElement).getPropertyValue(varName)
.split(",").map((x) => parseFloat(x));
}
// Gamma-curved color ramp: most cells stay near the base text color; only
// genuinely strong scores go vividly green.
function scoreColor(value, min, max) {
const base = cssRgb("--score-base"), best = cssRgb("--score-best");
if (!numeric(value) || max <= min) return null;
const p = Math.pow((value - min) / (max - min), 2.5);
const mix = base.map((b, i) => Math.round(b + (best[i] - b) * p));
return `rgb(${mix.join(",")})`;
}
function orgColor(org) {
let h = 0;
for (const ch of String(org || "?")) h = (h * 31 + ch.charCodeAt(0)) % 360;
return `hsl(${h}, 55%, 55%)`;
}
const fmtScore = (v) => (numeric(v) ? v.toFixed(3) : null);
// params_b is always in billions. Format at whichever scale keeps 2-3
// significant figures, matching the reference site (40.1M, 919k, 23.7k...).
function fmtParams(p) {
if (!numeric(p)) return "—";
const abs = Math.abs(p);
if (abs >= 1) return `${p.toFixed(p >= 100 ? 0 : 1)}B`;
if (abs >= 0.001) return `${(p * 1000).toFixed((p * 1000) >= 100 ? 0 : 1)}M`;
if (abs >= 0.000001) return `${(p * 1e6).toFixed((p * 1e6) >= 100 ? 0 : 1)}k`;
return `${Math.round(p * 1e9)}`;
}
function shortLabel(bench) {
return bench.label.replace(" (6-2026)", "");
}
// ---------------------------------------------------------------------
// Capability radar (replaces the old per-tier category-bar tables)
// ---------------------------------------------------------------------
// One shape per model: average category score across whichever benches are
// currently in view. Much faster to read than a stack of sliders per tier.
function categoryAverages(model, benches) {
const sums = {}, counts = {};
for (const b of benches) {
const run = model.runs && model.runs[b.id];
if (!run || !run.categories) continue;
for (const [cat, c] of Object.entries(run.categories)) {
const v = catVal(c);
if (!numeric(v)) continue;
sums[cat] = (sums[cat] || 0) + v;
counts[cat] = (counts[cat] || 0) + 1;
}
}
const cats = state.data.categories || Object.keys(sums);
return cats
.filter((c) => counts[c])
.map((c) => ({ cat: c, value: sums[c] / counts[c] }));
}
function radarSvg(points, size = 220) {
const n = points.length;
if (n < 3) {
return `<p class="radar-empty">Not enough category data for this view.</p>`;
}
const cx = size / 2, cy = size / 2, r = size / 2 - 30;
const angle = (i) => (Math.PI * 2 * i) / n - Math.PI / 2;
const pt = (i, frac) => [cx + Math.cos(angle(i)) * r * frac, cy + Math.sin(angle(i)) * r * frac];
// background rings at 25/50/75/100%
const rings = [0.25, 0.5, 0.75, 1].map((frac) => {
const path = points.map((_, i) => pt(i, frac).join(",")).join(" ");
return `<polygon points="${path}" class="radar-ring" />`;
}).join("");
// spokes
const spokes = points.map((_, i) => {
const [x, y] = pt(i, 1);
return `<line x1="${cx}" y1="${cy}" x2="${x}" y2="${y}" class="radar-spoke" />`;
}).join("");
// data polygon
const dataPath = points.map((p, i) => pt(i, Math.max(0.03, p.value)).join(",")).join(" ");
// labels
const labels = points.map((p, i) => {
const [x, y] = pt(i, 1.18);
const anchor = Math.cos(angle(i)) > 0.15 ? "start" : Math.cos(angle(i)) < -0.15 ? "end" : "middle";
const short = p.cat.replace(/-/g, " ");
return `<text x="${x}" y="${y}" text-anchor="${anchor}" class="radar-label" dominant-baseline="middle">${esc(short)}</text>`;
}).join("");
return `<svg viewBox="0 0 ${size} ${size}" class="radar-svg" role="img" aria-label="Category capability radar">
${rings}${spokes}
<polygon points="${dataPath}" class="radar-fill" />
${labels}
</svg>`;
}
function capabilityHtml(model, benches) {
const points = categoryAverages(model, benches);
const svg = radarSvg(points);
// compact per-tier headline strip alongside the shape
const tierRows = benches.map((b) => {
const run = model.runs && model.runs[b.id];
const v = run && numeric(run.score) ? run.score : null;
return `<div class="tier-row">
<span class="tier-name" title="${esc(b.description || "")}">${esc(shortLabel(b))}</span>
<div class="tier-bar"><i style="width:${v !== null ? Math.round(v * 100) : 0}%"></i></div>
<span class="tier-val">${v !== null ? v.toFixed(3) : "—"}</span>
</div>`;
}).join("");
return `<div class="capability-panel">
<div class="radar-wrap">${svg}</div>
<div class="tier-strip">${tierRows || "<em>No tier scores yet.</em>"}</div>
</div>`;
}
// ---------------------------------------------------------------------
// Rendering — text-QA leaderboard
// ---------------------------------------------------------------------
function renderStats(_models, _benches) {
// stat-pills removed
}
function renderChips() {
const latest = state.data.latest_generation;
const gens = [latest, "6-2026", "all"];
$("#gen-chips").innerHTML = gens
.map((g) => {
const label = g === "all" ? "All" : g;
const note = g === latest ? '<span class="chip-note">latest</span>' : "";
return `<button type="button" class="chip${state.gen === g ? " active" : ""}"
data-gen="${esc(g)}" role="tab" aria-selected="${state.gen === g}">${esc(label)}${note}</button>`;
})
.join("");
document.querySelectorAll("#gen-chips .chip").forEach((chip) =>
chip.addEventListener("click", () => {
state.gen = chip.dataset.gen;
state.expanded = null;
render();
})
);
}
function orgTag(org) {
if (!org) return "";
const c = orgColor(org);
return `<span class="org-tag" style="border:1px solid ${c}33;background:${c}22;color:${c}">
<span class="dot" style="background:${c}"></span>${esc(org)}</span>`;
}
function renderTable(models, benches) {
const head = $("#lb-head");
const body = $("#lb-body");
head.innerHTML = `<tr>
<th>#</th><th>Model</th><th>Org</th><th class="num">Params</th><th class="num">Avg</th>
${benches.map((b) => `<th class="num" title="${esc(b.description || "")}">${esc(shortLabel(b))}</th>`).join("")}
<th></th></tr>`;
// column ranges for the color ramp + best-value highlight
const ranges = benches.map((b) => {
const vals = models.map((m) => runScore(m, b.id)).filter(numeric);
return { min: Math.min(...vals), max: Math.max(...vals), best: vals.length ? Math.max(...vals) : null };
});
const avgs = models.map((m) => avgScore(m, benches)).filter(numeric);
const avgRange = { min: Math.min(...avgs), max: Math.max(...avgs), best: avgs.length ? Math.max(...avgs) : null };
const rows = [];
models.forEach((m, i) => {
const rank = i + 1;
const avg = avgScore(m, benches);
const open = state.expanded === m.id;
const avgColor = scoreColor(avg, avgRange.min, avgRange.max);
rows.push(`<tr class="model-row${open ? " open" : ""}" data-id="${esc(m.id)}">
<td class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</td>
<td class="model-cell">${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(m.name)}</a>` : esc(m.name)}</td>
<td>${orgTag(m.org)}</td>
<td class="num">${fmtParams(m.params_b)}</td>
<td class="num score${numeric(avg) && avg === avgRange.best ? " best" : ""}"
${avgColor ? `style="color:${avgColor}"` : ""}>${fmtScore(avg) ?? '<span class="na">—</span>'}</td>
${benches.map((b, j) => {
const v = runScore(m, b.id);
const color = scoreColor(v, ranges[j].min, ranges[j].max);
const best = numeric(v) && v === ranges[j].best;
return `<td class="num score${best ? " best" : ""}" ${color ? `style="color:${color}"` : ""}>
${fmtScore(v) ?? '<span class="na">TBD</span>'}</td>`;
}).join("")}
<td><span class="expand-hint">▶</span></td></tr>`);
if (open) {
rows.push(`<tr class="detail-row"><td colspan="${6 + benches.length}">${capabilityHtml(m, benches)}</td></tr>`);
}
});
body.innerHTML = rows.join("");
document.querySelectorAll("#lb-body tr.model-row").forEach((tr) =>
tr.addEventListener("click", () => {
state.expanded = state.expanded === tr.dataset.id ? null : tr.dataset.id;
render();
})
);
}
function renderCards(models, benches) {
$("#model-cards").innerHTML = models
.map((m, i) => {
const rank = i + 1;
const avg = avgScore(m, benches);
const open = state.expanded === m.id;
return `<div class="m-card${open ? " open" : ""}" data-id="${esc(m.id)}">
<div class="m-card-top">
<span style="display:flex;align-items:center;gap:9px;min-width:0;">
<span class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</span>
${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(m.name)}</a>` : `<span>${esc(m.name)}</span>`}
</span>
<span class="expand-hint">▶</span>
</div>
<div class="m-card-meta">${orgTag(m.org)}<span class="params">${fmtParams(m.params_b)}</span></div>
<div class="m-scores">
<div class="m-score"><div class="k">Avg</div><div class="v">${fmtScore(avg) ?? "—"}</div></div>
${benches.map((b) => {
const v = runScore(m, b.id);
return `<div class="m-score"><div class="k">${esc(shortLabel(b))}</div><div class="v">${fmtScore(v) ?? "—"}</div></div>`;
}).join("")}
</div>
${open ? `<div class="m-card-detail">${capabilityHtml(m, benches)}</div>` : ""}
</div>`;
})
.join("");
document.querySelectorAll("#model-cards .m-card").forEach((card) =>
card.addEventListener("click", () => {
state.expanded = state.expanded === card.dataset.id ? null : card.dataset.id;
render();
})
);
}
// ---------------------------------------------------------------------
// Rendering — text-to-image mini leaderboard (independent track)
// ---------------------------------------------------------------------
function t2iBenches() {
return (state.data.t2i_benchmarks || []).filter((b) => !b.unavailable);
}
function t2iRunValue(model, benchId) {
const run = model.runs && model.runs[benchId];
return run && numeric(run.score) ? run.score : null;
}
// FID: show at full reported precision (no artificial rounding — 4.8724
// stays 4.8724, 39.54 stays 39.54). CLIP: stored 0-1, display as a percent
// to one decimal, matching the reference site's "28.0%" formatting.
function fmtT2iValue(benchId, v) {
if (!numeric(v)) return null;
if (benchId === "t2i-clip-coco") return `${(v * 100).toFixed(1)}%`;
// strip trailing zeros beyond what was actually reported, cap at 4dp
return String(Math.round(v * 10000) / 10000);
}
// Composite score: each benchmark is min-max normalized across the visible
// set (inverted for lower_is_better), then averaged and scaled to 0-100.
// This mirrors the reference leaderboard's own "Overall Score" so ranking
// reflects both benchmarks together instead of just the first column.
function t2iComposite(model, benches, ranges) {
const parts = [];
benches.forEach((b, j) => {
const v = t2iRunValue(model, b.id);
if (!numeric(v)) return;
const { min, max } = ranges[j];
if (max === min) return;
let p = (v - min) / (max - min);
if (b.lower_is_better) p = 1 - p;
parts.push(p);
});
if (!parts.length) return null;
return (parts.reduce((s, v) => s + v, 0) / parts.length) * 100;
}
function renderT2i() {
const section = $("#t2i-section");
const benches = t2iBenches();
const models = (state.data.t2i_models || []).filter((m) =>
benches.some((b) => t2iRunValue(m, b.id) !== null)
);
if (!benches.length) {
section.hidden = true;
return;
}
section.hidden = false;
const head = $("#t2i-head");
head.innerHTML = `<tr>
<th>#</th><th>Model</th><th>Org</th><th class="num">Params</th>
<th class="num" title="Normalized blend of all benchmarks below (0-100, higher is better)">Score</th>
<th>Res.</th>
${benches.map((b) => `<th class="num" title="${esc(b.description || "")}">${esc(b.label)}${b.lower_is_better ? " ↓" : ""}</th>`).join("")}
<th>Released</th></tr>`;
if (!models.length) {
$("#t2i-body").innerHTML = "";
$("#t2i-cards").innerHTML = "";
$("#t2i-empty").hidden = false;
return;
}
$("#t2i-empty").hidden = true;
// ranges computed once, over the full visible set, before sorting
const ranges = benches.map((b) => {
const vals = models.map((m) => t2iRunValue(m, b.id)).filter(numeric);
const best = vals.length ? (b.lower_is_better ? Math.min(...vals) : Math.max(...vals)) : null;
return { min: Math.min(...vals), max: Math.max(...vals), best };
});
const withScore = models.map((m) => ({ m, score: t2iComposite(m, benches, ranges) }));
// sort by composite score desc; ties broken by the first (primary) bench
withScore.sort((a, b) => {
const sa = a.score === null ? -1 : a.score, sb = b.score === null ? -1 : b.score;
if (sb !== sa) return sb - sa;
const primary = benches[0];
const va = t2iRunValue(a.m, primary.id), vb = t2iRunValue(b.m, primary.id);
const aa = va === null ? Infinity : va, bb = vb === null ? Infinity : vb;
return primary.lower_is_better ? aa - bb : bb - aa;
});
const scores = withScore.map((x) => x.score).filter(numeric);
const scoreBest = scores.length ? Math.max(...scores) : null;
$("#t2i-body").innerHTML = withScore.map(({ m, score }, i) => {
const rank = i + 1;
const flagNote = benches.map((b) => m.runs && m.runs[b.id] && m.runs[b.id].notes).find(Boolean);
return `<tr${flagNote ? ` title="${esc(flagNote)}"` : ""}>
<td class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</td>
<td class="model-cell">${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener">${esc(m.name)}</a>` : esc(m.name)}${flagNote ? ' <span class="flag-dot" title="' + esc(flagNote) + '">⚠</span>' : ""}</td>
<td>${orgTag(m.org)}</td>
<td class="num">${fmtParams(m.params_b)}</td>
<td class="num score composite${numeric(score) && score === scoreBest ? " best" : ""}">${numeric(score) ? score.toFixed(1) : '<span class="na">—</span>'}</td>
<td class="res">${esc(m.resolution || "—")}</td>
${benches.map((b, j) => {
const v = t2iRunValue(m, b.id);
const best = numeric(v) && v === ranges[j].best;
return `<td class="num score${best ? " best" : ""}">${fmtT2iValue(b.id, v) ?? '<span class="na">TBD</span>'}</td>`;
}).join("")}
<td class="res">${esc(m.release_date || "—")}</td>
</tr>`;
}).join("");
// mobile cards, tappable like the main table's mobile cards
$("#t2i-cards").innerHTML = withScore.map(({ m, score }, i) => {
const rank = i + 1;
const flagNote = benches.map((b) => m.runs && m.runs[b.id] && m.runs[b.id].notes).find(Boolean);
const open = state.expanded === `t2i:${m.id}`;
return `<div class="t2i-m-card${open ? " open" : ""}" data-id="${esc(m.id)}">
<div class="m-card-top t2i-m-card-top">
<span style="display:flex;align-items:center;gap:9px;min-width:0;">
<span class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</span>
${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(m.name)}</a>` : `<span>${esc(m.name)}</span>`}
${flagNote ? ' <span class="flag-dot" title="' + esc(flagNote) + '">⚠</span>' : ""}
</span>
<span class="expand-hint">▶</span>
</div>
<div class="t2i-m-card-meta">${orgTag(m.org)}
<span class="params">${fmtParams(m.params_b)}</span>
<span class="res">${esc(m.resolution || "—")}</span>
<span class="date">${esc(m.release_date || "—")}</span>
</div>
<div class="t2i-m-scores">
<div class="t2i-m-score composite"><div class="k">Score</div><div class="v${numeric(score) && score === scoreBest ? " best" : ""}">${numeric(score) ? score.toFixed(1) : "—"}</div></div>
${benches.map((b, j) => {
const v = t2iRunValue(m, b.id);
const best = numeric(v) && v === ranges[j].best;
return `<div class="t2i-m-score"><div class="k">${esc(b.label)}</div><div class="v${best ? " best" : ""}">${fmtT2iValue(b.id, v) ?? "—"}</div></div>`;
}).join("")}
</div>
${open ? `<div class="t2i-m-card-detail">${flagNote ? `<p style="font-size:0.78rem;color:var(--muted);line-height:1.5;">${esc(flagNote)}</p>` : `<p style="font-size:0.78rem;color:var(--muted-2);font-style:italic;">No additional notes.</p>`}</div>` : ""}
</div>`;
}).join("");
document.querySelectorAll("#t2i-cards .t2i-m-card").forEach((card) =>
card.addEventListener("click", () => {
const key = `t2i:${card.dataset.id}`;
state.expanded = state.expanded === key ? null : key;
renderT2i();
})
);
}
// ---------------------------------------------------------------------
// Rendering — external text-to-text benchmarks (ARC, BLiMP, HellaSwag, etc.)
// ---------------------------------------------------------------------
const EXT_BENCH_IDS = ["arc-easy","arc-challenge","blimp","hellaswag","piqa","gci","arithmark3","arithmark2","wikitext2"];
function extBenches() {
return EXT_BENCH_IDS.map(id => state.data.benchmarks.find(b => b.id === id)).filter(Boolean);
}
function extRunValue(model, benchId) {
const run = model.runs && model.runs[benchId];
return run && numeric(run.score) ? run.score : null;
}
function fmtExtValue(benchId, v) {
if (!numeric(v)) return null;
if (benchId === "wikitext2") return v.toFixed(2);
if (benchId === "gci") return v.toFixed(2);
return (v * 100).toFixed(1) + "%";
}
function extSortKey(benchId) {
const idx = EXT_BENCH_IDS.indexOf(benchId);
return idx >= 0 ? idx : 99;
}
function extScoreColor(val, min, max, lowerBetter) {
const base = cssRgb("--score-base"), best = cssRgb(lowerBetter ? "--muted-2" : "--score-best");
if (!numeric(val) || max <= min) return null;
const clamped = Math.max(min, Math.min(max, val));
const p = lowerBetter ? (max - clamped) / (max - min) : (clamped - min) / (max - min);
const gamma = Math.pow(Math.max(0, Math.min(1, p)), 2.5);
const mix = base.map((b, i) => Math.round(b + (best[i] - b) * gamma));
return `rgb(${mix.join(",")})`;
}
function extAvgScore(model, benches) {
const vals = benches.map(b => extRunValue(model, b.id)).filter(numeric);
return vals.length ? vals.reduce((s, v) => s + v, 0) / vals.length : null;
}
function extAvgColor(val, min, max) {
const base = cssRgb("--score-base"), best = cssRgb("--score-best");
if (!numeric(val) || max <= min) return null;
const p = Math.pow(Math.max(0, Math.min(1, (val - min) / (max - min))), 2.5);
const mix = base.map((b, i) => Math.round(b + (best[i] - b) * p));
return `rgb(${mix.join(",")})`;
}
// Track which external benchmark filter is active
let extFilter = "all";
function renderExternal() {
const section = $("#external-section");
const allBenches = extBenches();
if (!allBenches.length) { section.hidden = true; return; }
const models = state.data.models.filter(m => allBenches.some(b => extRunValue(m, b.id) !== null));
section.hidden = false;
// filter chips for ext benchmarks
const chips = $("#ext-chips");
const benchKeys = allBenches.map(b => b.id);
const chipOpts = ["all", ...benchKeys];
chips.innerHTML = chipOpts.map(id => {
const label = id === "all" ? "All" : (allBenches.find(b => b.id === id) || {}).label || id;
const count = id === "all" ? models.length : models.filter(m => extRunValue(m, id) !== null).length;
return `<button type="button" class="chip${extFilter === id ? " active" : ""}" data-ext="${id}" role="tab" aria-selected="${extFilter === id}">${esc(label)} <span class="chip-note">${count}</span></button>`;
}).join("");
chips.querySelectorAll(".chip").forEach(chip =>
chip.addEventListener("click", () => {
extFilter = chip.dataset.ext;
renderExternal();
})
);
// filter models by active ext filter
let filtered = models;
if (extFilter !== "all") {
filtered = models.filter(m => extRunValue(m, extFilter) !== null);
// sort by the selected benchmark
filtered.sort((a, b) => {
const va = extRunValue(a, extFilter) || -1;
const vb = extRunValue(b, extFilter) || -1;
const bench = allBenches.find(x => x.id === extFilter);
return bench && bench.lower_is_better ? va - vb : vb - va;
});
} else {
// sort by avg
filtered.sort((a, b) => {
const aa = extAvgScore(a, allBenches) || -1;
const ab = extAvgScore(b, allBenches) || -1;
return ab - aa;
});
}
const visibleBenches = extFilter === "all" ? allBenches : allBenches.filter(b => b.id === extFilter);
const head = $("#ext-head");
head.innerHTML = `<tr>
<th>#</th><th>Model</th><th>Org</th><th class="num">Params</th><th class="num">Avg</th>
${visibleBenches.map(b => `<th class="num" title="${esc(b.description || "")}">${esc(b.label)}${b.lower_is_better ? " ↓" : ""}</th>`).join("")}
</tr>`;
const ranges = visibleBenches.map(b => {
const vals = filtered.map(m => extRunValue(m, b.id)).filter(numeric);
return { min: Math.min(...vals), max: Math.max(...vals), best: b.lower_is_better ? Math.min(...vals) : Math.max(...vals), lowerBetter: b.lower_is_better };
});
const avgs = filtered.map(m => extAvgScore(m, visibleBenches)).filter(numeric);
const avgRange = { min: Math.min(...avgs), max: Math.max(...avgs), best: avgs.length ? Math.max(...avgs) : null };
$("#ext-body").innerHTML = filtered.map((m, i) => {
const rank = i + 1;
const avg = extAvgScore(m, visibleBenches);
const avgColor = extAvgColor(avg, avgRange.min, avgRange.max);
return `<tr>
<td class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</td>
<td class="model-cell">${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener">${esc(m.name)}</a>` : esc(m.name)}</td>
<td>${orgTag(m.org)}</td>
<td class="num">${fmtParams(m.params_b)}</td>
<td class="num score${numeric(avg) && avg === avgRange.best ? " best" : ""}" ${avgColor ? `style="color:${avgColor}"` : ""}>${fmtScore(avg) ?? '<span class="na">—</span>'}</td>
${visibleBenches.map((b, j) => {
const v = extRunValue(m, b.id);
const color = extScoreColor(v, ranges[j].min, ranges[j].max, ranges[j].lowerBetter);
const best = numeric(v) && v === ranges[j].best;
return `<td class="num score${best ? " best" : ""}" ${color ? `style="color:${color}"` : ""}>${fmtExtValue(b.id, v) ?? '<span class="na">—</span>'}</td>`;
}).join("")}
</tr>`;
}).join("");
// mobile cards
$("#ext-cards").innerHTML = filtered.map((m, i) => {
const rank = i + 1;
const avg = extAvgScore(m, visibleBenches);
return `<div class="t2i-m-card" data-id="ext:${esc(m.id)}">
<div class="m-card-top t2i-m-card-top">
<span style="display:flex;align-items:center;gap:9px;min-width:0;">
<span class="rank rank-${rank <= 3 ? rank : "n"}">#${rank}</span>
${m.url ? `<a href="${esc(m.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${esc(m.name)}</a>` : `<span>${esc(m.name)}</span>`}
</span>
</div>
<div class="t2i-m-card-meta">${orgTag(m.org)}<span class="params">${fmtParams(m.params_b)}</span></div>
<div class="t2i-m-scores">
<div class="t2i-m-score"><div class="k">Avg</div><div class="v">${fmtScore(avg) ?? "—"}</div></div>
${visibleBenches.map(b => {
const v = extRunValue(m, b.id);
return `<div class="t2i-m-score"><div class="k">${esc(b.label)}</div><div class="v">${fmtExtValue(b.id, v) ?? "—"}</div></div>`;
}).join("")}
</div>
</div>`;
}).join("");
$("#ext-empty").hidden = filtered.length > 0;
renderExtCharts(filtered, visibleBenches, extFilter);
}
// ---------------------------------------------------------------------
// Charts — bar chart + scatter plot for external benchmarks
// ---------------------------------------------------------------------
function renderExtCharts(models, benches, activeFilter) {
const barEl = $("#ext-bar-chart");
const scatterEl = $("#ext-scatter-chart");
if (!barEl || !scatterEl) return;
const isEmpty = models.length === 0 || benches.length === 0;
if (isEmpty) {
barEl.innerHTML = '<p class="radar-empty" style="padding:2rem;text-align:center;">Not enough data for charts.</p>';
scatterEl.innerHTML = '';
return;
}
// Use the first (or only) benchmark for charts
const bench = benches[0];
const benchId = bench.id;
const lowerBetter = bench.lower_is_better || false;
// Get (model, value) pairs sorted
const pairs = models
.map(m => ({ model: m, val: extRunValue(m, benchId) }))
.filter(p => numeric(p.val));
if (pairs.length < 2) {
barEl.innerHTML = '<p class="radar-empty" style="padding:2rem;text-align:center;">Not enough data for chart.</p>';
scatterEl.innerHTML = '';
return;
}
// --- Bar chart: top N ---
const sorted = [...pairs].sort((a, b) => lowerBetter ? a.val - b.val : b.val - a.val);
const topN = sorted.slice(0, 12);
const maxVal = lowerBetter ? sorted[sorted.length - 1].val : sorted[0].val;
const minVal = lowerBetter ? sorted[0].val : sorted[sorted.length - 1].val;
const range = maxVal - minVal || 1;
const barW = 500, barH = Math.max(180, topN.length * 32 + 20);
const barPad = { top: 10, right: 10, bottom: 20, left: 10 };
const plotW = barW - barPad.left - barPad.right;
const plotH = barH - barPad.top - barPad.bottom;
const barGap = 2;
const barThick = Math.max(6, (plotH / topN.length) - barGap);
let barSvg = `<svg viewBox="0 0 ${barW} ${barH}" class="chart-svg" aria-label="Bar chart of top models for ${esc(bench.label)}">
<style>.bar-text { fill: var(--muted, #8b93a8); font-size: 7px; font-family: Inter, sans-serif; } .bar-fill { fill: var(--accent, #7c9cff); } .bar-fill.best { fill: var(--green, #7aff9c); }</style>`;
topN.forEach((p, i) => {
const x = barPad.left;
const y = barPad.top + i * (barThick + barGap);
const frac = (p.val - minVal) / range;
const w = Math.max(2, frac * plotW);
const name = p.model.name.length > 24 ? p.model.name.slice(0, 22) + '...' : p.model.name;
const bestClass = i === 0 ? ' bar-fill best' : ' bar-fill';
const fmt = benchId === 'wikitext2' ? p.val.toFixed(1) : (p.val * 100).toFixed(1) + '%';
barSvg += `<rect x="${x}" y="${y}" width="${w}" height="${barThick}" rx="2" class="${bestClass}"></rect>`;
barSvg += `<text x="${x + 4}" y="${y + barThick / 2 + 2.5}" class="bar-text" style="fill:var(--bg-0,#05060a);font-weight:600;font-size:6px;">${esc(fmt)}</text>`;
barSvg += `<text x="${x + w + 4}" y="${y + barThick / 2 + 2.5}" class="bar-text" dominant-baseline="middle">${esc(name)}</text>`;
});
barSvg += '</svg>';
barEl.innerHTML = barSvg;
// --- Scatter plot: score vs log(params) ---
const scatW = 500, scatH = 240;
const scatPad = { top: 16, right: 16, bottom: 30, left: 40 };
const spW = scatW - scatPad.left - scatPad.right;
const spH = scatH - scatPad.top - scatPad.bottom;
// Filter to models with params
const scatPairs = pairs.filter(p => numeric(p.model.params_b) && p.model.params_b > 0);
if (scatPairs.length < 3) {
scatterEl.innerHTML = '<p class="radar-empty" style="padding:2rem;text-align:center;">Need at least 3 models with known params for scatter plot.</p>';
return;
}
const xVals = scatPairs.map(p => Math.log10(p.model.params_b * 1e9)); // log10 of param count
const yVals = scatPairs.map(p => p.val);
const xMin = Math.min(...xVals) - 0.1;
const xMax = Math.max(...xVals) + 0.1;
const yMin = Math.min(...yVals);
const yMax = Math.max(...yVals);
const yRange = yMax - yMin || 1;
// Linear regression
const n = scatPairs.length;
const sumX = xVals.reduce((s, v) => s + v, 0);
const sumY = yVals.reduce((s, v) => s + v, 0);
const sumXY = xVals.reduce((s, v, i) => s + v * yVals[i], 0);
const sumX2 = xVals.reduce((s, v) => s + v * v, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
const scaleX = (v) => scatPad.left + ((v - xMin) / (xMax - xMin)) * spW;
const scaleY = (v) => scatPad.top + spH - ((v - yMin) / yRange) * spH;
let scatSvg = `<svg viewBox="0 0 ${scatW} ${scatH}" class="chart-svg" aria-label="Scatter plot of ${esc(bench.label)} vs parameters">
<style>.scat-dot { fill: var(--accent, #7c9cff); stroke: rgba(255,255,255,0.3); stroke-width: 0.5; } .scat-line { stroke: var(--green, #7aff9c); stroke-width: 1; stroke-dasharray: 3 2; fill: none; } .scat-label { fill: var(--muted-2, #5c6478); font-size: 6px; font-family: Inter, sans-serif; } .scat-title { fill: var(--muted, #8b93a8); font-size: 7px; font-family: Inter, sans-serif; }</style>`;
// Axes
scatSvg += `<line x1="${scaleX(xMin)}" y1="${scaleY(yMin)}" x2="${scaleX(xMax)}" y2="${scaleY(yMin)}" stroke="var(--line, rgba(255,255,255,0.08))" stroke-width="1"/>`;
scatSvg += `<line x1="${scaleX(xMin)}" y1="${scaleY(yMin)}" x2="${scaleX(xMin)}" y2="${scaleY(yMax)}" stroke="var(--line, rgba(255,255,255,0.08))" stroke-width="1"/>`;
// X axis labels
const xTicks = 5;
for (let i = 0; i <= xTicks; i++) {
const v = xMin + (xMax - xMin) * i / xTicks;
const x = scaleX(v);
const label = Math.round(10 ** v);
scatSvg += `<text x="${x}" y="${scatH - 8}" text-anchor="middle" class="scat-label">${label >= 1000 ? (label/1000).toFixed(0) + 'k' : label >= 1000000 ? (label/1000000).toFixed(0) + 'M' : label}</text>`;
}
// Y axis labels
const yTicks = 4;
for (let i = 0; i <= yTicks; i++) {
const v = yMin + yRange * i / yTicks;
const y = scaleY(v);
const label = benchId === 'wikitext2' ? v.toFixed(1) : (v * 100).toFixed(0) + '%';
scatSvg += `<text x="${scatPad.left - 6}" y="${y + 2}" text-anchor="end" class="scat-label">${esc(label)}</text>`;
}
// Axis titles
scatSvg += `<text x="${scatW/2}" y="${scatH - 2}" text-anchor="middle" class="scat-title">Parameters</text>`;
scatSvg += `<text x="8" y="${scatH/2}" text-anchor="middle" transform="rotate(-90, 8, ${scatH/2})" class="scat-title">${esc(bench.label)}</text>`;
// Regression line
const regX1 = xMin, regY1 = slope * regX1 + intercept;
const regX2 = xMax, regY2 = slope * regX2 + intercept;
scatSvg += `<line x1="${scaleX(regX1)}" y1="${scaleY(regY1)}" x2="${scaleX(regX2)}" y2="${scaleY(regY2)}" class="scat-line"/>`;
// Dots
scatPairs.forEach(p => {
const cx = scaleX(Math.log10(p.model.params_b * 1e9));
const cy = scaleY(p.val);
const r = 3;
scatSvg += `<circle cx="${cx}" cy="${cy}" r="${r}" class="scat-dot"><title>${esc(p.model.name)}: ${fmtExtValue(benchId, p.val) || p.val}</title></circle>`;
});
scatSvg += '</svg>';
scatterEl.innerHTML = scatSvg;
}
// ---------------------------------------------------------------------
// Boot / orchestration
// ---------------------------------------------------------------------
function render() {
const benches = benchesFor(state.gen);
const models = visibleModels(benches);
renderChips();
renderStats(models, benches);
const empty = models.length === 0;
$("#empty-state").hidden = !empty;
$("#lb").style.display = empty ? "none" : "";
$("#model-cards").style.visibility = empty ? "hidden" : "";
if (!empty) {
renderTable(models, benches);
renderCards(models, benches);
}
renderT2i();
renderExternal();
}
async function boot() {
const resp = await fetch("models.json");
state.data = await resp.json();
state.gen = state.data.latest_generation || "all";
$("#model-search").addEventListener("input", (e) => {
state.search = e.target.value.trim();
render();
});
$("#sort-select").addEventListener("change", (e) => {
state.sort = e.target.value;
render();
});
$("#show-legacy").addEventListener("click", () => {
state.gen = "6-2026";
render();
});
const scheme = window.matchMedia("(prefers-color-scheme: light)");
(scheme.addEventListener || scheme.addListener).call(scheme, "change", render);
render();
}
boot().catch((err) => {
$("#lb-body").innerHTML = `<tr><td colspan="9">Failed to load models.json: ${esc(err.message)}</td></tr>`;
});
})();