Skip to content

Commit 2fe268f

Browse files
committed
feat(rotator_library): ✨ add OpenAI Codex provider with Responses API support (#33)
* feat(rotator_library): ✨ add OpenAI Codex provider with Responses API support Adds a new provider for OpenAI Codex models (GPT-5, GPT-5.1, GPT-5.2, Codex, Codex Mini) via the ChatGPT Responses API with OAuth PKCE authentication. Key features: - OAuth base class for OpenAI authentication with PKCE flow and token refresh - Responses API streaming with SSE event handling - Reasoning/thinking output with configurable effort levels (minimal to xhigh) - Tool calling support translated from OpenAI format - System prompt validation using official opencode prompt - Usage tracking with proper litellm.Usage objects Files added: - codex_provider.py: Main provider implementation - openai_oauth_base.py: OAuth base class with PKCE support - codex_prompt.txt: Required system prompt for API validation * fix(codex): 🐛 prefix model names with provider identifier Models must be returned with the 'codex/' prefix (e.g., 'codex/gpt-5.2') to match the convention used by other providers like antigravity. This ensures proper provider routing in the RotatingClient. * feat(codex): ✨ add reasoning effort model variants Exposes reasoning effort levels as model variants: - codex/gpt-5.2:low, :medium, :high, :xhigh - codex/gpt-5.1:low, :medium, :high - And similar for all codex models This allows clients to control reasoning effort by model name, similar to how gemini models use :thinking suffix. Total models: 9 base + 30 reasoning variants = 39 models * feat(codex): ✨ add identity override for user system prompt precedence Injects an identity override as the first user message that tells the model to prioritize user-provided system prompts over the required opencode instructions. This mirrors the pattern used by Antigravity provider. Message order: 1. Identity override (<system_override> tag) 2. User's system message (converted to user message) 3. Rest of conversation Controlled by CODEX_INJECT_IDENTITY_OVERRIDE env var (default: true) * docs: 📝 add Codex provider configuration to .env.example Documents all Codex environment variables: - CODEX_REASONING_EFFORT: low/medium/high/xhigh - CODEX_REASONING_SUMMARY: auto/concise/detailed/none - CODEX_REASONING_COMPAT: think-tags/raw/none - CODEX_INJECT_IDENTITY_OVERRIDE: true/false - CODEX_INJECT_INSTRUCTION: true/false - CODEX_EMPTY_RESPONSE_ATTEMPTS: retry count - CODEX_EMPTY_RESPONSE_RETRY_DELAY: seconds - CODEX_OAUTH_PORT: callback port * fix(codex): 🐛 register Codex in provider factory for OAuth TUI Adds Codex to PROVIDER_MAP so it appears in the credential tool's "Add OAuth Credential" menu alongside other OAuth providers. * feat(codex): ✨ add quota tracking with rate limit header parsing Integrate CodexQuotaTracker mixin into CodexProvider to track API usage via response headers and periodic quota API calls. Enables smart credential selection based on remaining quota and automatic cooldowns when limits are approached. Signed-off-by: Moeeze Hassan <fammas.maz@gmail.com> * feat(codex): add gpt-5.3-codex to model list and reasoning config * feat(codex): wire quota header updates to UsageManager for TUI display - Add set_usage_manager() to CodexQuotaTracker mixin - Push parsed rate limit headers to UsageManager via update_quota_baseline - Inject UsageManager reference during RotatingClient initialization - Enables quota status display in TUI after each Codex API response * feat(codex): default to sequential credential selection for token caching Sequential mode reuses the same credential across requests, enabling upstream token caching on the Codex API. * fix(codex): standardize ChatGPT-Account-Id header casing Match the exact capitalization used by the Codex CLI, consistent with codex_quota_tracker.py. Addresses PR review feedback.
1 parent 66d8f68 commit 2fe268f

File tree

9 files changed

+3681
-0
lines changed

9 files changed

+3681
-0
lines changed

.env.example

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,65 @@
404404
# Default: 8085 (same as Gemini CLI, shared)
405405
# ANTIGRAVITY_OAUTH_PORT=8085
406406

407+
# ------------------------------------------------------------------------------
408+
# | [CODEX] OpenAI Codex Provider Configuration |
409+
# ------------------------------------------------------------------------------
410+
#
411+
# Codex provider uses OAuth authentication with OpenAI's ChatGPT backend API.
412+
# Credentials are stored in oauth_creds/ directory as codex_oauth_*.json files.
413+
#
414+
415+
# --- Reasoning Effort ---
416+
# Controls how much "thinking" the model does before responding.
417+
# Higher effort = more thorough reasoning but slower responses.
418+
#
419+
# Available levels (model-dependent):
420+
# - low: Minimal reasoning, fastest responses
421+
# - medium: Balanced (default)
422+
# - high: More thorough reasoning
423+
# - xhigh: Maximum reasoning (gpt-5.2, gpt-5.2-codex, gpt-5.1-codex-max only)
424+
#
425+
# Can also be controlled per-request via:
426+
# 1. Model suffix: codex/gpt-5.2:high
427+
# 2. Request param: "reasoning_effort": "high"
428+
#
429+
# CODEX_REASONING_EFFORT=medium
430+
431+
# --- Reasoning Summary ---
432+
# Controls how reasoning is summarized in responses.
433+
# Options: auto, concise, detailed, none
434+
# CODEX_REASONING_SUMMARY=auto
435+
436+
# --- Reasoning Output Format ---
437+
# How reasoning/thinking is presented in responses.
438+
# Options:
439+
# - think-tags: Wrap in <think>...</think> tags (default, matches other providers)
440+
# - raw: Include reasoning as-is
441+
# - none: Don't include reasoning in output
442+
# CODEX_REASONING_COMPAT=think-tags
443+
444+
# --- Identity Override ---
445+
# When true, injects an override that tells the model to prioritize
446+
# user-provided system prompts over the required opencode instructions.
447+
# CODEX_INJECT_IDENTITY_OVERRIDE=true
448+
449+
# --- Instruction Injection ---
450+
# When true, injects the required opencode system instruction.
451+
# Only disable if you know what you're doing (API may reject requests).
452+
# CODEX_INJECT_INSTRUCTION=true
453+
454+
# --- Empty Response Handling ---
455+
# Number of retry attempts when receiving empty responses.
456+
# CODEX_EMPTY_RESPONSE_ATTEMPTS=3
457+
458+
# Delay (seconds) between empty response retries.
459+
# CODEX_EMPTY_RESPONSE_RETRY_DELAY=2
460+
461+
# --- OAuth Configuration ---
462+
# OAuth callback port for Codex interactive authentication.
463+
# Default: 8086
464+
# CODEX_OAUTH_PORT=8086
465+
407466
# ------------------------------------------------------------------------------
408467
# | [ADVANCED] Debugging / Logging |
409468
# ------------------------------------------------------------------------------

src/rotator_library/client/rotating_client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,14 @@ async def initialize_usage_managers(self) -> None:
294294
lib_logger.info(
295295
f"Usage managers initialized: {', '.join(sorted(summaries))}"
296296
)
297+
298+
# Inject usage manager references into providers that support it
299+
# (e.g., CodexProvider via CodexQuotaTracker for header-based quota updates)
300+
for provider, manager in self._usage_managers.items():
301+
instance = self._get_provider_instance(provider)
302+
if instance and hasattr(instance, "set_usage_manager"):
303+
instance.set_usage_manager(manager)
304+
297305
self._usage_initialized = True
298306

299307
async def close(self):

src/rotator_library/credential_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"qwen_code": Path.home() / ".qwen",
1919
"iflow": Path.home() / ".iflow",
2020
"antigravity": Path.home() / ".antigravity",
21+
"codex": Path.home() / ".codex",
2122
# Add other providers like 'claude' here if they have a standard CLI path
2223
}
2324

@@ -28,6 +29,7 @@
2829
"antigravity": "ANTIGRAVITY",
2930
"qwen_code": "QWEN_CODE",
3031
"iflow": "IFLOW",
32+
"codex": "CODEX",
3133
}
3234

3335

src/rotator_library/credential_tool.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def _ensure_providers_loaded():
6666
"qwen_code": "Qwen Code",
6767
"iflow": "iFlow",
6868
"antigravity": "Antigravity",
69+
"codex": "OpenAI Codex",
6970
}
7071

7172

src/rotator_library/provider_factory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from .providers.qwen_auth_base import QwenAuthBase
88
from .providers.iflow_auth_base import IFlowAuthBase
99
from .providers.antigravity_auth_base import AntigravityAuthBase
10+
from .providers.openai_oauth_base import OpenAIOAuthBase
1011

1112
PROVIDER_MAP = {
1213
"gemini_cli": GeminiAuthBase,
1314
"qwen_code": QwenAuthBase,
1415
"iflow": IFlowAuthBase,
1516
"antigravity": AntigravityAuthBase,
17+
"codex": OpenAIOAuthBase,
1618
}
1719

1820
def get_provider_auth_class(provider_name: str):

src/rotator_library/providers/codex_prompt.txt

Lines changed: 318 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)