为啥浏览器中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%