diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c84eea..36cf74d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # fusionAIze Gate Changelog +## v2.1.2 - 2026-04-07 + +### Fixed + +- **Codex `chat_path` bug**: `openai-compat` backend appended `/chat/completions` to the Codex endpoint URL. Codex base_url IS the full endpoint; per-provider transport bindings now set `chat_path: ""` for all `openai-codex*` providers +- **`faigate-auth` JSON output**: `OAuthBackend._run_helper()` expects JSON on stdout; CLI was printing human-readable text → `json.JSONDecodeError` on every Codex request. Fixed: token data now printed as JSON to stdout; status messages go to stderr +- **`max_tokens` limit**: OpenCode model limit was 32768, Codex endpoint maximum is 8192 → `invalid_request_error`. All Codex models now capped at 8192 +- **Fallback to paid providers**: Removed Sonnet/GPT-4o from Codex `degrade_to` chains +- **`oauth` backend validation**: Added `oauth` to `_SUPPORTED_BACKENDS` in `config.py` + +### Added + +- **GPT-5.4 reasoning effort control**: `default_extra_body` mechanism in `providers.py` injects provider-configured fields into every request. New providers: `openai-codex-5.4-xhigh` (`extra_high`), `openai-codex-5.4-high` (`high`), `openai-codex-5.4-medium` (`medium`), `openai-codex-5.4-low` (`low`) +- **Codex 5.3 reasoning variants**: `openai-codex-xhigh` → `gpt-5.3-codex-xhigh`, `openai-codex-low` → `gpt-5.3-codex-low` +- **All 20 Codex model variants** in `_CANONICAL_MODEL_LANES` (gpt-5.4, gpt-5.3-codex family, gpt-5.2, gpt-5.1 series, gpt-4.1, gpt-4o, o4-mini) +- **OAuth transport profile**: Default transport binding for `oauth` backend with `chat_path: ""` +- **OpenCode model menu**: 10 Codex model entries covering all reasoning levels + ## v2.1.1 - 2026-04-06 ### Added diff --git a/README.md b/README.md index 673d2b4..d460338 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # fusionAIze Gate -[![repo-safety](https://github.com/fusionAIze/faigate/actions/workflows/repo-safety.yml/badge.svg)](https://github.com/fusionAIze/faigate/actions/workflows/repo-safety.yml) [![CI](https://github.com/fusionAIze/faigate/actions/workflows/ci.yml/badge.svg)](https://github.com/fusionAIze/faigate/actions/workflows/ci.yml) [![CodeQL](https://github.com/fusionAIze/faigate/actions/workflows/codeql.yml/badge.svg)](https://github.com/fusionAIze/faigate/actions/workflows/codeql.yml) [![Release](https://img.shields.io/github/v/release/fusionAIze/faigate?display_name=tag)](https://github.com/fusionAIze/faigate/releases) diff --git a/config.yaml b/config.yaml index 9bf9c77..55f979b 100644 --- a/config.yaml +++ b/config.yaml @@ -1009,24 +1009,337 @@ providers: # connect_s: 10 # read_s: 90 - # openai-codex: - # # Token from ~/.codex/auth.json (auth_mode=chatgpt). Run: faigate-auth openai-codex - # # Inference endpoint: chatgpt.com/backend-api/codex/responses (NOT api.openai.com) - # # Refresh tokens are single-use; faigate rewrites auth.json after every refresh. - # backend: oauth - # oauth: - # helper: "faigate-auth openai-codex" - # client_id: "app_EMoamEEZ73f0CkXaXp7hrann" - # token_endpoint: "https://auth.openai.com/oauth/token" - # refresh_endpoint: "https://auth.openai.com/oauth/token" - # scope: "openid profile email offline_access" - # underlying_backend: openai-compat - # base_url: "https://chatgpt.com/backend-api/codex/responses" - # model: openai-codex/gpt-5.3-codex - # tier: default - # timeout: - # connect_s: 10 - # read_s: 60 + openai-codex: + # Token from ~/.codex/auth.json (auth_mode=chatgpt). Run: faigate-auth openai-codex + # Inference endpoint: chatgpt.com/backend-api/codex/responses (NOT api.openai.com) + # Refresh tokens are single-use; faigate rewrites auth.json after every refresh. + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.4 + tier: default + capabilities: + cost_tier: free + latency_tier: balanced + reasoning: true + streaming: true + lane: + canonical_model: openai-codex/gpt-5.4 + cluster: elite-reasoning + benchmark_cluster: quality-coding + quality_tier: premium + reasoning_strength: high + context_strength: high + tool_strength: medium + route_type: direct + name: codex + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 90 + openai-codex-mini: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.1-codex-mini + tier: default + capabilities: + cost_tier: free + latency_tier: fast + reasoning: true + streaming: true + lane: + canonical_model: openai-codex/gpt-5.1-codex-mini + cluster: fast-workhorse + benchmark_cluster: fast-coding + quality_tier: mid + reasoning_strength: mid + context_strength: high + tool_strength: medium + route_type: direct + name: codex-mini + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 60 + openai-codex-spark: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.3-codex-spark + tier: default + capabilities: + cost_tier: free + latency_tier: fast + streaming: true + lane: + canonical_model: openai-codex/gpt-5.3-codex-spark + cluster: fast-workhorse + benchmark_cluster: fast-coding + quality_tier: mid + route_type: direct + name: codex-spark + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 60 + openai-codex-high: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.3-codex-high + tier: default + capabilities: + cost_tier: free + latency_tier: balanced + reasoning: true + streaming: true + lane: + canonical_model: openai-codex/gpt-5.3-codex-high + cluster: elite-reasoning + benchmark_cluster: quality-coding + quality_tier: premium + reasoning_strength: high + context_strength: high + tool_strength: medium + route_type: direct + name: codex-high + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 90 + openai-codex-xhigh: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.3-codex-xhigh + tier: default + capabilities: + cost_tier: free + latency_tier: quality + reasoning: true + streaming: true + lane: + canonical_model: openai-codex/gpt-5.3-codex-xhigh + cluster: elite-reasoning + benchmark_cluster: quality-coding + quality_tier: premium + reasoning_strength: high + context_strength: high + tool_strength: medium + route_type: direct + name: codex-xhigh + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 120 + openai-codex-low: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.3-codex-low + tier: default + capabilities: + cost_tier: free + latency_tier: fast + streaming: true + lane: + canonical_model: openai-codex/gpt-5.3-codex-low + cluster: fast-workhorse + benchmark_cluster: fast-coding + quality_tier: mid + reasoning_strength: low + context_strength: high + tool_strength: medium + route_type: direct + name: codex-low + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 60 + # GPT-5.4 with explicit reasoning_effort (injected via extra_body) + openai-codex-5.4-xhigh: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.4 + extra_body: + reasoning_effort: extra_high + tier: default + capabilities: + cost_tier: free + latency_tier: quality + reasoning: true + streaming: true + lane: + family: openai + canonical_model: openai-codex/gpt-5.4 + cluster: elite-reasoning + benchmark_cluster: quality-coding + quality_tier: premium + reasoning_strength: high + route_type: direct + name: codex-5.4-xhigh + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 120 + openai-codex-5.4-high: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.4 + extra_body: + reasoning_effort: high + tier: default + capabilities: + cost_tier: free + latency_tier: balanced + reasoning: true + streaming: true + lane: + family: openai + canonical_model: openai-codex/gpt-5.4 + cluster: elite-reasoning + benchmark_cluster: quality-coding + quality_tier: premium + reasoning_strength: high + route_type: direct + name: codex-5.4-high + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 90 + openai-codex-5.4-medium: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.4 + extra_body: + reasoning_effort: medium + tier: default + capabilities: + cost_tier: free + latency_tier: balanced + streaming: true + lane: + family: openai + canonical_model: openai-codex/gpt-5.4 + cluster: quality-workhorse + benchmark_cluster: quality-coding + quality_tier: high + reasoning_strength: mid + route_type: direct + name: codex-5.4-medium + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 60 + openai-codex-5.4-low: + backend: oauth + oauth: + helper: "faigate-auth openai-codex" + client_id: "app_EMoamEEZ73f0CkXaXp7hrann" + token_endpoint: "https://auth.openai.com/oauth/token" + refresh_endpoint: "https://auth.openai.com/oauth/token" + scope: "openid profile email offline_access" + underlying_backend: openai-compat + base_url: "https://chatgpt.com/backend-api/codex/responses" + model: gpt-5.4 + extra_body: + reasoning_effort: low + tier: default + capabilities: + cost_tier: free + latency_tier: fast + streaming: true + lane: + family: openai + canonical_model: openai-codex/gpt-5.4 + cluster: fast-workhorse + benchmark_cluster: fast-coding + quality_tier: mid + reasoning_strength: low + route_type: direct + name: codex-5.4-low + pricing: + input: 0.0 + output: 0.0 + timeout: + connect_s: 10 + read_s: 45 # Antigravity (Google OAuth – Authorization Code + PKCE → Google Generative Language API) # Network discovery: Antigravity's client interface is a LOCAL ephemeral gRPC language @@ -1389,6 +1702,73 @@ static_rules: - deepseek-chat name: explicit-chat route_to: deepseek-chat + - match: + model_requested: + - codex-mini + - openai-codex-mini + - gpt-5.1-codex-mini + - codex-spark + - openai-codex-spark + - gpt-5.3-codex-spark + name: explicit-codex-mini + route_to: openai-codex-mini + - match: + model_requested: + - codex-xhigh + - openai-codex-xhigh + - gpt-5.3-codex-xhigh + name: explicit-codex-xhigh + route_to: openai-codex-xhigh + - match: + model_requested: + - codex-high + - openai-codex-high + - gpt-5.3-codex-high + name: explicit-codex-high + route_to: openai-codex-high + - match: + model_requested: + - codex-low + - openai-codex-low + - gpt-5.3-codex-low + name: explicit-codex-low + route_to: openai-codex-low + - match: + model_requested: + - codex-5.4-xhigh + - openai-codex-5.4-xhigh + - gpt-5.4-xhigh + name: explicit-codex-5.4-xhigh + route_to: openai-codex-5.4-xhigh + - match: + model_requested: + - codex-5.4-high + - openai-codex-5.4-high + - gpt-5.4-high + name: explicit-codex-5.4-high + route_to: openai-codex-5.4-high + - match: + model_requested: + - codex-5.4-medium + - openai-codex-5.4-medium + - gpt-5.4-medium + name: explicit-codex-5.4-medium + route_to: openai-codex-5.4-medium + - match: + model_requested: + - codex-5.4-low + - openai-codex-5.4-low + - gpt-5.4-low + name: explicit-codex-5.4-low + route_to: openai-codex-5.4-low + - match: + model_requested: + - codex + - openai-codex + - gpt-5.4 + - gpt-5.3-codex + name: explicit-codex + route_to: openai-codex - match: any: - system_prompt_contains: diff --git a/faigate/__init__.py b/faigate/__init__.py index 8f8acbc..892e64d 100644 --- a/faigate/__init__.py +++ b/faigate/__init__.py @@ -1,3 +1,3 @@ """fusionAIze Gate package.""" -__version__ = "2.1.1" +__version__ = "2.1.2" diff --git a/faigate/config.py b/faigate/config.py index 4f20725..7362f50 100644 --- a/faigate/config.py +++ b/faigate/config.py @@ -31,7 +31,7 @@ from .hooks import get_registered_request_hooks, load_community_hooks from .lane_registry import get_provider_lane_binding, get_provider_transport_binding -_SUPPORTED_BACKENDS = {"openai-compat", "google-genai", "anthropic-compat"} +_SUPPORTED_BACKENDS = {"openai-compat", "google-genai", "anthropic-compat", "oauth"} _SUPPORTED_PROVIDER_CONTRACTS = {"generic", "local-worker", "image-provider"} _SUPPORTED_CACHE_MODES = {"none", "implicit", "explicit"} _BOOL_CAPABILITY_FIELDS = { diff --git a/faigate/lane_registry.py b/faigate/lane_registry.py index 5c7faaa..55ceb57 100644 --- a/faigate/lane_registry.py +++ b/faigate/lane_registry.py @@ -537,6 +537,247 @@ def get_active_model_label(canonical_id: str) -> str: "preferred_degrades": ["qw/vision-model", "google/gemini-pro-low", "deepseek/chat"], "last_reviewed": "2026-04-03", }, + # ── OpenAI Codex family (ChatGPT OAuth → chatgpt.com/backend-api/codex/responses) ── + "openai-codex/gpt-5.4": { + "family": "openai", + "name": "Codex GPT-5.4", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.3-codex", "openai/gpt-4o"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex": { + "family": "openai", + "name": "Codex GPT-5.3", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini", "openai/gpt-4o"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex-xhigh": { + "family": "openai", + "name": "Codex GPT-5.3 xHigh", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.3-codex-high"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex-high": { + "family": "openai", + "name": "Codex GPT-5.3 High", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.3-codex"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex-low": { + "family": "openai", + "name": "Codex GPT-5.3 Low", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "low", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex-none": { + "family": "openai", + "name": "Codex GPT-5.3 None", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "none", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.3-codex-low"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.3-codex-spark": { + "family": "openai", + "name": "Codex GPT-5.3 Spark", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.1-codex-mini": { + "family": "openai", + "name": "Codex Mini", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["deepseek/chat", "google/gemini-flash"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.1-codex-mini-high": { + "family": "openai", + "name": "Codex Mini High", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.2-codex": { + "family": "openai", + "name": "Codex GPT-5.2", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.2": { + "family": "openai", + "name": "GPT-5.2", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.2-codex"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.1-codex-max": { + "family": "openai", + "name": "Codex GPT-5.1 Max", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.1-codex": { + "family": "openai", + "name": "Codex GPT-5.1", + "cluster": "quality-workhorse", + "benchmark_cluster": "quality-coding", + "quality_tier": "high", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5.1": { + "family": "openai", + "name": "GPT-5.1", + "cluster": "quality-workhorse", + "benchmark_cluster": "quality-coding", + "quality_tier": "high", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5-codex": { + "family": "openai", + "name": "Codex GPT-5", + "cluster": "quality-workhorse", + "benchmark_cluster": "quality-coding", + "quality_tier": "high", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-5-codex-mini": { + "family": "openai", + "name": "Codex GPT-5 Mini", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/codex-mini": { + "family": "openai", + "name": "Codex Mini (alias)", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/o4-mini": { + "family": "openai", + "name": "o4-mini (Codex)", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "preferred_degrades": ["openai-codex/gpt-5.1-codex-mini"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-4.1": { + "family": "openai", + "name": "GPT-4.1 (Codex)", + "cluster": "balanced-workhorse", + "benchmark_cluster": "balanced-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "high", + "preferred_degrades": ["openai/gpt-4o"], + "last_reviewed": "2026-04-07", + }, + "openai-codex/gpt-4o": { + "family": "openai", + "name": "GPT-4o (Codex)", + "cluster": "balanced-workhorse", + "benchmark_cluster": "balanced-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "high", + "preferred_degrades": ["openai/gpt-4o"], + "last_reviewed": "2026-04-07", + }, } _PROVIDER_LANE_BINDINGS: dict[str, dict[str, Any]] = { @@ -935,7 +1176,63 @@ def get_active_model_label(canonical_id: str) -> str: "openai-codex": { "family": "openai", "name": "codex", - "canonical_model": "openai-codex/gpt-5.3-codex", + "canonical_model": "openai-codex/gpt-5.4", + "route_type": "direct", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "same_model_group": "openai-codex/gpt-5.4", + "degrade_to": ["openai-codex-mini", "openai-codex-spark"], + }, + "openai-codex-mini": { + "family": "openai", + "name": "codex-mini", + "canonical_model": "openai-codex/gpt-5.1-codex-mini", + "route_type": "direct", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "same_model_group": "openai-codex/gpt-5.1-codex-mini", + "degrade_to": ["deepseek/chat", "google/gemini-flash"], + }, + "openai-codex-spark": { + "family": "openai", + "name": "codex-spark", + "canonical_model": "openai-codex/gpt-5.3-codex-spark", + "route_type": "direct", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "mid", + "context_strength": "high", + "tool_strength": "medium", + "same_model_group": "openai-codex/gpt-5.3-codex-spark", + "degrade_to": ["openai-codex-mini", "deepseek/chat"], + }, + "openai-codex-high": { + "family": "openai", + "name": "codex-high", + "canonical_model": "openai-codex/gpt-5.3-codex-high", + "route_type": "direct", + "cluster": "elite-reasoning", + "benchmark_cluster": "quality-coding", + "quality_tier": "premium", + "reasoning_strength": "high", + "context_strength": "high", + "tool_strength": "medium", + "same_model_group": "openai-codex/gpt-5.3-codex-high", + "degrade_to": ["openai-codex", "openai-codex-mini"], + }, + "openai-codex-xhigh": { + "family": "openai", + "name": "codex-xhigh", + "canonical_model": "openai-codex/gpt-5.3-codex-xhigh", "route_type": "direct", "cluster": "elite-reasoning", "benchmark_cluster": "quality-coding", @@ -943,8 +1240,22 @@ def get_active_model_label(canonical_id: str) -> str: "reasoning_strength": "high", "context_strength": "high", "tool_strength": "medium", - "same_model_group": "openai-codex/gpt-5.3-codex", - "degrade_to": ["openai/gpt-4o", "anthropic/sonnet-4.6"], + "same_model_group": "openai-codex/gpt-5.3-codex-xhigh", + "degrade_to": ["openai-codex-high", "openai-codex"], + }, + "openai-codex-low": { + "family": "openai", + "name": "codex-low", + "canonical_model": "openai-codex/gpt-5.3-codex-low", + "route_type": "direct", + "cluster": "fast-workhorse", + "benchmark_cluster": "fast-coding", + "quality_tier": "mid", + "reasoning_strength": "low", + "context_strength": "high", + "tool_strength": "medium", + "same_model_group": "openai-codex/gpt-5.3-codex-low", + "degrade_to": ["openai-codex-mini", "openai-codex-spark"], }, "claude-code": { "family": "anthropic", @@ -1368,6 +1679,23 @@ def _lane_binding_with_freshness(binding: dict[str, Any]) -> dict[str, Any]: "supports_models_probe": False, "notes": ["genai backend uses generateContent instead of OpenAI-compatible chat paths"], }, + "oauth": { + "profile": "oauth-openai-compat", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "chat", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "/chat/completions", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["OAuth-wrapped backend; token injected at request time via faigate-auth helper"], + }, } _PROVIDER_TRANSPORT_BINDINGS: dict[str, dict[str, Any]] = { @@ -1507,6 +1835,170 @@ def _lane_binding_with_freshness(binding: dict[str, Any]) -> dict[str, Any]: "free-tier model availability and path behavior should be revalidated regularly", ], }, + # Codex providers: base_url IS the full endpoint; chat_path must be "" to avoid + # appending /chat/completions → chatgpt.com/backend-api/codex/responses/chat/completions + "openai-codex": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": [ + "Codex endpoint is the full URL; no /chat/completions suffix", + "OAuth token injected by OAuthBackend from ~/.codex/auth.json", + ], + }, + "openai-codex-mini": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["Codex mini endpoint; no /chat/completions suffix"], + }, + "openai-codex-spark": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["Codex spark endpoint; no /chat/completions suffix"], + }, + "openai-codex-high": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["Codex high endpoint; no /chat/completions suffix"], + }, + "openai-codex-xhigh": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["Codex xhigh endpoint; no /chat/completions suffix"], + }, + "openai-codex-low": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "probe_payload_kind": "openai-chat-minimal", + "probe_payload_text": "ping", + "probe_payload_max_tokens": 1, + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["Codex low reasoning endpoint; no /chat/completions suffix"], + }, + # GPT-5.4 reasoning-effort variants (extra_body injection via provider config) + "openai-codex-5.4-xhigh": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["GPT-5.4 extra_high reasoning via reasoning_effort=extra_high in body"], + }, + "openai-codex-5.4-high": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["GPT-5.4 high reasoning via reasoning_effort=high in body"], + }, + "openai-codex-5.4-medium": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["GPT-5.4 medium reasoning via reasoning_effort=medium in body"], + }, + "openai-codex-5.4-low": { + "profile": "oauth-codex", + "compatibility": "native", + "probe_confidence": "low", + "auth_mode": "bearer", + "probe_strategy": "none", + "models_path": "", + "chat_path": "", + "image_generation_path": "", + "image_edit_path": "", + "requires_api_key": False, + "supports_models_probe": False, + "notes": ["GPT-5.4 low reasoning via reasoning_effort=low in body"], + }, } _CANONICAL_MODEL_ROUTE_REGISTRY: dict[str, list[dict[str, Any]]] = { diff --git a/faigate/oauth/cli.py b/faigate/oauth/cli.py index 2db36fd..80f3d20 100644 --- a/faigate/oauth/cli.py +++ b/faigate/oauth/cli.py @@ -939,10 +939,9 @@ def main() -> None: ) sys.exit(1) - # Tokens are written to the provider credentials file by each auth function. - # Do not print any value derived from token_data to stdout. - print(f"Authentication successful for {args.provider}.") - print("Token stored in credentials file.") + # Output token data as JSON to stdout so OAuthBackend._run_helper() can parse it. + # All human-readable status messages go to stderr (see above). + print(json.dumps(token_data)) except Exception as e: logger.error("Failed to obtain token: %s", e) diff --git a/faigate/providers.py b/faigate/providers.py index 531f646..a435d9f 100644 --- a/faigate/providers.py +++ b/faigate/providers.py @@ -131,6 +131,7 @@ def __init__(self, name: str, cfg: dict): self.cache = dict(cfg.get("cache", {})) self.image = dict(cfg.get("image", {})) self.lane = dict(cfg.get("lane", {})) + self.default_extra_body = dict(cfg.get("extra_body", {}) or {}) self.transport = { **get_provider_transport_binding( name, @@ -647,6 +648,8 @@ async def complete( body["tools"] = tools if stream: body["stream"] = True + if self.default_extra_body: + body.update(self.default_extra_body) if extra_body: body.update(extra_body) diff --git a/faigate/registry.py b/faigate/registry.py index ca7ce88..ea12660 100644 --- a/faigate/registry.py +++ b/faigate/registry.py @@ -82,7 +82,7 @@ class ProviderDef(TypedDict, total=False): api_key_env="OPENAI_CODEX_TOKEN", auth_optional=True, tier="default", - example_model="openai-codex/gpt-5.3-codex", + example_model="openai-codex/gpt-5.4", pricing={"input": 0.0, "output": 0.0}, notes=( "OpenAI Codex (ChatGPT OAuth) – token from ~/.codex/auth.json. " diff --git a/pyproject.toml b/pyproject.toml index 10ba670..3aa0c90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "faigate" -version = "2.1.1" +version = "2.1.2" description = "Local OpenAI-compatible routing gateway for OpenClaw and other AI-native clients." readme = "README.md" license = "Apache-2.0"