Files
OpenCodeGoEvals/scripts/refresh-evals.mjs
T
devadmin 46447d422b
Deploy to Cloudflare Pages / deploy (push) Failing after 25s
Add deploy script, eval refresh script and Go-priced eval data
- scripts/deploy.sh: manual Cloudflare Pages deploy (same steps as CI)
- scripts/refresh-evals.mjs: re-scrape Artificial Analysis + OpenCode Data into evals.js
- evals.js now carries OpenCode Go list prices; Hy4 and LongCat 2.5 left empty
  rather than borrowing a neighbouring model's numbers
- workflow creates the Pages project idempotently
2026-10-21 15:27:11 +00:00

180 lines
7.5 KiB
JavaScript

#!/usr/bin/env node
// Regenerates site/assets/evals.js from live sources.
//
// node scripts/refresh-evals.mjs
//
// Sources
// - Artificial Analysis model pages (https://artificialanalysis.ai/models/<slug>)
// The benchmark payload is embedded in the page as React Server Component flight data.
// - OpenCode Data model JSON (https://opencode.ai/data/<lab>/<model>.json)
// for context windows and OpenCode Go list prices.
//
// Usage of the AI slug differs slightly from the OpenCode Go model id, so they are
// mapped explicitly below.
import { writeFileSync } from "node:fs";
import { GO_MODELS } from "../site/assets/data.js";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const UA =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126 Safari/537.36";
// go model id -> { aa: artificial analysis slug, od: opencode data path }
const MODELS = {
"minimax-m3.1-flash-free": { aa: "minimax-m3-1-flash", od: "minimax/minimax-m3-1-flash" },
"gemini-4-argon-contributor": { aa: "gemini-4-argon", od: "google/gemini-4-argon" },
"claude-haiku-5-5": { aa: "claude-haiku-5-5", od: "anthropic/claude-haiku-5-5" },
"beam-free": { aa: "reflection-beam", od: "reflection/beam" },
// No Artificial Analysis entry for these two yet — they stay empty rather than
// borrowing a different model's numbers (LongCat 2.0 / Hy3 are not LongCat 2.5 / Hy4).
"longcat-2.5-preview-free": { aa: null, od: "meituan/longcat-2-5-preview" },
"mimo-v2.6-pro": { aa: "mimo-v2-6-pro", od: "xiaomi/mimo-v2-6-pro" },
"gpt-6-luna": { aa: "gpt-6-luna", od: "openai/gpt-6-luna" },
"grok-4.7": { aa: "grok-4-7", od: "xai/grok-4-7" },
"step-5-preview": { aa: "step-5", od: "stepfun/step-5-preview" },
"deepseek-v4.1-flash": { aa: "deepseek-v4-1-flash", od: "deepseek/deepseek-v4-1-flash" },
"muse-spark-1.3-contributor": { aa: "muse-spark-1-3", od: "meta/muse-spark-1-3-contributor" },
"hy4-preview": { aa: null, od: "tencent/hy4-preview" },
"qwen3.8-flash": { aa: "qwen3-8-flash-next", od: "alibaba/qwen3-8-flash" },
"glm-5.3-flash": { aa: "glm-5-3-flash", od: "zhipuai/glm-5-3-flash" },
"glm-5.3": { aa: "glm-5-3", od: "zhipuai/glm-5-3" },
"qwen3.8-max": { aa: "qwen3-8-max", od: "alibaba/qwen3-8-max" },
"kimi-k3": { aa: "kimi-k3", od: "moonshotai/kimi-k3" },
"kimi-k2.7-code": { aa: "kimi-k2-7-code", od: "moonshotai/kimi-k2-7-code" },
"qwen3.7-plus": { aa: "qwen3-7-plus", od: "alibaba/qwen3-7-plus" },
"minimax-m3": { aa: "minimax-m3", od: "minimax/minimax-m3" },
"deepseek-v4-pro": { aa: "deepseek-v4-pro", od: "deepseek/deepseek-v4-pro" },
"mimo-v2.5-pro": { aa: "mimo-v2-5-pro", od: "xiaomi/mimo-v2-5-pro" },
"mimo-v2.5": { aa: "mimo-v2-5-0424", od: "xiaomi/mimo-v2-5" }
};
const pct = (v) => (v === null || v === undefined ? null : Math.round(v * 1000) / 10);
/* --- Artificial Analysis page parsing ------------------------------------ */
function flightPayload(html) {
const parts = [];
const re = /self\.__next_f\.push\(\[1,"/g;
let m;
while ((m = re.exec(html))) {
const start = re.lastIndex;
const end = html.indexOf('"])</script>', start);
if (end < 0) continue;
try {
parts.push(JSON.parse('"' + html.slice(start, end).replace(/\\\//g, "/") + '"'));
} catch {
/* ignore chunk */
}
}
return parts.join("");
}
function extractModelObjects(html) {
const payload = flightPayload(html);
const out = {};
const re = /"slug":"/g;
let m;
while ((m = re.exec(payload))) {
const start = payload.lastIndexOf('{"id":"', m.index);
if (start < 0) continue;
let depth = 0, inStr = false, esc = false, k = start;
for (k = start; k < payload.length; k++) {
const c = payload[k];
if (esc) { esc = false; continue; }
if (c === "\\") { esc = true; continue; }
if (c === '"') { inStr = !inStr; continue; }
if (inStr) continue;
if (c === "{") depth++;
else if (c === "}") { depth--; if (depth === 0) { k++; break; } }
}
let obj;
try { obj = JSON.parse(payload.slice(start, k)); } catch { continue; }
if (obj && obj.slug && obj.intelligenceIndex != null) out[obj.slug] = obj;
}
return out;
}
async function fetchAA(slug) {
const res = await fetch(`https://artificialanalysis.ai/models/${slug}`, { headers: { "user-agent": UA } });
if (!res.ok) return null;
const models = extractModelObjects(await res.text());
return models[slug] || null;
}
async function fetchOD(path) {
try {
const res = await fetch(`https://opencode.ai/data/${path}.json`);
if (!res.ok) return null;
return (await res.json()).model;
} catch {
return null;
}
}
/* --- build ---------------------------------------------------------------- */
// Names, OpenCode Go list prices and free-tier flags come from site/assets/data.js
// (hand-maintained from the OpenCode Go docs), so the chart always shows Go pricing
// rather than the provider's public list price.
const CATALOG = Object.fromEntries(GO_MODELS.map((m) => [m.id, m]));
const rows = [];
for (const [id, { aa, od }] of Object.entries(MODELS)) {
const [aaModel, odModel] = await Promise.all([aa ? fetchAA(aa) : null, fetchOD(od)]);
if (!aaModel) {
console.warn(` ! no Artificial Analysis entry for ${id}${aa ? ` (slug ${aa})` : ""} — left empty`);
const cat = CATALOG[id];
rows.push({
id, n: cat?.name || odModel?.name || id, lab: cat?.lab || odModel?.labName || "",
free: cat ? !!cat.free : !(odModel?.priceUsdPer1M?.input),
pi: cat?.price?.in ?? odModel?.priceUsdPer1M?.input ?? 0,
po: cat?.price?.out ?? odModel?.priceUsdPer1M?.output ?? 0, ii: null,
fc: null, scicode: null, tb21: null, tb40: null, tbh: null, tau2: null, taub: null, apex: null,
analyst: null, auto: null, gpqa: null, critpt: null, hle: null, lcr: null, ifbench: null,
mmmu: null, gdpv: null, gdppdf: null, ctx: odModel?.contextTokens ?? 1048576
});
continue;
}
const cat = CATALOG[id];
const price = cat?.price || odModel?.priceUsdPer1M || {};
const ctx = odModel?.contextTokens ?? aaModel.contextWindowTokens ?? 1048576;
const free = cat ? !!cat.free : !(odModel?.priceUsdPer1M?.input);
rows.push({
id,
n: cat?.name || aaModel.name,
lab: cat?.lab || odModel?.labName || aaModel.name,
free,
pi: price.in ?? price.input ?? 0,
po: price.out ?? price.output ?? 0,
ii: Math.round(aaModel.intelligenceIndex * 10) / 10,
fc: pct(aaModel.frontierCode),
scicode: pct(aaModel.scicode),
tb21: pct(aaModel.terminalBench21),
tb40: pct(aaModel.terminalBench40),
tbh: pct(aaModel.terminalbenchHard),
tau2: pct(aaModel.tau2),
taub: pct(aaModel.tauBanking),
apex: pct(aaModel.apexAgents),
analyst: pct(aaModel.analystAgent),
auto: pct(aaModel.automationBenchPartialScore),
gpqa: pct(aaModel.gpqa),
critpt: pct(aaModel.critpt),
hle: pct(aaModel.hle),
lcr: pct(aaModel.lcr),
ifbench: pct(aaModel.ifbench),
mmmu: pct(aaModel.mmmuPro),
gdpv: pct(aaModel.gdpvalNormalized),
gdppdf: pct(aaModel.gdpPdfAllPass),
ctx
});
console.log(` + ${id} (index ${rows.at(-1).ii})`);
}
const header = `// Evaluation data scraped from Artificial Analysis model pages (single consistent harness).
// Values are already scaled to 0-100. Regenerate with: node scripts/refresh-evals.mjs
export const RAW = [
`;
const body = rows.map((r) => " " + JSON.stringify(r)).join(",\n");
writeFileSync(resolve(ROOT, "site/assets/evals.js"), header + body + "\n];\n");
console.log(`\nwrote site/assets/evals.js with ${rows.length} models`);