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:
co-authored by
Claude Opus 4.7
parent
13bd2e6bfd
commit
ac4cbdd9e6
@@ -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}`);
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
Reference in New Issue
Block a user