Files
llm-atlas/scripts/check-deepseek-browser.mjs
T
2026-07-29 11:17:53 +08:00

304 lines
16 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 ?? "9227";
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 navigate = async (path) => {
await command("Page.navigate", { url: `${baseUrl}${path}` });
for (let attempt = 0; attempt < 70; 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("/deepseek/");
await screenshot("/tmp/llm-atlas-deepseek-desktop.png");
const overview = await evaluate(`(() => ({
title: document.querySelector("h1")?.textContent.trim(),
sections: document.querySelectorAll(".article-section").length,
tocLinks: document.querySelectorAll(".side-rail a").length,
ledgers: document.querySelectorAll(".ledger-card").length,
waves: document.querySelectorAll(".wave-grid > article").length,
paperLinks: document.querySelectorAll("[data-deepseek-paper-chain] a").length,
labTabs: document.querySelectorAll("[data-ds-tab]").length,
labPanels: document.querySelectorAll("[data-ds-panel]").length,
branches: document.querySelectorAll(".branch-grid > a").length,
followups: document.querySelectorAll(".lineage-row.followup").length,
navLinks: document.querySelectorAll(".top-nav a").length,
activeNav: document.querySelector('.top-nav a[aria-current="page"]')?.textContent.trim(),
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
}))()`);
const capacity = await evaluate(`(() => {
const root = document.querySelector("[data-deepseek-lab]");
const read = () => ({
panel: root.querySelector("[data-ds-panel]:not([hidden])").dataset.dsPanel,
total: root.querySelector("[data-total-capacity]").textContent.trim(),
active: root.querySelector("[data-active-compute]").textContent.trim(),
combinations: root.querySelector("[data-combinations]").textContent.trim(),
communication: root.querySelector("[data-communication]").textContent.trim(),
name: root.querySelector("[data-capacity-name]").textContent.trim(),
explain: root.querySelector("[data-capacity-explain]").textContent.trim(),
});
const initial = read();
root.querySelector('[data-capacity-preset="dense"]').click();
const dense = read();
root.querySelector('[data-capacity-preset="deepseekmoe"]').click();
const fine = read();
root.querySelector('[data-capacity-preset="v3"]').click();
const v3 = read();
return { initial, dense, fine, v3 };
})()`);
const cache = await evaluate(`(() => {
const root = document.querySelector("[data-deepseek-lab]");
root.querySelector('[data-ds-tab="cache"]').click();
const read = () => ({
panel: root.querySelector("[data-ds-panel]:not([hidden])").dataset.dsPanel,
mha: root.querySelector("[data-mha-elements]").textContent.trim(),
gqa: root.querySelector("[data-gqa-elements]").textContent.trim(),
mla: root.querySelector("[data-mla-elements]").textContent.trim(),
rope: root.querySelector("[data-rope-cache]").textContent.trim(),
selected: root.querySelector("[data-selected-cache]").textContent.trim(),
baseline: root.querySelector("[data-mha-cache]").textContent.trim(),
reduction: root.querySelector("[data-cache-reduction]").textContent.trim(),
boundary: root.querySelector("[data-cache-boundary]").textContent.trim(),
});
const initial = read();
const rope = root.querySelector("[data-rope-dim]");
rope.value = "0";
rope.dispatchEvent(new Event("input", { bubbles: true }));
const noRope = read();
const context = root.querySelector("[data-context-length]");
context.value = "1048576";
context.dispatchEvent(new Event("input", { bubbles: true }));
const million = read();
return { initial, noRope, million };
})()`);
const codesign = await evaluate(`(() => {
const root = document.querySelector("[data-deepseek-lab]");
root.querySelector('[data-ds-tab="codesign"]').click();
const readSchedule = () => ({
bubble: root.querySelector("[data-bubble]").textContent.trim(),
exposed: root.querySelector("[data-exposed-comm]").textContent.trim(),
});
const oneWay = readSchedule();
root.querySelector('[data-schedule="dual"]').click();
const dual = readSchedule();
root.querySelector('[data-precision="naive"]').click();
const naive = {
risk: root.querySelector("[data-risk-label]").textContent.trim(),
accum: root.querySelector("[data-accum-dtype]").textContent.trim(),
explain: root.querySelector("[data-precision-explain]").textContent.trim(),
};
root.querySelector('[data-precision="mixed"]').click();
const mixed = {
risk: root.querySelector("[data-risk-label]").textContent.trim(),
accum: root.querySelector("[data-accum-dtype]").textContent.trim(),
sensitive: root.querySelector("[data-sensitive-dtype]").textContent.trim(),
};
root.querySelector('[data-mtp-role="off"]').click();
const off = root.querySelector("[data-mtp-supervision]").textContent.trim();
root.querySelector('[data-mtp-role="draft"]').click();
const draft = {
supervision: root.querySelector("[data-mtp-supervision]").textContent.trim(),
cost: root.querySelector("[data-mtp-main-cost]").textContent.trim(),
explain: root.querySelector("[data-mtp-explain]").textContent.trim(),
};
return { panel: root.querySelector("[data-ds-panel]:not([hidden])").dataset.dsPanel, oneWay, dual, naive, mixed, off, draft };
})()`);
const rl = await evaluate(`(() => {
const root = document.querySelector("[data-deepseek-lab]");
root.querySelector('[data-ds-tab="rl"]').click();
const read = () => ({
signal: root.querySelector("[data-signal-state]").textContent.trim(),
mean: root.querySelector("[data-reward-mean]").textContent.trim(),
std: root.querySelector("[data-reward-std]").textContent.trim(),
effective: root.querySelector("[data-effective]").textContent.trim(),
provenance: root.querySelector("[data-provenance]").textContent.trim(),
algorithm: root.querySelector("[data-algorithm-name]").textContent.trim(),
boundary: root.querySelector("[data-rl-boundary]").textContent.trim(),
weights: [...root.querySelectorAll("[data-advantage-rows] > div span:last-child")].map((node) => node.textContent.trim()),
});
const initial = read();
root.querySelector('[data-reward-preset="same"]').click();
const same = read();
root.querySelector('[data-reward-preset="longwrong"]').click();
root.querySelector('[data-rl-algorithm="dapo"]').click();
const dapo = read();
root.querySelector('[data-rl-algorithm="dr"]').click();
const dr = read();
root.querySelector('[data-r1-mode="r1"]').click();
const r1 = root.querySelector("[data-r1-mode-explain]").textContent.trim();
root.querySelector('[data-r1-mode="distill"]').click();
const distill = root.querySelector("[data-r1-mode-explain]").textContent.trim();
const first = root.querySelector('[data-ds-tab="capacity"]');
first.focus();
first.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", bubbles: true }));
return {
initial, same, dapo, dr, r1, distill,
keyboardSelected: root.querySelector('[data-ds-tab][aria-selected="true"]').dataset.dsTab,
keyboardVisible: root.querySelector("[data-ds-panel]:not([hidden])").dataset.dsPanel,
};
})()`);
await evaluate(`(() => {
document.querySelector("[data-deepseek-lab]").scrollIntoView({ block: "start", behavior: "instant" });
window.scrollBy(0, -82);
})()`);
await pause(180);
await screenshot("/tmp/llm-atlas-deepseek-lab-desktop.png");
await navigate("/");
const home = await evaluate(`(() => ({
releaseCards: document.querySelectorAll(".release-card").length,
firstRelease: document.querySelector(".release-card h2").textContent.trim(),
firstHref: document.querySelector(".release-card").getAttribute("href"),
paperCount: document.querySelector(".hero-stats div:nth-child(3) b").textContent.trim(),
navLinks: document.querySelectorAll(".top-nav a").length,
}))()`);
await navigate("/papers/");
const papers = await evaluate(`(() => {
const button = [...document.querySelectorAll("[data-filter]")].find((node) => node.textContent.trim() === "DeepSeek");
button?.click();
return {
total: document.querySelectorAll("[data-paper]").length,
visible: document.querySelectorAll("[data-paper]:not([hidden])").length,
hasFilter: Boolean(button),
hasCoder: document.body.textContent.includes("DeepSeek-Coder-V2"),
hasEngram: document.body.textContent.includes("Conditional Memory via Scalable Lookup"),
};
})()`);
await command("Emulation.setDeviceMetricsOverride", {
width: 390,
height: 844,
deviceScaleFactor: 1,
mobile: true,
});
await navigate("/deepseek/");
const mobile = await evaluate(`(() => {
const root = document.querySelector("[data-deepseek-lab]");
root.scrollIntoView({ block: "start", behavior: "instant" });
const toggle = document.querySelector("#menu-toggle");
toggle?.click();
return {
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
menuVisible: getComputedStyle(toggle).display !== "none",
menuOpen: toggle.getAttribute("aria-expanded"),
mobileLinks: document.querySelectorAll("#mobile-nav a").length,
tabs: root.querySelectorAll("[data-ds-tab]").length,
offenders: [...document.querySelectorAll("body *")]
.filter((node) => !node.closest(".paper-chain, .advantage-table, .precision-table, .mapping-table, [data-deepseek-lab]"))
.filter((node) => node.getBoundingClientRect().right > document.documentElement.clientWidth + 1)
.slice(0, 12)
.map((node) => ({
tag: node.tagName,
className: typeof node.className === "string" ? node.className : "",
right: Math.round(node.getBoundingClientRect().right),
width: Math.round(node.getBoundingClientRect().width),
})),
};
})()`);
await evaluate(`(() => {
document.querySelector("#menu-toggle")?.click();
window.scrollBy(0, -82);
})()`);
await pause(180);
await screenshot("/tmp/llm-atlas-deepseek-mobile.png");
const report = { overview, capacity, cache, codesign, rl, home, papers, mobile, exceptions };
console.log(JSON.stringify(report, null, 2));
const numeric = (text) => Number.parseFloat(text.replaceAll(",", ""));
const failures = [];
if (!overview.title.includes("为什么转向")) failures.push("专题标题异常");
if (overview.sections !== 25 || overview.tocLinks !== 25) failures.push("二十四个编号专题加阅读链的目录结构异常");
if (overview.ledgers !== 24 || overview.waves !== 10) failures.push("二十四张问题账或十次转向结构异常");
if (overview.paperLinks !== 60 || overview.branches !== 5 || overview.followups !== 1) failures.push("论文链、旁支或公开后续标记异常");
if (overview.labTabs !== 4 || overview.labPanels !== 4) failures.push("四联实验结构异常");
if (overview.navLinks !== 20 || home.navLinks !== 20 || mobile.mobileLinks !== 20 || overview.activeNav !== "DeepSeek") failures.push("全站导航未同步 DeepSeek");
if (overview.documentOverflow > 1 || mobile.documentOverflow > 1) failures.push("桌面或移动端存在文档级横向溢出");
if (capacity.initial.panel !== "capacity" || capacity.initial.total !== "32.1× FFN" || capacity.initial.active !== "1.13× FFN") failures.push("V3 稀疏容量初始账异常");
if (!capacity.dense.name.includes("DENSE") || capacity.dense.communication !== "NONE" || numeric(capacity.dense.total) !== numeric(capacity.dense.active)) failures.push("Dense 容量预设异常");
if (!capacity.fine.name.includes("FINE-GRAINED") || !capacity.fine.explain.includes("shared")) failures.push("DeepSeekMoE 预设异常");
if (!capacity.v3.combinations.includes("10^") || capacity.v3.communication !== "HIGH") failures.push("V3 路由组合或通信方向异常");
if (cache.initial.panel !== "cache" || numeric(cache.initial.mha) !== 32768 || numeric(cache.initial.gqa) !== 2048 || numeric(cache.initial.mla) !== 576 || numeric(cache.initial.rope) !== 64) failures.push("MLA 精确元素账异常");
if (numeric(cache.initial.reduction) !== 98.2 || numeric(cache.noRope.mla) !== 512 || numeric(cache.noRope.reduction) <= numeric(cache.initial.reduction)) failures.push("RoPE cache 或 MLA reduction 异常");
if (!cache.million.selected.includes("GiB") || !cache.million.boundary.includes("1,048,576")) failures.push("百万 Token 缓存账异常");
if (numeric(codesign.dual.bubble) >= numeric(codesign.oneWay.bubble) || numeric(codesign.dual.exposed) >= numeric(codesign.oneWay.exposed)) failures.push("Dual-ended toy 没有减少空泡或暴露通信");
if (codesign.naive.risk !== "CRITICAL" || codesign.naive.accum !== "FP8" || codesign.mixed.risk !== "MANAGED" || !codesign.mixed.accum.includes("FP32")) failures.push("FP8 角色合同异常");
if (!codesign.off.includes("1 token") || !codesign.draft.supervision.includes("draft") || !codesign.draft.explain.includes("验收率")) failures.push("MTP 生命周期异常");
if (rl.initial.signal !== "GROUP-RELATIVE SIGNAL" || rl.same.signal !== "ZERO GROUP SIGNAL" || !rl.same.boundary.includes("优势为零")) failures.push("GRPO 零方差信号异常");
if (!rl.dapo.provenance.includes("2503.14476") || !rl.dapo.algorithm.includes("FOLLOW-UP") || !rl.dr.provenance.includes("2503.20783")) failures.push("DAPO / Dr.GRPO 来源边界异常");
if (!rl.r1.includes("cold start") || !rl.distill.includes("没有重演")) failures.push("R1 / distill 身份切换异常");
if (rl.keyboardSelected !== "cache" || rl.keyboardVisible !== "cache") failures.push("实验键盘 tab 导航异常");
if (home.releaseCards !== 16 || !home.firstRelease.includes("从 Dense 到百万上下文") || home.firstHref !== "/deepseek/" || home.paperCount !== "486") failures.push("首页 DeepSeek 首发入口或论文数异常");
if (papers.total !== 486 || !papers.hasFilter || papers.visible < 20 || !papers.hasCoder || !papers.hasEngram) failures.push("论文库 DeepSeek 聚光异常");
if (!mobile.menuVisible || mobile.menuOpen !== "true" || mobile.tabs !== 4) failures.push("移动端导航或实验异常");
if (mobile.offenders.length) failures.push(`移动端越界元素:${JSON.stringify(mobile.offenders)}`);
if (exceptions.length) failures.push(`浏览器异常:${exceptions.join(" | ")}`);
if (failures.length) {
console.error(`\nFAIL\n- ${failures.join("\n- ")}`);
process.exitCode = 1;
} else {
console.log("\nPASS DeepSeek browser regression");
}
socket.close();