fix: replay buffered success streams incrementally

This commit is contained in:
2026-07-08 21:27:39 +08:00
parent f9a71663f6
commit 26070fb7d5
3 changed files with 139 additions and 39 deletions
+65 -32
View File
@@ -299,11 +299,19 @@ function startFakeUpstream(port) {
return;
}
if (parsed.stream) {
createSseResponse(res, [
`data: ${JSON.stringify({ type: "response.output_text.delta", delta: "hello", response_id: "resp_stream", thread_id: parsed.thread_id || "thread_stream", retry_attempt: reasoningAttempt })}\n\n`,
const deltaChunkCount = Number.isInteger(parsed.test_stream_delta_chunks) && parsed.test_stream_delta_chunks > 0
? parsed.test_stream_delta_chunks
: 1;
const deltaTextBase = parsed.test_stream_delta_text || "hello";
const streamChunks = Array.from({ length: deltaChunkCount }, (_, index) => {
const deltaText = deltaChunkCount === 1 ? deltaTextBase : `${deltaTextBase}-${index + 1}`;
return `data: ${JSON.stringify({ type: "response.output_text.delta", delta: deltaText, response_id: "resp_stream", thread_id: parsed.thread_id || "thread_stream", retry_attempt: reasoningAttempt })}\n\n`;
});
streamChunks.push(
`data: {"response":{"usage":{"output_tokens_details":{"reasoning_tokens":${reasoning}}}}}\n\n`,
"data: [DONE]\n\n",
], parsed.test_reasoning_response_delay_ms ?? parsed.test_stream_chunk_delay_ms ?? 20, {
);
createSseResponse(res, streamChunks, parsed.test_reasoning_response_delay_ms ?? parsed.test_stream_chunk_delay_ms ?? 20, {
headers: reasoningAttempt
? { "x-upstream-reasoning-attempt": `${reasoningAttempt}` }
: {},
@@ -430,40 +438,49 @@ function startGateway(configPath, logPath) {
};
}
async function readSseUntilClose(url, requestBody) {
const response = await fetch(url, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(requestBody),
});
async function readSseUntilClose(url, requestBody) {
const startedAt = Date.now();
const response = await fetch(url, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(requestBody),
});
const reader = response.body.getReader();
const decoder = new TextDecoder("utf8");
let text = "";
let closedByError = false;
while (true) {
try {
const { done, value } = await reader.read();
if (done) {
break;
}
text += decoder.decode(value, { stream: true });
} catch (error) {
closedByError = true;
text += `\n[[reader-error:${error?.name || "unknown"}]]`;
break;
const reader = response.body.getReader();
const decoder = new TextDecoder("utf8");
let text = "";
let closedByError = false;
let readCount = 0;
let firstChunkDelayMs = null;
while (true) {
try {
const { done, value } = await reader.read();
if (done) {
break;
}
readCount += 1;
if (firstChunkDelayMs === null) {
firstChunkDelayMs = Date.now() - startedAt;
}
text += decoder.decode(value, { stream: true });
} catch (error) {
closedByError = true;
text += `\n[[reader-error:${error?.name || "unknown"}]]`;
break;
}
}
text += decoder.decode();
return {
status: response.status,
headers: response.headers,
text,
closedByError,
};
}
return {
status: response.status,
headers: response.headers,
text,
closedByError,
readCount,
firstChunkDelayMs,
};
}
async function run() {
assert(
@@ -1052,6 +1069,22 @@ async function run() {
assert(okStream.status === 200, `${streamPath} 128 首状态异常: ${okStream.status}`);
assert(okStream.text.includes("[DONE]"), `${streamPath} 流式 128 未完整结束`);
assert(!okStream.closedByError, `${streamPath} 流式 128 不应异常断开`);
if (streamPath === "/responses" || streamPath === "/v1/responses") {
const replayedStream = await readSseUntilClose(
`http://127.0.0.1:${gatewayPort}${streamPath}`,
{
stream: true,
test_reasoning_tokens: 128,
test_stream_delta_chunks: 48,
test_stream_delta_text: "chunk",
test_stream_chunk_delay_ms: 2,
},
);
assert(replayedStream.status === 200, `${streamPath} 回放流首状态异常: ${replayedStream.status}`);
assert(replayedStream.text.includes("chunk-48"), `${streamPath} 回放流未保留尾部 delta`);
assert(replayedStream.readCount > 1, `${streamPath} 成功流不应退化为单块回放`);
}
}
const streamProgressPromise = fetch(`http://127.0.0.1:${gatewayPort}/responses`, {