// The assistant against a real model server, as a reader uses it: not part of CI, which // has no model. Prints what the model did and how long it took. // // node build.mjs --pkg PATH/TO/PKG && node test/assistant-live.mjs http://127.0.0.1:8080/v1 // // Asks it to explain the Bell example, then to write a three-qubit GHZ program; each // program it proposes is checked and run by the engine before it is shown, as always. import { createServer } from 'node:http'; import { existsSync, readFileSync, statSync } from 'node:fs'; import { dirname, extname, join, normalize, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const endpoint = process.argv[2] ?? 'http://127.0.0.1:8080/v1'; const { chromium } = (await import(process.env.WAI_E2E_DRIVER ?? 'playwright')).default; const DIST = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'dist'); const TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.css': 'text/css', '.wasm': 'application/wasm' }; const server = createServer((req, res) => { let file = normalize(join(DIST, decodeURIComponent(new URL(req.url, 'http://x').pathname))); if (!file.startsWith(DIST)) { res.writeHead(403).end(); return; } if (existsSync(file) && statSync(file).isDirectory()) file = join(file, 'index.html'); if (!existsSync(file)) { res.writeHead(404).end(); return; } res.writeHead(200, { 'content-type': TYPES[extname(file)] ?? 'application/octet-stream' }); res.end(readFileSync(file)); }); await new Promise((r) => server.listen(0, '127.0.0.1', r)); const browser = await chromium.launch({ channel: process.env.WAI_E2E_CHANNEL ?? 'chrome', headless: true }); const page = await (await browser.newContext({ viewport: { width: 1400, height: 1000 } })).newPage(); await page.goto(`http://127.0.0.1:${server.address().port}/`, { waitUntil: 'domcontentloaded' }); await page.waitForFunction(() => /qubits/.test(document.querySelector('#qs-lint')?.textContent ?? ''), null, { timeout: 120_000 }); await page.selectOption('#qs-example', 'bell'); await page.click('#qs-ai-gear'); await page.fill('#qs-ai-endpoint', endpoint); await page.click('#qs-ai-connect'); await page.waitForFunction(() => !/connecting/.test(document.querySelector('#qs-ai-state')?.textContent ?? ''), null, { timeout: 60_000 }); console.log('model:', await page.textContent('#qs-ai-state')); async function ask(q) { const before = await page.$$eval('.qs-ai-answer, .qs-ai-error', (a) => a.length); const t0 = Date.now(); await page.fill('#qs-ai-input', q); await page.click('#qs-ai-send'); await page.waitForFunction((n) => document.querySelectorAll('.qs-ai-answer, .qs-ai-error').length > n, before, { timeout: 600_000 }); const items = await page.$$eval('#qs-ai-log > li', (ls) => ls.map((l) => `[${l.className}] ${l.textContent.replace(/\s+/g, ' ').slice(0, 900)}`)); console.log(`\n=== ${q} (${((Date.now() - t0) / 1000).toFixed(1)} s)`); for (const i of items.slice(-8)) console.log(i); } await ask('Explain this circuit.'); await ask('Write a three-qubit GHZ program, measured, and tell me its counts.'); const proposals = await page.$$eval('.qs-ai-proposal', (p) => p.map((x) => x.textContent)); console.log('\nproposals checked:', proposals.length, proposals.join(' | ')); const answers = await page.$$eval('.qs-ai-answer', (a) => a.map((x) => x.innerText)); for (const a of answers) console.log('\n--- answer as shown ---\n' + a); const { mkdirSync } = await import('node:fs'); mkdirSync(join(dirname(fileURLToPath(import.meta.url)), 'screenshots'), { recursive: true }); await page.locator('.qs-ai').screenshot({ path: join(dirname(fileURLToPath(import.meta.url)), 'screenshots', 'assistant-live.png') }); await browser.close(); server.close();