背景
在用大模型做一个对话应用时,因为要输出stream,所以只能用fetch,传统的xmlrequest无法实现。
使用过程
后端代码:
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const response = await fetch('https://api.openai.com/v1/chat/completions', fetchOptions)
const stream = new ReadableStream({
async start(controller) {
const onParse = (event: ParsedEvent | ReconnectInterval) => {
if (event.type === 'event') {
const data = event.data;
try {
const json = JSON.parse(data);
if (json.choices[0].finish_reason != null) {
controller.close();
return;
}
const text = json.choices[0].delta.content;
const queue = encoder.encode(text);
controller.enqueue(queue);
} catch (e) {
controller.error(e);
}
}
};
const parser = createParser(onParse);
for await (const chunk of response.body as any) {
parser.feed(decoder.decode(chunk));
}
},
});
return new Response(stream)
};
前端代码:
const controller = new AbortController();
const chatRes = await fetch('/api/chat/', {
method: 'post',
body: body,
signal: controller.signal
})
const data = chatRes.body;
const reader = data!.getReader();
const decoder = new TextDecoder();
let done = false;
let text = '';
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
const chunkValue = decoder.decode(value);
text += chunkValue;
console.log(text);
}
由于前端body参数错误,导致大模型返回了错误,后端代码也没有报错,结果前端读流读不出来。
const { value, done: doneReading } = await reader.read();
这个value是undefined,done直接是true
按照正常思路,如果是传统的json格式一定会解析错误,很容易就能知道参数有问题,变成流以后,错误也是流的方式,只不过读取不到值,因此排查比较困难。
解决
在后端代码那一部分:
const response = await fetch('https://api.openai.com/v1/chat/completions', fetchOptions)
// 加上一个状态判断就ok了,如果是没有问题的,openai默认正确的都是200,错误的就不是流的方式,直接是json的方式,因此加了一个类似异常捕获。
if (response.status !== 200) {
const text = await response.text()
throw new Error(text)
}
最后
写fetch无论是不是流的方式,最好都加一个异常判断,这样错误容易及早发现。