为啥浏览器中js请求gemini兼容openai的api出现断句?是不是const chunk = decoder.decode(value);有问题?

实际获得数据eventstream是
data: {"choices":[{"delta":{"content":"我","role":"assistant"},"index":0}],"created":1750638999,"id":"l6FYaK3oJv_bgLUPvdfCsQU","model":"gemini-2.0-flash","object":"chat.completion.chunk"}
data: {"choices":[{"delta":{"content":"是一个大型语言","role":"assistant"},"index":0}],"created":1750638999,"id":"l6FYaK3oJv_bgLUPvdfCsQU","model":"gemini-2.0-flash","object":"chat.completion.chunk"}
data: {"choices":[{"delta":{"content":"模型,由 Google 训练。\n","role":"assistant"},"finish_reason":"stop","index":0}],"created":1750638999,"id":"l6FYaK3oJv_bgLUPvdfCsQU","model":"gemini-2.0-flash","object":"chat.completion.chunk"}
data: [DONE]
try {
// 3. 发送请求到后端(即当前PHP文件)
const response = await fetch("https://generativelanguage.googleapis.com/v1beta/openai", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body:JSON.stringify(resdata.postdata) ,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 4. 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let assistantResponse = '';
while (true) {
const {
value,
done
} = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.substring(6);
if (jsonStr.trim() === '[DONE]') {
break;
}
try {
const parsed = JSON.parse(jsonStr);
const content = parsed.choices[0]?.delta?.content || '';
if (content) {
assistantResponse += content;
assistantMessageDiv.innerText = assistantResponse; // 使用innerText防止XSS
scrollToBottom();
}
} catch (e) {
console.error('Error parsing stream JSON:', e);
}
}
}
}
// 将完整的AI回复添加到历史记录
chatHistory.push({
role: 'assistant', content: assistantResponse
});
} catch (error) {
console.error('Fetch error:', error);
assistantMessageDiv.innerText = '抱歉,我好像出错了。请稍后再试。';
} finally {
// 重新启用按钮
sendButton.disabled = false;
messageInput.focus();
}
} else {
console.error('请求失败:', responseurl.status);
}
网友回复
主要问题:未处理不完整的消息块:
你的代码 const chunk = decoder.decode(value); const lines = chunk.split('\n'); 是在拿到一个数据块后立即按换行符分割。
如果一个数据块是 data: {"choices":[{"delta":{"content":"我是一个 (注意,JSON 在这里被截断了),你的代码会尝试解析 {"choices":[{"delta":{"content":"我是一个,这必然导致 JSON.parse 失败,然后这个 "我是一个" 的内容就永久丢失了。
次要问题:对 [DONE] 的处理逻辑有误:
在你的 for 循环中,当遇到 data: [DONE] 时,你使用了 break;
修复后
try {
// 3. 发送请求到后端
const response = await fetch(resdata.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(resdata.postdata),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 4. 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let assistantResponse = '';
assistantMessageDiv.innerHTML = ''; // 清除“思考中”动画
// --- 新增代码:缓冲区 ---
let buffer = '';
while (true) {
const { value, done } = await reader.read();
// 当流结束时,done 为 true
if (done) {
// 在退出前,检查缓冲区是否还有剩余的、未处理的数据
if (buffer.length > 0) {
// 这种情况不常见,但为了健壮性最好加上
console.warn('Stream ended with unprocessed data in buffer:', buffer);
}
break; // 正常退出循环
}
// 将新收到的数据块解码并追加到缓冲区
// 使用 { stream: true } 可以更好地处理多字节字符(如中文)被截断的情况
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
// -...点击查看剩余70%


