polyglot-alpha / ui /scripts /loop_a2_sse.js
licaomeng
deploy: main@8970ffb → HF Spaces (2026-05-27T05:19Z)
88d2f2a
// A2 sub-agent: focused SSE + extra UI walk session.
// One trigger, then probe SSE streams, /agents/{address}, /events/{id}/bids,
// /events/{id}/translations, /builder_fees, /health endpoints.
/* eslint-disable no-console */
const fs = require("fs");
const path = require("path");
const http = require("http");
const { chromium } = require("playwright");
const BASE_UI = "http://127.0.0.1:3001";
const BASE_API = "http://127.0.0.1:8000";
const OUT_DIR = path.resolve(__dirname, "..", "..", "outputs");
const SHOT_DIR = path.join(OUT_DIR, "loop_screenshots");
const FINDINGS = path.join(OUT_DIR, "playwright_loop_findings.md");
const SESSION_TAG = `a2sse-${Date.now()}`;
function ts() { return new Date().toISOString(); }
function append(line) { fs.appendFileSync(FINDINGS, line + "\n"); }
function postJson(url, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body);
const u = new URL(url);
const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method: "POST",
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(data) }},
(res) => { let chunks = ""; res.on("data", c => chunks += c);
res.on("end", () => { try { resolve({ status: res.statusCode, body: JSON.parse(chunks) }); }
catch (e) { resolve({ status: res.statusCode, body: chunks }); }});
});
req.on("error", reject); req.write(data); req.end();
});
}
function getJson(url) {
return new Promise((resolve, reject) => {
http.get(url, (res) => { let chunks = ""; res.on("data", c => chunks += c);
res.on("end", () => { try { resolve({ status: res.statusCode, body: JSON.parse(chunks) }); }
catch (e) { resolve({ status: res.statusCode, body: chunks }); }});
}).on("error", reject);
});
}
async function waitForTerminal(eventId, timeoutMs = 150000) {
const t0 = Date.now();
const terminal = new Set(["COMMITTED", "SUBMITTED", "FAILED", "REJECTED"]);
while (Date.now() - t0 < timeoutMs) {
const r = await getJson(`${BASE_API}/events/${eventId}`);
if (r.body && terminal.has(r.body.status)) return r.body;
await new Promise(r => setTimeout(r, 2500));
}
return null;
}
// SSE probe: collect first ~5 lines or 10s
function sseProbe(pathPart) {
return new Promise((resolve) => {
const u = new URL(`${BASE_API}${pathPart}`);
const req = http.get({ hostname: u.hostname, port: u.port, path: u.pathname, headers: { Accept: "text/event-stream" }},
(res) => {
const lines = [];
const status = res.statusCode;
res.on("data", (c) => {
lines.push(c.toString().slice(0, 200));
if (lines.length >= 8) { req.destroy(); resolve({ status, lines, ended: false }); }
});
res.on("end", () => resolve({ status, lines, ended: true }));
setTimeout(() => { req.destroy(); resolve({ status, lines, ended: false }); }, 10000);
});
req.on("error", (e) => resolve({ error: e.message }));
});
}
(async () => {
append(`\n---`);
append(`# A2 SSE+Extras Loop ${ts()}`);
// 1) Trigger one event with 2 mock bids that we expect to potentially succeed
const trigger = {
event_source: "user_payload",
title: `Will event SSE-X happen by 2026-12-31? [${SESSION_TAG}]`,
sources: [{ name: "test-sse", url: `https://test/sse?s=${SESSION_TAG}` }],
language: "en",
auction_mode: "mock",
mock_bids: [
{ agent_address: "0xagent_a", bid_amount: 0.5, stake_amount: 5.0, reputation: 0.9 },
{ agent_address: "0xagent_b", bid_amount: 0.7, stake_amount: 5.0, reputation: 0.8 },
],
};
append(`\n## SSE Cycle (single): ${ts()}`);
const tr = await postJson(`${BASE_API}/trigger/event`, trigger);
append(`- Trigger HTTP ${tr.status}: \`${JSON.stringify(tr.body).slice(0,200)}\``);
const eventId = tr.body && tr.body.event_id;
// 2) While lifecycle runs, probe SSE in parallel (10s window)
const ssePromise1 = sseProbe("/sse/events");
const ssePromise2 = sseProbe("/sse/auctions");
const [sseEvents, sseAuctions] = await Promise.all([ssePromise1, ssePromise2]);
append(`- /sse/events: status=${sseEvents.status}, lines=${(sseEvents.lines||[]).length}, ended=${sseEvents.ended}`);
if (sseEvents.lines && sseEvents.lines[0]) append(` - first: \`${sseEvents.lines[0].replace(/\n/g,"\\n").slice(0,140)}\``);
append(`- /sse/auctions: status=${sseAuctions.status}, lines=${(sseAuctions.lines||[]).length}, ended=${sseAuctions.ended}`);
if (sseAuctions.lines && sseAuctions.lines[0]) append(` - first: \`${sseAuctions.lines[0].replace(/\n/g,"\\n").slice(0,140)}\``);
// 3) Wait terminal
let finalEvent = null;
if (eventId) {
finalEvent = await waitForTerminal(eventId);
append(`- Lifecycle: ${finalEvent ? finalEvent.status : "TIMEOUT"}${finalEvent ? `, winner=${finalEvent.winner_address}` : ""}`);
}
// 4) Probe extra REST endpoints
const endpoints = [
"/health",
"/builder_fees",
`/events/${eventId}`,
`/events/${eventId}/bids`,
`/events/${eventId}/phases`,
`/events/${eventId}/translations`,
"/agents/0xagent_a",
"/agents/0xagent_a/history",
"/leaderboard",
];
append(`\n### REST endpoint probes`);
for (const ep of endpoints) {
try {
const r = await getJson(`${BASE_API}${ep}`);
const bodyLen = typeof r.body === "object" ? JSON.stringify(r.body).length : r.body.length;
append(`- \`${ep}\` -> HTTP ${r.status}, body-len=${bodyLen}`);
} catch (e) {
append(`- \`${ep}\` -> exception: ${e.message}`);
}
}
// 5) UI walk for that event + /history page
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await ctx.newPage();
const errors = [];
page.on("pageerror", e => errors.push(`pageerror: ${e.message}`));
page.on("console", m => { if (m.type() === "error") errors.push(`console.error: ${m.text()}`); });
let step = 0;
const shot = async (label) => {
step += 1;
try { await page.screenshot({ path: path.join(SHOT_DIR, `sse_step_${step}_${label}.png`) }); }
catch (e) {}
};
append(`\n### UI walk`);
try {
if (eventId) {
await page.goto(`${BASE_UI}/events/${eventId}`, { waitUntil: "domcontentloaded", timeout: 20000 });
await page.waitForTimeout(2500);
await shot("event_detail");
const sub = await page.$('[data-testid="sub-phase-chips"]');
const debate = await page.$('[data-testid="agent-debate-panel"], [data-testid="agent-debate-panel-empty"]');
append(`- /events/${eventId}: sub-phase-chips=${!!sub}, debate-panel=${!!debate}`);
}
// Visit /history
await page.goto(`${BASE_UI}/history`, { waitUntil: "domcontentloaded", timeout: 15000 });
await page.waitForTimeout(1500);
await shot("history");
const histText = await page.textContent("body");
append(`- /history: loaded, body-text-len=${(histText || "").length}`);
// Visit /leaderboard
await page.goto(`${BASE_UI}/leaderboard`, { waitUntil: "domcontentloaded", timeout: 15000 });
await page.waitForTimeout(1500);
await shot("leaderboard");
const lbText = await page.textContent("body");
append(`- /leaderboard: loaded, body-text-len=${(lbText || "").length}`);
} catch (e) {
append(`- UI walk error: ${e.message}`);
}
append(`- JS errors observed: ${errors.length}`);
for (const er of errors.slice(0, 5)) append(` - ${er}`);
await ctx.close();
await browser.close();
append(`- SSE+Extras cycle finished at ${ts()}`);
console.log("DONE");
})();