| (() => { |
| "use strict"; |
|
|
| |
| |
| |
|
|
| const state = { |
| data: null, |
| gen: null, |
| search: "", |
| sort: "avg-desc", |
| expanded: null, |
| }; |
|
|
| const $ = (sel) => document.querySelector(sel); |
|
|
| |
| |
| |
|
|
| const numeric = (v) => typeof v === "number" && isFinite(v); |
|
|
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
|
|
| const esc = (s) => |
| String(s).replace(/[&<>"']/g, (ch) => |
| ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch])); |
|
|
| function cssRgb(varName) { |
| return getComputedStyle(document.documentElement).getPropertyValue(varName) |
| .split(",").map((x) => parseFloat(x)); |
| } |
|
|
| |
| |
| 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); |
| |
| |
| 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)", ""); |
| } |
|
|
| |
| |
| |
|
|
| |
| |
| 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]; |
|
|
| |
| 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(""); |
|
|
| |
| 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(""); |
|
|
| |
| const dataPath = points.map((p, i) => pt(i, Math.max(0.03, p.value)).join(",")).join(" "); |
|
|
| |
| 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); |
|
|
| |
| 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>`; |
| } |
|
|
| |
| |
| |
|
|
| function renderStats(_models, _benches) { |
| |
| } |
|
|
| 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>`; |
|
|
| |
| 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(); |
| }) |
| ); |
| } |
|
|
| |
| |
| |
|
|
| 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; |
| } |
|
|
| |
| |
| |
| function fmtT2iValue(benchId, v) { |
| if (!numeric(v)) return null; |
| if (benchId === "t2i-clip-coco") return `${(v * 100).toFixed(1)}%`; |
| |
| return String(Math.round(v * 10000) / 10000); |
| } |
|
|
| |
| |
| |
| |
| 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; |
|
|
| |
| 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) })); |
| |
| 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(""); |
|
|
| |
| $("#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(); |
| }) |
| ); |
| } |
|
|
| |
| |
| |
|
|
| 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(",")})`; |
| } |
|
|
| |
| 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; |
|
|
| |
| 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(); |
| }) |
| ); |
|
|
| |
| let filtered = models; |
| if (extFilter !== "all") { |
| filtered = models.filter(m => extRunValue(m, extFilter) !== null); |
| |
| 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 { |
| |
| 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(""); |
|
|
| |
| $("#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); |
| } |
|
|
| |
| |
| |
|
|
| 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; |
| } |
|
|
| |
| const bench = benches[0]; |
| const benchId = bench.id; |
| const lowerBetter = bench.lower_is_better || false; |
|
|
| |
| 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; |
| } |
|
|
| |
| 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; |
|
|
| |
| 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; |
|
|
| |
| 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)); |
| 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; |
|
|
| |
| 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>`; |
|
|
| |
| 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"/>`; |
|
|
| |
| 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>`; |
| } |
|
|
| |
| 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>`; |
| } |
|
|
| |
| 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>`; |
|
|
| |
| 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"/>`; |
|
|
| |
| 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; |
| } |
|
|
| |
| |
| |
|
|
| 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>`; |
| }); |
| })(); |