Download app/tests/cached-start.mjs from Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP: direct link, hf CLI and curl.
- Browser
- Download file 7.64 kB
-
https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/resolve/main/app/tests/cached-start.mjs
- Command line
-
hf download hf://spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/app/tests/cached-start.mjs
-
curl -L -o cached-start.mjs https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/resolve/main/app/tests/cached-start.mjs
7.64 kB
| import { chromium } from 'playwright'; | |
| import fs from 'node:fs/promises'; | |
| import assert from 'node:assert/strict'; | |
| const url = process.argv[2] ?? 'http://127.0.0.1:4173/?source=local'; | |
| const label = process.argv[3] ?? 'agent-cached-start'; | |
| const revision = '04a6c49fcba3a65a0351c92644c3a7e9d4343059'; | |
| const profile = new URL('../../validation/agent-browser-profile', import.meta.url).pathname; | |
| const gpuArgs = ['--enable-unsafe-webgpu', '--use-angle=vulkan', '--enable-features=Vulkan', '--disable-vulkan-surface']; | |
| const result = { date: new Date().toISOString(), url, checks: [], errors: [] }; | |
| let context, page, receipts; | |
| const requests = []; | |
| async function open(args = gpuArgs) { | |
| context = await chromium.launchPersistentContext(profile, { executablePath: '/usr/bin/chromium', headless: true, viewport: { width: 1440, height: 1000 }, args }); | |
| context.setDefaultTimeout(180_000); | |
| context.on('request', r => { if (/\/models\/|\/resolve\//.test(r.url())) requests.push(r.url()); }); | |
| page = await context.newPage(); page.on('pageerror', e => result.errors.push(e.stack)); | |
| await page.goto(url); await page.waitForFunction(() => window.browserPi?.terminal); await page.evaluate(() => window.browserPi.ready); | |
| await page.evaluate(() => document.querySelector('#new-chat').onclick()); | |
| await page.evaluate(() => { | |
| window.dialogOpens = 0; | |
| new MutationObserver(() => { if (document.querySelector('#model-dialog').open) window.dialogOpens++; }).observe(document.querySelector('#model-dialog'), { attributes: true, attributeFilter: ['open'] }); | |
| }); | |
| } | |
| async function cacheReceipts(value) { | |
| return page.evaluate(async ({ revision, value }) => { | |
| const root = await (await navigator.storage.getDirectory()).getDirectoryHandle('pi-minicpm5-' + revision); | |
| const handle = await root.getFileHandle('verified.json'); | |
| if (value === undefined) return (await handle.getFile()).text(); | |
| const stream = await handle.createWritable(); await stream.write(value); await stream.close(); | |
| }, { revision, value }); | |
| } | |
| async function holdCacheLock() { | |
| await page.evaluate(revision => { | |
| window.lockHeld = false; | |
| window.cacheLock = navigator.locks.request('pi-minicpm5-download-' + revision, async () => { | |
| window.lockHeld = true; await new Promise(resolve => { window.releaseCacheLock = resolve; }); | |
| }); | |
| }, revision); | |
| await page.waitForFunction(() => window.lockHeld); | |
| } | |
| async function releaseCacheLock() { await page.evaluate(async () => { window.releaseCacheLock?.(); await window.cacheLock; }); } | |
| async function send(text) { | |
| await page.locator('.xterm-helper-textarea').focus(); await page.keyboard.press('Control+e'); await page.keyboard.press('Control+u'); | |
| await page.keyboard.type(text); await page.locator('#send').click(); | |
| } | |
| async function restoredDraft(text) { | |
| await page.waitForFunction(text => !window.browserPi.busy && !document.querySelector('#model-dialog').open && window.browserPi.terminal.text === text, text); | |
| } | |
| try { | |
| await open(); | |
| if (!await page.evaluate(() => window.browserPi.rpc('cache_status'))) { | |
| await page.evaluate(() => window.browserPi.load()); | |
| await context.close(); await open(); | |
| } | |
| receipts = await cacheReceipts(); | |
| assert.equal(await page.evaluate(() => window.browserPi.loaded), false); | |
| const task = 'Read cached-start.txt using the read tool and tell me its exact contents.'; | |
| for (const invalid of ['{}', 'null', '{broken']) { | |
| await cacheReceipts(invalid); | |
| assert.equal(await page.evaluate(() => window.browserPi.rpc('cache_status')), false); | |
| const before = requests.length; | |
| await send(task); await page.locator('#model-dialog[open]').waitFor(); | |
| assert.equal(requests.length, before); await page.locator('#model-cancel').click(); await restoredDraft(task); | |
| } | |
| await cacheReceipts(receipts); | |
| result.checks.push('Incomplete or malformed verification receipts require confirmation without model requests'); | |
| await holdCacheLock(); | |
| const beforeRace = requests.length; | |
| await send(task); await page.locator('#stop').waitFor({ state: 'visible' }); | |
| assert.equal(await page.locator('#model-dialog').evaluate(el => el.open), false); | |
| await cacheReceipts('{}'); await releaseCacheLock(); | |
| await page.locator('#model-dialog[open]').waitFor(); | |
| assert.equal(requests.length, beforeRace); | |
| await page.locator('#model-cancel').click(); await restoredDraft(task); await cacheReceipts(receipts); | |
| result.checks.push('Cache invalidated after the initial check falls back to confirmation without fetching weights'); | |
| await holdCacheLock(); | |
| const beforeCancel = await page.evaluate(() => window.dialogOpens); | |
| await send(task); await page.locator('#stop').waitFor({ state: 'visible' }); | |
| await page.locator('#stop').click(); await restoredDraft(task); await releaseCacheLock(); | |
| assert.equal(await page.evaluate(() => window.dialogOpens), beforeCancel); | |
| assert.equal((await page.evaluate(() => window.browserPi.rpc('snapshot'))).messages.length, 0); | |
| result.checks.push('Stop cancels a queued cached restore, keeps the draft and opens no modal'); | |
| await context.close(); await open(['--disable-gpu', '--disable-software-rasterizer']); | |
| await send(task); await page.locator('#model-dialog[open]').waitFor(); | |
| assert.match(await page.locator('#model-error').textContent(), /WebGPU/); | |
| assert.equal(await page.locator('#model-confirm').textContent(), 'Try again'); | |
| await page.locator('#model-cancel').click(); await restoredDraft(task); | |
| result.checks.push('A real GPU initialization failure offers retry and preserves the message'); | |
| await context.close(); await open(); | |
| const nonce = 'CACHE-' + crypto.randomUUID().slice(0, 8); | |
| await page.evaluate(nonce => window.browserPi.rpc('write', { path: '/workspace/cached-start.txt', content: nonce }), nonce); | |
| assert.equal(await page.evaluate(() => window.browserPi.rpc('cache_status')), true); | |
| const before = requests.length, start = Date.now(); | |
| // A complete verified cache must suffice even if every model URL is unavailable. | |
| await context.route('**/models/**', route => route.abort()); await context.route('**/resolve/**', route => route.abort()); | |
| await send(task); | |
| await page.waitForFunction(() => window.browserPi.loaded && !window.browserPi.busy && window.browserPi.diagnostics.events.includes('agent_end')); | |
| const state = await page.evaluate(() => window.browserPi.rpc('snapshot')); | |
| assert.equal(await page.evaluate(() => window.dialogOpens), 0); | |
| assert.equal(requests.length, before); | |
| assert.equal(state.messages.filter(m => m.role === 'user').length, 1); | |
| assert(state.messages.some(m => m.role === 'toolResult' && m.toolName === 'read' && !m.isError)); | |
| assert(state.messages.filter(m => m.role === 'assistant').at(-1).content.some(c => c.text?.includes(nonce))); | |
| assert.equal(await page.evaluate(() => window.browserPi.terminal.text), ''); | |
| result.elapsedMs = Date.now() - start; result.messages = state.messages; | |
| result.checks.push('Reloaded app restores the real WebGPU model and sends exactly once, with zero modal openings and zero model requests'); | |
| await page.screenshot({ path: `../validation/${label}.png`, fullPage: true }); | |
| assert.deepEqual(result.errors, []); result.passed = true; | |
| console.log(JSON.stringify({ passed: true, elapsedMs: result.elapsedMs, checks: result.checks })); | |
| } catch (error) { result.failure = error.stack; throw error; } | |
| finally { | |
| if (receipts && page && !page.isClosed()) await cacheReceipts(receipts).catch(() => {}); | |
| await releaseCacheLock().catch(() => {}); | |
| await fs.writeFile(`../validation/${label}.json`, JSON.stringify(result, null, 2)); await context?.close(); | |
| } | |