// Shared SSE reader for the server text-generation endpoint (/text/generate/stream). // Parses the `event:`/`data:` wire format and dispatches each event to onEvent. // Used by both engineServer.js (the model-bar "Server / ZeroGPU" engine) and // codingModel.js (the Skill Forge coding-model picker) so the parser lives once. export async function streamSse(url, body, { onEvent, signal } = {}) { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal, }) if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`) const reader = res.body.getReader() const decoder = new TextDecoder() let buf = '' while (true) { const { value, done } = await reader.read() if (done) break buf += decoder.decode(value, { stream: true }) const events = buf.split(/\n\n/) buf = events.pop() ?? '' for (const evChunk of events) { const lines = evChunk.split('\n') let evt = 'message' const dataLines = [] for (const line of lines) { if (line.startsWith('event:')) evt = line.slice(6).trim() else if (line.startsWith('data:')) dataLines.push(line.slice(5).trimStart()) } const data = dataLines.join('\n') if (!data) continue let parsed = null try { parsed = JSON.parse(data) } catch { /* ignore */ } if (evt === 'error') throw new Error(parsed?.error || data) onEvent?.(evt, parsed, data) } } }