Skip to content

Commit f26fae0

Browse files
committed
feat(codex): Responses API rewrite, dynamic model discovery, and OAuth exports
1 parent b62f6e4 commit f26fae0

11 files changed

Lines changed: 5931 additions & 33 deletions

File tree

.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.3-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
# ------------------------------------------------------------------------------

.github/workflows/docker-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
uses: docker/build-push-action@v6
103103
with:
104104
context: .
105-
platforms: linux/amd64,linux/arm64
105+
platforms: linux/amd64
106106
push: true
107107
tags: ${{ steps.meta.outputs.tags }}
108108
labels: ${{ steps.meta.outputs.labels }}

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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ def _discover_env_oauth_credentials(self) -> Dict[str, List[str]]:
9191
if refresh_key in self.env_vars and self.env_vars[refresh_key]:
9292
found_indices.add(index)
9393

94+
# For Codex provider, also check for API_KEY-only credentials
95+
# Codex can exchange OAuth tokens for persistent API keys, so
96+
# CODEX_N_API_KEY alone (without a refresh token) is valid
97+
if provider == "codex":
98+
api_key_pattern = re.compile(rf"^{env_prefix}_(\d+)_API_KEY$")
99+
for key in self.env_vars.keys():
100+
match = api_key_pattern.match(key)
101+
if match:
102+
index = match.group(1)
103+
if index not in found_indices and self.env_vars[key]:
104+
found_indices.add(index)
105+
94106
# Check for legacy single credential (PROVIDER_ACCESS_TOKEN pattern)
95107
# Only use this if no numbered credentials exist
96108
if not found_indices:
@@ -105,6 +117,12 @@ def _discover_env_oauth_credentials(self) -> Dict[str, List[str]]:
105117
# Use "0" as the index for legacy single credential
106118
found_indices.add("0")
107119

120+
# For Codex, also accept legacy API_KEY-only format
121+
if not found_indices and provider == "codex":
122+
api_key = f"{env_prefix}_API_KEY"
123+
if api_key in self.env_vars and self.env_vars[api_key]:
124+
found_indices.add("0")
125+
108126
if found_indices:
109127
env_credentials[provider] = found_indices
110128
lib_logger.info(

0 commit comments

Comments
 (0)