|
async function* streamAsyncIterable(stream) { |
|
const reader = stream.getReader(); |
|
try { |
|
while (true) { |
|
const { done, value } = await reader.read(); |
|
if (done) { |
|
return; |
|
} |
|
yield value; |
|
} |
|
} finally { |
|
reader.releaseLock(); |
|
} |
|
} |
|
|
|
|
|
async function fetchSSE(url, options, fetch2 = fetch) { |
|
const { createParser } = await import("eventsource-parser"); |
|
const { onMessage, timeout , ...fetchOptions } = options; |
|
const controller = new AbortController(); |
|
const timeoutId = setTimeout(() => controller.abort(), timeout||30000) |
|
|
|
const res = await fetch2(url, {...fetchOptions,signal:controller.signal}); |
|
clearTimeout(timeoutId); |
|
|
|
if (!res.ok) { |
|
let reason; |
|
try { |
|
reason = await res.text(); |
|
} catch (err) { |
|
reason = res.statusText; |
|
} |
|
const msg = `ChatGPT error ${res.status}: ${reason}`; |
|
const error = new ChatGPTError(msg, { cause: res }); |
|
error.statusCode = res.status; |
|
error.statusText = res.statusText; |
|
error.context = { url, options }; |
|
throw error; |
|
} |
|
const parser = createParser((event) => { |
|
if (event.type === "event") { |
|
onMessage(event.data); |
|
} |
|
}); |
|
if (!res.body.getReader) { |
|
const body = res.body; |
|
if (!body.on || !body.read) { |
|
throw new ChatGPTError('unsupported "fetch" implementation'); |
|
} |
|
body.on("readable", () => { |
|
let chunk; |
|
while (null !== (chunk = body.read())) { |
|
parser.feed(chunk.toString()); |
|
} |
|
}); |
|
} else { |
|
for await (const chunk of streamAsyncIterable(res.body)) { |
|
const str = new TextDecoder().decode(chunk); |
|
parser.feed(str); |
|
} |
|
} |
|
} |
|
|
|
module.exports = fetchSSE; |