Spaces:
Paused
Paused
| import { createParser } from 'eventsource-parser' | |
| export async function* streamAsyncIterable<T>(stream: ReadableStream<T>) { | |
| const reader = stream.getReader() | |
| try { | |
| while (true) { | |
| const { done, value } = await reader.read() | |
| if (done) { | |
| return | |
| } | |
| yield value | |
| } | |
| } finally { | |
| reader.releaseLock() | |
| } | |
| } | |
| export class llmError extends Error { | |
| statusCode?: number; | |
| statusText?: string; | |
| isFinal?: boolean; | |
| accountId?: string; | |
| cause?: any; | |
| constructor(message: string, cause?: any) { | |
| super(message); | |
| if (cause) { | |
| this.cause = cause; | |
| } | |
| // Set the prototype explicitly. | |
| Object.setPrototypeOf(this, llmError.prototype); | |
| } | |
| } | |
| export async function fetchSSE( | |
| url: string, | |
| options: Parameters<typeof fetch>[1] & { | |
| onMessage: (data: string) => void | |
| onError?: (error: any) => void | |
| }, | |
| ) { | |
| const { onMessage, onError, ...fetchOptions } = options | |
| const res = await fetch(url, fetchOptions) | |
| if (!res.ok) { | |
| let reason: string | |
| try { | |
| reason = await res.text() | |
| } catch (err) { | |
| reason = res.statusText | |
| } | |
| const msg = `llm error ${res.status}: ${reason}` | |
| const error = new llmError(msg, { cause: res }) | |
| error.statusCode = res.status | |
| error.statusText = res.statusText | |
| throw error | |
| } | |
| const parser = createParser((event) => { | |
| onMessage(event.data) | |
| }) | |
| // handle special response errors | |
| const feed = (chunk: string) => { | |
| let response = null | |
| try { | |
| response = JSON.parse(chunk) | |
| } catch { | |
| // ignore | |
| } | |
| if (response?.detail?.type === 'invalid_request_error') { | |
| const msg = `llm error ${response.detail.message}: ${response.detail.code} (${response.detail.type})` | |
| const error = new llmError(msg, { cause: response }) | |
| error.statusCode = response.detail.code | |
| error.statusText = response.detail.message | |
| if (onError) { | |
| onError(error) | |
| } else { | |
| console.error(error) | |
| } | |
| // don't feed to the event parser | |
| return | |
| } | |
| parser.feed(chunk) | |
| } | |
| if (!res?.body?.getReader) { | |
| // Vercel polyfills `fetch` with `node-fetch`, which doesn't conform to | |
| // web standards, so this is a workaround... | |
| const body: NodeJS.ReadableStream = res.body as any | |
| if (!body.on || !body.read) { | |
| throw new llmError('unsupported "fetch" implementation') | |
| } | |
| body.on('readable', () => { | |
| let chunk: string | Buffer | |
| while (null !== (chunk = body.read())) { | |
| feed(chunk.toString()) | |
| } | |
| }) | |
| } else { | |
| for await (const chunk of streamAsyncIterable(res.body)) { | |
| const str = new TextDecoder().decode(chunk) | |
| feed(str) | |
| } | |
| } | |
| } |