import { RAW } from "./evals.js"; import { GO_MODELS, FREE_NOTES, CATEGORIES } from "./data.js"; /* ---------- shared helpers ---------- */ const el = (id) => document.getElementById(id); const tip = el("tip"); function showTip(html, e) { tip.style.display = "block"; tip.style.left = Math.min(e.clientX + 14, window.innerWidth - 260) + "px"; tip.style.top = Math.min(e.clientY + 14, window.innerHeight - 140) + "px"; tip.innerHTML = html; } const hideTip = () => (tip.style.display = "none"); function color(v) { if (v === null || v === undefined) return "transparent"; const stops = [ [14, 16, 26], [28, 50, 80], [31, 106, 143], [47, 163, 160], [87, 204, 138], [182, 227, 106], [242, 214, 75], [243, 156, 61], [239, 83, 80] ]; const p = Math.max(0, Math.min(0.999, v / 100)) * (stops.length - 1); const i = Math.floor(p), f = p - i, a = stops[i], b = stops[i + 1] || a; const c = a.map((x, k) => Math.round(x + (b[k] - x) * f)); return `rgb(${c[0]},${c[1]},${c[2]})`; } const txtCol = (v) => (v >= 62 ? "#101319" : "#e6e9f2"); const TASKS = [ { key: "coding", label: "Frontier coding", picks: [["fc", "FrontierCode"]] }, { key: "scicode", label: "Sci coding", picks: [["scicode", "SciCode"]] }, { key: "terminal", label: "Terminal agent", picks: [["tb21", "Terminal-Bench 2.1"], ["tb40", "Terminal-Bench 4.0"], ["tbh", "Terminal-Bench Hard"]] }, { key: "tools", label: "Tool use (τ-bench)", picks: [["tau2", "τ²-bench"], ["taub", "τ-bench Banking"]] }, { key: "agentic", label: "Agent workflows", picks: [["apex", "APEX-Agents"], ["analyst", "Analyst Agent"]] }, { key: "auto", label: "Automation", picks: [["auto", "AutomationBench"]] }, { key: "science", label: "Science reasoning", picks: [["gpqa", "GPQA Diamond"], ["critpt", "CritPt"]] }, { key: "hle", label: "Knowledge (HLE)", picks: [["hle", "Humanity's Last Exam"]] }, { key: "lcr", label: "Long context", picks: [["lcr", "AA Long-Context Reasoning"]] }, { key: "ifbench", label: "Instruction following", picks: [["ifbench", "IFBench"]] }, { key: "vision", label: "Vision (MMMU-Pro)", picks: [["mmmu", "MMMU-Pro"]] }, { key: "pro", label: "Professional work", picks: [["gdpv", "GDPval-AA"], ["gdppdf", "GDP-PDF"]] } ]; RAW.forEach((m) => { m.tasks = {}; m.src = {}; TASKS.forEach((t) => { for (const [f, label] of t.picks) { const v = m[f]; if (v !== null && v !== undefined) { m.tasks[t.key] = v; m.src[t.key] = label; break; } } }); m.blend = m.free ? 0 : 0.7 * m.pi + 0.3 * m.po; }); const byId = Object.fromEntries(RAW.map((m) => [m.id, m])); /* ---------- header cards ---------- */ const scored = RAW.filter((m) => m.ii !== null); const topOverall = scored.slice().sort((a, b) => b.ii - a.ii)[0]; const bestValue = scored.filter((m) => !m.free).slice().sort((a, b) => b.ii / b.blend - a.ii / a.blend)[0]; el("cards").innerHTML = [ ["Models in Go", RAW.length, "in the Go catalog"], ["With public evals", scored.length, "Artificial Analysis harness"], ["Top overall", topOverall.n, "index " + topOverall.ii.toFixed(1)], ["Best value", bestValue.n, "index " + bestValue.ii.toFixed(1) + " at $" + bestValue.blend.toFixed(2) + "/1M"], ["Free tier", RAW.filter((m) => m.free).length, "limited-time free models"] ].map(([k, v, s]) => `
${k}
${v}
${s}
`).join(""); /* ---------- matrix ---------- */ const cols = [ { key: "name", label: "Model", get: (m) => m.n, txt: true }, { key: "ii", label: "Index", get: (m) => m.ii }, { key: "blend", label: "Price $/1M", get: (m) => m.blend, price: true }, ...TASKS.map((t) => ({ key: t.key, label: t.label, get: (m) => m.tasks[t.key] })) ]; let sortKey = "ii", sortDir = -1; function renderMatrix() { const best = {}; cols.forEach((c) => { if (c.key === "name" || c.key === "blend") return; const vals = RAW.map((m) => c.get(m)).filter((v) => v !== null && v !== undefined); best[c.key] = vals.length ? Math.max(...vals) : null; }); const rows = RAW.slice().sort((a, b) => { const c = cols.find((x) => x.key === sortKey); const va = c.get(a), vb = c.get(b); if (sortKey === "name") return String(va).localeCompare(String(vb)) * sortDir; if (va === null || va === undefined) return 1; if (vb === null || vb === undefined) return -1; return (va - vb) * sortDir; }); let h = ""; cols.forEach((c) => { h += `${c.label}${c.key === sortKey ? (sortDir < 0 ? " ▾" : " ▴") : ""}`; }); h += ""; rows.forEach((m) => { h += `${m.n}${m.free ? 'free' : ""}
${m.lab} · $${m.pi}/${m.po} · ${(m.ctx / 1e6).toFixed(m.ctx % 1e6 ? 2 : 0)}M ctx${m.ii === null ? " · no public evals" : ""}
`; cols.forEach((c) => { if (c.key === "name") return; const v = c.get(m); if (c.key === "ii") { h += `
${v === null ? 'n/a' : `${v.toFixed(1)}`}
`; return; } if (c.key === "blend") { h += `${m.free ? "$0" : "$" + v.toFixed(2)}`; return; } if (v === null || v === undefined) { h += `
–
`; return; } const isBest = best[c.key] !== null && Math.abs(v - best[c.key]) < 0.001; h += `
${v.toFixed(0)}
`; }); h += ""; }); h += ""; const t = el("tbl"); t.innerHTML = h; t.querySelectorAll("th").forEach((th) => { th.onclick = () => { const k = th.dataset.k; if (k === sortKey) sortDir *= -1; else { sortKey = k; sortDir = k === "name" ? 1 : -1; } renderMatrix(); }; }); t.querySelectorAll(".cell[data-m]").forEach((c) => { c.onmousemove = (e) => showTip(`${c.dataset.m}
${c.dataset.t} — ${c.dataset.src}`, e); c.onmouseleave = hideTip; }); } renderMatrix(); /* ---------- svg helpers ---------- */ function svgEl(tag, attrs, parent) { const e = document.createElementNS("http://www.w3.org/2000/svg", tag); for (const k in attrs) e.setAttribute(k, attrs[k]); if (parent) parent.appendChild(e); return e; } /* ---------- scatter ---------- */ (function scatter() { const box = el("scatter"); box.innerHTML = ""; const W = 520, H = 340, m = { l: 52, r: 16, t: 16, b: 44 }; const svg = svgEl("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: "auto" }, box); const pts = scored.filter((x) => x.ii !== null); const maxX = Math.max(...pts.map((p) => p.blend)) * 1.1 || 1, maxY = 60; const X = (v) => m.l + (v / maxX) * (W - m.l - m.r); const Y = (v) => H - m.b - (v / maxY) * (H - m.t - m.b); for (let g = 0; g <= maxY; g += 10) { svgEl("line", { x1: m.l, y1: Y(g), x2: W - m.r, y2: Y(g), stroke: "#232838" }, svg); svgEl("text", { x: m.l - 8, y: Y(g) + 4, fill: "#8b93a7", "font-size": 10, "text-anchor": "end" }, svg).textContent = g; } for (let g = 0; g <= maxX; g += 2) { svgEl("line", { x1: X(g), y1: m.t, x2: X(g), y2: H - m.b, stroke: "#1c2130" }, svg); svgEl("text", { x: X(g), y: H - m.b + 16, fill: "#8b93a7", "font-size": 10, "text-anchor": "middle" }, svg).textContent = "$" + g; } svgEl("text", { x: (m.l + W - m.r) / 2, y: H - 6, fill: "#8b93a7", "font-size": 11, "text-anchor": "middle" }, svg).textContent = "blended price $/1M tokens"; svgEl("text", { x: 14, y: (m.t + H - m.b) / 2, fill: "#8b93a7", "font-size": 11, "text-anchor": "middle", transform: `rotate(-90 14 ${(m.t + H - m.b) / 2})` }, svg).textContent = "Intelligence Index"; pts.forEach((p) => { const c = svgEl("circle", { cx: X(p.blend), cy: Y(p.ii), r: 5, fill: p.free ? "#2ecc71" : "#7cc4ff", "fill-opacity": 0.85, stroke: "#0b0d12", "stroke-width": 1.5 }, svg); const lbl = svgEl("text", { x: X(p.blend) + 8, y: Y(p.ii) + 3, fill: "#c8cede", "font-size": 10 }, svg); lbl.textContent = p.n.length > 22 ? p.n.slice(0, 21) + "…" : p.n; c.onmousemove = (e) => showTip(`${p.n}
Index ${p.ii.toFixed(1)}
${p.free ? "Free" : `$${p.pi} in / $${p.po} out per 1M`}`, e); c.onmouseleave = hideTip; }); })(); /* ---------- value ranking ---------- */ (function valueChart() { const box = el("value"); box.innerHTML = ""; const W = 520, H = 340, m = { l: 96, r: 16, t: 12, b: 28 }; const svg = svgEl("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: "auto" }, box); const pts = scored.filter((p) => !p.free).map((p) => ({ ...p, v: p.ii / p.blend })).sort((a, b) => b.v - a.v); const maxV = Math.max(...pts.map((p) => p.v)) * 1.08; const step = (H - m.t - m.b) / Math.max(pts.length, 1); pts.forEach((p, i) => { const y = 26 + i * step; const w = Math.max(2, (p.v / maxV) * (W - m.l - m.r)); svgEl("rect", { x: m.l, y: y - 9, width: w, height: 15, rx: 3, fill: "#7cc4ff", "fill-opacity": 0.85 }, svg); svgEl("text", { x: m.l - 8, y: y + 3, fill: "#c8cede", "font-size": 11, "text-anchor": "end" }, svg).textContent = p.n.length > 17 ? p.n.slice(0, 16) + "…" : p.n; svgEl("text", { x: m.l + w + 6, y: y + 3, fill: "#8b93a7", "font-size": 10 }, svg).textContent = p.v.toFixed(0) + " pts/$"; }); const free = scored.filter((p) => p.free); svgEl("text", { x: m.l, y: H - 6, fill: "#2ecc71", "font-size": 11 }, svg).textContent = "Free models (not ranked): " + free.map((f) => f.n).join(", "); })(); /* ---------- model guide ---------- */ function modelCard(m) { const tags = (m.best || []).map((b) => { const key = Object.keys(CATEGORIES).find((k) => CATEGORIES[k].label.toLowerCase() === b.toLowerCase()); const cls = key === "free" ? "tag free" : key === "imagegen" ? "tag warn" : "tag"; return `${b}`; }).join(""); const ev = byId[m.id]; const idx = ev && ev.ii !== null ? `index ${ev.ii.toFixed(1)}` : "no public evals"; return `

${m.name}

${m.lab}
released ${m.released} · $${m.price.in}/${m.price.out} per 1M · ${m.ctx} context · ${m.modalities} · ${idx}
${tags}

${m.blurb}

Targeted for: ${m.target}

${m.caveats && m.caveats.length ? `
Caveats
` : ""}
`; } // loose keyword matching so a model's plain-language tags still hit the right filter const FILTER_WORDS = { agentic: ["agent"], coding: ["cod", "repo"], data: ["data", "spreadsheet", "analys"], vision: ["vision", "image", "multimodal", "perception", "visual"], imagegen: ["generation", "generat"], research: ["research", "knowledge work", "document", "deep"], fast: ["cheap", "fast", "high-volume", "efficient", "budget"] }; function renderGuide(filter) { const list = GO_MODELS.filter((m) => { if (!filter || filter === "all") return true; if (filter === "free") return m.free; if (filter === "new") return m.released >= "2026-08-01"; const words = FILTER_WORDS[filter] || []; const hay = m.best.join(" ").toLowerCase(); return words.some((w) => hay.includes(w)); }); const extra = filter === "all" || filter === "free" ? `
Also free outside the Go plan
${FREE_NOTES.map((f) => `${f.name} (${f.lab}, ${f.where}) — ${f.target} ${f.note}`).join("
")}
` : ""; const none = list.length ? "" : `
No model in the Go catalog matches this category. Every Go model is text-output — none of them generate images, so image generation needs a different provider.
`; el("guide").innerHTML = list.map(modelCard).join("") + extra + none; } const filters = [ ["all", "All models"], ["free", "Free tier"], ["new", "New (since Aug 2026)"], ["agentic", "Agentic"], ["coding", "Coding"], ["data", "Data analysis"], ["vision", "Image understanding"], ["imagegen", "Image generation"], ["research", "Deep research"], ["fast", "Fast & cheap"] ]; el("filters").innerHTML = filters.map(([k, l]) => `
${l}
`).join(""); el("filters").querySelectorAll(".chip").forEach((c) => { c.onclick = () => { el("filters").querySelectorAll(".chip").forEach((x) => x.classList.remove("on")); c.classList.add("on"); renderGuide(c.dataset.f); }; }); renderGuide("all"); el("foot").innerHTML = ` How to read the matrix
• Each cell is the highest-scoring published evaluation for that task family on a 0–100 scale; the benchmark behind it is named in the hover tooltip.
• Terminal-agent, tool-use, science and professional-work columns fall back to a second benchmark when a model has not been run on the first (Terminal-Bench 2.1 → 4.0 → Hard; τ²-bench → τ-bench Banking; GPQA → CritPt; GDPval → GDP-PDF). Scores are comparable within a column, but the underlying benchmark can differ per row — hover to check.
• "Index" is the Artificial Analysis Intelligence Index, a weighted composite of those evaluations.
• Prices are OpenCode Go list prices per 1M tokens (input/output). Blended price = 70% input / 30% output.
• Free-tier models (MiniMax M3.1 Flash Free, Beam Free, LongCat 2.5 Preview Free) are limited-time offers with unlimited included usage.
• Hy4 preview and LongCat 2.5 Preview Free have no public third-party evaluations yet, so they show as "n/a". Qwen3.8 Flash maps to Artificial Analysis' "Qwen3.8-Flash-Next" entry.
• No model in the Go catalog generates images — all Go models are text-output. "Image understanding" rows mean the model can read images, video, PDFs or screenshots as input.

Sources: Artificial Analysis (benchmarks, Intelligence Index), OpenCode Data (catalog, usage, context windows) and OpenCode Go docs (pricing, allowances, privacy). Vendor announcements for model positioning. Retrieved 2026-10-21.`;