@@ -67,6 +67,7 @@ const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
|
||||
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
||||
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
||||
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
|
||||
const PREVIOUS_RESPONSE_NOT_FOUND_CODE = "previous_response_not_found";
|
||||
|
||||
const CODEX_RESPONSE_STATUSES = new Set<CodexResponseStatus>([
|
||||
"completed",
|
||||
@@ -273,6 +274,7 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
|
||||
const httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
|
||||
const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
|
||||
const transport = options?.transport || "auto";
|
||||
let startEmitted = false;
|
||||
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(options?.sessionId);
|
||||
if (websocketDisabledForSession) {
|
||||
recordWebSocketSseFallback(options?.sessionId);
|
||||
@@ -281,6 +283,7 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
|
||||
if (transport !== "sse" && !websocketDisabledForSession) {
|
||||
let websocketStarted = false;
|
||||
let retriedWebSocketConnectionLimit = false;
|
||||
let retriedMissingWebSocketContinuation = false;
|
||||
while (true) {
|
||||
websocketStarted = false;
|
||||
try {
|
||||
@@ -293,6 +296,10 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
|
||||
model,
|
||||
() => {
|
||||
websocketStarted = true;
|
||||
if (!startEmitted) {
|
||||
startEmitted = true;
|
||||
stream.push({ type: "start", partial: output });
|
||||
}
|
||||
},
|
||||
httpTimeoutMs,
|
||||
websocketConnectTimeoutMs,
|
||||
@@ -312,6 +319,11 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
|
||||
} catch (error) {
|
||||
const aborted = options?.signal?.aborted;
|
||||
const connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);
|
||||
const previousResponseNotFound = isPreviousResponseNotFoundError(error);
|
||||
if (!aborted && previousResponseNotFound && !retriedMissingWebSocketContinuation) {
|
||||
retriedMissingWebSocketContinuation = true;
|
||||
continue;
|
||||
}
|
||||
if (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {
|
||||
retriedWebSocketConnectionLimit = true;
|
||||
continue;
|
||||
@@ -432,7 +444,10 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
|
||||
throw new Error("No response body");
|
||||
}
|
||||
|
||||
stream.push({ type: "start", partial: output });
|
||||
if (!startEmitted) {
|
||||
startEmitted = true;
|
||||
stream.push({ type: "start", partial: output });
|
||||
}
|
||||
await processStream(response, output, stream, model, options);
|
||||
|
||||
if (options?.signal?.aborted) {
|
||||
@@ -636,6 +651,10 @@ function isWebSocketConnectionLimitReachedError(error: unknown): boolean {
|
||||
return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
|
||||
}
|
||||
|
||||
function isPreviousResponseNotFoundError(error: unknown): boolean {
|
||||
return error instanceof CodexApiError && error.code === PREVIOUS_RESPONSE_NOT_FOUND_CODE;
|
||||
}
|
||||
|
||||
function extractCodexEventError(event: Record<string, unknown>): { code?: string; message?: string } {
|
||||
const nested = event.error && typeof event.error === "object" ? (event.error as Record<string, unknown>) : undefined;
|
||||
return {
|
||||
@@ -1358,8 +1377,6 @@ function buildCachedWebSocketRequestBody(entry: CachedWebSocketConnection, body:
|
||||
|
||||
async function* startWebSocketOutputOnFirstEvent(
|
||||
events: AsyncIterable<ResponseStreamEvent>,
|
||||
output: AssistantMessage,
|
||||
stream: AssistantMessageEventStream,
|
||||
onStart: () => void,
|
||||
): AsyncGenerator<ResponseStreamEvent> {
|
||||
let started = false;
|
||||
@@ -1367,7 +1384,6 @@ async function* startWebSocketOutputOnFirstEvent(
|
||||
if (!started) {
|
||||
started = true;
|
||||
onStart();
|
||||
stream.push({ type: "start", partial: output });
|
||||
}
|
||||
yield event;
|
||||
}
|
||||
@@ -1422,8 +1438,6 @@ async function processWebSocketStream(
|
||||
await processResponsesStream(
|
||||
startWebSocketOutputOnFirstEvent(
|
||||
mapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs)),
|
||||
output,
|
||||
stream,
|
||||
onStart,
|
||||
),
|
||||
output,
|
||||
|
||||
Reference in New Issue
Block a user