fix: replay buffered success streams incrementally
This commit is contained in:
@@ -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`, {
|
||||
|
||||
Reference in New Issue
Block a user