使用 fetch 请求 openai stream 响应时,内容偶尔会被“切断”

查看 87|回复 5
作者:s609926202   
const response = await fetch(...);
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (!done) {
    const { value, done: readerDone } = await reader.read();
    if (value) {
            const char = decoder.decode(value);
        console.log(char);
    }
}
代码如上,有时候打印出来的 char 为:
data: {"id":"chatcmpl-7Y79egENb17GOU20IaW5KgJJhbf4M","object":"chat.completion.chunk","created":1688365010,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-7Y79egEN
图示: https://i.imgur.com/P1YQs4q.png
也就是从"id"中间被切断了,导致内容少 1 到 2 个字。
请问有啥可改进的方法吗?

const, char, value, await

Opportunity   
为啥不直接用 EventSource 读,要自己手写这玩意?非要手写的话可以去参考下 EventSource 的 polyfill 怎么实现的。
s609926202
OP
  
@Opportunity #1 不会。现在都是还是网上东拼西凑来的。。
Erroad   
当服务器端向客户端发送一段 HTTP 流( HTTP Streaming )时,数据是以块( chunks )的形式发送的,而不是一次性发送全部。在浏览器环境中,我们可以使用 Fetch API 的流( stream )读取器读取到这些数据。
这是一个基本的例子:
```javascript
fetch('/your-http-streaming-url')
.then(response => {
const reader = response.body.getReader();
const stream = new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
push();
})
.catch(error => {
console.error(error);
controller.error(error);
})
}
push();
}
});
return new Response(stream, { headers: { "Content-Type": "text/html" } });
})
.then(response => response.text())
.then(result => {
console.log(result);
})
.catch(err => {
console.error(err);
});
```
这个示例做了以下事情:
1. 使用 `fetch` API 获取数据流。
2. 创建一个流读取器( stream reader )读取响应主体。
3. 创建一个新的 `ReadableStream`,在它的 `start` 函数中读取数据,并通过 `controller.enqueue` 方法将数据加入队列中。
4. 如果读取过程中出现错误,使用 `controller.error` 将错误信息发送出去。
5. 当数据全部读取完毕,关闭控制器 `controller.close`。
6. 最后,获取到的数据通过 `Response.text()` 转化为文本格式,并输出。
注意,上述示例仅适用于文本数据流,如果你需要处理的是二进制数据流,可能需要进行适当的调整。例如,你可能需要使用 `Response.blob()` 代替 `Response.text()`。
chatGPT 的回答
zhuisui   
你好像没有正确处理 done
s609926202
OP
  
@zhuisui #4 在循环体中处理的
```
if (choices.finish_reason === 'stop' || choices.finish_reason === 'function_call') {
done = true;
break;
}
```
您需要登录后才可以回帖 登录 | 立即注册

返回顶部