Skip to content

Commit 186a151

Browse files
committed
fix: 修复node中的stream支持,使用chatgpt-api中的fetchSSE实现SSE
fetchSSE code by https://github.com/transitive-bullshit/chatgpt-api/blob/600b35eaec985bbbfcb6c77776dc30d4614bd085/src/fetch-sse.ts#L7
1 parent aa383f1 commit 186a151

File tree

3 files changed

+87
-7
lines changed

3 files changed

+87
-7
lines changed

cjs/fetchSSE.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const { createParser } = require('eventsource-parser');
2+
3+
module.exports = async function fetchSSE(url, options, fetch) {
4+
const { onmessage, onError, ...fetchOptions } = options;
5+
const res = await fetch(url, fetchOptions);
6+
if (!res.ok) {
7+
let reason;
8+
9+
try {
10+
reason = await res.text();
11+
} catch (err) {
12+
reason = res.statusText;
13+
}
14+
15+
const msg = `ChatGPT error ${res.status}: ${reason}`;
16+
const error = new Error(msg, { cause: res });
17+
error.statusCode = res.status;
18+
error.statusText = res.statusText;
19+
throw error;
20+
}
21+
22+
const parser = createParser((event) => {
23+
if (event.type === 'event') {
24+
onmessage(event.data);
25+
}
26+
});
27+
28+
// handle special response errors
29+
const feed = (chunk) => {
30+
let response = null;
31+
32+
try {
33+
response = JSON.parse(chunk);
34+
} catch {
35+
// ignore
36+
}
37+
38+
if (response?.detail?.type === 'invalid_request_error') {
39+
const msg = `ChatGPT error ${response.detail.message}: ${response.detail.code} (${response.detail.type})`;
40+
const error = new Error(msg, { cause: response });
41+
error.statusCode = response.detail.code;
42+
error.statusText = response.detail.message;
43+
44+
if (onError) {
45+
onError(error);
46+
} else {
47+
console.error(error);
48+
}
49+
50+
// don't feed to the event parser
51+
return;
52+
}
53+
54+
parser.feed(chunk);
55+
};
56+
57+
if (!res.body.getReader) {
58+
// Vercel polyfills `fetch` with `node-fetch`, which doesn't conform to
59+
// web standards, so this is a workaround...
60+
const body = res.body;
61+
62+
if (!body.on || !body.read) {
63+
throw new Error('unsupported "fetch" implementation');
64+
}
65+
66+
body.on('readable', () => {
67+
let chunk;
68+
while (null !== (chunk = body.read())) {
69+
feed(chunk.toString());
70+
}
71+
});
72+
} else {
73+
for await (const chunk of streamAsyncIterable(res.body)) {
74+
const str = new TextDecoder().decode(chunk);
75+
feed(str);
76+
}
77+
}
78+
};

cjs/index.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const fetchEventSource = require('@microsoft/fetch-event-source');
1+
const fetchSSE = require('./fetchSSE.js');
22
const fetch = require('node-fetch');
33

44
module.exports = class Api2d {
@@ -53,27 +53,28 @@ module.exports = class Api2d {
5353
// throw new Error( "Timeout "+ this.timeout );
5454
reject(new Error(`[408]:Timeout by ${this.timeout} ms`));
5555
}, this.timeout);
56-
const response = await fetchEventSource(url, {
56+
const response = await fetchSSE(url, {
5757
signal: this.controller.signal,
5858
method: 'POST',
59+
openWhenHidden: true,
60+
fetch: fetch,
5961
headers: { ...headers, Accept: 'text/event-stream' },
6062
body: JSON.stringify({ ...restOptions, model: model || 'gpt-3.5-turbo' }),
6163
async onopen(response) {
6264
if (response.status != 200) {
6365
throw new Error(`[${response.status}]:${response.statusText}`);
6466
}
6567
},
66-
onmessage: (e) => {
68+
onmessage: (data) => {
6769
if (timeout_handle) {
6870
clearTimeout(timeout_handle);
6971
}
70-
if (e.data == '[DONE]') {
72+
if (data == '[DONE]') {
7173
// console.log( 'DONE' );
7274
if (onEnd) onEnd(chars);
7375
resolve(chars);
7476
} else {
75-
// console.log( e.data );
76-
const event = JSON.parse(e.data);
77+
const event = JSON.parse(data);
7778
if (event.choices[0].delta.content) chars += event.choices[0].delta.content;
7879
if (onMessage) onMessage(chars);
7980
}
@@ -82,7 +83,7 @@ module.exports = class Api2d {
8283
console.log(error);
8384
throw new Error(String(error)?.match(/\[(\d+)\]/)?.[1] ? error : `[500]:${error}`);
8485
}
85-
});
86+
}, global.fetch || fetch);
8687

8788
// const ret = await response.json();
8889
} catch (error) {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"private": false,
1212
"dependencies": {
1313
"@microsoft/fetch-event-source": "^2.0.1",
14+
"eventsource-parser": "^1.0.0",
1415
"node-fetch": "^2.6.9"
1516
}
1617
}

0 commit comments

Comments
 (0)