( stream: ReadableStream<Uint8Array>, model: string, )
| 179 | } |
| 180 | |
| 181 | async function aggregateStreamToJSON( |
| 182 | stream: ReadableStream<Uint8Array>, |
| 183 | model: string, |
| 184 | ) { |
| 185 | const decoder = new TextDecoder(); |
| 186 | const reader = stream.getReader(); |
| 187 | let buf = ""; |
| 188 | let content = ""; |
| 189 | |
| 190 | for (;;) { |
| 191 | const { value, done } = await reader.read(); |
| 192 | if (done) break; |
| 193 | buf += decoder.decode(value, { stream: true }); |
| 194 | |
| 195 | let idx: number; |
| 196 | while ((idx = buf.indexOf("\n")) >= 0) { |
| 197 | const line = buf.slice(0, idx).trim(); |
| 198 | buf = buf.slice(idx + 1); |
| 199 | |
| 200 | if (!line.startsWith("data: ")) continue; |
| 201 | if (line === "data: [DONE]") continue; |
| 202 | |
| 203 | try { |
| 204 | const j = JSON.parse(line.slice(6)); |
| 205 | const delta = j?.choices?.[0]?.delta; |
| 206 | if (delta?.content) content += delta.content as string; |
| 207 | } catch { |
| 208 | /* ignore */ |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | return { |
| 214 | id: `chatcmpl-${crypto.randomUUID()}`, |
| 215 | object: "chat.completion", |
| 216 | created: Math.floor(Date.now() / 1000), |
| 217 | model, |
| 218 | choices: [{ |
| 219 | index: 0, |
| 220 | message: { role: "assistant", content }, |
| 221 | finish_reason: "stop", |
| 222 | }], |
| 223 | usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, |
| 224 | }; |
| 225 | } |
| 226 | |
| 227 | // ───────────────────────────────────────── handlers ── |
| 228 | async function handleModels(req: ServerRequest) { |
no outgoing calls
no test coverage detected