File size: 1,498 Bytes
1f1908e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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)
    }
  }
}