fix: retry Codex overload error codes
This commit is contained in:
@@ -88,25 +88,22 @@ function createTerminatedSseResponse(res, chunks, destroyDelayMs = 20, options =
|
||||
}, destroyDelayMs);
|
||||
}
|
||||
|
||||
function buildCapacityStreamPayload(message, shape = "default") {
|
||||
function buildCapacityStreamPayload(message, shape = "default", errorCode = "") {
|
||||
const error = {
|
||||
message,
|
||||
type: "server_error",
|
||||
...(errorCode ? { code: errorCode } : {}),
|
||||
};
|
||||
if (shape === "response_failed") {
|
||||
return {
|
||||
type: "response.failed",
|
||||
response: {
|
||||
status: "failed",
|
||||
error: {
|
||||
message,
|
||||
type: "server_error",
|
||||
},
|
||||
error,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: {
|
||||
message,
|
||||
type: "server_error",
|
||||
},
|
||||
};
|
||||
return { error };
|
||||
}
|
||||
|
||||
function createCapacityErrorSseResponse(
|
||||
@@ -116,7 +113,11 @@ function createCapacityErrorSseResponse(
|
||||
options = {},
|
||||
) {
|
||||
const eventName = options.eventName || "error";
|
||||
const payload = options.payload || buildCapacityStreamPayload(message, options.payloadShape || "default");
|
||||
const payload = options.payload || buildCapacityStreamPayload(
|
||||
message,
|
||||
options.payloadShape || "default",
|
||||
options.errorCode || "",
|
||||
);
|
||||
createSseResponse(
|
||||
res,
|
||||
[
|
||||
@@ -231,7 +232,7 @@ function startFakeUpstream(port, options = {}) {
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if (parsed.test_capacity_error) {
|
||||
if (parsed.test_capacity_error && !parsed.stream) {
|
||||
const capacityMessage = parsed.test_capacity_message || "Selected model is at capacity. Please try a different model.";
|
||||
createJsonResponse(
|
||||
res,
|
||||
@@ -240,6 +241,7 @@ function startFakeUpstream(port, options = {}) {
|
||||
error: {
|
||||
message: capacityMessage,
|
||||
type: "server_error",
|
||||
...(parsed.test_capacity_code ? { code: parsed.test_capacity_code } : {}),
|
||||
},
|
||||
},
|
||||
{ "x-upstream-test": `capacity-error-${parsed.test_capacity_status ?? 503}` },
|
||||
@@ -255,6 +257,8 @@ function startFakeUpstream(port, options = {}) {
|
||||
parsed.test_capacity_status ?? "default",
|
||||
parsed.test_capacity_stream_event_name || "",
|
||||
parsed.test_capacity_stream_payload_shape || "",
|
||||
parsed.test_capacity_code || "",
|
||||
parsed.test_capacity_message || "",
|
||||
].join(":");
|
||||
const capacityCount = (capacityBeforeSuccessCounts.get(capacityKey) || 0) + 1;
|
||||
capacityBeforeSuccessCounts.set(capacityKey, capacityCount);
|
||||
@@ -268,6 +272,7 @@ function startFakeUpstream(port, options = {}) {
|
||||
{
|
||||
eventName: parsed.test_capacity_stream_event_name || "error",
|
||||
payloadShape: parsed.test_capacity_stream_payload_shape || "default",
|
||||
errorCode: parsed.test_capacity_code || "",
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -279,6 +284,7 @@ function startFakeUpstream(port, options = {}) {
|
||||
error: {
|
||||
message: capacityMessage,
|
||||
type: "server_error",
|
||||
...(parsed.test_capacity_code ? { code: parsed.test_capacity_code } : {}),
|
||||
},
|
||||
},
|
||||
{ "x-upstream-test": `capacity-error-${parsed.test_capacity_status ?? 503}` },
|
||||
@@ -294,6 +300,7 @@ function startFakeUpstream(port, options = {}) {
|
||||
{
|
||||
eventName: parsed.test_capacity_stream_event_name || "error",
|
||||
payloadShape: parsed.test_capacity_stream_payload_shape || "default",
|
||||
errorCode: parsed.test_capacity_code || "",
|
||||
},
|
||||
);
|
||||
return;
|
||||
@@ -1482,6 +1489,28 @@ async function run() {
|
||||
"stream+response.failed capacity error 返回体未标记 retry trigger",
|
||||
);
|
||||
|
||||
const streamSlowDownCodeResponse = await fetch(`http://127.0.0.1:${gatewayPort}/responses`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
stream: true,
|
||||
test_capacity_error: true,
|
||||
test_capacity_message: "Raw provider asked the client to wait.",
|
||||
test_capacity_code: "slow_down",
|
||||
test_capacity_stream_event_name: "response.failed",
|
||||
test_capacity_stream_payload_shape: "response_failed",
|
||||
}),
|
||||
});
|
||||
const streamSlowDownCodeBody = await streamSlowDownCodeResponse.json();
|
||||
assert(
|
||||
streamSlowDownCodeResponse.status === 502,
|
||||
`stream+slow_down 持续过载未返回 502: ${streamSlowDownCodeResponse.status}`,
|
||||
);
|
||||
assert(
|
||||
streamSlowDownCodeBody?.error?.code === "upstream_error_retry_triggered",
|
||||
"stream+slow_down 持续过载未标记 retry trigger",
|
||||
);
|
||||
|
||||
const streamCapacityRecoveredResponse = await fetch(`http://127.0.0.1:${gatewayPort}/responses`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
@@ -1520,6 +1549,43 @@ async function run() {
|
||||
);
|
||||
assert(streamCapacityResponseFailedRecoveredEntry, "stream response.failed capacity 恢复后的请求记录未保留重试次数");
|
||||
|
||||
const overloadCodeThreadId = "thread-server-overloaded-code-retry";
|
||||
const streamOverloadCodeRecoveredResponse = await readSseUntilClose(
|
||||
`http://127.0.0.1:${gatewayPort}/responses`,
|
||||
{
|
||||
stream: true,
|
||||
thread_id: overloadCodeThreadId,
|
||||
test_capacity_before_success_times: 2,
|
||||
test_capacity_message: "Raw provider reported a temporary overload.",
|
||||
test_capacity_code: "server_is_overloaded",
|
||||
test_capacity_stream_event_name: "response.failed",
|
||||
test_capacity_stream_payload_shape: "response_failed",
|
||||
test_reasoning_tokens: 128,
|
||||
},
|
||||
);
|
||||
assert(
|
||||
streamOverloadCodeRecoveredResponse.status === 200,
|
||||
`stream+server_is_overloaded 未自动恢复: ${streamOverloadCodeRecoveredResponse.status}`,
|
||||
);
|
||||
assert(
|
||||
streamOverloadCodeRecoveredResponse.text.includes("hello"),
|
||||
"stream+server_is_overloaded 恢复后未拿到正常 SSE 内容",
|
||||
);
|
||||
assert(
|
||||
!streamOverloadCodeRecoveredResponse.text.includes("Raw provider reported a temporary overload."),
|
||||
"stream+server_is_overloaded 自动恢复前不应向客户端泄漏失败轮次",
|
||||
);
|
||||
const overloadCodeRequestsResponse = await fetch(
|
||||
`http://127.0.0.1:${gatewayPort}/__codex_retry_gateway/api/requests?query=${encodeURIComponent(overloadCodeThreadId)}`,
|
||||
{ headers: adminHeaders },
|
||||
);
|
||||
const overloadCodeRequestsPayload = await overloadCodeRequestsResponse.json();
|
||||
const overloadCodeEntry = (overloadCodeRequestsPayload?.entries || []).find(
|
||||
(entry) => entry.thread_id === overloadCodeThreadId,
|
||||
);
|
||||
assert(overloadCodeEntry?.status_code === 200, "server_is_overloaded 恢复请求未记录最终 200");
|
||||
assert(overloadCodeEntry?.upstream_attempt_count === 3, "server_is_overloaded 恢复请求未记录 3 次上游尝试");
|
||||
|
||||
const streamThreadResponse = await readSseUntilClose(
|
||||
`http://127.0.0.1:${gatewayPort}/responses`,
|
||||
{ stream: true, test_reasoning_tokens: 128, thread_id: "thread_stream_ok" },
|
||||
|
||||
Reference in New Issue
Block a user