Files
sql-server-query-ai/lib/stream.ts
ahmed mohammedandClaude Opus 4.7 ac4cbdd9e6 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>
2026-09-22 08:32:22 +03:00

37 lines
1.5 KiB
TypeScript

export const sse = (event: string, data: unknown) => `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
export async function* readSse(body: ReadableStream<Uint8Array>): AsyncGenerator<{ event: string; data: any }> {
const reader = body.getReader(); const dec = new TextDecoder(); let buf = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
let at: number;
while ((at = buf.indexOf("\n\n")) !== -1) {
const frame = buf.slice(0, at); buf = buf.slice(at + 2);
const event = /^event: (.+)$/m.exec(frame)?.[1] ?? "message";
const raw = /^data: (.*)$/m.exec(frame)?.[1] ?? "";
try { yield { event, data: JSON.parse(raw) }; } catch { /* keep-alive or unparsable frame */ }
}
}
}
export async function* openAiTextDeltas(res: Response): AsyncGenerator<string> {
const reader = res.body!.getReader(); const dec = new TextDecoder(); let buf = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
let at: number;
while ((at = buf.indexOf("\n\n")) !== -1) {
const line = (buf.slice(0, at).split("\n").find((l) => l.startsWith("data:")) ?? "").slice(5).trim();
buf = buf.slice(at + 2);
if (!line) continue;
if (line === "[DONE]") return;
let delta: unknown;
try { delta = JSON.parse(line)?.choices?.[0]?.delta?.content; } catch { continue; }
if (typeof delta === "string" && delta) yield delta;
}
}
}