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.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 < 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("/scaling/"); await screenshot("/tmp/llm-atlas-scaling-desktop.png"); const surface = await evaluate(`(() => { const root = document.querySelector("[data-scaling-lab]"); const initial = { model: root.querySelector('[data-surface-metric="model"]').textContent, data: root.querySelector('[data-surface-metric="data"]').textContent, gap: root.querySelector('[data-surface-metric="gap"]').textContent, path: root.querySelector("[data-surface-path]").getAttribute("d").length, }; const tpp = root.querySelector('[data-surface-input="tpp"]'); tpp.value = "500"; tpp.dispatchEvent(new Event("input", { bubbles: true })); return { initial, extreme: { gap: root.querySelector('[data-surface-metric="gap"]').textContent, reading: root.querySelector("[data-surface-reading]").textContent, label: root.querySelector("[data-surface-current] text").textContent, }, }; })()`); const serve = await evaluate(`(() => { const root = document.querySelector("[data-scaling-lab]"); root.querySelector('[data-scaling-tab="serve"]').click(); root.querySelector('[data-serve-preset="lab"]').click(); const lab = { train: root.querySelector('[data-serve-metric="train"]').textContent, total: root.querySelector('[data-serve-metric="total"]').textContent, share: root.querySelector('[data-serve-metric="share"]').textContent, }; root.querySelector('[data-serve-preset="mass"]').click(); return { lab, mass: { train: root.querySelector('[data-serve-metric="train"]').textContent, total: root.querySelector('[data-serve-metric="total"]').textContent, share: root.querySelector('[data-serve-metric="share"]').textContent, reading: root.querySelector("[data-serve-reading]").textContent, candidates: root.querySelectorAll(".candidate-row").length, }, }; })()`); const data = await evaluate(`(() => { const root = document.querySelector("[data-scaling-lab]"); root.querySelector('[data-scaling-tab="data"]').click(); root.querySelector('[data-data-preset="unique"]').click(); const unique = { seen: root.querySelector('[data-data-metric="seen"]').textContent, effective: root.querySelector('[data-data-metric="effective"]').textContent, next: root.querySelector('[data-data-metric="next"]').textContent, }; root.querySelector('[data-data-preset="twenty"]').click(); return { unique, twenty: { seen: root.querySelector('[data-data-metric="seen"]').textContent, effective: root.querySelector('[data-data-metric="effective"]').textContent, next: root.querySelector('[data-data-metric="next"]').textContent, reading: root.querySelector("[data-data-reading]").textContent, bars: root.querySelectorAll(".epoch-bars > i").length, more: root.querySelector(".epoch-more")?.textContent ?? "", }, }; })()`); const emergence = await evaluate(`(() => { const root = document.querySelector("[data-scaling-lab]"); root.querySelector('[data-scaling-tab="emergence"]').click(); const scale = root.querySelector('[data-emergence-input="scale"]'); const measure = () => ({ token: root.querySelector('[data-emergence-metric="token"]').textContent, threshold: root.querySelector('[data-emergence-metric="threshold"]').textContent, exact: root.querySelector('[data-emergence-metric="exact"]').textContent, }); scale.value = "59"; scale.dispatchEvent(new Event("input", { bubbles: true })); const below = measure(); scale.value = "60"; scale.dispatchEvent(new Event("input", { bubbles: true })); const above = measure(); const firstTab = root.querySelector('[data-scaling-tab="surface"]'); firstTab.focus(); firstTab.dispatchEvent(new KeyboardEvent("keydown", { key: "End", bubbles: true })); return { below, above, keyboard: { selected: root.querySelector('[data-scaling-tab][aria-selected="true"]').dataset.scalingTab, visible: root.querySelector("[data-scaling-view]:not([hidden])").dataset.scalingView, }, paths: [...root.querySelectorAll("[data-emergence-line]")].map((path) => path.getAttribute("d").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-scaling-tab]").length, views: document.querySelectorAll("[data-scaling-view]").length, }; })()`); await evaluate(`(() => { document.documentElement.style.scrollBehavior = "auto"; document.querySelector("[data-scaling-lab]").scrollIntoView({ block: "start", behavior: "instant" }); })()`); await pause(150); await screenshot("/tmp/llm-atlas-scaling-lab-desktop.png"); await command("Emulation.setDeviceMetricsOverride", { width: 390, height: 844, deviceScaleFactor: 1, mobile: true, }); await navigate("/scaling/"); await evaluate(`document.querySelector("[data-scaling-lab]").scrollIntoView({ block: "start", behavior: "instant" })`); await pause(100); await screenshot("/tmp/llm-atlas-scaling-lab-mobile.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"), mobileLinks: document.querySelectorAll("#mobile-nav a").length, selectedTab: document.querySelector('[data-scaling-tab][aria-selected="true"]').dataset.scalingTab, }; })()`); 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, firstHref: document.querySelector(".release-card").getAttribute("href"), paperCount: document.querySelector(".hero-stats div:nth-child(3) b").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-scaling-release.png"); const report = { surface, serve, data, emergence, layout, mobile, home, exceptions }; console.log(JSON.stringify(report, null, 2)); const numeric = (value) => Number.parseFloat(value); const modelSize = (value) => Number.parseFloat(value); const scaledCount = (value) => { const number = Number.parseFloat(value); if (value.includes("T")) return number * 1000; if (value.includes("M")) return number / 1000; return number; }; const failures = []; if (!surface.initial.model || !surface.initial.data || surface.initial.path < 1000) failures.push("IsoFLOP 曲线或最优点未渲染"); if (numeric(surface.extreme.gap) <= numeric(surface.initial.gap) || !surface.extreme.reading.includes("模型偏小")) { failures.push("极端 TPP 没有产生预期的 compute-allocation loss gap"); } if (serve.mass.candidates !== 8) failures.push("训练—部署候选模型数量异常"); if (modelSize(serve.mass.total) >= modelSize(serve.mass.train) || !serve.mass.reading.includes("更小模型")) { failures.push("海量调用没有把生命周期最优点推向更小模型"); } if (data.unique.seen !== data.unique.effective) failures.push("单遍数据的 seen 与 effective 不相等"); if (scaledCount(data.twenty.effective) >= scaledCount(data.twenty.seen) || numeric(data.twenty.next) >= 20) { failures.push("数据枯竭预设没有体现复用边际衰减"); } if (data.twenty.bars !== 16 || !data.twenty.more.includes("+4")) failures.push("20 epoch 可视化截断异常"); if (!emergence.below.threshold.includes("FAIL") || !emergence.above.threshold.includes("PASS")) { failures.push("连续能力跨过二值评分线时没有出现离散阶跃"); } if (Math.abs(numeric(emergence.above.token) - numeric(emergence.below.token)) > 2) { failures.push("相邻规模的底层连续能力变化异常过大"); } if (emergence.keyboard.selected !== "emergence" || emergence.keyboard.visible !== "emergence") { failures.push("实验页签键盘导航异常"); } if (emergence.paths.some((length) => length < 500)) failures.push("涌现多指标曲线未完整渲染"); if (layout.articleSections !== 16 || layout.paperLinks !== 29 || layout.labTabs !== 4 || layout.views !== 4) { failures.push("章节、论文或实验数量异常"); } if (layout.navLinks !== 12 || mobile.mobileLinks !== 12 || home.navLinks !== 12) failures.push("全站导航未同步 Scaling"); 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 !== 5 || !home.firstRelease.includes("大一点") || home.firstHref !== "/scaling/") { failures.push("首页 Scaling 新章入口异常"); } if (home.paperCount !== "182") failures.push(`首页论文总数异常:${home.paperCount}`); if (exceptions.length) failures.push(`浏览器脚本异常:${exceptions.join("; ")}`); socket.close(); if (failures.length) { failures.forEach((failure) => console.error(`- ${failure}`)); process.exit(1); }