fix(ai): abort Codex SSE body reads

This commit is contained in:
Mario Zechner
2026-05-29 23:20:18 +02:00
parent 7921ae499a
commit a36a132c70
2 changed files with 121 additions and 2 deletions

View File

@@ -543,7 +543,7 @@ async function processStream(
model: Model<"openai-codex-responses">,
options?: OpenAICodexResponsesOptions,
): Promise<void> {
await processResponsesStream(mapCodexEvents(parseSSE(response)), output, stream, model, {
await processResponsesStream(mapCodexEvents(parseSSE(response, options?.signal)), output, stream, model, {
serviceTier: options?.serviceTier,
resolveServiceTier: resolveCodexServiceTier,
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
@@ -621,16 +621,26 @@ function normalizeCodexStatus(status: unknown): CodexResponseStatus | undefined
// SSE Parsing
// ============================================================================
async function* parseSSE(response: Response): AsyncGenerator<Record<string, unknown>> {
async function* parseSSE(response: Response, signal?: AbortSignal): AsyncGenerator<Record<string, unknown>> {
if (!response.body) return;
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
const onAbort = () => {
void reader.cancel().catch(() => {});
};
signal?.addEventListener("abort", onAbort, { once: true });
try {
while (true) {
if (signal?.aborted) {
throw new Error("Request was aborted");
}
const { done, value } = await reader.read();
if (signal?.aborted) {
throw new Error("Request was aborted");
}
if (done) break;
buffer += decoder.decode(value, { stream: true });
@@ -660,6 +670,7 @@ async function* parseSSE(response: Response): AsyncGenerator<Record<string, unkn
}
}
} finally {
signal?.removeEventListener("abort", onAbort);
try {
await reader.cancel();
} catch {}