244 lines
10 KiB
JavaScript
244 lines
10 KiB
JavaScript
import { writeFileSync } from "node:fs";
|
||
|
||
const cdpPort = process.env.CDP_PORT ?? "9225";
|
||
const baseUrl = process.env.SITE_URL ?? "http://127.0.0.1:4324";
|
||
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.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.text);
|
||
return result.result.value;
|
||
};
|
||
const navigate = async (path) => {
|
||
await command("Page.navigate", { url: `${baseUrl}${path}` });
|
||
for (let attempt = 0; attempt < 50; attempt += 1) {
|
||
await pause(100);
|
||
if (await evaluate("document.readyState === 'complete'")) return;
|
||
}
|
||
throw new Error(`${path} 加载超时`);
|
||
};
|
||
const screenshot = async (path, full = false) => {
|
||
const params = { format: "png", captureBeyondViewport: full };
|
||
if (full) {
|
||
const metrics = await command("Page.getLayoutMetrics");
|
||
params.clip = {
|
||
x: 0,
|
||
y: 0,
|
||
width: metrics.cssContentSize.width,
|
||
height: metrics.cssContentSize.height,
|
||
scale: 1,
|
||
};
|
||
}
|
||
const result = await command("Page.captureScreenshot", params);
|
||
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("/training-systems/");
|
||
await screenshot("/tmp/llm-atlas-training-systems-desktop.png");
|
||
|
||
const memory = await evaluate(`(() => {
|
||
const root = document.querySelector("[data-systems-lab]");
|
||
root.querySelector('[data-memory-preset="single"]').click();
|
||
const single = {
|
||
total: root.querySelector("[data-memory-total]").textContent,
|
||
verdict: root.querySelector("[data-memory-verdict]").textContent,
|
||
overflow: root.querySelector("[data-hbm-card]").classList.contains("is-overflow"),
|
||
};
|
||
root.querySelector('[data-memory-preset="k3"]').click();
|
||
return {
|
||
single,
|
||
k3: {
|
||
total: root.querySelector("[data-memory-total]").textContent,
|
||
verdict: root.querySelector("[data-memory-verdict]").textContent,
|
||
offload: root.querySelector('[data-memory-metric="offload"]').textContent,
|
||
formula: root.querySelector("[data-memory-formula]").textContent,
|
||
},
|
||
};
|
||
})()`);
|
||
|
||
const mesh = await evaluate(`(() => {
|
||
const root = document.querySelector("[data-systems-lab]");
|
||
root.querySelector('[data-system-tab="mesh"]').click();
|
||
root.querySelector('[data-mesh-preset="v3"]').click();
|
||
const v3 = {
|
||
equation: root.querySelector("[data-mesh-equation]").textContent,
|
||
status: root.querySelector("[data-mesh-capacity]").textContent,
|
||
invalid: root.querySelector("[data-mesh-status]").classList.contains("is-invalid"),
|
||
};
|
||
root.querySelector('[data-mesh-preset="invalid"]').click();
|
||
return {
|
||
v3,
|
||
invalid: {
|
||
equation: root.querySelector("[data-mesh-equation]").textContent,
|
||
status: root.querySelector("[data-mesh-capacity]").textContent,
|
||
invalid: root.querySelector("[data-mesh-status]").classList.contains("is-invalid"),
|
||
},
|
||
};
|
||
})()`);
|
||
|
||
const pipeline = await evaluate(`(() => {
|
||
const root = document.querySelector("[data-systems-lab]");
|
||
root.querySelector('[data-system-tab="pipeline"]').click();
|
||
root.querySelector('[data-schedule="1f1b"]').click();
|
||
const one = {
|
||
copies: root.querySelector('[data-pipeline-metric="copies"]').textContent,
|
||
reading: root.querySelector("[data-pipeline-reading]").textContent,
|
||
rows: root.querySelectorAll(".timeline-row").length,
|
||
};
|
||
root.querySelector('[data-schedule="dual"]').click();
|
||
return {
|
||
one,
|
||
dual: {
|
||
copies: root.querySelector('[data-pipeline-metric="copies"]').textContent,
|
||
reading: root.querySelector("[data-pipeline-reading]").textContent,
|
||
note: root.querySelector("[data-pipeline-note]").textContent,
|
||
},
|
||
};
|
||
})()`);
|
||
|
||
const communication = await evaluate(`(() => {
|
||
const root = document.querySelector("[data-systems-lab]");
|
||
root.querySelector('[data-system-tab="communication"]').click();
|
||
const overlap = root.querySelector('[data-comm-input="overlap"]');
|
||
overlap.value = "0";
|
||
overlap.dispatchEvent(new Event("input", { bubbles: true }));
|
||
const exposed = {
|
||
bytes: root.querySelector('[data-comm-metric="bytes"]').textContent,
|
||
time: root.querySelector('[data-comm-metric="exposed"]').textContent,
|
||
};
|
||
overlap.value = "95";
|
||
overlap.dispatchEvent(new Event("input", { bubbles: true }));
|
||
return {
|
||
exposed,
|
||
hidden: {
|
||
bytes: root.querySelector('[data-comm-metric="bytes"]').textContent,
|
||
time: root.querySelector('[data-comm-metric="exposed"]').textContent,
|
||
},
|
||
moe: {
|
||
deep: root.querySelector('[data-moe-metric="deep-buffer"]').textContent,
|
||
moon: root.querySelector('[data-moe-metric="moon-buffer"]').textContent,
|
||
slots: root.querySelector('[data-moe-metric="slots"]').textContent,
|
||
bars: root.querySelectorAll("[data-expert-rank]").length,
|
||
},
|
||
};
|
||
})()`);
|
||
|
||
const layout = await evaluate(`(() => {
|
||
const nav = document.querySelector(".top-nav");
|
||
const meta = document.querySelector(".header-meta");
|
||
return {
|
||
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||
navGap: Number((meta.getBoundingClientRect().left - nav.getBoundingClientRect().right).toFixed(1)),
|
||
navLinks: document.querySelectorAll(".top-nav a").length,
|
||
articleSections: document.querySelectorAll(".article-section").length,
|
||
paperLinks: document.querySelectorAll(".paper-chain a").length,
|
||
labTabs: document.querySelectorAll("[data-system-tab]").length,
|
||
};
|
||
})()`);
|
||
|
||
await evaluate(`(() => {
|
||
document.documentElement.style.scrollBehavior = "auto";
|
||
document.querySelector("[data-systems-lab]").scrollIntoView({ block: "start", behavior: "instant" });
|
||
})()`);
|
||
await pause(150);
|
||
await screenshot("/tmp/llm-atlas-training-systems-lab-desktop.png");
|
||
|
||
await command("Emulation.setDeviceMetricsOverride", {
|
||
width: 390,
|
||
height: 844,
|
||
deviceScaleFactor: 1,
|
||
mobile: true,
|
||
});
|
||
await navigate("/training-systems/");
|
||
await screenshot("/tmp/llm-atlas-training-systems-mobile-closed.png");
|
||
const mobile = await evaluate(`(() => {
|
||
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"),
|
||
title: document.querySelector("h1").innerText,
|
||
};
|
||
})()`);
|
||
|
||
await command("Emulation.setDeviceMetricsOverride", {
|
||
width: 1440,
|
||
height: 1100,
|
||
deviceScaleFactor: 1,
|
||
mobile: false,
|
||
});
|
||
await navigate("/");
|
||
const home = await evaluate(`({
|
||
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||
releaseCards: document.querySelectorAll(".release-card").length,
|
||
firstRelease: document.querySelector(".release-card h2").textContent,
|
||
navLinks: document.querySelectorAll(".top-nav a").length,
|
||
})`);
|
||
await evaluate(`document.querySelector("#new-chapters").scrollIntoView({ block: "start", behavior: "instant" })`);
|
||
await pause(100);
|
||
await screenshot("/tmp/llm-atlas-home-training-systems-release.png");
|
||
|
||
const report = { memory, mesh, pipeline, communication, layout, mobile, home, exceptions };
|
||
console.log(JSON.stringify(report, null, 2));
|
||
|
||
const numeric = (value) => Number.parseFloat(value);
|
||
const failures = [];
|
||
if (!memory.single.overflow || !memory.single.verdict.includes("OOM")) failures.push("单卡 OOM 场景未触发");
|
||
if (!memory.k3.formula.includes("14P") || !memory.k3.verdict.includes("可以放入")) failures.push("K3 分层存储预设异常");
|
||
if (mesh.v3.invalid || !mesh.v3.equation.includes("2,048")) failures.push("V3 设备网格异常");
|
||
if (!mesh.invalid.invalid || !mesh.invalid.status.includes("相差")) failures.push("无效设备网格没有被拒绝");
|
||
if (pipeline.one.rows !== 4 || pipeline.one.copies !== "1×") failures.push("1F1B 时间轴异常");
|
||
if (pipeline.dual.copies !== "2×" || !pipeline.dual.note.includes("near-zero")) failures.push("DualPipe 边界解释缺失");
|
||
if (communication.exposed.bytes !== communication.hidden.bytes) failures.push("overlap 错误改变了通信字节");
|
||
if (numeric(communication.exposed.time) <= numeric(communication.hidden.time)) failures.push("overlap 没有降低 exposed time");
|
||
if (communication.moe.bars !== 12 || !communication.moe.slots.includes("E/R")) failures.push("MoonEP 对照异常");
|
||
if (layout.articleSections !== 16 || layout.paperLinks !== 37 || layout.labTabs !== 4) failures.push("章节、论文或实验数量异常");
|
||
if (layout.documentOverflow > 0 || mobile.documentOverflow > 0 || home.documentOverflow > 0) failures.push("页面存在横向溢出");
|
||
if (layout.navGap < 0) failures.push(`桌面导航碰撞:${layout.navGap}px`);
|
||
if (!mobile.menuVisible || mobile.menuOpen !== "true") failures.push("移动端菜单不可用");
|
||
if (home.releaseCards !== 10 || !home.firstRelease.includes("DPO 没有")) failures.push("首页 Transformer 新章入口异常");
|
||
if (exceptions.length) failures.push(`浏览器脚本异常:${exceptions.join("; ")}`);
|
||
|
||
socket.close();
|
||
if (failures.length) {
|
||
failures.forEach((failure) => console.error(`- ${failure}`));
|
||
process.exit(1);
|
||
}
|