Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
2028c27
refactor(core): 🔨 modularize client and usage architecture
Mirrowel Jan 21, 2026
cd744cc
refactor(core): 🔨 finalize modular architecture and preserve legacy i…
Mirrowel Jan 21, 2026
26f2846
feat(core): ✨ add async credential waiting and quota group sync
Mirrowel Jan 21, 2026
1c86c22
feat(core): ✨ parse granular Google quota details and cleanup streaming
Mirrowel Jan 21, 2026
523ea31
feat(usage): ✨ implement granular tracking, cost calculation, and hooks
Mirrowel Jan 21, 2026
01dffe7
feat(core): ✨ integrate advanced logging and dynamic usage configuration
Mirrowel Jan 21, 2026
673ca0e
feat(usage): ✨ implement quota group aggregation and explicit initial…
Mirrowel Jan 21, 2026
3450c73
feat(core): ✨ enhance usage stats aggregation and enforce async clien…
Mirrowel Jan 21, 2026
790c01e
refactor(core): 🔨 transition availability checks to async and expose …
Mirrowel Jan 21, 2026
4f1cceb
feat(usage): ✨ track reasoning and cache write tokens
Mirrowel Jan 22, 2026
2136d98
refactor(core): 🔨 preserve legacy client and usage manager implementa…
Mirrowel Jan 22, 2026
dfd2070
feat(usage): ✨ track internal provider retries and enhance api docume…
Mirrowel Jan 22, 2026
22ef3f6
fix(usage): 🐛 prevent stale api data from overwriting local counts
Mirrowel Jan 22, 2026
7ed4c9c
feat(client): ✨ add Anthropic API compatibility handler
Mirrowel Jan 22, 2026
ba60834
refactor(usage): 🔨 centralize window aggregation and reconcile usage …
Mirrowel Jan 22, 2026
94231e0
refactor(client): 🔨 centralize request execution setup logic
Mirrowel Jan 22, 2026
39731b7
refactor(usage): 🔨 decouple model and group usage statistics
Mirrowel Jan 23, 2026
b56dd6b
feat(usage): ✨ make window limits optional and restore legacy logging
Mirrowel Jan 23, 2026
7635027
fix(usage): 🐛 make group and model quota updates mutually exclusive
Mirrowel Jan 23, 2026
effdd28
refactor(usage): 🔨 standardize window config and add human-readable t…
Mirrowel Jan 23, 2026
3a046f5
refactor(usage): 🔨 improve quota resolution strategy and defaults
Mirrowel Jan 23, 2026
610fe8e
fix(usage): 🐛 respect window limit configuration in tracking engine
Mirrowel Jan 23, 2026
9e75529
refactor(client): 🔨 share provider instances across client components
Mirrowel Jan 23, 2026
aa744f1
refactor(providers): 🔨 enforce singleton pattern for provider instances
Mirrowel Jan 23, 2026
2ce5478
fix(providers): 🐛 refine quota exhaustion logic and fetch contexts
Mirrowel Jan 23, 2026
87d04d1
feat(usage): ✨ implement flexible quota thresholds and advanced durat…
Mirrowel Jan 23, 2026
cdded49
fix(usage): 🐛 enforce independent checks for model and group caps
Mirrowel Jan 23, 2026
06db1e2
feat(usage): ✨ implement historical max request tracking
Mirrowel Jan 23, 2026
b212f50
feat(quota-viewer): ✨ enhance quota viewer with multi-window support …
Mirrowel Jan 24, 2026
03a22ef
fix(usage): 🐛 lazy initialize window start and reset times
Mirrowel Jan 24, 2026
ba35176
refactor(usage): 🔨 update quota sorting logic to prioritize limit size
Mirrowel Jan 24, 2026
731e848
feat(quota-viewer): ✨ implement fair cycle and custom cap visualization
Mirrowel Jan 24, 2026
dc763c0
refactor(ui): 🔨 replace raw emojis with rich markup aliases
Mirrowel Jan 24, 2026
640be5a
refactor(core): 🔨 remove legacy modules and simplify executor logic
Mirrowel Jan 24, 2026
f1c963c
chore(config): 🧹 update usage persistence mounts and documentation
Mirrowel Jan 24, 2026
89f0358
refactor(core): 🔨 extract executor helpers and centralize credential …
Mirrowel Jan 24, 2026
bb6e627
refactor(core): 🔨 extract credential logging and callback helpers
Mirrowel Jan 24, 2026
3a2763d
Merge pull request #98 from Mirrowel/refactor/core
Mirrowel Jan 24, 2026
a990d92
chore(config): 🧹 update project metadata and python requirement
Mirrowel Jan 24, 2026
55e1829
fix(usage): 🐛 sync group timing to models and preserve window limits
Mirrowel Jan 24, 2026
93882eb
refactor(core): 🔨 enhance credential masking and sanitize usage logs
Mirrowel Jan 24, 2026
ef0d096
refactor(usage): 🔨 use internal helper for model group retrieval
Mirrowel Jan 24, 2026
35270ee
feat(usage): ✨ add support for offset and percentage-based custom caps
Mirrowel Jan 24, 2026
7947158
fix(anthropic): 🐛 await stream completion call
Mirrowel Jan 24, 2026
c9e14e8
fix(client): 🐛 filter transaction_context from litellm arguments
Mirrowel Jan 24, 2026
69ab47f
feat(client): ✨ enhance acquisition logging with provider context
Mirrowel Jan 24, 2026
9363cd2
fix(providers): 🐛 normalize finish_reason and fix final chunk handling
Mirrowel Jan 24, 2026
b345127
fix(providers): 🐛 combine qwen finish_reason and usage chunks
Mirrowel Jan 24, 2026
b84714f
build(docker): 📦 upgrade python base image to 3.12
Mirrowel Jan 24, 2026
b776a39
feat(providers): ✨ clear cooldowns on verified quota availability
Mirrowel Jan 24, 2026
a0a71e8
fix(providers): 🐛 reduce priority multipliers and enforce safe concur…
Mirrowel Jan 24, 2026
5992105
feat(providers): ✨ add device profile simulation and binding
Mirrowel Jan 24, 2026
fa7d158
refactor(providers): 🔨 standardize tier names to canonical uppercase …
Mirrowel Jan 24, 2026
bf9d4b4
refactor(core): 🔨 delegate 503 capacity handling to providers
Mirrowel Jan 24, 2026
1ebf508
feat(rotation): ✨ prevent premature fair cycle resets via cooldown th…
Mirrowel Jan 24, 2026
2364ac5
fix(usage): 🐛 purge stale tracking windows on tier changes
Mirrowel Jan 24, 2026
0af9591
feat(usage): ✨ implement dynamic tier-specific tracking windows
Mirrowel Jan 24, 2026
4f68828
fix(providers): 🐛 exempt 503 retries from usage counting
Mirrowel Jan 24, 2026
4fc6c2b
fix(usage): 🐛 restrict usage stats to currently active credentials
Mirrowel Jan 24, 2026
bf8b2e7
fix(usage): 🐛 remove duplicate usage tracking logic
Mirrowel Jan 24, 2026
d21b633
feat(auth): ✨ implement automatic GCP project discovery fallback
Mirrowel Jan 24, 2026
627e10d
fix(auth): 🐛 allow server-managed project creation for paid tiers
Mirrowel Jan 24, 2026
c6b09bc
refactor(providers): 🔨 centralize tier definitions and normalization …
Mirrowel Jan 25, 2026
3e2318b
refactor(providers): 🔨 centralize project metadata logic and auth uti…
Mirrowel Jan 25, 2026
63c4a3c
feat(providers): ✨ implement full tier name caching and display logging
Mirrowel Jan 25, 2026
e0b54cf
refactor(tracking): 🔨 remove check_expired_windows method
Mirrowel Jan 25, 2026
54d2385
fix(build): 🐛 collect rich unicode data submodules
Mirrowel Jan 25, 2026
f6e88ae
refactor(providers): 🔨 replace interactive re-auth with permanent cre…
Mirrowel Jan 25, 2026
2da1c3f
fix(providers): 🐛 fix race condition in lock creation
Mirrowel Jan 25, 2026
4a13407
refactor(providers): 🔨 replace interactive re-auth with manual expiry
Mirrowel Jan 25, 2026
ba4e613
fix(providers): 🐛 fix interactive auth tools and fix queue init
Mirrowel Jan 25, 2026
5293b13
feat(providers): ✨ display full tier name in setup success message
Mirrowel Jan 25, 2026
043d35a
fix(providers): 🐛 update antigravity user-agent string
Mirrowel Jan 29, 2026
8ab7334
feat(providers): ✨ implement comprehensive device fingerprinting
Mirrowel Jan 29, 2026
1020f5a
fix(usage): 🐛 restrict selection and stats to active credentials only
Mirrowel Jan 30, 2026
ee77de5
fix(client): 🐛 exclude inactive providers from usage statistics
Mirrowel Jan 30, 2026
bdf6094
fix(providers): 🐛 correct retry loop limit for antigravity
Mirrowel Jan 30, 2026
d68c147
feat(providers): ✨ implement thinking mode and reasoning preservation…
Mirrowel Jan 30, 2026
b973db8
feat(providers): ✨ implement cookie-based authentication for iFlow
Mirrowel Jan 30, 2026
4291c10
fix(providers): 🐛 update emulation to gemini-cli v0.28.0 and onboarding
Mirrowel Jan 30, 2026
dda8805
fix(dedaluslabs): Remove tool_choice=auto to avoid 422 error
b3nw Jan 31, 2026
7c6a6b4
Merge pull request #111 from b3nw/upstream-fix/dedaluslabs-tool-choic…
Mirrowel Jan 31, 2026
d73e85c
feat(providers): ✨ add support for claude-opus-4.6 model
Mirrowel Feb 6, 2026
570bf5b
feat(providers): ✨ add support for kimi-k2.5 model to iFlow
Mirrowel Feb 6, 2026
99eb90d
fix(iflow): handle null data in API response during cookie auth
tiesworkman1025 Feb 7, 2026
2265aa5
fix(iflow): add signed request headers for chat completions
Feb 11, 2026
66d8f68
Merge pull request #120 from redzrush101/fix/iflow-cookie-auth-none-data
Mirrowel Feb 11, 2026
221f048
feat: add OpenAI Codex OAuth provider integration
shuv1337 Feb 12, 2026
fc7b139
fix: use /auth/callback for OpenAI Codex OAuth
shuv1337 Feb 12, 2026
17037c1
fix: multi-account identity matching for OpenAI Codex credentials
shuv1337 Feb 17, 2026
6a3321f
fix: remove unsupported max_output_tokens from Codex payload
shuv1337 Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@
# Path to your iFlow credential file (e.g., ~/.iflow/oauth_creds.json).
#IFLOW_OAUTH_1=""

# --- OpenAI Codex (ChatGPT OAuth) ---
# One-time import from Codex CLI auth files (copied into oauth_creds/openai_codex_oauth_*.json)
#OPENAI_CODEX_OAUTH_1="~/.codex/auth.json"

# Stateless env credentials (legacy single account)
#OPENAI_CODEX_ACCESS_TOKEN=""
#OPENAI_CODEX_REFRESH_TOKEN=""
#OPENAI_CODEX_EXPIRY_DATE="0"
#OPENAI_CODEX_ID_TOKEN=""
#OPENAI_CODEX_ACCOUNT_ID=""
#OPENAI_CODEX_EMAIL=""

# Stateless env credentials (numbered multi-account)
#OPENAI_CODEX_1_ACCESS_TOKEN=""
#OPENAI_CODEX_1_REFRESH_TOKEN=""
#OPENAI_CODEX_1_EXPIRY_DATE="0"
#OPENAI_CODEX_1_ID_TOKEN=""
#OPENAI_CODEX_1_ACCOUNT_ID=""
#OPENAI_CODEX_1_EMAIL=""

# OpenAI Codex routing/config
#OPENAI_CODEX_API_BASE="https://chatgpt.com/backend-api"
#OPENAI_CODEX_OAUTH_PORT=1455
#OPENAI_CODEX_MODELS='["gpt-5.1-codex","gpt-5-codex"]'
#ROTATION_MODE_OPENAI_CODEX=sequential


# ------------------------------------------------------------------------------
# | [ADVANCED] Provider-Specific Settings |
Expand Down Expand Up @@ -162,6 +188,7 @@
#
# Provider Defaults:
# - antigravity: sequential (free tier accounts with daily quotas)
# - openai_codex: sequential (account-level quota behavior)
# - All others: balanced
#
# Example:
Expand Down Expand Up @@ -401,8 +428,24 @@
# ------------------------------------------------------------------------------
#
# OAuth callback port for Antigravity interactive re-authentication.
# Default: 8085 (same as Gemini CLI, shared)
# ANTIGRAVITY_OAUTH_PORT=8085
# Default: 51121
# ANTIGRAVITY_OAUTH_PORT=51121

# ------------------------------------------------------------------------------
# | [ADVANCED] iFlow OAuth Configuration |
# ------------------------------------------------------------------------------
#
# OAuth callback port for iFlow interactive re-authentication.
# Default: 11451
# IFLOW_OAUTH_PORT=11451

# ------------------------------------------------------------------------------
# | [ADVANCED] OpenAI Codex OAuth Configuration |
# ------------------------------------------------------------------------------
#
# OAuth callback port for OpenAI Codex interactive authentication.
# Default: 1455
# OPENAI_CODEX_OAUTH_PORT=1455

# ------------------------------------------------------------------------------
# | [ADVANCED] Debugging / Logging |
Expand Down
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ staged_changes.txt
launcher_config.json
quota_viewer_config.json
cache/antigravity/thought_signatures.json
logs/
cache/
/logs/
/cache/
*.env

oauth_creds/
/oauth_creds/

/usage/
80 changes: 60 additions & 20 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ This architecture cleanly separates the API interface from the resilience logic,

This library is the heart of the project, containing all the logic for managing a pool of API keys, tracking their usage, and handling provider interactions to ensure application resilience.

### 2.1. `client.py` - The `RotatingClient`
### 2.1. `client/rotating_client.py` - The `RotatingClient`

The `RotatingClient` is the central class that orchestrates all operations. It is designed as a long-lived, async-native object.
The `RotatingClient` is the central class that orchestrates all operations. It is now a slim facade that delegates to modular components (executor, filters, transforms) while remaining a long-lived, async-native object.

#### Initialization

Expand All @@ -35,7 +35,7 @@ client = RotatingClient(
api_keys=api_keys,
oauth_credentials=oauth_credentials,
max_retries=2,
usage_file_path="key_usage.json",
usage_file_path="usage.json",
configure_logging=True,
global_timeout=30,
abort_on_callback_error=True,
Expand All @@ -50,7 +50,7 @@ client = RotatingClient(
- `api_keys` (`Optional[Dict[str, List[str]]]`, default: `None`): A dictionary mapping provider names to a list of API keys.
- `oauth_credentials` (`Optional[Dict[str, List[str]]]`, default: `None`): A dictionary mapping provider names to a list of file paths to OAuth credential JSON files.
- `max_retries` (`int`, default: `2`): The number of times to retry a request with the *same key* if a transient server error occurs.
- `usage_file_path` (`str`, default: `"key_usage.json"`): The path to the JSON file where usage statistics are persisted.
- `usage_file_path` (`str`, optional): Base path for usage persistence (defaults to `usage/` in the data directory). The client stores per-provider files under `usage/usage_<provider>.json`.
- `configure_logging` (`bool`, default: `True`): If `True`, configures the library's logger to propagate logs to the root logger.
- `global_timeout` (`int`, default: `30`): A hard time limit (in seconds) for the entire request lifecycle.
- `abort_on_callback_error` (`bool`, default: `True`): If `True`, any exception raised by `pre_request_callback` will abort the request.
Expand Down Expand Up @@ -96,9 +96,9 @@ The `_safe_streaming_wrapper` is a critical component for stability. It:
* **Error Interception**: Detects if a chunk contains an API error (like a quota limit) instead of content, and raises a specific `StreamedAPIError`.
* **Quota Handling**: If a specific "quota exceeded" error is detected mid-stream multiple times, it can terminate the stream gracefully to prevent infinite retry loops on oversized inputs.

### 2.2. `usage_manager.py` - Stateful Concurrency & Usage Management
### 2.2. `usage/manager.py` - Stateful Concurrency & Usage Management

This class is the stateful core of the library, managing concurrency, usage tracking, cooldowns, and quota resets.
This class is the stateful core of the library, managing concurrency, usage tracking, cooldowns, and quota resets. Usage tracking now lives in the `rotator_library/usage/` package with per-provider managers and `usage/usage_<provider>.json` storage.

#### Key Concepts

Expand Down Expand Up @@ -205,15 +205,17 @@ The `CredentialManager` class (`credential_manager.py`) centralizes the lifecycl

On startup (unless `SKIP_OAUTH_INIT_CHECK=true`), the manager performs a comprehensive sweep:

1. **System-Wide Scan**: Searches for OAuth credential files in standard locations:
1. **System-Wide Scan / Import Sources**:
- `~/.gemini/` → All `*.json` files (typically `credentials.json`)
- `~/.qwen/` → All `*.json` files (typically `oauth_creds.json`)
- `~/.iflow/` → All `*. json` files
- `~/.iflow/` → All `*.json` files
- `~/.codex/auth.json` + `~/.codex-accounts.json` → OpenAI Codex first-run import sources

2. **Local Import**: Valid credentials are **copied** (not moved) to the project's `oauth_creds/` directory with standardized names:
- `gemini_cli_oauth_1.json`, `gemini_cli_oauth_2.json`, etc.
- `gemini_cli_oauth_1.json`, `gemini_cli_oauth_2.json`, etc.
- `qwen_code_oauth_1.json`, `qwen_code_oauth_2.json`, etc.
- `iflow_oauth_1.json`, `iflow_oauth_2.json`, etc.
- `openai_codex_oauth_1.json`, `openai_codex_oauth_2.json`, etc.

3. **Intelligent Deduplication**:
- The manager inspects each credential file for a `_proxy_metadata` field containing the user's email or ID
Expand Down Expand Up @@ -292,6 +294,24 @@ IFLOW_EMAIL
IFLOW_API_KEY
```

**OpenAI Codex Environment Variables:**
```
OPENAI_CODEX_ACCESS_TOKEN
OPENAI_CODEX_REFRESH_TOKEN
OPENAI_CODEX_EXPIRY_DATE
OPENAI_CODEX_ID_TOKEN
OPENAI_CODEX_ACCOUNT_ID
OPENAI_CODEX_EMAIL

# Numbered multi-account format
OPENAI_CODEX_1_ACCESS_TOKEN
OPENAI_CODEX_1_REFRESH_TOKEN
OPENAI_CODEX_1_EXPIRY_DATE
OPENAI_CODEX_1_ID_TOKEN
OPENAI_CODEX_1_ACCOUNT_ID
OPENAI_CODEX_1_EMAIL
```

**How it works:**
- If the manager finds (e.g.) `GEMINI_CLI_ACCESS_TOKEN` or `GEMINI_CLI_1_ACCESS_TOKEN`, it constructs an in-memory credential object that mimics the file structure
- The credential is referenced internally as `env://gemini_cli/0` (legacy) or `env://gemini_cli/1` (numbered)
Expand All @@ -304,17 +324,19 @@ IFLOW_API_KEY
env://{provider}/{index}

Examples:
- env://gemini_cli/1 → GEMINI_CLI_1_ACCESS_TOKEN, etc.
- env://gemini_cli/0 → GEMINI_CLI_ACCESS_TOKEN (legacy single credential)
- env://antigravity/1 → ANTIGRAVITY_1_ACCESS_TOKEN, etc.
- env://gemini_cli/1 → GEMINI_CLI_1_ACCESS_TOKEN, etc.
- env://gemini_cli/0 → GEMINI_CLI_ACCESS_TOKEN (legacy single credential)
- env://antigravity/1 → ANTIGRAVITY_1_ACCESS_TOKEN, etc.
- env://openai_codex/1 → OPENAI_CODEX_1_ACCESS_TOKEN, etc.
- env://openai_codex/0 → OPENAI_CODEX_ACCESS_TOKEN (legacy single credential)
```

#### 2.6.3. Credential Tool Integration

The `credential_tool.py` provides a user-friendly CLI interface to the `CredentialManager`:

**Key Functions:**
1. **OAuth Setup**: Wraps provider-specific `AuthBase` classes (`GeminiAuthBase`, `QwenAuthBase`, `IFlowAuthBase`) to handle interactive login flows
1. **OAuth Setup**: Wraps provider-specific `AuthBase` classes (`GeminiAuthBase`, `QwenAuthBase`, `IFlowAuthBase`, `OpenAICodexAuthBase`) to handle interactive login flows
2. **Credential Export**: Reads local `.json` files and generates `.env` format output for stateless deployment
3. **API Key Management**: Adds or updates `PROVIDER_API_KEY_N` entries in the `.env` file

Expand Down Expand Up @@ -419,7 +441,7 @@ The `CooldownManager` handles IP or account-level rate limiting that affects all
- All subsequent `acquire_key()` calls for that provider will wait until the cooldown expires


### 2.10. Credential Prioritization System (`client.py` & `usage_manager.py`)
### 2.10. Credential Prioritization System (`client/rotating_client.py` & `usage/manager.py`)

The library now includes an intelligent credential prioritization system that automatically detects credential tiers and ensures optimal credential selection for each request.

Expand Down Expand Up @@ -762,7 +784,7 @@ Acquiring key for model antigravity/claude-opus-4.5. Tried keys: 0/12(17,cd:3,fc
```

**Persistence:**
Cycle state is persisted in `key_usage.json` under the `__fair_cycle__` key.
Cycle state is persisted alongside usage data in `usage/usage_<provider>.json`.

### 2.20. Custom Caps

Expand Down Expand Up @@ -1426,12 +1448,13 @@ Each OAuth provider uses a local callback server during authentication. The call
| Gemini CLI | 8085 | `GEMINI_CLI_OAUTH_PORT` |
| Antigravity | 51121 | `ANTIGRAVITY_OAUTH_PORT` |
| iFlow | 11451 | `IFLOW_OAUTH_PORT` |
| OpenAI Codex | 1455 | `OPENAI_CODEX_OAUTH_PORT` |

**Configuration Methods:**

1. **Via TUI Settings Menu:**
- Main Menu → `4. View Provider & Advanced Settings` → `1. Launch Settings Tool`
- Select the provider (Gemini CLI, Antigravity, or iFlow)
- Select the provider (Gemini CLI, Antigravity, iFlow, or OpenAI Codex)
- Modify the `*_OAUTH_PORT` setting
- Use "Reset to Default" to restore the original port

Expand All @@ -1441,6 +1464,7 @@ Each OAuth provider uses a local callback server during authentication. The call
GEMINI_CLI_OAUTH_PORT=8085
ANTIGRAVITY_OAUTH_PORT=51121
IFLOW_OAUTH_PORT=11451
OPENAI_CODEX_OAUTH_PORT=1455
```

**When to Change Ports:**
Expand Down Expand Up @@ -1528,7 +1552,7 @@ The following providers use `TimeoutConfig`:
| `iflow_provider.py` | `acompletion()` | `streaming()` |
| `qwen_code_provider.py` | `acompletion()` | `streaming()` |

**Note:** iFlow, Qwen Code, and Gemini CLI providers always use streaming internally (even for non-streaming requests), aggregating chunks into a complete response. Only Antigravity has a true non-streaming path.
**Note:** iFlow, Qwen Code, Gemini CLI, and OpenAI Codex providers always use streaming internally (even for non-streaming requests), aggregating chunks into a complete response. Only Antigravity has a true non-streaming path.

#### Tuning Recommendations

Expand Down Expand Up @@ -1649,7 +1673,23 @@ QUOTA_GROUPS_GEMINI_CLI_3_FLASH="gemini-3-flash-preview"
* **Schema Cleaning**: Similar to Qwen, it aggressively sanitizes tool schemas to prevent 400 errors.
* **Dedicated Logging**: Implements `_IFlowFileLogger` to capture raw chunks for debugging proprietary API behaviors.

### 3.4. Google Gemini (`gemini_provider.py`)
### 3.4. OpenAI Codex (`openai_codex_provider.py`)

* **Auth Base**: Uses `OpenAICodexAuthBase` with Authorization Code + PKCE, queue-based refresh/re-auth, and local-first credential persistence (`oauth_creds/openai_codex_oauth_*.json`).
* **First-Run Import**: `CredentialManager` imports from `~/.codex/auth.json` and `~/.codex-accounts.json` when no local/OpenAI Codex env creds exist.
* **Endpoint Translation**: Implements OpenAI-compatible `/v1/chat/completions` by transforming chat payloads into Codex Responses payloads and calling `POST /codex/responses`.
* **SSE Translation**: Maps Codex SSE event families (e.g. `response.output_item.*`, `response.output_text.delta`, `response.function_call_arguments.*`, `response.completed`) into LiteLLM/OpenAI chunk objects.
* **Rotation Compatibility**: Emits typed `httpx.HTTPStatusError` for transport/status failures and includes provider-specific `parse_quota_error()` for retry/cooldown extraction (`Retry-After`, `error.resets_at`).
* **Default Rotation**: `sequential` (account-level quota behavior).

**OpenAI Codex Troubleshooting Notes:**

- **Malformed JWT payload**: If access/id tokens cannot be decoded, account/email metadata can be missing; re-authenticate to rebuild token metadata.
- **Missing account-id claim**: Requests require `chatgpt-account-id`; if absent, refresh/re-auth to repopulate `_proxy_metadata.account_id`.
- **Callback port conflicts**: Change `OPENAI_CODEX_OAUTH_PORT` when port `1455` is already in use.
- **Header mismatch / 403**: Ensure provider sends `Authorization`, `chatgpt-account-id`, and expected Codex headers (`OpenAI-Beta`, `originator`) when routing to `/codex/responses`.

### 3.5. Google Gemini (`gemini_provider.py`)

* **Thinking Parameter**: Automatically handles the `thinking` parameter transformation required for Gemini 2.5 models (`thinking` -> `gemini-2.5-pro` reasoning parameter).
* **Safety Settings**: Ensures default safety settings (blocking nothing) are applied if not provided, preventing over-sensitive refusals.
Expand Down Expand Up @@ -1773,7 +1813,7 @@ The system follows a strict hierarchy of survival:

2. **Credential Management (Level 2)**: OAuth tokens are cached in memory first. If credential files are deleted, the proxy continues using cached tokens. If a token refresh succeeds but the file cannot be written, the new token is buffered for retry and saved on shutdown.

3. **Usage Tracking (Level 3)**: Usage statistics (`key_usage.json`) are maintained in memory via `ResilientStateWriter`. If the file is deleted, the system tracks usage internally and attempts to recreate the file on the next save interval. Pending writes are flushed on shutdown.
3. **Usage Tracking (Level 3)**: Usage statistics (`usage/usage_<provider>.json`) are maintained in memory via `ResilientStateWriter`. If the file is deleted, the system tracks usage internally and attempts to recreate the file on the next save interval. Pending writes are flushed on shutdown.

4. **Provider Cache (Level 4)**: The provider cache tracks disk health and continues operating in memory-only mode if disk writes fail. Has its own shutdown mechanism.

Expand Down Expand Up @@ -1813,7 +1853,7 @@ INFO:rotator_library.resilient_io:Shutdown flush: all 2 write(s) succeeded
This architecture supports a robust development workflow:

- **Log Cleanup**: You can safely run `rm -rf logs/` while the proxy is serving traffic. The system will recreate the directory structure on the next request.
- **Config Reset**: Deleting `key_usage.json` resets the persistence layer, but the running instance preserves its current in-memory counts for load balancing consistency.
- **Config Reset**: Deleting `usage/usage_<provider>.json` resets the persistence layer, but the running instance preserves its current in-memory counts for load balancing consistency.
- **File Recovery**: If you delete a critical file, the system attempts directory auto-recreation before every write operation.
- **Safe Exit**: Ctrl+C triggers graceful shutdown with final data flush attempt.

Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build stage
FROM python:3.11-slim AS builder
FROM python:3.12-slim AS builder

WORKDIR /app

Expand All @@ -21,7 +21,7 @@ COPY src/rotator_library ./src/rotator_library
RUN pip install --no-cache-dir --user -r requirements.txt

# Production stage
FROM python:3.11-slim
FROM python:3.12-slim

WORKDIR /app

Expand Down
Loading
Loading