| 46 | * - Custom headers for Codex backend |
| 47 | */ |
| 48 | export class OpenAiCodexHandler extends BaseProvider implements SingleCompletionHandler { |
| 49 | protected options: ApiHandlerOptions |
| 50 | private readonly providerName = "OpenAI Codex" |
| 51 | private client?: OpenAI |
| 52 | // Complete response output array |
| 53 | private lastResponseOutput: any[] | undefined |
| 54 | // Last top-level response id |
| 55 | private lastResponseId: string | undefined |
| 56 | // Abort controller for cancelling ongoing requests |
| 57 | private abortController?: AbortController |
| 58 | // Session ID for the Codex API (persists for the lifetime of the handler) |
| 59 | private readonly sessionId: string |
| 60 | /** |
| 61 | * Some Codex/Responses streams emit tool-call argument deltas without stable call id/name. |
| 62 | * Track the last observed tool identity from output_item events so we can still |
| 63 | * emit `tool_call_partial` chunks (tool-call-only streams). |
| 64 | */ |
| 65 | private pendingToolCallId: string | undefined |
| 66 | private pendingToolCallName: string | undefined |
| 67 | // Tracks whether this response already emitted text to avoid duplicate done-event rendering. |
| 68 | private sawTextOutputInCurrentResponse = false |
| 69 | // Tracks whether text arrived through delta events so content_part events can be treated as fallback-only. |
| 70 | private sawTextDeltaInCurrentResponse = false |
| 71 | // Tracks tool call IDs emitted via streaming partial events to prevent done-event duplicates. |
| 72 | private streamedToolCallIds = new Set<string>() |
| 73 | |
| 74 | // Event types handled by the shared event processor |
| 75 | private readonly coreHandledEventTypes = new Set<string>([ |
| 76 | "response.text.delta", |
| 77 | "response.output_text.delta", |
| 78 | "response.text.done", |
| 79 | "response.output_text.done", |
| 80 | "response.content_part.added", |
| 81 | "response.content_part.done", |
| 82 | "response.reasoning.delta", |
| 83 | "response.reasoning_text.delta", |
| 84 | "response.reasoning_summary.delta", |
| 85 | "response.reasoning_summary_text.delta", |
| 86 | "response.refusal.delta", |
| 87 | "response.output_item.added", |
| 88 | "response.output_item.done", |
| 89 | "response.done", |
| 90 | "response.completed", |
| 91 | "response.tool_call_arguments.delta", |
| 92 | "response.function_call_arguments.delta", |
| 93 | "response.tool_call_arguments.done", |
| 94 | "response.function_call_arguments.done", |
| 95 | ]) |
| 96 | |
| 97 | constructor(options: ApiHandlerOptions) { |
| 98 | super() |
| 99 | this.options = options |
| 100 | // Generate a new session ID for standalone handler usage (fallback) |
| 101 | this.sessionId = uuidv7() |
| 102 | } |
| 103 | |
| 104 | private normalizeUsage(usage: any, model: OpenAiCodexModel): ApiStreamUsageChunk | undefined { |
| 105 | if (!usage) return undefined |
nothing calls this directly
no outgoing calls
no test coverage detected