AutoMedBench-Leaderboard / efficiency.html
MitakaKuma's picture
Add Gemma 4 12B and Mistral 7B results
8efa690 verified
Raw
History Blame Contribute Delete
49.6 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="AutoMedBench Lite efficiency frontier: overall score against cost, time, tokens, and turns per task.">
<meta name="color-scheme" content="light dark">
<title>AutoMedBench Lite - Efficiency Frontier</title>
<style>
:root {
--canvas: #f6f1e6;
--panel: #faf6ee;
--panel-strong: #fffdf8;
--ink: #3b4538;
--ink-soft: #737a6d;
--ink-faint: #9aa093;
--edge: #e6decc;
--edge-strong: #d9d0bc;
--primary: #4a7355;
--primary-dark: #3e6248;
--primary-soft: #e6ede5;
--mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
--shadow: 0 12px 32px rgba(59, 69, 56, 0.08);
}
[data-theme="dark"] {
--canvas: #20241d;
--panel: #262b21;
--panel-strong: #2b3126;
--ink: #e9e5d7;
--ink-soft: #aab09f;
--ink-faint: #7f8474;
--edge: #3a4033;
--edge-strong: #464d3c;
--primary: #82a98d;
--primary-dark: #9abea3;
--primary-soft: #333f2e;
--shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}
* { box-sizing: border-box; }
html { background: var(--canvas); }
body {
margin: 0;
min-width: 320px;
color: var(--ink);
background:
radial-gradient(circle at 15% 0%, rgba(74, 115, 85, 0.08), transparent 30rem),
var(--canvas);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 16px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
a { color: inherit; }
.page {
width: min(100% - 32px, 1180px);
margin: 0 auto;
padding: 44px 0 36px;
}
.hero {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
margin-bottom: 24px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-mark {
display: grid;
width: 42px;
height: 42px;
flex: 0 0 auto;
place-items: center;
border-radius: 11px;
color: #fff;
background: var(--primary);
box-shadow: 0 5px 14px rgba(74, 115, 85, 0.2);
font-family: Georgia, "Times New Roman", serif;
font-size: 23px;
font-weight: 700;
}
.eyebrow {
margin: 0 0 2px;
color: var(--primary);
font-size: 12px;
font-weight: 750;
letter-spacing: 0.11em;
text-transform: uppercase;
}
h1 {
margin: 0;
font-family: Georgia, "Times New Roman", serif;
font-size: clamp(28px, 4vw, 38px);
font-weight: 650;
letter-spacing: -0.025em;
line-height: 1.1;
}
.button.theme-fab {
position: fixed;
right: 22px;
bottom: 22px;
z-index: 30;
min-height: 50px;
padding: 13px 22px;
border: 2px solid var(--primary-dark);
border-radius: 999px;
color: #fff;
background: var(--primary);
font-size: 15px;
font-weight: 750;
letter-spacing: 0.01em;
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.28);
}
.button.theme-fab:hover,
.button.theme-fab:focus-visible {
color: #fff;
background: var(--primary-dark);
border-color: var(--primary-dark);
transform: translateY(-1px);
}
.actions {
display: flex;
flex: 0 0 auto;
gap: 8px;
}
.button {
display: inline-flex;
align-items: center;
min-height: 36px;
padding: 7px 12px;
border: 1px solid var(--edge-strong);
border-radius: 8px;
color: var(--ink);
background: var(--panel-strong);
font: inherit;
font-size: 13px;
font-weight: 650;
text-decoration: none;
cursor: pointer;
transition: border-color 140ms ease, background 140ms ease, color 140ms ease;
}
.button:hover,
.button:focus-visible {
border-color: var(--primary);
color: var(--primary-dark);
background: var(--panel-strong);
outline: none;
}
.frontier {
overflow: hidden;
border: 1px solid var(--edge-strong);
border-radius: 12px;
background: var(--panel-strong);
box-shadow: var(--shadow);
}
.tabbar {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 4px;
padding: 6px;
border-bottom: 1px solid var(--edge);
background: var(--panel);
}
.tab {
min-height: 44px;
padding: 7px 10px;
border: 0;
border-radius: 8px;
color: var(--ink-soft);
background: transparent;
font: inherit;
font-size: 15.5px;
font-weight: 700;
cursor: pointer;
transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;
}
.tab small {
display: block;
font-size: 11px;
font-weight: 650;
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.75;
}
.tab:hover { color: var(--ink); background: rgba(255, 253, 248, 0.7); }
.tab:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.tab[aria-selected="true"] {
color: #fff;
background: var(--primary);
box-shadow: 0 2px 6px rgba(59, 69, 56, 0.14);
}
.frontier-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24px;
padding: 18px 20px 12px;
}
.frontier-copy { min-width: 0; }
.kicker {
margin: 0 0 4px;
color: var(--primary);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.09em;
text-transform: uppercase;
}
.frontier h2 {
margin: 0;
color: var(--ink);
font-family: Georgia, "Times New Roman", serif;
font-size: clamp(21px, 3vw, 27px);
font-weight: 650;
letter-spacing: -0.02em;
line-height: 1.15;
}
.frontier-subtitle {
max-width: 680px;
margin: 6px 0 0;
color: var(--ink-soft);
font-size: 15px;
}
.head-controls {
display: flex;
flex: 0 0 auto;
align-items: center;
gap: 8px;
padding-top: 4px;
}
.count-pill {
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 5px 13px;
border: 1px solid var(--edge);
border-radius: 999px;
color: var(--ink-soft);
background: var(--panel);
font-family: var(--mono);
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
.oss-toggle {
display: inline-flex;
align-items: center;
gap: 7px;
min-height: 34px;
padding: 5px 13px;
border: 1px dashed var(--edge-strong);
border-radius: 8px;
color: var(--ink-soft);
background: var(--panel);
font-family: var(--mono);
font-size: 12.5px;
font-weight: 700;
cursor: pointer;
transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.oss-toggle .glyph {
color: var(--primary);
font-weight: 800;
}
.oss-toggle:hover { border-color: var(--primary); color: var(--ink); }
.oss-toggle:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.oss-toggle[aria-pressed="true"] {
border-style: solid;
border-color: var(--primary-dark);
color: #fff;
background: var(--primary);
}
.oss-toggle[aria-pressed="true"] .glyph { color: #d7e4d8; }
.legend {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 14px;
padding: 2px 20px 12px;
color: var(--ink-soft);
font-size: 14px;
}
.legend-item {
display: inline-flex;
align-items: center;
gap: 7px;
white-space: nowrap;
}
.legend-button {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 3px 9px;
border: 1px solid transparent;
border-radius: 7px;
color: var(--ink-soft);
background: transparent;
font: inherit;
white-space: nowrap;
cursor: pointer;
transition: border-color 140ms ease, color 140ms ease, background 140ms ease;
}
.legend-button:hover {
border-color: var(--edge-strong);
color: var(--ink);
background: var(--panel);
}
.legend-button:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.legend-button[aria-pressed="true"] {
border-color: var(--primary);
color: var(--primary-dark);
background: var(--primary-soft);
font-weight: 700;
}
.legend-sep {
width: 1px;
height: 16px;
background: var(--edge-strong);
}
.swatch-quad {
width: 15px;
height: 11px;
border: 1px solid rgba(74, 115, 85, 0.35);
border-radius: 2px;
background: rgba(74, 115, 85, 0.1);
}
.swatch-pareto {
width: 20px;
border-top: 2px dotted #6b7263;
}
.provider-dot {
width: 9px;
height: 9px;
flex: 0 0 auto;
border-radius: 50%;
background: var(--dot-color);
}
.provider-logo {
display: block;
width: 16px;
height: 16px;
flex: 0 0 auto;
}
.plot-wrap {
position: relative;
padding: 0 10px;
}
#plot {
display: block;
width: 100%;
height: auto;
}
.tooltip {
position: fixed;
z-index: 20;
max-width: 320px;
padding: 12px 14px;
border: 1px solid var(--edge-strong);
border-radius: 9px;
color: var(--ink);
background: var(--panel-strong);
box-shadow: var(--shadow);
font-size: 14px;
line-height: 1.5;
pointer-events: none;
}
.tooltip .tt-name {
display: flex;
align-items: center;
gap: 7px;
margin-bottom: 2px;
font-weight: 750;
font-size: 15px;
}
.tt-chip {
padding: 1px 6px;
border: 1px solid #bed0c0;
border-radius: 5px;
color: var(--primary-dark);
background: var(--primary-soft);
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
}
.tooltip .tt-provider {
color: var(--ink-soft);
margin-bottom: 6px;
}
.tooltip .tt-row {
display: flex;
justify-content: space-between;
gap: 18px;
}
.tooltip .tt-row span:first-child { color: var(--ink-faint); }
.tooltip .tt-row span:last-child {
font-family: var(--mono);
font-size: 12.5px;
font-weight: 600;
}
.tooltip .tt-or {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed var(--edge);
color: var(--ink-faint);
font-family: var(--mono);
font-size: 12px;
word-break: break-all;
}
.frontier-foot {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20px;
padding: 12px 20px;
border-top: 1px solid var(--edge);
color: var(--ink-soft);
background: var(--panel);
font-size: 12px;
}
.tracks { margin: 0; flex: 0 0 auto; }
.method {
margin: 0;
max-width: 640px;
text-align: right;
}
.note {
margin: 16px auto 0;
max-width: 900px;
color: var(--ink-faint);
font-size: 12px;
text-align: center;
}
.note a { color: var(--primary-dark); font-weight: 650; text-decoration: none; }
.note a:hover { text-decoration: underline; }
@media (max-width: 860px) {
.frontier-head { flex-direction: column; gap: 12px; }
.head-controls { padding-top: 0; }
.frontier-foot { flex-direction: column; gap: 8px; }
.method { text-align: left; max-width: none; }
}
@media (max-width: 640px) {
.page { width: min(100% - 20px, 1180px); padding-top: 26px; }
.hero { display: block; }
.actions { margin: 20px 0 0 56px; }
.tab { font-size: 11px; padding: 6px 4px; }
.tab small { display: none; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { transition: none !important; }
}
</style>
</head>
<body>
<main class="page">
<header class="hero">
<div>
<div class="brand">
<div class="brand-mark" aria-hidden="true">A</div>
<div>
<p class="eyebrow">AutoMedBench Lite</p>
<h1>Efficiency Frontier</h1>
</div>
</div>
</div>
<nav class="actions" aria-label="Related links">
<a class="button" href="index.html">Leaderboard</a>
<a class="button" href="https://openrouter.ai/models" target="_blank" rel="noreferrer">OpenRouter</a>
</nav>
</header>
<section class="frontier" aria-labelledby="chart-title">
<div class="tabbar" role="tablist" aria-label="Efficiency metric">
<button class="tab" role="tab" data-metric="cost" aria-selected="true"><small>Overall vs.</small>Cost per Task</button>
<button class="tab" role="tab" data-metric="time" aria-selected="false"><small>Overall vs.</small>Time per Task</button>
<button class="tab" role="tab" data-metric="tokens" aria-selected="false"><small>Overall vs.</small>Tokens per Task</button>
<button class="tab" role="tab" data-metric="turns" aria-selected="false"><small>Overall vs.</small>Turns per Task</button>
</div>
<div class="frontier-head">
<div class="frontier-copy">
<p class="kicker">7-track average</p>
<h2 id="chart-title">Overall score vs. Cost per Task</h2>
<p class="frontier-subtitle" id="chart-subtitle"></p>
</div>
<div class="head-controls">
<span class="count-pill" id="count-pill">28 of 28 models</span>
<button class="oss-toggle" id="oss-toggle" type="button" aria-pressed="false" title="Show only open-weight models"><span class="glyph">&lt;/&gt;</span>Open Source Only</button>
</div>
</div>
<div class="legend" id="legend">
<span class="legend-item"><i class="swatch-quad" aria-hidden="true"></i>Most attractive quadrant</span>
<span class="legend-item"><i class="swatch-pareto" aria-hidden="true"></i>Pareto line</span>
<span class="legend-sep" aria-hidden="true"></span>
</div>
<div class="plot-wrap">
<svg id="plot" viewBox="0 0 1120 560" role="img" aria-label="Scatter plot of overall score against per-task efficiency metric"></svg>
<div class="tooltip" id="tooltip" hidden></div>
</div>
<footer class="frontier-foot">
<p class="tracks">Classification &middot; Synthesis &middot; Detection &middot; Segmentation &middot; VQA &middot; Report Generation &middot; Enhancement</p>
<p class="method" id="method-note"></p>
</footer>
</section>
</main>
<button class="button theme-fab" id="theme-toggle" type="button" aria-pressed="false">&#9790; Dark mode</button>
<script>
(() => {
const MODELS = [
{ name: "Claude Opus 4.8", provider: "Anthropic", open: false, overall: 81.94, cost: 2.14, time: 14.5, turns: 18.7, slug: "anthropic/claude-opus-4.8", pin: 5, pout: 25 },
{ name: "Claude Opus 4.6", provider: "Anthropic", open: false, overall: 77.23, cost: 6.57, time: 18.2, turns: 18.3, slug: "anthropic/claude-opus-4.6", pin: 5, pout: 25 },
{ name: "Gemini 3.1 Pro", provider: "Google", open: false, overall: 75.21, cost: 3.85, time: 18.0, turns: 18.1, slug: "google/gemini-3.1-pro-preview", pin: 2, pout: 12 },
{ name: "GPT-5.6 Sol", provider: "OpenAI", open: false, overall: 75.05, cost: 0.95, time: 10.8, turns: 11.7, slug: "openai/gpt-5.6-sol", pin: 5, pout: 30 },
{ name: "Claude Sonnet 5", provider: "Anthropic", open: false, overall: 75.04, cost: 2.29, time: 15.1, turns: 30.5, slug: "anthropic/claude-sonnet-5", pin: 2, pout: 10 },
{ name: "GPT-5.5", provider: "OpenAI", open: false, overall: 74.14, cost: 1.90, time: 20.5, turns: 15.4, slug: "openai/gpt-5.5", pin: 5, pout: 30 },
{ name: "GLM-5", provider: "Z.ai", open: true, overall: 72.86, cost: 1.29, time: 17.8, turns: 31.3, slug: "z-ai/glm-5", pin: 0.95, pout: 2.55 },
{ name: "DeepSeek V4 Pro", provider: "DeepSeek", open: true, overall: 72.43, cost: 0.30, time: 18.3, turns: 24.6, slug: "deepseek/deepseek-v4-pro", pin: 0.435, pout: 0.87 },
{ name: "Qwen3.5-397B", provider: "Alibaba Cloud", open: true, overall: 69.87, cost: 1.01, time: 20.8, turns: 25.1, slug: "qwen/qwen3.5-397b-a17b", pin: 0.39, pout: 2.34 },
{ name: "GPT-5.6 Terra", provider: "OpenAI", open: false, overall: 68.29, cost: 0.45, time: 10.7, turns: 10.2, slug: "openai/gpt-5.6-terra", pin: 1, pout: 6 },
{ name: "MiniMax M2.5", provider: "MiniMax", open: true, overall: 66.31, cost: 1.36, time: 20.6, turns: 28.5, slug: "minimax/minimax-m2.5", pin: 0.15, pout: 0.9 },
{ name: "GPT-5.4", provider: "OpenAI", open: false, overall: 66.09, cost: 2.36, time: 16.7, turns: 10.4, slug: "openai/gpt-5.4", pin: 2.5, pout: 15 },
{ name: "Qwen3.6-27B", provider: "Alibaba Cloud", open: true, overall: 65.29, cost: 0, time: 23.0, turns: 32.0, slug: "qwen/qwen3.6-27b", pin: 0.3, pout: 2, local: true },
{ name: "GPT-5.6 Luna", provider: "OpenAI", open: false, overall: 63.98, cost: 0.21, time: 8.9, turns: 13.4, slug: "openai/gpt-5.6-luna", pin: 0.1, pout: 0.6 },
{ name: "Qwen3.5-35B-A3B", provider: "Alibaba Cloud", open: true, overall: 57.94, cost: 0, time: 28.4, turns: 30.5, slug: "qwen/qwen3.5-35b-a3b", pin: 0.14, pout: 1, local: true },
{ name: "GLM-5.2", provider: "Z.ai", open: true, overall: 56.32, cost: 0.73, time: 15.7, turns: 28.6, slug: "z-ai/glm-5.2", pin: 1.19, pout: 3.74 },
{ name: "Nemotron 3.5 Lightning 30B-A3B", provider: "NVIDIA", open: true, overall: 39.65, cost: 0.09, time: 14.3, turns: 42.5, slug: "nvidia/nemotron-3.5-lightning", pin: 0.1, pout: 0.25 },
{ name: "Gemma 4 26B-A4B", provider: "Google", open: true, overall: 33.89, cost: 0.04885072, time: 17.3, turns: 14.8, tokens: 384651, slug: "google/gemma-4-26b-a4b-it", pin: 0.12, pout: 0.40 },
{ name: "Gemma 4 31B", provider: "Google", open: true, overall: 30.43, cost: 0.01653110, time: 27.9, turns: 8.1, tokens: 154003, slug: "google/gemma-4-31b-it", pin: 0.10, pout: 0.34 },
{ name: "GPT-OSS-120B", provider: "OpenAI", open: true, overall: 24.33, cost: 0, time: 4.4, turns: 9.4, slug: "openai/gpt-oss-120b", pin: 0.037, pout: 0.17 },
{ name: "Gemma 4 12B", provider: "Google", open: true, overall: 22.43, cost: 0.0239965, time: 24.0, turns: 17.1, tokens: 455099, slug: null, pin: 0.05, pout: 0.15, proxy: true },
{ name: "Gemma 4 E4B", provider: "Google", open: true, overall: 21.94, cost: 0.007112, time: 7.4, turns: 6.8, tokens: 110196, slug: null, pin: 0.06, pout: 0.12, proxy: true },
{ name: "Qwen3.5-9B", provider: "Alibaba Cloud", open: true, overall: 19.87, cost: 0, time: 19.3, turns: 52.4, slug: "qwen/qwen3.5-9b", pin: 0.1, pout: 0.15, local: true },
{ name: "Nemotron Nano 9B v2", provider: "NVIDIA", open: true, overall: 18.68, cost: 0, time: 12.0, turns: 22.8, slug: "nvidia/nemotron-nano-9b-v2:free", pin: 0, pout: 0, local: true },
{ name: "Nemotron 3 Nano 30B-A3B", provider: "NVIDIA", open: true, overall: 18.01, cost: 0.01507026, time: 2.5, turns: 16.3, tokens: 249310, slug: "nvidia/nemotron-3-nano-30b-a3b", pin: 0.05, pout: 0.20 },
{ name: "GPT-OSS-20B", provider: "OpenAI", open: true, overall: 17.63, cost: 0, time: 2.7, turns: 5.1, slug: "openai/gpt-oss-20b", pin: 0.03, pout: 0.13 },
{ name: "Nemotron 3 Nano 4B", provider: "NVIDIA", open: true, overall: 9.06, cost: 0.00673588, time: 5.9, turns: 9.7, tokens: 118117, slug: null, pin: 0.05, pout: 0.20, proxy: true },
{ name: "Gemma 4 E2B", provider: "Google", open: true, overall: 9.02, cost: 0.02203914, time: 4.3, turns: 18.9, tokens: 359541, slug: null, pin: 0.06, pout: 0.12, proxy: true },
{ name: "Mistral 7B Instruct v0.3", provider: "Mistral AI", open: true, overall: 6.80, cost: 0, time: 1.5, turns: 2.7, tokens: 42507, slug: null, pin: null, pout: null },
{ name: "Qwen3.5-4B", provider: "Alibaba Cloud", open: true, overall: 6.05, cost: 0, time: 13.6, turns: 72.6, slug: null, pin: null, pout: null, local: true }
];
const blended = (m) => 0.75 * m.pin + 0.25 * m.pout; // $/M at 3:1 input:output
// Metered models: tokens from recorded cost at OpenRouter prices.
for (const m of MODELS) {
m.tokens = m.tokens ?? ((m.cost > 0 && m.pin != null) ? (m.cost / blended(m)) * 1e6 : null);
}
// Local open models: estimate tokens from the median tokens-per-turn of metered
// models, then price at OpenRouter API rates so they still count in the cost view.
const perTurn = MODELS.filter((m) => m.tokens).map((m) => m.tokens / m.turns).sort((a, b) => a - b);
const MED_TOK_PER_TURN = perTurn[Math.floor(perTurn.length / 2)];
for (const m of MODELS) {
if (m.tokens == null) m.tokens = MED_TOK_PER_TURN * m.turns;
m.costEst = (m.cost === 0 && m.pin) ? (m.tokens / 1e6) * blended(m) : 0;
}
const effCost = (m) => (m.cost > 0 ? m.cost : m.costEst);
// Each provider's flagship (highest Overall) always shows its label.
const FLAGSHIPS = new Set();
{
const bestByProvider = new Map();
for (const m of MODELS) {
const cur = bestByProvider.get(m.provider);
if (!cur || m.overall > cur.overall) bestByProvider.set(m.provider, m);
}
for (const m of bestByProvider.values()) FLAGSHIPS.add(m.name);
}
const modelColors = new Map([
["claude opus 4.8", "#b98272"], ["claude opus 4.6", "#c0917f"], ["claude sonnet 5", "#c9a08f"],
["gemini 3.1 pro", "#829b84"], ["gpt-5.6 sol", "#788b84"], ["gpt-5.5", "#87968f"],
["gpt-5.6 terra", "#96a29a"], ["gpt-5.4", "#8f9188"], ["gpt-5.6 luna", "#a2aaa3"],
["gpt-oss-120b", "#777f79"], ["glm-5", "#7d95a7"], ["glm-5.2", "#91a5b2"],
["deepseek v4 pro", "#858ca5"], ["qwen3.5-397b", "#b8896c"], ["qwen3.6-27b", "#bd9477"],
["qwen3.5-35b-a3b", "#c2a083"], ["qwen3.5-9b", "#aa8f7c"], ["qwen3.5-4b", "#9e8574"],
["minimax m2.5", "#9d8794"]
]);
const familyPalette = [
{ match: /anthropic|claude/i, color: "#bd907d" },
{ match: /openai|chatgpt|gpt-/i, color: "#858e87" },
{ match: /google|gemini/i, color: "#8da28e" },
{ match: /z\.ai|glm-/i, color: "#8ca1af" },
{ match: /deepseek/i, color: "#8d93a8" },
{ match: /alibaba|qwen/i, color: "#b99a7c" },
{ match: /minimax/i, color: "#a18d99" }
];
const fallbackColors = ["#9a8fa2", "#899da0", "#a29b84", "#a78e86", "#8f99a6"];
const modelColor = (name, provider) => {
const exact = modelColors.get(name.toLowerCase());
if (exact) return exact;
const identity = `${name} ${provider}`;
const family = familyPalette.find(({ match }) => match.test(identity));
if (family) return family.color;
const hash = Array.from(identity).reduce((t, c) => (t * 31 + c.charCodeAt(0)) >>> 0, 0);
return fallbackColors[hash % fallbackColors.length];
};
const fmtTok = (v) => v >= 1e6 ? (v / 1e6).toFixed(2) + "M" : Math.round(v / 1e3) + "K";
const METRICS = {
cost: {
title: "Overall score vs. Cost per Task",
sub: "AutoMedBench Lite Overall \u00b7 average cost (USD) per task at OpenRouter list prices",
xTitle: "Cost per Task (USD, log scale)",
dmin: 0.006, dmax: 9,
ticks: [[0.01, "$0*"], [0.02, "$0.02"], [0.05, "$0.05"], [0.1, "$0.10"], [0.2, "$0.20"], [0.5, "$0.50"], [1, "$1"], [2, "$2"], [4, "$4"], [8, "$8"]],
value: (m) => (effCost(m) > 0 ? effCost(m) : 0.01),
shown: () => true,
fmt: (m) => (m.cost > 0 ? "$" + m.cost.toFixed(2) : m.costEst > 0 ? "~$" + m.costEst.toFixed(2) + " (est. API)" : "$0"),
note: "Price standard: OpenRouter API rates checked Aug 12, 2026. Exact listing unavailable \u2192 comparable proxy rate."
},
time: {
title: "Overall score vs. Time per Task",
sub: "AutoMedBench Lite Overall \u00b7 average wall-clock minutes per task",
xTitle: "Time per Task (minutes, log scale)",
dmin: 3, dmax: 34,
ticks: [[5, "5 min"], [10, "10"], [15, "15"], [20, "20"], [30, "30 min"]],
value: (m) => m.time,
shown: () => true,
fmt: (m) => m.time.toFixed(1) + " min",
note: "Average wall-clock minutes per task."
},
tokens: {
title: "Overall score vs. Tokens per Task",
sub: "AutoMedBench Lite Overall \u00b7 estimated total tokens per task (input + output)",
xTitle: "Estimated tokens per Task (log scale)",
dmin: 8e4, dmax: 6e6,
ticks: [[1e5, "100K"], [2e5, "200K"], [5e5, "500K"], [1e6, "1M"], [2e6, "2M"], [4e6, "4M"]],
value: (m) => m.tokens,
shown: () => true,
fmt: (m) => "~" + fmtTok(m.tokens),
note: "Tokens per task use recorded input plus output tokens when available; remaining models use the displayed estimate method."
},
turns: {
title: "Overall score vs. Turns per Task",
sub: "AutoMedBench Lite Overall \u00b7 average conversational turns per task",
xTitle: "Turns per Task (log scale)",
dmin: 6, dmax: 90,
ticks: [[10, "10"], [15, "15"], [20, "20"], [30, "30"], [50, "50"], [70, "70"]],
value: (m) => m.turns,
shown: () => true,
fmt: (m) => m.turns.toFixed(1),
note: "Average conversational turns per task."
}
};
const SVGNS = "http://www.w3.org/2000/svg";
const W = 1120, H = 560, M = { t: 26, r: 26, b: 64, l: 66 };
const PW = W - M.l - M.r, PH = H - M.t - M.b;
const YMIN = 0, YMAX = 90, YTICKS = [0, 15, 30, 45, 60, 75, 90];
const QY = 55;
const svg = document.getElementById("plot");
const tooltip = document.getElementById("tooltip");
const countPill = document.getElementById("count-pill");
const ossToggle = document.getElementById("oss-toggle");
const tabs = Array.from(document.querySelectorAll(".tab"));
const titleEl = document.getElementById("chart-title");
const subEl = document.getElementById("chart-subtitle");
const noteEl = document.getElementById("method-note");
let activeMetric = "cost";
let openOnly = false;
let activeProvider = null;
const PALETTES = {
light: {
grid: "rgba(115,122,109,0.2)", base: "rgba(77,87,75,0.36)", tickText: "#9aa093",
vgrid: "rgba(115,122,109,0.12)", tickMark: "#c8c0ad",
quadFill: "rgba(74,115,85,0.09)", quadLine: "rgba(74,115,85,0.25)",
pareto: "#6b7263", label: "#3b4538", pointStroke: "#fffdf8"
},
dark: {
grid: "rgba(170,176,159,0.16)", base: "rgba(200,205,190,0.35)", tickText: "#8f9484",
vgrid: "rgba(170,176,159,0.10)", tickMark: "#4a5040",
quadFill: "rgba(130,169,141,0.10)", quadLine: "rgba(130,169,141,0.32)",
pareto: "#aab09f", label: "#e9e5d7", pointStroke: "#2b3126"
}
};
let stored = null;
try { stored = localStorage.getItem("amb-theme"); } catch (_) {}
let theme = stored
|| (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
const pal = () => PALETTES[theme] || PALETTES.light;
const hashParams = new URLSearchParams(location.hash.replace(/^#/, ""));
if (METRICS[hashParams.get("metric")]) activeMetric = hashParams.get("metric");
if (hashParams.get("open") === "1") openOnly = true;
if (hashParams.get("provider")) activeProvider = hashParams.get("provider");
if (hashParams.get("theme") === "dark" || hashParams.get("theme") === "light") theme = hashParams.get("theme");
const el = (tag, attrs = {}) => {
const node = document.createElementNS(SVGNS, tag);
for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);
return node;
};
const text = (x, y, content, attrs = {}) => {
const node = el("text", { x, y, ...attrs });
node.textContent = content;
return node;
};
function render() {
const cfg = METRICS[activeMetric];
const P = pal();
titleEl.textContent = cfg.title;
subEl.textContent = cfg.sub;
noteEl.textContent = cfg.note;
const shown = MODELS
.filter((m) => (openOnly ? m.open : true))
.filter((m) => (activeProvider ? m.provider === activeProvider : true))
.filter(cfg.shown);
countPill.textContent = `${shown.length} of ${MODELS.length} models`;
const xS = (v) => M.l + ((Math.log10(v) - Math.log10(cfg.dmin)) / (Math.log10(cfg.dmax) - Math.log10(cfg.dmin))) * PW;
const yS = (s) => M.t + ((YMAX - s) / (YMAX - YMIN)) * PH;
svg.replaceChildren();
const qx = Math.sqrt(cfg.dmin * cfg.dmax);
svg.append(el("rect", {
x: M.l, y: M.t, width: xS(qx) - M.l, height: yS(QY) - M.t,
fill: P.quadFill
}));
svg.append(el("line", { x1: xS(qx), y1: M.t, x2: xS(qx), y2: yS(QY), stroke: P.quadLine, "stroke-dasharray": "3 4" }));
svg.append(el("line", { x1: M.l, y1: yS(QY), x2: xS(qx), y2: yS(QY), stroke: P.quadLine, "stroke-dasharray": "3 4" }));
for (const t of YTICKS) {
const y = yS(t);
svg.append(el("line", {
x1: M.l, y1: y, x2: M.l + PW, y2: y,
stroke: t === 0 ? P.base : P.grid,
"stroke-dasharray": t === 0 ? "none" : "4 4"
}));
svg.append(text(M.l - 10, y + 4, String(t), { "text-anchor": "end", fill: P.tickText, "font-size": "11.5", "font-family": "SFMono-Regular, Consolas, monospace" }));
}
for (const [v, label] of cfg.ticks) {
const x = xS(v);
svg.append(el("line", { x1: x, y1: M.t, x2: x, y2: M.t + PH, stroke: P.vgrid }));
svg.append(el("line", { x1: x, y1: M.t + PH, x2: x, y2: M.t + PH + 5, stroke: P.tickMark }));
svg.append(text(x, M.t + PH + 20, label, { "text-anchor": "middle", fill: P.tickText, "font-size": "11.5", "font-family": "SFMono-Regular, Consolas, monospace" }));
}
const yTitle = text(18, M.t + PH / 2, "AutoMedBench Lite Overall Score", {
"text-anchor": "middle", fill: P.tickText, "font-size": "11.5", "font-weight": "750",
"letter-spacing": "0.08em", transform: `rotate(-90 18 ${M.t + PH / 2})`
});
yTitle.style.textTransform = "uppercase";
svg.append(yTitle);
const xTitle = text(M.l + PW / 2, H - 10, cfg.xTitle, {
"text-anchor": "middle", fill: P.tickText, "font-size": "11.5", "font-weight": "750", "letter-spacing": "0.08em"
});
xTitle.style.textTransform = "uppercase";
svg.append(xTitle);
const pts = shown.map((m) => ({
m, x: xS(cfg.value(m)), y: yS(m.overall), color: modelColor(m.name, m.provider)
}));
const sorted = [...pts].sort((a, b) => a.x - b.x || b.m.overall - a.m.overall);
const frontier = [];
let best = -Infinity;
for (const p of sorted) {
if (p.m.overall > best) { frontier.push(p); best = p.m.overall; }
}
if (frontier.length > 1) {
const d = frontier.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
svg.append(el("path", { d, fill: "none", stroke: P.pareto, "stroke-width": "2", "stroke-dasharray": "1 6", "stroke-linecap": "round", opacity: "0.85" }));
}
// Greedy labeling: flagships always show (nudged to a free slot);
// other overlapped labels hide and appear in the hover tooltip instead.
const labels = [];
const placedBoxes = [];
const byScore = [...pts].sort((a, b) =>
(FLAGSHIPS.has(b.m.name) - FLAGSHIPS.has(a.m.name)) || b.m.overall - a.m.overall
);
for (const p of byScore) {
const flagship = FLAGSHIPS.has(p.m.name);
const w = 10 + p.m.name.length * 7.4;
let anchor = "start";
let lx = p.x + 12;
let x0 = lx, x1 = lx + w;
if (x1 > W - 4) {
if (!flagship) continue;
anchor = "end";
lx = p.x - 12;
x0 = lx - w; x1 = lx;
}
const fits = (ly) =>
ly >= M.t + 10 && ly <= M.t + PH - 2 &&
!placedBoxes.some((b) => Math.abs(b.y - ly) < 16 && x0 < b.x1 && b.x0 < x1) &&
!pts.some((q) => q !== p && q.x > x0 - 9 && q.x < x1 + 9 && Math.abs(q.y - ly + 4) < 12);
let ly = null;
for (const dy of flagship ? [0, -16, 16, -30, 30, -44, 44] : [0]) {
if (fits(p.y + 4.5 + dy)) { ly = p.y + 4.5 + dy; break; }
}
if (ly == null) {
if (!flagship) continue;
ly = Math.max(M.t + 10, Math.min(M.t + PH - 2, p.y + 4.5));
}
placedBoxes.push({ y: ly, x0, x1 });
labels.push({ p, x: lx, y: ly, anchor });
}
for (const p of pts) {
const g = el("g", { cursor: "pointer" });
g.append(el("circle", { cx: p.x, cy: p.y, r: 12, fill: "transparent" }));
g.append(el("circle", { cx: p.x, cy: p.y, r: 7, fill: p.color, stroke: P.pointStroke, "stroke-width": "1.6" }));
g.addEventListener("mouseenter", (e) => showTip(e, p.m));
g.addEventListener("mousemove", moveTip);
g.addEventListener("mouseleave", hideTip);
svg.append(g);
}
for (const l of labels) {
if (Math.abs(l.y - (l.p.y + 4.5)) > 8) {
svg.append(el("line", {
x1: l.p.x + (l.anchor === "start" ? 8 : -8), y1: l.p.y,
x2: l.x + (l.anchor === "start" ? -2 : 2), y2: l.y - 4,
stroke: P.tickMark, "stroke-width": "1"
}));
}
svg.append(text(l.x, l.y, l.p.m.name, {
"text-anchor": l.anchor,
fill: P.label, "font-size": "13.5", "font-weight": "600", "letter-spacing": "-0.01em",
stroke: P.pointStroke, "stroke-width": "3", "paint-order": "stroke", "stroke-linejoin": "round"
}));
}
}
function tipHtml(m) {
const rows = [
["Overall", m.overall.toFixed(2)],
["Cost / task", m.cost > 0 ? "$" + m.cost.toFixed(2) : m.costEst > 0 ? "~$" + m.costEst.toFixed(2) + " (est. API)" : "$0"],
["Time / task", m.time.toFixed(1) + " min"],
["Tokens / task", "~" + fmtTok(m.tokens)],
["Turns / task", m.turns.toFixed(1)]
].map(([k, v]) => `<div class="tt-row"><span>${k}</span><span>${v}</span></div>`).join("");
const or = (m.pin != null && m.pin > 0)
? `$${m.pin}/M in \u00b7 $${m.pout}/M out${m.proxy ? " \u00b7 proxy" : ""}`
: "$0";
return `<div class="tt-name">${m.name}${m.open ? '<span class="tt-chip">open source</span>' : ""}</div>
<div class="tt-provider">${m.provider}</div>${rows}<div class="tt-or">${or}</div>`;
}
function showTip(e, m) {
tooltip.innerHTML = tipHtml(m);
tooltip.hidden = false;
moveTip(e);
}
function moveTip(e) {
const pad = 14;
const r = tooltip.getBoundingClientRect();
let x = e.clientX + pad, y = e.clientY + pad;
if (x + r.width > window.innerWidth - 8) x = e.clientX - r.width - pad;
if (y + r.height > window.innerHeight - 8) y = e.clientY - r.height - pad;
tooltip.style.left = x + "px";
tooltip.style.top = y + "px";
}
function hideTip() { tooltip.hidden = true; }
const LOGOS = {"Anthropic": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+QW50aHJvcGljPC90aXRsZT48cGF0aCBkPSJNMTMuODI3IDMuNTJoMy42MDNMMjQgMjBoLTMuNjAzbC02LjU3LTE2LjQ4em0tNy4yNTggMGgzLjc2N0wxNi45MDYgMjBoLTMuNjc0bC0xLjM0My0zLjQ2MUg1LjAxN2wtMS4zNDQgMy40NkgwTDYuNTcgMy41MjJ6bTQuMTMyIDkuOTU5TDguNDUzIDcuNjg3IDYuMjA1IDEzLjQ4SDEwLjd6Ij48L3BhdGg+PC9zdmc+Cg==", "OpenAI": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+T3BlbkFJPC90aXRsZT48cGF0aCBkPSJNOS4yMDUgOC42NTh2LTIuMjZjMC0uMTkuMDcyLS4zMzMuMjM4LS40MjhsNC41NDMtMi42MTZjLjYxOS0uMzU3IDEuMzU2LS41MjMgMi4xMTctLjUyMyAyLjg1NCAwIDQuNjYyIDIuMjEyIDQuNjYyIDQuNTY2IDAgLjE2NyAwIC4zNTctLjAyNC41NDdsLTQuNzEtMi43NTlhLjc5Ny43OTcgMCAwMC0uODU2IDBsLTUuOTcgMy40NzN6bTEwLjYwOSA4LjhWMTIuMDZjMC0uMzMzLS4xNDMtLjU3LS40MjktLjczN2wtNS45Ny0zLjQ3MyAxLjk1LTEuMTE4YS40MzMuNDMzIDAgMDEuNDc2IDBsNC41NDMgMi42MTdjMS4zMDkuNzYgMi4xODkgMi4zNzggMi4xODkgMy45NDggMCAxLjgwOC0xLjA3IDMuNDczLTIuNzYgNC4xNjN6TTcuODAyIDEyLjcwM2wtMS45NS0xLjE0MmMtLjE2Ny0uMDk1LS4yMzktLjIzOC0uMjM5LS40MjhWNS44OTljMC0yLjU0NSAxLjk1LTQuNDcyIDQuNTkxLTQuNDcyIDEgMCAxLjkyNy4zMzMgMi43MTIuOTI4TDguMjMgNS4wNjdjLS4yODUuMTY2LS40MjguNDA0LS40MjguNzM3djYuODk4ek0xMiAxNS4xMjhsLTIuNzk1LTEuNTd2LTMuMzNMMTIgOC42NThsMi43OTUgMS41N3YzLjMzTDEyIDE1LjEyOHptMS43OTYgNy4yM2MtMSAwLTEuOTI3LS4zMzItMi43MTItLjkyN2w0LjY4Ni0yLjcxMmMuMjg1LS4xNjYuNDI4LS40MDQuNDI4LS43Mzd2LTYuODk4bDEuOTc0IDEuMTQyYy4xNjcuMDk1LjIzOC4yMzguMjM4LjQyOHY1LjIzM2MwIDIuNTQ1LTEuOTc0IDQuNDcyLTQuNjE0IDQuNDcyem0tNS42MzctNS4zMDNsLTQuNTQ0LTIuNjE3Yy0xLjMwOC0uNzYxLTIuMTg4LTIuMzc4LTIuMTg4LTMuOTQ4QTQuNDgyIDQuNDgyIDAgMDE0LjIxIDYuMzI3djUuNDIzYzAgLjMzMy4xNDMuNTcxLjQyOC43MzhsNS45NDcgMy40NDktMS45NSAxLjExOGEuNDMyLjQzMiAwIDAxLS40NzYgMHptLS4yNjIgMy45Yy0yLjY4OCAwLTQuNjYyLTIuMDIxLTQuNjYyLTQuNTE5IDAtLjE5LjAyNC0uMzguMDQ3LS41N2w0LjY4NiAyLjcxYy4yODYuMTY3LjU3MS4xNjcuODU2IDBsNS45Ny0zLjQ0OHYyLjI2YzAgLjE5LS4wNy4zMzMtLjIzNy40MjhsLTQuNTQzIDIuNjE2Yy0uNjE5LjM1Ny0xLjM1Ni41MjMtMi4xMTcuNTIzem01Ljg5OSAyLjgzYTUuOTQ3IDUuOTQ3IDAgMDA1LjgyNy00Ljc1NkMyMi4yODcgMTguMzM5IDI0IDE1Ljg0IDI0IDEzLjI5NmMwLTEuNjY1LS43MTMtMy4yODItMS45OTgtNC40NDguMTE5LS41LjE5LS45OTkuMTktMS40OTggMC0zLjQwMS0yLjc1OS01Ljk0Ny01Ljk0Ni01Ljk0Ny0uNjQyIDAtMS4yNi4wOTUtMS44OC4zMUE1Ljk2MiA1Ljk2MiAwIDAwMTAuMjA1IDBhNS45NDcgNS45NDcgMCAwMC01LjgyNyA0Ljc1N0MxLjcxMyA1LjQ0NyAwIDcuOTQ1IDAgMTAuNDljMCAxLjY2Ni43MTMgMy4yODMgMS45OTggNC40NDgtLjExOS41LS4xOSAxLS4xOSAxLjQ5OSAwIDMuNDAxIDIuNzU5IDUuOTQ2IDUuOTQ2IDUuOTQ2LjY0MiAwIDEuMjYtLjA5NSAxLjg4LS4zMDlhNS45NiA1Ljk2IDAgMDA0LjE2MiAxLjcxM3oiPjwvcGF0aD48L3N2Zz4K", "Google": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2VtaW5pPC90aXRsZT48cGF0aCBkPSJNMjAuNjE2IDEwLjgzNWExNC4xNDcgMTQuMTQ3IDAgMDEtNC40NS0zLjAwMSAxNC4xMTEgMTQuMTExIDAgMDEtMy42NzgtNi40NTIuNTAzLjUwMyAwIDAwLS45NzUgMCAxNC4xMzQgMTQuMTM0IDAgMDEtMy42NzkgNi40NTIgMTQuMTU1IDE0LjE1NSAwIDAxLTQuNDUgMy4wMDFjLS42NS4yOC0xLjMxOC41MDUtMi4wMDIuNjc4YS41MDIuNTAyIDAgMDAwIC45NzVjLjY4NC4xNzIgMS4zNS4zOTcgMi4wMDIuNjc3YTE0LjE0NyAxNC4xNDcgMCAwMTQuNDUgMy4wMDEgMTQuMTEyIDE0LjExMiAwIDAxMy42NzkgNi40NTMuNTAyLjUwMiAwIDAwLjk3NSAwYy4xNzItLjY4NS4zOTctMS4zNTEuNjc3LTIuMDAzYTE0LjE0NSAxNC4xNDUgMCAwMTMuMDAxLTQuNDUgMTQuMTEzIDE0LjExMyAwIDAxNi40NTMtMy42NzguNTAzLjUwMyAwIDAwMC0uOTc1IDEzLjI0NSAxMy4yNDUgMCAwMS0yLjAwMy0uNjc4eiI+PC9wYXRoPjwvc3ZnPgo=", "Z.ai": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+Wi5haTwvdGl0bGU+PHBhdGggZD0iTTEyLjEwNSAyTDkuOTI3IDQuOTUzSC42NTNMMi44MyAyaDkuMjc2ek0yMy4yNTQgMTkuMDQ4TDIxLjA3OCAyMmgtOS4yNDJsMi4xNzQtMi45NTJoOS4yNDR6TTI0IDJMOS4yNjQgMjJIMEwxNC43MzYgMkgyNHoiPjwvcGF0aD48L3N2Zz4K", "DeepSeek": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+RGVlcFNlZWs8L3RpdGxlPjxwYXRoIGQ9Ik0yMy43NDggNC42NTFjLS4yNTQtLjEyNC0uMzY0LjExMy0uNTEyLjIzMy0uMDUxLjA0LS4wOTQuMDktLjEzNy4xMzctLjM3Mi4zOTctLjgwNi42NTctMS4zNzMuNjI2LS44MjktLjA0Ni0xLjUzNy4yMTQtMi4xNjMuODQ4LS4xMzMtLjc4Mi0uNTc1LTEuMjQ4LTEuMjQ3LTEuNTQ4LS4zNTItLjE1NS0uNzA4LS4zMTEtLjk1NS0uNjUtLjE3Mi0uMjQtLjIxOS0uNTA5LS4zMDUtLjc3NC0uMDU1LS4xNi0uMTEtLjMyMy0uMjkzLS4zNS0uMi0uMDMxLS4yNzguMTM2LS4zNTYuMjc2LS4zMTMuNTcyLS40MzQgMS4yMDItLjQyMiAxLjg0LjAyNyAxLjQzNi42MzMgMi41OCAxLjgzOCAzLjM5My4xMzcuMDk0LjE3Mi4xODcuMTI5LjMyMy0uMDgyLjI4LS4xOC41NTMtLjI2Ni44MzMtLjA1NS4xNzktLjEzNy4yMTgtLjMyOC4xNGE1LjUgNS41IDAgMCAxLTEuNzM3LTEuMTc5Yy0uODU3LS44MjgtMS42MzEtMS43NDMtMi41OTctMi40NmExMiAxMiAwIDAgMC0uNjg5LS40N2MtLjk4NS0uOTU3LjEzLTEuNzQzLjM4Ny0xLjgzNi4yNy0uMDk4LjA5NC0uNDMzLS43NzgtLjQyOC0uODcyLjAwMy0xLjY3LjI5NS0yLjY4Ny42ODVhMyAzIDAgMCAxLS40NjUuMTM2IDkuNiA5LjYgMCAwIDAtMi44ODMtLjEwMWMtMS44ODUuMjEtMy4zOSAxLjEtNC40OTcgMi42MjJDLjA4MiA4Ljc3Ni0uMjMxIDEwLjg1NC4xNTIgMTMuMDJjLjQwMyAyLjI4NCAxLjU2OCA0LjE3NSAzLjM2IDUuNjUzIDEuODU3IDEuNTMzIDMuOTk3IDIuMjg0IDYuNDM4IDIuMTQgMS40ODItLjA4NSAzLjEzMi0uMjg0IDQuOTk0LTEuODYuNDcuMjM0Ljk2Mi4zMjggMS43OC4zOTguNjI5LjA1OCAxLjIzNS0uMDMxIDEuNzA1LS4xMjkuNzM1LS4xNTUuNjg0LS44MzYuNDE4LS45NjEtMi4xNTUtMS4wMDQtMS42ODItLjU5NS0yLjExMi0uOTI2IDEuMDk1LTEuMjk1IDIuNzY4LTMuNTk4IDMuMjg0LTYuNzMzLjA1LS4zNDYuMTE1LS44MzQuMTA4LTEuMTE0LS4wMDQtLjE3MS4wMzUtLjIzOC4yMy0uMjU3YTQuMiA0LjIgMCAwIDAgMS41NDUtLjQ3NWMxLjM5Ny0uNzYzIDEuOTYtMi4wMTYgMi4wOTMtMy41MTcuMDItLjIzLS4wMDQtLjQ2Ny0uMjQ3LS41ODhNMTEuNTggMTguMTY4Yy0yLjA4OC0xLjY0Mi0zLjEwMS0yLjE4My0zLjUyLTIuMTYtLjM5LjAyNC0uMzIuNDcyLS4yMzQuNzYzLjA5LjI4OC4yMDcuNDg3LjM3MS43NC4xMTQuMTY3LjE5Mi40MTYtLjExMy42MDMtLjY3My40MTYtMS44NDItLjE0LTEuODk3LS4xNjgtMS4zNjEtLjgwMS0yLjUtMS44Ni0zLjMwMS0zLjMwNi0uNzc1LTEuMzkzLTEuMjI1LTIuODg4LTEuMjk5LTQuNDgyLS4wMi0uMzg1LjA5NC0uNTIyLjQ3Ny0uNTkyYTQuNyA0LjcgMCAwIDEgMS41My0uMDM4YzIuMTMxLjMxMSAzLjk0NiAxLjI2NCA1LjQ2NyAyLjc3NC44NjguODYgMS41MjUgMS44ODcgMi4yMDIgMi44OS43MiAxLjA2NiAxLjQ5NCAyLjA4MiAyLjQ4IDIuOTE1LjM0OC4yOTEuNjI2LjUxMy44OTIuNjc3LS44MDIuMDktMi4xNC4xMDktMy4wNTUtLjYxNXptMS4wMDEtNi40NGEuMzA2LjMwNiAwIDAgMSAuNDE1LS4yODcuMy4zIDAgMCAxIC4xMTMuMDc0LjMuMyAwIDAgMSAuMDg2LjIxNGMwIC4xNy0uMTM2LjMwNy0uMzA4LjMwN2EuMzAzLjMwMyAwIDAgMS0uMzA2LS4zMDdtMy4xMSAxLjU5NmMtLjIuMDgxLS40LjE1MS0uNTkxLjE2YTEuMjUgMS4yNSAwIDAgMS0uNzk4LS4yNTRjLS4yNzQtLjIzLS40Ny0uMzU4LS41NTEtLjc1OGExLjcgMS43IDAgMCAxIC4wMTUtLjU4OGMuMDctLjMyNy0uMDA3LS41MzctLjIzOC0uNzI3LS4xODgtLjE1Ni0uNDI2LS4xOTktLjY4OS0uMTk5YS42LjYgMCAwIDEtLjI1NC0uMDc4LjI1My4yNTMgMCAwIDEtLjExNC0uMzU4IDEgMSAwIDAgMSAuMTkyLS4yMWMuMzU2LS4yMDIuNzY3LS4xMzYgMS4xNDYuMDE2LjM1Mi4xNDQuNjE4LjQwOCAxLjAwMS43ODIuMzkyLjQ1MS40NjIuNTc2LjY4NS45MTUuMTc2LjI2NC4zMzYuNTM2LjQ0Ni44NDguMDY2LjE5NC0uMDIuMzUzLS4yNS40NSIvPjwvc3ZnPgo=", "Alibaba Cloud": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+UXdlbjwvdGl0bGU+PHBhdGggZD0iTTEyLjYwNCAxLjM0Yy4zOTMuNjkuNzg0IDEuMzgyIDEuMTc0IDIuMDc1YS4xOC4xOCAwIDAwLjE1Ny4wOTFoNS41NTJjLjE3NCAwIC4zMjIuMTEuNDQ2LjMyN2wxLjQ1NCAyLjU3Yy4xOS4zMzcuMjQuNDc4LjAyNC44MzctLjI2LjQzLS41MTMuODY0LS43NiAxLjNsLS4zNjcuNjU4Yy0uMTA2LjE5Ni0uMjIzLjI4LS4wNC41MTJsMi42NTIgNC42MzdjLjE3Mi4zMDEuMTExLjQ5NC0uMDQzLjc3LS40MzcuNzg1LS44ODIgMS41NjQtMS4zMzUgMi4zNC0uMTU5LjI3Mi0uMzUyLjM3NS0uNjguMzctLjc3Ny0uMDE2LTEuNTUyLS4wMS0yLjMyNy4wMTZhLjA5OS4wOTkgMCAwMC0uMDgxLjA1IDU3NS4wOTcgNTc1LjA5NyAwIDAxLTIuNzA1IDQuNzRjLS4xNjkuMjkzLS4zOC4zNjMtLjcyNS4zNjQtLjk5Ny4wMDMtMi4wMDIuMDA0LTMuMDE3LjAwMmEuNTM3LjUzNyAwIDAxLS40NjUtLjI3MWwtMS4zMzUtMi4zMjNhLjA5LjA5IDAgMDAtLjA4My0uMDQ5SDQuOTgyYy0uMjg1LjAzLS41NTMtLjAwMS0uODA1LS4wOTJsLTEuNjAzLTIuNzdhLjU0My41NDMgMCAwMS0uMDAyLS41NGwxLjIwNy0yLjEyYS4xOTguMTk4IDAgMDAwLS4xOTcgNTUwLjk1MSA1NTAuOTUxIDAgMDEtMS44NzUtMy4yNzJsLS43OS0xLjM5NWMtLjE2LS4zMS0uMTczLS40OTYuMDk1LS45NjUuNDY1LS44MTMuOTI3LTEuNjI1IDEuMzg3LTIuNDM2LjEzMi0uMjM0LjMwNC0uMzM0LjU4NC0uMzM1YTMzOC4zIDMzOC4zIDAgMDEyLjU4OS0uMDAxLjEyNC4xMjQgMCAwMC4xMDctLjA2M2wyLjgwNi00Ljg5NWEuNDg4LjQ4OCAwIDAxLjQyMi0uMjQ2Yy41MjQtLjAwMSAxLjA1MyAwIDEuNTgzLS4wMDZMMTEuNzA0IDFjLjM0MS0uMDAzLjcyNC4wMzIuOS4zNHptLTMuNDMyLjQwM2EuMDYuMDYgMCAwMC0uMDUyLjAzTDYuMjU0IDYuNzg4YS4xNTcuMTU3IDAgMDEtLjEzNS4wNzhIMy4yNTNjLS4wNTYgMC0uMDcuMDI1LS4wNDEuMDc0bDUuODEgMTAuMTU2Yy4wMjUuMDQyLjAxMy4wNjItLjAzNC4wNjNsLTIuNzk1LjAxNWEuMjE4LjIxOCAwIDAwLS4yLjExNmwtMS4zMiAyLjMxYy0uMDQ0LjA3OC0uMDIxLjExOC4wNjguMTE4bDUuNzE2LjAwOGMuMDQ2IDAgLjA4LjAyLjEwNC4wNjFsMS40MDMgMi40NTRjLjA0Ni4wODEuMDkyLjA4Mi4xMzkgMGw1LjAwNi04Ljc2Ljc4My0xLjM4MmEuMDU1LjA1NSAwIDAxLjA5NiAwbDEuNDI0IDIuNTNhLjEyMi4xMjIgMCAwMC4xMDcuMDYybDIuNzYzLS4wMmEuMDQuMDQgMCAwMC4wMzUtLjAyLjA0MS4wNDEgMCAwMDAtLjA0bC0yLjktNS4wODZhLjEwOC4xMDggMCAwMTAtLjExM2wuMjkzLS41MDcgMS4xMi0xLjk3N2MuMDI0LS4wNDEuMDEyLS4wNjItLjAzNS0uMDYySDkuMmMtLjA1OSAwLS4wNzMtLjAyNi0uMDQzLS4wNzdsMS40MzQtMi41MDVhLjEwNy4xMDcgMCAwMDAtLjExNEw5LjIyNSAxLjc3NGEuMDYuMDYgMCAwMC0uMDUzLS4wMzF6bTYuMjkgOC4wMmMuMDQ2IDAgLjA1OC4wMi4wMzQuMDZsLS44MzIgMS40NjUtMi42MTMgNC41ODVhLjA1Ni4wNTYgMCAwMS0uMDUuMDI5LjA1OC4wNTggMCAwMS0uMDUtLjAyOUw4LjQ5OCA5Ljg0MWMtLjAyLS4wMzQtLjAxLS4wNTIuMDI4LS4wNTRsLjIxNi0uMDEyIDYuNzIyLS4wMTJ6Ij48L3BhdGg+PC9zdmc+Cg==", "MiniMax": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+TWluaW1heDwvdGl0bGU+PHBhdGggZD0iTTE2LjI3OCAyYzEuMTU2IDAgMi4wOTMuOTI3IDIuMDkzIDIuMDd2MTIuNTAxYS43NC43NCAwIDAwLjc0NC43MDkuNzQuNzQgMCAwMC43NDMtLjcwOVY5LjA5OWEyLjA2IDIuMDYgMCAwMTIuMDcxLTIuMDQ5QTIuMDYgMi4wNiAwIDAxMjQgOS4xdjYuNTYxYS42NDkuNjQ5IDAgMDEtLjY1Mi42NDUuNjQ5LjY0OSAwIDAxLS42NTMtLjY0NVY5LjFhLjc2Mi43NjIgMCAwMC0uNzY2LS43NTguNzYyLjc2MiAwIDAwLS43NjYuNzU4djcuNDcyYTIuMDM3IDIuMDM3IDAgMDEtMi4wNDggMi4wMjYgMi4wMzcgMi4wMzcgMCAwMS0yLjA0OC0yLjAyNnYtMTIuNWEuNzg1Ljc4NSAwIDAwLS43ODgtLjc1My43ODUuNzg1IDAgMDAtLjc4OS43NTJsLS4wMDEgMTUuOTA0QTIuMDM3IDIuMDM3IDAgMDExMy40NDEgMjJhMi4wMzcgMi4wMzcgMCAwMS0yLjA0OC0yLjAyNlYxOC4wNGMwLS4zNTYuMjkyLS42NDUuNjUyLS42NDUuMzYgMCAuNjUyLjI4OS42NTIuNjQ1djEuOTM0YzAgLjI2My4xNDIuNTA2LjM3Mi42MzguMjMuMTMxLjUxNC4xMzEuNzQ0IDBhLjczNC43MzQgMCAwMC4zNzItLjYzOFY0LjA3YzAtMS4xNDMuOTM3LTIuMDcgMi4wOTMtMi4wN3ptLTUuNjc0IDBjMS4xNTYgMCAyLjA5My45MjcgMi4wOTMgMi4wN3YxMS41MjNhLjY0OC42NDggMCAwMS0uNjUyLjY0NS42NDguNjQ4IDAgMDEtLjY1Mi0uNjQ1VjQuMDdhLjc4NS43ODUgMCAwMC0uNzg5LS43OC43ODUuNzg1IDAgMDAtLjc4OS43OHYxNC4wMTNhMi4wNiAyLjA2IDAgMDEtMi4wNyAyLjA0OCAyLjA2IDIuMDYgMCAwMS0yLjA3MS0yLjA0OFY5LjFhLjc2Mi43NjIgMCAwMC0uNzY2LS43NTguNzYyLjc2MiAwIDAwLS43NjYuNzU4djMuOGEyLjA2IDIuMDYgMCAwMS0yLjA3MSAyLjA0OUEyLjA2IDIuMDYgMCAwMTAgMTIuOXYtMS4zNzhjMC0uMzU3LjI5Mi0uNjQ2LjY1Mi0uNjQ2LjM2IDAgLjY1My4yOS42NTMuNjQ2VjEyLjljMCAuNDE4LjM0My43NTcuNzY2Ljc1N3MuNzY2LS4zMzkuNzY2LS43NTdWOS4wOTlhMi4wNiAyLjA2IDAgMDEyLjA3LTIuMDQ4IDIuMDYgMi4wNiAwIDAxMi4wNzEgMi4wNDh2OC45ODRjMCAuNDE5LjM0My43NTguNzY3Ljc1OC40MjMgMCAuNzY2LS4zMzkuNzY2LS43NThWNC4wN2MwLTEuMTQzLjkzNy0yLjA3IDIuMDkzLTIuMDd6Ij48L3BhdGg+PC9zdmc+Cg==", "NVIDIA": "data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjNzM3YTZkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGhlaWdodD0iMWVtIiBzdHlsZT0iZmxleDpub25lO2xpbmUtaGVpZ2h0OjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+TnZpZGlhPC90aXRsZT48cGF0aCBkPSJNMTAuMjEyIDguOTc2VjcuNjJjLjEyNy0uMDEuMjU2LS4wMTcuMzg4LS4wMjEgMy41OTYtLjExNyA1Ljk1NyAzLjE4NCA1Ljk1NyAzLjE4NHMtMi41NDggMy42NDctNS4yODIgMy42NDdhMy4yMjcgMy4yMjcgMCAwMS0xLjA2My0uMTc1di00LjEwOWMxLjQuMTc0IDEuNjgxLjgxMiAyLjUyMyAyLjI1OGwxLjg3My0xLjYyN2E0LjkwNSA0LjkwNSAwIDAwLTMuNjctMS44NDYgNi41OTQgNi41OTQgMCAwMC0uNzI5LjA0NG0wLTQuNDc2djIuMDI1Yy4xMy0uMDEuMjU5LS4wMTkuMzg4LS4wMjQgNS4wMDItLjE3NCA4LjI2MSA0LjIyNiA4LjI2MSA0LjIyNnMtMy43NDMgNC42OS03LjY0MyA0LjY5Yy0uMzM4IDAtLjY3NS0uMDMxLTEuMDA3LS4wOTJ2MS4yNWMuMjc4LjAzOC41NTguMDU3LjgzOC4wNTcgMy42MjkgMCA2LjI1My0xLjkxIDguNzk0LTQuMTY5LjQyMS4zNDcgMi4xNDYgMS4xOTMgMi41MDEgMS41NjQtMi40MTYgMi4wODMtOC4wNDggMy43NjMtMTEuMjQgMy43NjMtLjMwOCAwLS42MDMtLjAyLS44OTQtLjA0OFYxOS41SDI0di0xNUgxMC4yMXptMCA5Ljc1NnYxLjA2OGMtMy4zNTYtLjYxNi00LjI4Ny00LjIxLTQuMjg3LTQuMjFhNy4xNzMgNy4xNzMgMCAwMTQuMjg3LTIuMTM4djEuMTcyaC0uMDA1YTMuMTgyIDMuMTgyIDAgMDAtMi41MDIgMS4xNzhzLjYxNSAyLjI3NiAyLjUwNyAyLjkzMW0tNS45NjEtMy4zYzEuNDM2LTEuOTM1IDMuNjA0LTMuMTQ4IDUuOTYxLTMuMzM2VjYuNTIzQzUuODEgNi44ODcgMiAxMC43MjMgMiAxMC43MjNzMi4xNTggNi40MjcgOC4yMSA3LjAxNXYtMS4xNjZDNS43NyAxNiA0LjI1IDEwLjk1OCA0LjI1IDEwLjk1OGgtLjAwMnoiPjwvcGF0aD48L3N2Zz4K"};
LOGOS.Google = "assets/google.svg";
const legend = document.getElementById("legend");
const providerButtons = [];
for (const provider of ["Anthropic", "OpenAI", "Google", "Z.ai", "DeepSeek", "Alibaba Cloud", "MiniMax", "NVIDIA"]) {
const sample = MODELS.find((m) => m.provider === provider);
const btn = document.createElement("button");
btn.type = "button";
btn.className = "legend-button";
btn.dataset.provider = provider;
btn.setAttribute("aria-pressed", "false");
btn.title = `Show only ${provider} models`;
const dot = document.createElement("i");
dot.className = "provider-dot";
dot.style.setProperty("--dot-color", modelColor(sample.name, sample.provider));
btn.append(dot, document.createTextNode(provider));
if (LOGOS[provider]) {
const logo = document.createElement("img");
logo.className = "provider-logo";
logo.src = LOGOS[provider];
logo.alt = "";
btn.append(logo);
}
btn.addEventListener("click", () => {
activeProvider = activeProvider === provider ? null : provider;
syncControls();
render();
});
legend.append(btn);
providerButtons.push(btn);
}
function syncControls() {
tabs.forEach((t) => t.setAttribute("aria-selected", String(t.dataset.metric === activeMetric)));
ossToggle.setAttribute("aria-pressed", String(openOnly));
providerButtons.forEach((b) => b.setAttribute("aria-pressed", String(b.dataset.provider === activeProvider)));
themeToggle.setAttribute("aria-pressed", String(theme === "dark"));
themeToggle.innerHTML = theme === "dark" ? "&#9788; Light mode" : "&#9790; Dark mode";
document.documentElement.dataset.theme = theme;
}
tabs.forEach((tab) => {
tab.addEventListener("click", () => {
activeMetric = tab.dataset.metric;
syncControls();
render();
});
});
ossToggle.addEventListener("click", () => {
openOnly = !openOnly;
syncControls();
render();
});
const themeToggle = document.getElementById("theme-toggle");
themeToggle.addEventListener("click", () => {
theme = theme === "dark" ? "light" : "dark";
try { localStorage.setItem("amb-theme", theme); } catch (_) {}
syncControls();
render();
});
syncControls();
render();
})();
</script>
</body>
</html>