From cca034fce3f943bf6ac6daac169b35f63dfda94e Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sun, 6 Sep 2026 15:55:27 -0700 Subject: [PATCH] [eric] models: GPT-6 Astra on both lanes (gpt-6-astra, 1.05M ctx, $10/$50); the gpt-5 body patch covers gpt-6; the sub-lane row is unverified until ChatGPT is reconnected Co-Authored-By: Claude Fable 5.1 --- backend/apps/agents/9router_gpt5_patch.js | 3 ++- backend/apps/agents/providers/openrouter.py | 1 + backend/apps/agents/providers/pricing.py | 3 +++ backend/apps/agents/providers/registry.py | 15 +++++++++++++ backend/tests/test_gpt6_catalog.py | 25 +++++++++++++++++++++ backend/tests/test_history_prune_state.py | 8 +++++++ frontend/src/app/Main.tsx | 2 +- 7 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 backend/tests/test_gpt6_catalog.py diff --git a/backend/apps/agents/9router_gpt5_patch.js b/backend/apps/agents/9router_gpt5_patch.js index 7e1ddfa0..572a8c86 100644 --- a/backend/apps/agents/9router_gpt5_patch.js +++ b/backend/apps/agents/9router_gpt5_patch.js @@ -148,7 +148,8 @@ function isGpt5Model(model) { for (const p of prefixes) { if (m.startsWith(p)) { m = m.slice(p.length); break; } } - return m.startsWith('gpt-5'); + // GPT-6 Astra (2026-09-03) keeps every GPT-5 contract this patch exists for: max_completion_tokens, no sampling fields, the reasoning floor. + return m.startsWith('gpt-5') || m.startsWith('gpt-6'); } // GPT-5 burns 8-30K reasoning tokens before any output; the CLI's default 4096 caps before content lands. Floor at 32K and only raise, never lower. diff --git a/backend/apps/agents/providers/openrouter.py b/backend/apps/agents/providers/openrouter.py index 8e7e9005..7d49e6ae 100644 --- a/backend/apps/agents/providers/openrouter.py +++ b/backend/apps/agents/providers/openrouter.py @@ -21,6 +21,7 @@ p_9router_cache: dict = {"available": None, "checked_at": 0} # Per-model published pricing in $/1M tokens (input, output) for direct API key lanes. Sourced from each provider's official pricing page as of May 2026. The Claude Agent SDK ALWAYS computes total_cost_usd at Anthropic rates; for any non-Anthropic upstream the SDK number is 50-1000x wrong and we MUST recompute. Used by agent_manager's cost recompute logic. P_DIRECT_API_PRICING: dict[str, tuple[float, float]] = { # OpenAI GPT-5.x family (source: platform.openai.com/docs/pricing). + "gpt-6-astra": (10.00, 50.00), "gpt-5.5": (1.25, 10.00), "gpt-5.4": (1.25, 10.00), "gpt-5.4-mini": (0.25, 2.00), diff --git a/backend/apps/agents/providers/pricing.py b/backend/apps/agents/providers/pricing.py index a750f2ef..12a0fe16 100644 --- a/backend/apps/agents/providers/pricing.py +++ b/backend/apps/agents/providers/pricing.py @@ -39,6 +39,9 @@ MODEL_TIERS: dict[str, tuple[int, int, int]] = { "anthropic/claude-3-haiku": (2, 5, 1), # OpenAI + "gpt-6-astra": (5, 2, 5), + "openai/gpt-6-astra": (5, 2, 5), + "gpt-6": (5, 2, 5), "gpt-5.5": (5, 2, 5), "openai/gpt-5.5": (5, 2, 5), "gpt-5.5-pro": (5, 1, 5), diff --git a/backend/apps/agents/providers/registry.py b/backend/apps/agents/providers/registry.py index 3709b01f..3394514f 100644 --- a/backend/apps/agents/providers/registry.py +++ b/backend/apps/agents/providers/registry.py @@ -88,6 +88,14 @@ BUILTIN_MODELS: dict[str, list[dict[str, Any]]] = { ], "OpenAI": [ + # GPT-6 Astra (released 2026-09-03; API id gpt-6-astra; 1,050,000 ctx, 128k out; $10/$50 per 1M at short + # context, 2x input and 1.5x output above 272K input). The sub lane rides the same cx translator that + # forwards the 5.6 family to the ChatGPT Responses backend: EXPECTED, NOT VERIFIED on the pinned 0.3.60, + # because the only ChatGPT login on the dev box is dead (ENG-471); verify with one 1-token call once it is + # reconnected, and pull the row if ChatGPT's Codex backend names the model differently. + {"value": "gpt-6", "label": "GPT-6 Astra", + "context_window": 1_050_000, "router_model_id": "cx/gpt-6-astra", + "api": "codex", "subscription_only": True, "reasoning": True}, # Codex sub lanes re-probed 2026-07-26: cx/gpt-5.6-{sol,terra,luna} AND the previously pulled # cx/gpt-5.5 all return real completions on the pinned 0.3.60 (the old 404 healed upstream; # the cx translator forwards to the ChatGPT Responses backend, which now serves them). @@ -115,6 +123,9 @@ BUILTIN_MODELS: dict[str, list[dict[str, Any]]] = { # cp-openai passthrough, whose scrubs prefix-match "gpt-5" (max_tokens rename, sampling strip, # and the reasoning_effort-with-tools drop that 5.6 still requires on /chat/completions). # 1M+ ctx, 128k out; $5/$30 Sol, $2.50/$15 Terra, $1/$6 Luna per 1M. + {"value": "gpt-6-api", "label": "GPT-6 Astra (API key)", + "context_window": 1_050_000, "router_model_id": "cp-openai/gpt-6-astra", "model_id": "gpt-6-astra", + "api": "openai", "reasoning": True, "route": "api"}, {"value": "gpt-5.6-api", "label": "GPT-5.6 Sol (API key)", "context_window": 1_000_000, "router_model_id": "cp-openai/gpt-5.6-sol", "model_id": "gpt-5.6-sol", "api": "openai", "reasoning": True, "route": "api"}, @@ -404,11 +415,15 @@ COST_PER_1M_TOKENS: dict[tuple[str, str], tuple[float, float]] = { ("Anthropic", "opus-4-8"): (5.0, 25.0), ("Anthropic", "opus-5"): (5.0, 25.0), ("Anthropic", "haiku"): (1.0, 5.0), + # OpenAI API-key rates. GPT-6 Astra at its SHORT-context rate ($10/$50, 2026-09-03); above 272K input OpenAI + # bills 2x input and 1.5x output, which this flat table cannot express, so a long-context turn is UNDER-estimated here. + ("OpenAI", "gpt-6-api"): (10.0, 50.0), # OpenAI API-key rates (GPT-5.6 tiers, GA 2026-07-09) ("OpenAI", "gpt-5.6-api"): (5.0, 30.0), ("OpenAI", "gpt-5.6-terra-api"): (2.5, 15.0), ("OpenAI", "gpt-5.6-luna-api"): (1.0, 6.0), # OpenAI; Codex subscription path, user pays nothing per token + ("OpenAI", "gpt-6"): (0.0, 0.0), ("OpenAI", "gpt-5.6"): (0.0, 0.0), ("OpenAI", "gpt-5.6-terra"): (0.0, 0.0), ("OpenAI", "gpt-5.6-luna"): (0.0, 0.0), diff --git a/backend/tests/test_gpt6_catalog.py b/backend/tests/test_gpt6_catalog.py new file mode 100644 index 00000000..c61d8e40 --- /dev/null +++ b/backend/tests/test_gpt6_catalog.py @@ -0,0 +1,25 @@ +"""GPT-6 Astra (2026-09-03): both lanes in the catalog, priced, tiered, and the passthrough rules extended to its prefix.""" +from backend.apps.agents.providers.registry import find_builtin_model, BUILTIN_MODELS as PROVIDER_MODELS + + +def test_gpt6_astra_is_on_both_lanes_with_the_documented_ids(): + sub = find_builtin_model("gpt-6") + assert sub and sub["router_model_id"] == "cx/gpt-6-astra" and sub["api"] == "codex" and sub["subscription_only"] is True + api = find_builtin_model("gpt-6-api") + assert api and api["router_model_id"] == "cp-openai/gpt-6-astra" and api["model_id"] == "gpt-6-astra" and api["route"] == "api" + assert sub["context_window"] == api["context_window"] == 1_050_000 + + +def test_gpt6_astra_prices_and_tier(): + from backend.apps.agents.providers.registry import COST_PER_1M_TOKENS as MODEL_PRICING + assert MODEL_PRICING[("OpenAI", "gpt-6-api")] == (10.0, 50.0), "the short-context API rate" + assert MODEL_PRICING[("OpenAI", "gpt-6")] == (0.0, 0.0), "the subscription lane bills nothing per token" + from backend.apps.agents.providers.pricing import MODEL_TIERS + assert MODEL_TIERS["gpt-6-astra"] == (5, 2, 5) + from backend.apps.agents.providers.openrouter import P_DIRECT_API_PRICING + assert P_DIRECT_API_PRICING["gpt-6-astra"] == (10.00, 50.00) + + +def test_gpt6_sits_beside_the_gpt5_family_in_the_openai_list(): + values = [m["value"] for m in PROVIDER_MODELS["OpenAI"]] + assert values.index("gpt-6") < values.index("gpt-5.6") and values.index("gpt-6-api") < values.index("gpt-5.6-api") diff --git a/backend/tests/test_history_prune_state.py b/backend/tests/test_history_prune_state.py index 5a66bf20..7df645bf 100644 --- a/backend/tests/test_history_prune_state.py +++ b/backend/tests/test_history_prune_state.py @@ -72,3 +72,11 @@ def test_the_patch_loads_the_pruner_eagerly_not_lazily() -> None: src = open("backend/apps/agents/9router_gpt5_patch.js").read() assert re.search(r"^loadHistoryPrune\(\);", src, re.M), "a lazy load only speaks on the first request; the boot log would stay empty" assert src.index("loadHistoryPrune();") < src.index("function historyPrune(") + + +def test_the_gpt5_patch_covers_gpt6(): + # GPT-6 Astra needs the same body rewrites on /chat/completions (max_completion_tokens, no sampling, the reasoning floor); + # a prefix rule that stops at gpt-5 would silently 400 every GPT-6 API-key turn. + src = open("backend/apps/agents/9router_gpt5_patch.js").read() + assert "m.startsWith('gpt-5') || m.startsWith('gpt-6')" in src + diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index e4129451..35f62a9f 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -203,7 +203,7 @@ const DEFAULT_MODEL_PRIORITY: string[] = [ const DEFAULT_MODEL_PICKS: Record = { Anthropic: ['sonnet-5-cc', 'sonnet-5', 'sonnet-5-api', 'opus-5-cc', 'opus-5'], - OpenAI: ['gpt-5.6-terra', 'gpt-5.6', 'gpt-5.6-api', 'gpt-5.5', 'gpt-5.5-api'], + OpenAI: ['gpt-5.6-terra', 'gpt-5.6', 'gpt-5.6-api', 'gpt-5.5', 'gpt-5.5-api', 'gpt-6-api', 'gpt-6'], Google: ['gemini-3.6-flash-api', 'gemini-3.5-flash-api', 'gemini-3.1-flash-lite'], 'OpenSwarm Pro': ['sonnet', 'opus'], OpenSwarm: ['gpt-5-mini', 'claude-haiku-4.5', 'gpt-4.1'],