Files
llm-atlas/scripts/check-k3-attnres-gradient-browser.mjs
2026-07-30 10:24:42 +08:00

232 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { writeFileSync } from "node:fs";
const cdpPort = process.env.CDP_PORT ?? "9228";
const baseUrl = process.env.SITE_URL ?? "http://127.0.0.1:4328";
const pages = await fetch(`http://127.0.0.1:${cdpPort}/json/list`).then((response) => response.json());
const page = pages.find((entry) => entry.type === "page");
if (!page) throw new Error(`CDP ${cdpPort} 没有可用页面`);
const socket = new WebSocket(page.webSocketDebuggerUrl);
await new Promise((resolve, reject) => {
socket.addEventListener("open", resolve, { once: true });
socket.addEventListener("error", reject, { once: true });
});
let nextId = 0;
const pending = new Map();
const exceptions = [];
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.id && pending.has(message.id)) {
const { resolve, reject } = pending.get(message.id);
pending.delete(message.id);
if (message.error) reject(new Error(message.error.message));
else resolve(message.result);
}
if (message.method === "Runtime.exceptionThrown") {
exceptions.push(message.params.exceptionDetails.exception?.description ?? message.params.exceptionDetails.text);
}
});
const command = (method, params = {}) => new Promise((resolve, reject) => {
const id = ++nextId;
pending.set(id, { resolve, reject });
socket.send(JSON.stringify({ id, method, params }));
});
const pause = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
const evaluate = async (expression) => {
const result = await command("Runtime.evaluate", { expression, returnByValue: true, awaitPromise: true });
if (result.exceptionDetails) throw new Error(result.exceptionDetails.exception?.description ?? result.exceptionDetails.text);
return result.result.value;
};
const navigate = async (path) => {
await command("Page.navigate", { url: `${baseUrl}${path}` });
for (let attempt = 0; attempt < 100; attempt += 1) {
await pause(100);
if (await evaluate("document.readyState === 'complete'")) return;
}
throw new Error(`${path} 加载超时`);
};
const screenshot = async (path) => {
const result = await command("Page.captureScreenshot", { format: "png", captureBeyondViewport: false });
writeFileSync(path, Buffer.from(result.data, "base64"));
};
await command("Page.enable");
await command("Runtime.enable");
await command("Emulation.setDeviceMetricsOverride", {
width: 1440,
height: 1100,
deviceScaleFactor: 1,
mobile: false,
});
await navigate("/k3/");
const desktop = await evaluate(`(() => {
const root = document.querySelector("[data-gradient-lab]");
root.scrollIntoView({ block: "start", behavior: "instant" });
window.scrollBy(0, -78);
const text = (selector) => root.querySelector(selector)?.textContent.trim();
const panel = () => root.querySelector("[data-gradient-panel]:not([hidden])")?.dataset.gradientPanel;
const points = (selector) => root.querySelector(selector)?.getAttribute("points");
const setSelect = (selector, value) => {
const node = root.querySelector(selector);
node.value = value;
node.dispatchEvent(new Event("change", { bubbles: true }));
};
const initial = {
panel: panel(),
tabs: root.querySelectorAll("[data-gradient-tab]").length,
panels: root.querySelectorAll("[data-gradient-panel]").length,
ledger: root.querySelectorAll(".gradient-ledger article").length,
definition: root.textContent.includes("论文作者就是这样算的") &&
root.textContent.includes("activation gradient") &&
root.textContent.includes("parameter gradient"),
};
root.querySelector('[data-gradient-tab="spectrum"]').click();
const spectrumInitial = {
panel: panel(),
baseCv: text("[data-spectrum-base-cv]"),
blockCv: text("[data-spectrum-block-cv]"),
baseRatio: text("[data-spectrum-base-ratio]"),
blockRatio: text("[data-spectrum-block-ratio]"),
baseLine: points('[data-chart-line="baseline"]'),
blockLine: points('[data-chart-line="block"]'),
boundaries: root.querySelectorAll("[data-chart-groups] .group-boundary").length,
};
root.querySelector('[data-spectrum-scale="normalized"]').click();
const normalized = {
title: text("[data-spectrum-title]"),
baseLine: points('[data-chart-line="baseline"]'),
};
root.querySelector('[data-spectrum-depth="16"]').click();
const depth16 = {
baseCv: text("[data-spectrum-base-cv]"),
blockCv: text("[data-spectrum-block-cv]"),
pointCount: points('[data-chart-line="baseline"]').split(" ").length,
boundaries: root.querySelectorAll("[data-chart-groups] .group-boundary").length,
};
setSelect("[data-spectrum-seed]", "2026073001");
setSelect("[data-spectrum-step]", "2000");
const seedStep = {
state: text("[data-spectrum-state]"),
baseCv: text("[data-spectrum-base-cv]"),
blockCv: text("[data-spectrum-block-cv]"),
};
root.querySelector('[data-gradient-tab="timeline"]').click();
const timelineInitial = {
panel: panel(),
title: text("[data-time-title]"),
line: points('[data-time-line="block"]'),
pointCount: root.querySelectorAll('[data-time-points="block"] circle').length,
};
root.querySelector('[data-time-metric="imbalance"]').click();
const timelineChanged = {
title: text("[data-time-title]"),
line: points('[data-time-line="block"]'),
};
root.querySelector('[data-gradient-tab="output"]').click();
const outputInitial = {
panel: panel(),
pointCount: points('[data-output-line="block"]').split(" ").length,
bars: root.querySelectorAll("[data-output-bars] i").length,
boundaries: root.querySelectorAll("[data-output-groups] .group-boundary").length,
copy: text("[data-output-copy]"),
};
root.querySelector('[data-output-depth="16"]').click();
const outputDepth16 = {
pointCount: points('[data-output-line="block"]').split(" ").length,
bars: root.querySelectorAll("[data-output-bars] i").length,
copy: text("[data-output-copy]"),
};
root.querySelector('[data-gradient-tab="verdict"]').click();
const verdict = {
panel: panel(),
rows: root.querySelectorAll(".verdict-table tbody tr").length,
metrics: root.querySelectorAll(".metric-pairs article").length,
costs: root.querySelectorAll(".cost-compare article").length,
hashes: root.querySelectorAll(".hash-ledger code").length,
exact: root.textContent.includes("model + optimizer exact") &&
root.textContent.includes("all frozen fields exact"),
mixed: root.textContent.includes("depth-dependent or inconclusive"),
};
const first = root.querySelector('[data-gradient-tab="definition"]');
first.focus();
first.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", bubbles: true }));
const keyboard = {
selected: root.querySelector('[data-gradient-tab][aria-selected="true"]').dataset.gradientTab,
panel: panel(),
};
return {
initial, spectrumInitial, normalized, depth16, seedStep,
timelineInitial, timelineChanged, outputInitial, outputDepth16,
verdict, keyboard,
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
rootOverflow: root.scrollWidth - root.clientWidth,
};
})()`);
await pause(180);
await screenshot("/tmp/llm-atlas-k3-attnres-gradient-desktop.png");
await command("Emulation.setDeviceMetricsOverride", {
width: 390,
height: 844,
deviceScaleFactor: 1,
mobile: true,
});
await navigate("/k3/");
const mobile = await evaluate(`(() => {
const root = document.querySelector("[data-gradient-lab]");
root.scrollIntoView({ block: "start", behavior: "instant" });
window.scrollBy(0, -64);
root.querySelector('[data-gradient-tab="spectrum"]').click();
root.querySelector('[data-spectrum-depth="16"]').click();
root.querySelector('[data-gradient-tab="output"]').click();
root.querySelector('[data-output-depth="16"]').click();
return {
tabs: root.querySelectorAll("[data-gradient-tab]").length,
ledger: root.querySelectorAll(".gradient-ledger article").length,
outputBars: root.querySelectorAll("[data-output-bars] i").length,
visiblePanel: root.querySelector("[data-gradient-panel]:not([hidden])")?.dataset.gradientPanel,
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
rootOverflow: root.scrollWidth - root.clientWidth,
};
})()`);
await pause(180);
await screenshot("/tmp/llm-atlas-k3-attnres-gradient-mobile.png");
const report = { desktop, mobile, exceptions };
console.log(JSON.stringify(report, null, 2));
const numeric = (value) => Number.parseFloat(value.replace("−", "-"));
const failures = [];
if (desktop.initial.panel !== "definition" || desktop.initial.tabs !== 5 || desktop.initial.panels !== 5 || desktop.initial.ledger !== 6) failures.push("五视图初始结构异常");
if (!desktop.initial.definition) failures.push("定义与对象边界缺失");
if (desktop.spectrumInitial.panel !== "spectrum" || Math.abs(numeric(desktop.spectrumInitial.baseCv) - 0.3786) > 1e-4 || Math.abs(numeric(desktop.spectrumInitial.blockCv) - 0.5996) > 1e-4) failures.push("depth-32 final spectrum 读数异常");
if (desktop.spectrumInitial.boundaries !== 7 || desktop.spectrumInitial.baseLine === desktop.normalized.baseLine || !desktop.normalized.title.includes("NORMALIZED")) failures.push("绝对/归一化谱或组边界异常");
if (desktop.depth16.pointCount !== 16 || desktop.depth16.boundaries !== 7 || Math.abs(numeric(desktop.depth16.baseCv) - 0.4246) > 1e-4 || Math.abs(numeric(desktop.depth16.blockCv) - 0.4678) > 1e-4) failures.push("depth-16 spectrum 切换异常");
if (!desktop.seedStep.state.includes("2026073001") || !desktop.seedStep.state.includes("2,000") || Math.abs(numeric(desktop.seedStep.baseCv) - 0.4101) > 1e-4 || Math.abs(numeric(desktop.seedStep.blockCv) - 0.3656) > 1e-4) failures.push("seed/checkpoint spectrum 切换异常");
if (desktop.timelineInitial.panel !== "timeline" || desktop.timelineInitial.pointCount !== 6 || desktop.timelineInitial.line === desktop.timelineChanged.line || !desktop.timelineChanged.title.includes("FIRST / LAST")) failures.push("六时点轨迹指标切换异常");
if (desktop.outputInitial.panel !== "output" || desktop.outputInitial.pointCount !== 32 || desktop.outputInitial.bars !== 32 || desktop.outputInitial.boundaries !== 7 || desktop.outputDepth16.pointCount !== 16 || desktop.outputDepth16.bars !== 16 || !desktop.outputDepth16.copy.includes("DEPTH 16")) failures.push("Output RMS 深度/组节律切换异常");
if (desktop.verdict.panel !== "verdict" || desktop.verdict.rows !== 2 || desktop.verdict.metrics !== 4 || desktop.verdict.costs !== 3 || desktop.verdict.hashes !== 4 || !desktop.verdict.exact || !desktop.verdict.mixed) failures.push("联合判定、成本或重放视图异常");
if (desktop.keyboard.selected !== "spectrum" || desktop.keyboard.panel !== "spectrum") failures.push("键盘 tab 导航异常");
if (desktop.documentOverflow > 1 || desktop.rootOverflow > 1 || mobile.documentOverflow > 1 || mobile.rootOverflow > 1) failures.push("桌面或移动端出现文档级横向溢出");
if (mobile.tabs !== 5 || mobile.ledger !== 6 || mobile.outputBars !== 16 || mobile.visiblePanel !== "output") failures.push("移动端交互结构异常");
if (exceptions.length) failures.push(`浏览器异常:${exceptions.join(" | ")}`);
if (failures.length) {
console.error(`\nFAIL\n- ${failures.join("\n- ")}`);
process.exitCode = 1;
} else {
console.log("\nPASS K3 AttnRes gradient browser regression");
}
socket.close();