feat: analysis pipeline with staged SSE

Adds @openuidev/lang-core (one-line reason: the prompt-side library must build in a non-React Node runtime; react-lang's createContext crashes the route's server bundle).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ahmed mohammed
2026-09-22 08:32:22 +03:00
co-authored by Claude Opus 4.7
parent 13bd2e6bfd
commit ac4cbdd9e6
9 changed files with 320 additions and 88 deletions
+6 -2
View File
@@ -1,11 +1,15 @@
import test from "node:test";
import assert from "node:assert/strict";
const { library, reportPromptOptions } = await import("../lib/report-ui.ts");
const { promptLibrary, reportPromptOptions, specs } = await import("../lib/report-spec.ts");
test("the prompt advertises every component and the root", () => {
const p = library.prompt(reportPromptOptions);
const p = promptLibrary.prompt(reportPromptOptions);
for (const n of ["Report", "Headline", "VerdictBlock", "MetricGrid", "Findings", "OptionTable",
"StepList", "SqlBlock", "ScoreBar", "Callout"]) assert.ok(p.includes(n), `missing ${n}`);
assert.match(p, /root/i);
assert.match(p, /never invent/i);
});
test("every spec has a renderer, so the model can never emit an unrenderable block", async () => {
const { library } = await import("../lib/report-ui.ts");
for (const s of specs) assert.ok(library.components[s.name], `no renderer for ${s.name}`);
});
+35
View File
@@ -0,0 +1,35 @@
import test from "node:test";
import assert from "node:assert/strict";
const { sse, readSse, openAiTextDeltas } = await import("../lib/stream.ts");
const streamOf = (parts: string[]) => new ReadableStream<Uint8Array>({ start(c) {
parts.forEach((p) => c.enqueue(new TextEncoder().encode(p))); c.close(); } });
test("sse frames one event", () =>
assert.equal(sse("stage", { stage: "digesting" }), 'event: stage\ndata: {"stage":"digesting"}\n\n'));
test("readSse rejoins frames split anywhere", async () => {
const raw = sse("stage", { stage: "digesting" }) + sse("oul", { chunk: "root = Report([])" });
const events: any[] = [];
for await (const e of readSse(streamOf([raw.slice(0, 12), raw.slice(12, 40), raw.slice(40)]))) events.push(e);
assert.deepEqual(events, [{ event: "stage", data: { stage: "digesting" } },
{ event: "oul", data: { chunk: "root = Report([])" } }]);
});
test("readSse ignores frames it cannot parse", async () => {
const events: any[] = [];
for await (const e of readSse(streamOf([": ping\n\n", sse("done", { id: "1" })]))) events.push(e);
assert.deepEqual(events, [{ event: "done", data: { id: "1" } }]);
});
test("openAiTextDeltas yields content and stops at [DONE]", async () => {
const res = new Response(streamOf(['data: {"choices":[{"delta":{"content":"ab"}}]}\n\n',
'data: {"choices":[{"delta":{"content":"cd"}}]}\n\n', "data: [DONE]\n\n"]), { status: 200 });
const out: string[] = [];
for await (const d of openAiTextDeltas(res)) out.push(d);
assert.equal(out.join(""), "abcd");
});
test("openAiTextDeltas survives a delta split across chunks", async () => {
const res = new Response(streamOf(['data: {"choices":[{"delta":{"con', 'tent":"xy"}}]}\n\ndata: [DONE]\n\n']), { status: 200 });
const out: string[] = [];
for await (const d of openAiTextDeltas(res)) out.push(d);
assert.equal(out.join(""), "xy");
});