import { chromium } from 'playwright'; import fs from 'node:fs/promises'; const root = new URL('../..', import.meta.url).pathname; const url = process.argv[2] ?? 'https://mike0021-minicpm5-2b-webgpu-pi-http.static.hf.space/'; const gist = 'https://gist.githubusercontent.com/mbostock/4063269/raw/5e72bffb17d12e15fb9da3a8b6794ac19db802a2/README.md'; const result = { date: new Date().toISOString(), url, prompt: `Fetch ${gist} and tell me what kind of chart it describes.`, errors: [], requests: [] }; const context = await chromium.launchPersistentContext(root + '/validation/http-browser-profile', { executablePath: '/usr/bin/chromium', headless: true, viewport: { width: 1440, height: 1000 }, args: ['--enable-unsafe-webgpu', '--use-angle=vulkan', '--enable-features=Vulkan', '--disable-vulkan-surface'], }); context.setDefaultTimeout(120_000); const page = await context.newPage(); page.on('pageerror', error => result.errors.push(error.message)); context.on('request', request => { if (request.url().startsWith('https://gist.githubusercontent.com/')) result.requests.push({ url: request.url(), method: request.method() }); }); let timer; try { await page.goto(url); await page.waitForFunction(() => window.browserPi?.terminal); await page.evaluate(() => window.browserPi.ready); await page.waitForFunction(() => !window.browserPi.busy); await page.waitForTimeout(300); const loadStart = Date.now(); await page.evaluate(() => window.browserPi.load()); result.modelLoadMs = Date.now() - loadStart; await page.waitForFunction(() => !window.browserPi.busy); await page.locator('#new-chat').click(); await page.waitForFunction(() => !window.browserPi.busy); await page.waitForTimeout(300); const start = Date.now(); const pending = page.evaluate(prompt => window.browserPi.prompt(prompt), result.prompt); try { await Promise.race([pending, new Promise((_, reject) => { timer = setTimeout(() => reject(Error('Gist trial exceeded 120 seconds')), 120_000); })]); } catch (error) { await page.evaluate(() => window.browserPi.rpc('stop')); await pending.catch(() => {}); throw error; } finally { clearTimeout(timer); } result.elapsedMs = Date.now() - start; result.state = await page.evaluate(() => window.browserPi.rpc('snapshot')); result.calls = result.state.messages.filter(m => m.role === 'assistant').flatMap(m => m.content.filter(c => c.type === 'toolCall')); result.toolResults = result.state.messages.filter(m => m.role === 'toolResult'); result.final = result.state.messages.filter(m => m.role === 'assistant').at(-1)?.content.filter(c => c.type === 'text').map(c => c.text).join('\n') ?? ''; result.passed = result.requests.some(r => r.url === gist) && result.toolResults.some(t => !t.isError && t.toolName === 'bash' && /bubble charts/i.test(JSON.stringify(t.content))) && /bubble chart/i.test(result.final) && !result.errors.length; console.log(JSON.stringify({ passed: result.passed, elapsedMs: result.elapsedMs, final: result.final, calls: result.calls })); } catch (error) { result.failure = error.stack; result.passed = false; console.error(error.stack); } finally { clearTimeout(timer); await fs.writeFile(root + '/validation/http-any-https.json', JSON.stringify(result, null, 2) + '\n'); await context.close(); if (!result.passed) process.exitCode = 1; }