Spaces:
Running
Running
| // 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) | |
| } | |
| } | |
| } | |