From 03487e9ed9b2cd4b2a321c00bd5e79d144e6524c Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:50:04 +0530 Subject: [PATCH] fix(models): keep an observed Codex catalog cap when a refresh fails After the 1h context TTL, a failed catalog refresh sent resolution to the static fallback, which applied the -900k bump without the max_context_window the same token and endpoint had already published. A known 600K cap became 900K with no newer evidence. The fallback branch now passes the cached cap. Introduced with the cap itself in #115872. Reported by andrexibiza on #128288. --- agent/model_metadata.py | 11 ++++++++--- tests/agent/test_model_metadata.py | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 5ae8ccd65a..d87e2bef82 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -1914,15 +1914,20 @@ def _resolve_codex_oauth_context_length_with_source(model: str, access_token: st # main-agent path normalizes it away before reaching here, but display/auxiliary callers pass it through # (#92797 review). lookup_bare = _bare_codex_slug(strip_codex_context_variant_suffix(model_bare)) + live_max: Dict[str, int] = {} + + def _catalog_key(keys) -> Optional[str]: # exact slug, then case-insensitive in case casing drifts + return lookup_bare if lookup_bare in keys else next((s for s in keys if s.lower() == lookup_bare.lower()), None) + if access_token: live, fresh_probe = _fetch_codex_oauth_context_lengths_with_source(access_token, base_url=base_url) live_max = _codex_oauth_max_context_cache.get(_codex_oauth_token_fingerprint(access_token, base_url), {}) - # Exact slug, then case-insensitive in case casing drifts. - slug = lookup_bare if lookup_bare in live else next((s for s in live if s.lower() == lookup_bare.lower()), None) + slug = _catalog_key(live) if slug is not None: return _apply_verified_bump(live[slug], "live" if fresh_probe else "memory", live_max.get(slug)) hit = _longest_key_match(_CODEX_OAUTH_CONTEXT_FALLBACK, lookup_bare.lower()) - return _apply_verified_bump(hit[1], "fallback") if hit else (None, "") + # A failed refresh past the TTL must not lift an already observed catalog cap. + return _apply_verified_bump(hit[1], "fallback", live_max.get(_catalog_key(live_max))) if hit else (None, "") def _resolve_nous_context_length(model: str, base_url: str = "", api_key: str = "") -> Tuple[Optional[int], str]: diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index 98c6c29bf4..28fd5dddf8 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -428,6 +428,7 @@ class TestCodexOAuthContextLength: def setup_method(self): import agent.model_metadata as mm mm._codex_oauth_context_cache = {} + mm._codex_oauth_max_context_cache = {} # the fallback branch reads it too def test_live_catalogue_cache_is_scoped_to_access_token(self): @@ -680,6 +681,23 @@ class TestCodexOAuthContextLength: ) assert ctx == 272_000 + def test_failed_refresh_past_ttl_keeps_the_observed_catalog_cap(self): + """A refresh that fails after the context TTL must not lift a cap the same token and + endpoint already published: no newer evidence, so the -900k alias stays at the old max.""" + import agent.model_metadata as mm + + ok = MagicMock(status_code=200) + ok.json.return_value = {"models": [{"slug": "GPT-5.6-Luna", "context_window": 272_000, "max_context_window": 600_000}]} + route = dict(base_url="https://chatgpt.com/backend-api/codex", api_key=_codex_jwt("fake-token"), provider="openai-codex") + with patch.object(mm, "_codex_oauth_context_cache", {}), patch.object(mm, "_codex_oauth_max_context_cache", {}), \ + patch("agent.model_metadata.get_cached_context_length", return_value=None), \ + patch("agent.model_metadata.save_context_length"): + with patch("agent.model_metadata.model_metadata_http.get", return_value=ok): + assert mm.get_model_context_length(model="gpt-5.6-luna-900k", **route) == 600_000 + with patch("agent.model_metadata.model_metadata_http.get", side_effect=OSError("catalog down")), \ + patch("agent.model_metadata.time.time", return_value=time.time() + mm._CODEX_OAUTH_CONTEXT_CACHE_TTL + 1): + assert mm.get_model_context_length(model="gpt-5.6-luna-900k", **route) == 600_000 + def test_catalog_max_is_published_before_the_context_entry(self): """A reader that sees a fresh context entry must already be able to see its cap, or a concurrent -900k resolution between the two writes skips the catalog max."""