From 452a61eb1bebd644c7e454a6936ee350194233c8 Mon Sep 17 00:00:00 2001 From: Tao He Date: Sat, 4 Mar 2023 21:27:37 +0800 Subject: [PATCH] Handle possible token expired errors Signed-off-by: Tao He --- src/chatgpt-unofficial-proxy-api.ts | 5 +++- src/fetch-sse.ts | 36 +++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/chatgpt-unofficial-proxy-api.ts b/src/chatgpt-unofficial-proxy-api.ts index 87eae0a..68570b2 100644 --- a/src/chatgpt-unofficial-proxy-api.ts +++ b/src/chatgpt-unofficial-proxy-api.ts @@ -219,8 +219,11 @@ export class ChatGPTUnofficialProxyAPI { } } catch (err) { // ignore for now; there seem to be some non-json messages - // console.warn('fetchSSE onMessage unexpected error', err) + reject(err) } + }, + onError: (err) => { + reject(err) } }, this._fetch diff --git a/src/fetch-sse.ts b/src/fetch-sse.ts index 1900efa..e9ea479 100644 --- a/src/fetch-sse.ts +++ b/src/fetch-sse.ts @@ -6,10 +6,13 @@ import { streamAsyncIterable } from './stream-async-iterable' export async function fetchSSE( url: string, - options: Parameters[1] & { onMessage: (data: string) => void }, + options: Parameters[1] & { + onMessage: (data: string) => void + onError?: (error: any) => void + }, fetch: types.FetchFn = globalFetch ) { - const { onMessage, ...fetchOptions } = options + const { onMessage, onError, ...fetchOptions } = options const res = await fetch(url, fetchOptions) if (!res.ok) { let reason: string @@ -33,6 +36,31 @@ export async function fetchSSE( } }) + // check if the response is an error, if so, throw it + const feed = (chunk: string) => { + let response = null + try { + response = JSON.parse(chunk) + } catch { + /// ignore + } + if (response?.detail) { + if (response.detail.type === 'invalid_request_error') { + const msg = `ChatGPT error ${response.detail.message}: ${response.detail.code} (${response.detail.type})` + const error = new types.ChatGPTError(msg, { cause: response }) + error.statusCode = response.detail.code + error.statusText = response.detail.message + if (onError) { + onError(error) + } else { + console.error(error) + } + return // don't feed to event parser + } + } + 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... @@ -45,13 +73,13 @@ export async function fetchSSE( body.on('readable', () => { let chunk: string | Buffer while (null !== (chunk = body.read())) { - parser.feed(chunk.toString()) + feed(chunk.toString()) } }) } else { for await (const chunk of streamAsyncIterable(res.body)) { const str = new TextDecoder().decode(chunk) - parser.feed(str) + feed(str) } } }