Skip to content

Commit b63b8da

Browse files
committed
0.1.8
1 parent 34f6c54 commit b63b8da

2 files changed

Lines changed: 295 additions & 4590 deletions

File tree

qwen2api-cf.js

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ export default {
204204
}
205205
},
206206

207-
// ---------------------- 流式响应处理(重构并去重) ----------------------
207+
// ---------------------- 流式响应处理(改进) ----------------------
208208
async handleStreamResponse(fetchResponse, requestId, modelName) {
209209
const { readable, writable } = new TransformStream();
210210
const writer = writable.getWriter();
@@ -215,8 +215,9 @@ export default {
215215
await writer.write(encoder.encode(`data: ${payload}\n\n`));
216216
};
217217

218-
// 用于去重:记录上一次完整接收到的 delta 内容
218+
// 用于去重和累积内容
219219
let previousDelta = "";
220+
let cumulativeContent = ""; // 累积完整内容,解决断流问题
220221

221222
const processStream = async () => {
222223
try {
@@ -227,67 +228,29 @@ export default {
227228

228229
while (true) {
229230
const { done, value } = await reader.read();
230-
if (done) break;
231+
if (done) {
232+
// 确保最后一个缓冲区也被处理
233+
if (buffer.trim()) {
234+
await processBuffer(buffer);
235+
}
236+
break;
237+
}
231238

232239
const chunkStr = decoder.decode(value, { stream: true });
233240
buffer += chunkStr;
234241

235-
// SSE 消息通常以 "\n\n" 分隔
236-
const parts = buffer.split('\n\n');
237-
buffer = parts.pop() || '';
238-
239-
for (const part of parts) {
240-
if (!part.trim()) continue;
241-
242-
const lines = part.split('\n');
243-
for (const line of lines) {
244-
if (!line.startsWith('data: ')) continue;
245-
246-
const dataStr = line.slice('data: '.length).trim();
247-
if (dataStr === '[DONE]') {
248-
await sendSSE('[DONE]');
249-
console.log('收到 [DONE],流结束');
250-
break;
251-
}
252-
253-
try {
254-
const jsonData = JSON.parse(dataStr);
255-
const delta = jsonData?.choices?.[0]?.delta;
256-
if (!delta) continue;
257-
258-
let currentDelta = delta.content || "";
259-
// 去除重复:如果当前内容以上次完整内容为前缀,则只保留新增部分
260-
let newContent = currentDelta;
261-
if (previousDelta && currentDelta.startsWith(previousDelta)) {
262-
newContent = currentDelta.substring(previousDelta.length);
263-
}
264-
previousDelta = currentDelta;
265-
if (!newContent) continue;
266-
267-
const openaiChunk = {
268-
id: `chatcmpl-${requestId}`,
269-
object: 'chat.completion.chunk',
270-
created: Date.now(),
271-
model: modelName,
272-
choices: [
273-
{
274-
index: 0,
275-
delta: isFirstChunk
276-
? { role: 'assistant', content: newContent }
277-
: { content: newContent },
278-
finish_reason: null
279-
}
280-
]
281-
};
282-
283-
if (isFirstChunk) isFirstChunk = false;
284-
await sendSSE(JSON.stringify(openaiChunk));
285-
} catch (err) {
286-
console.error('解析 SSE JSON 失败:', dataStr, err);
287-
}
288-
}
242+
// 更可靠的处理方式:按照 SSE 规范处理双换行符分隔的消息
243+
await processBuffer(buffer);
244+
245+
// 仅保留可能不完整的最后一部分
246+
const lastBoundaryIndex = buffer.lastIndexOf('\n\n');
247+
if (lastBoundaryIndex !== -1) {
248+
buffer = buffer.substring(lastBoundaryIndex + 2);
289249
}
290250
}
251+
252+
// 确保发送最终 DONE 信号
253+
console.log(`流处理完成,累积内容长度: ${cumulativeContent.length}`);
291254
await sendSSE('[DONE]');
292255
} catch (err) {
293256
console.error('处理 SSE 流时出错:', err);
@@ -305,14 +268,100 @@ export default {
305268
]
306269
};
307270
try {
308-
await writer.write(encoder.encode(`data: ${JSON.stringify(errorChunk)}\n\n`));
309-
await writer.write(encoder.encode('data: [DONE]\n\n'));
271+
await sendSSE(JSON.stringify(errorChunk));
272+
await sendSSE('[DONE]');
310273
} catch (_) {}
311274
} finally {
312275
await writer.close();
313276
}
314277
};
315278

279+
// 处理缓冲区内的完整 SSE 消息
280+
const processBuffer = async (buffer) => {
281+
// 按 data: 行分割
282+
const dataLineRegex = /^data: (.+)$/gm;
283+
let match;
284+
285+
while ((match = dataLineRegex.exec(buffer)) !== null) {
286+
const dataStr = match[1].trim();
287+
288+
if (dataStr === '[DONE]') {
289+
await sendSSE('[DONE]');
290+
console.log('收到 [DONE],流结束');
291+
continue;
292+
}
293+
294+
try {
295+
const jsonData = JSON.parse(dataStr);
296+
const delta = jsonData?.choices?.[0]?.delta;
297+
if (!delta) continue;
298+
299+
let currentDelta = delta.content || "";
300+
301+
// 改进的去重逻辑:如果有完整内容,检查是否为前缀
302+
if (currentDelta) {
303+
let newContent = currentDelta;
304+
let needsSending = true;
305+
306+
if (previousDelta && currentDelta.startsWith(previousDelta)) {
307+
// 只提取新增部分
308+
newContent = currentDelta.substring(previousDelta.length);
309+
// 如果没有新增内容,跳过发送
310+
if (!newContent) needsSending = false;
311+
}
312+
313+
if (needsSending) {
314+
// 创建并发送内容块
315+
const openaiChunk = {
316+
id: `chatcmpl-${requestId}`,
317+
object: 'chat.completion.chunk',
318+
created: Date.now(),
319+
model: modelName,
320+
choices: [
321+
{
322+
index: 0,
323+
delta: isFirstChunk
324+
? { role: 'assistant', content: newContent }
325+
: { content: newContent },
326+
finish_reason: null
327+
}
328+
]
329+
};
330+
331+
if (isFirstChunk) isFirstChunk = false;
332+
await sendSSE(JSON.stringify(openaiChunk));
333+
334+
// 累积内容
335+
cumulativeContent += newContent;
336+
}
337+
338+
// 更新之前的内容为当前完整内容
339+
previousDelta = currentDelta;
340+
}
341+
342+
// 处理完成标志
343+
if (jsonData?.choices?.[0]?.finish_reason) {
344+
const finishChunk = {
345+
id: `chatcmpl-${requestId}`,
346+
object: 'chat.completion.chunk',
347+
created: Date.now(),
348+
model: modelName,
349+
choices: [
350+
{
351+
index: 0,
352+
delta: {},
353+
finish_reason: jsonData.choices[0].finish_reason
354+
}
355+
]
356+
};
357+
await sendSSE(JSON.stringify(finishChunk));
358+
}
359+
} catch (err) {
360+
console.error('解析 SSE JSON 失败:', dataStr, err);
361+
}
362+
}
363+
};
364+
316365
processStream();
317366
return new Response(readable, {
318367
headers: {

0 commit comments

Comments
 (0)