Files
llm-atlas/scripts/check-deepseek-cross-source-sampling-browser.mjs
T
2026-07-30 03:57:17 +08:00

315 lines
13 KiB
JavaScript
Raw 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 ?? "9230";
const baseUrl = process.env.SITE_URL ?? "http://127.0.0.1:4327";
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 screenshot = async (path) => {
const result = await command("Page.captureScreenshot", {
format: "png",
captureBeyondViewport: false,
});
writeFileSync(path, Buffer.from(result.data, "base64"));
};
const assert = (condition, message) => {
if (!condition) throw new Error(message);
};
await command("Page.enable");
await command("Runtime.enable");
await command("Emulation.setDeviceMetricsOverride", {
width: 1440,
height: 1100,
deviceScaleFactor: 1,
mobile: false,
});
await command("Page.navigate", { url: `${baseUrl}/deepseek/` });
for (let attempt = 0; attempt < 100; attempt += 1) {
await pause(100);
if (await evaluate("document.readyState === 'complete'")) break;
}
const overview = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
if (!root) return null;
document.documentElement.style.scrollBehavior = "auto";
window.scrollTo(0, root.getBoundingClientRect().top + window.scrollY);
const facts = Object.fromEntries(
[...root.querySelectorAll(".cs-ledger article")].map((node) => [
node.querySelector("span").textContent.trim(),
node.querySelector("b").textContent.trim(),
]),
);
return {
tabs: root.querySelectorAll("[data-cs-tab]").length,
panels: root.querySelectorAll("[data-cs-panel]").length,
active: root.querySelector("[data-cs-panel]:not([hidden])")?.dataset.csPanel,
sourceCards: root.querySelectorAll("[data-cs-source-cards] > article").length,
seedDots: root.querySelectorAll(".seed-dots > i").length,
outputCount: root.querySelector("[data-cs-output-count]").textContent.trim(),
naturalEos: root.querySelector("[data-cs-natural-eos]").textContent.trim(),
facts,
labs: [...document.querySelectorAll(".page-facts > div")]
.find((node) => node.querySelector("dt")?.textContent.trim() === "LABS")
?.querySelector("dd")?.textContent.trim(),
status: [...document.querySelectorAll(".page-facts > div")]
.find((node) => node.querySelector("dt")?.textContent.trim() === "STATUS")
?.querySelector("dd")?.textContent.trim(),
overflow: document.documentElement.scrollWidth
- document.documentElement.clientWidth,
};
})()`);
await pause(250);
await screenshot("/tmp/llm-atlas-cross-source-desktop.png");
const hierarchySwitch = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
const domain = root.querySelector("[data-cs-domain]");
const condition = root.querySelector("[data-cs-condition]");
domain.value = "code";
domain.dispatchEvent(new Event("change", { bubbles: true }));
condition.value = "s1_period";
condition.dispatchEvent(new Event("change", { bubbles: true }));
return {
cards: [...root.querySelectorAll("[data-cs-source-cards] > article")].map((node) => ({
id: node.dataset.sourceId,
eos: node.querySelector("header em").textContent.trim(),
footer: node.querySelector(":scope > p").textContent.trim(),
dots: node.querySelectorAll(".seed-dots > i").length,
})),
outputCount: root.querySelector("[data-cs-output-count]").textContent.trim(),
naturalEos: root.querySelector("[data-cs-natural-eos]").textContent.trim(),
};
})()`);
const taskMatrices = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
root.querySelector('[data-cs-tab="tasks"]').click();
const read = () => ({
active: root.querySelector("[data-cs-panel]:not([hidden])").dataset.csPanel,
total: root.querySelector("[data-cs-task-total]").textContent.trim(),
rows: [...root.querySelectorAll("[data-cs-task-matrix] > div")].map((row) => ({
id: row.dataset.taskId,
cells: [...row.querySelectorAll("b")].map((cell) => cell.textContent.trim()),
total: row.querySelector("strong").textContent.trim(),
})),
range: root.querySelector("[data-cs-task-range]").textContent.trim(),
interaction: root.querySelector("[data-cs-task-interaction]").textContent.trim(),
failure: root.querySelector("[data-cs-task-failure]").textContent.trim(),
});
const math = read();
root.querySelector('[data-cs-task-domain="code"]').click();
const code = read();
return { math, code };
})()`);
await pause(150);
await screenshot("/tmp/llm-atlas-cross-source-task-matrix.png");
const directions = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
root.querySelector('[data-cs-tab="directions"]').click();
const select = root.querySelector("[data-cs-direction-domain]");
const read = () => ({
active: root.querySelector("[data-cs-panel]:not([hidden])").dataset.csPanel,
shorter: root.querySelector("[data-cs-shorter]").textContent.trim(),
mean: root.querySelector("[data-cs-domain-mean]").textContent.trim(),
median: root.querySelector("[data-cs-domain-median]").textContent.trim(),
rows: [...root.querySelectorAll("[data-cs-direction-rows] > article")].map((row) => ({
id: row.dataset.directionSource,
value: row.querySelector("b").textContent.trim(),
dotClass: row.querySelector("u").className,
})),
});
select.value = "english";
select.dispatchEvent(new Event("change", { bubbles: true }));
const english = read();
select.value = "code";
select.dispatchEvent(new Event("change", { bubbles: true }));
const code = read();
return { english, code };
})()`);
const keyboard = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
const first = root.querySelector('[data-cs-tab="hierarchy"]');
first.click();
first.focus();
first.dispatchEvent(new KeyboardEvent("keydown", {
key: "ArrowRight", bubbles: true,
}));
return {
selected: root.querySelector('[data-cs-tab][aria-selected="true"]').dataset.csTab,
active: root.querySelector("[data-cs-panel]:not([hidden])").dataset.csPanel,
focused: document.activeElement.dataset.csTab,
};
})()`);
const reproduction = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
root.querySelector('[data-cs-tab="reproduction"]').click();
return {
fields: [...root.querySelectorAll(".repro-fields article")].map((node) => (
node.querySelector("b").textContent.trim()
)),
artifacts: [...root.querySelectorAll(".hash-chain article")].map((node) => ({
label: node.querySelector("span").textContent.trim(),
hash: node.querySelector("b").textContent.trim(),
})),
active: root.querySelector("[data-cs-panel]:not([hidden])").dataset.csPanel,
};
})()`);
await command("Emulation.setDeviceMetricsOverride", {
width: 390,
height: 844,
deviceScaleFactor: 1,
mobile: true,
});
await pause(300);
const mobile = await evaluate(`(() => {
const root = document.querySelector("[data-cross-source-lab]");
root.querySelector('[data-cs-tab="tasks"]').click();
root.scrollIntoView();
return {
documentOverflow: document.documentElement.scrollWidth
- document.documentElement.clientWidth,
rootOverflow: root.scrollWidth - root.clientWidth,
matrixWidth: root.querySelector("[data-cs-task-matrix]").getBoundingClientRect().width,
viewport: window.innerWidth,
};
})()`);
await screenshot("/tmp/llm-atlas-cross-source-mobile.png");
assert(overview, "找不到跨来源采样实验");
assert(overview.tabs === 4 && overview.panels === 4, "四页签/面板合同异常");
assert(overview.active === "hierarchy", "初始面板不是 hierarchy");
assert(overview.sourceCards === 4 && overview.seedDots === 16, "source/seed 层级渲染异常");
assert(overview.outputCount === "16 / 16", "初始输出总账异常");
assert(overview.facts.SOURCES === "16", "source headline 异常");
assert(overview.facts["SAMPLED OUTPUTS"] === "256", "output headline 异常");
assert(overview.facts["NATURAL EOS"] === "250 / 256", "EOS headline 异常");
assert(overview.facts["MATH · STRICT"] === "47 / 64", "Math headline 异常");
assert(overview.facts["CODE · TESTS"] === "52 / 64", "Code headline 异常");
assert(overview.labs === "21 个可操作实验", "DeepSeek LABS 总账异常");
assert(overview.status === "七轮 · 512 条采样", "DeepSeek STATUS 总账异常");
assert(overview.overflow <= 1, `桌面横向溢出 ${overview.overflow}px`);
assert(hierarchySwitch.cards.length === 4, "Code source cards 数量异常");
assert(hierarchySwitch.cards.every((row) => row.dots === 4), "每题 seed 数不为 4");
assert(
hierarchySwitch.cards.find((row) => row.id === "HumanEval/133")
?.footer.includes("0 / 4 strict task pass"),
"HumanEval/133 × s1_period 应为 0/4",
);
assert(hierarchySwitch.naturalEos === "16 / 16", "Code s1_period EOS 异常");
assert(taskMatrices.math.total === "47 / 64", "Math total 异常");
assert(
taskMatrices.math.rows.map((row) => row.cells.join(",")).join("|")
=== "3 / 4,4 / 4,3 / 4,4 / 4|4 / 4,4 / 4,4 / 4,4 / 4|2 / 4,2 / 4,3 / 4,1 / 4|1 / 4,4 / 4,1 / 4,3 / 4",
"Math 4×4 pass matrix 异常",
);
assert(taskMatrices.math.range === "8 → 16 / 16", "Math source range 异常");
assert(taskMatrices.math.failure === "17 / 64", "Math failure 账异常");
assert(taskMatrices.code.total === "52 / 64", "Code total 异常");
assert(
taskMatrices.code.rows.map((row) => row.cells.join(",")).join("|")
=== "4 / 4,4 / 4,4 / 4,4 / 4|2 / 4,1 / 4,1 / 4,4 / 4|4 / 4,4 / 4,4 / 4,0 / 4|4 / 4,4 / 4,4 / 4,4 / 4",
"Code 4×4 pass matrix 异常",
);
assert(taskMatrices.code.interaction === "1↑ · 2= · 1↓", "Code interaction 方向异常");
assert(taskMatrices.code.failure === "12 / 64", "Code failure 账异常");
assert(directions.english.shorter === "1 / 4", "English 方向数异常");
assert(directions.english.mean === "−8.5 tokens".replace("−", "-"), "English mean 异常");
assert(directions.english.median === "+21.2 tokens", "English median 异常");
assert(
directions.english.rows.map((row) => row.value).join("|")
=== "-121.0 tokens|+44.5 tokens|+41.0 tokens|+1.4 tokens",
"English source contrast 异常",
);
assert(directions.code.shorter === "4 / 4", "Code 方向数异常");
assert(directions.code.rows.every((row) => row.dotClass === "negative"), "Code 应四条全负");
assert(
keyboard.selected === "tasks"
&& keyboard.active === "tasks"
&& keyboard.focused === "tasks",
"页签键盘导航异常",
);
assert(reproduction.active === "reproduction", "复现面板切换异常");
assert(reproduction.fields.length === 8, "复现字段数不为 8");
assert(reproduction.fields.every((value) => value === "64 / 64"), "复现字段未全部 exact");
assert(reproduction.artifacts.length === 5, "hash chain 工件数不为 5");
assert(mobile.documentOverflow <= 1, `移动端 document 横向溢出 ${mobile.documentOverflow}px`);
assert(mobile.rootOverflow <= 1, `移动端实验横向溢出 ${mobile.rootOverflow}px`);
assert(mobile.matrixWidth <= mobile.viewport, "移动端任务矩阵超出 viewport");
assert(exceptions.length === 0, `浏览器异常:${exceptions.join(" | ")}`);
console.log(JSON.stringify({
overview,
hierarchySwitch,
taskMatrices,
directions,
keyboard,
reproduction,
mobile,
screenshots: [
"/tmp/llm-atlas-cross-source-desktop.png",
"/tmp/llm-atlas-cross-source-task-matrix.png",
"/tmp/llm-atlas-cross-source-mobile.png",
],
}, null, 2));
socket.close();