fix(models): mark the Anthropic curated fallback as a placeholder

_anthropic_catalog() returned the curated list as a plain list when the live
/v1/models fetch failed, so a transient outage on a proxy base_url was cached
with the full 1h TTL as if it were the account's/proxy's real catalog, and
neither a same-credentials restart nor Refresh Models could shake it before the
TTL lapsed. Wrap the fallback return in CuratedFallbackModels, the placeholder
type #107391 introduced for the same class of bug.

Salvaged from #121248 (the Anthropic half; the Codex half lands via the
typed-fallback commit before this one).
This commit is contained in:
AhmetArif0
2026-10-02 11:51:22 +05:30
committed by kshitij
parent 3fbace2e76
commit ba2a38e771
2 changed files with 14 additions and 1 deletions

View File

@@ -1436,7 +1436,9 @@ def _anthropic_catalog(normalized: str, force_refresh: bool) -> list[str]:
live = _fetch_anthropic_models(base_url=cfg_base_url or None, api_key=cfg_api_key or None)
curated = list(_PROVIDER_MODELS.get("anthropic", []))
if not live:
return curated
# A placeholder for the outage, not this account/proxy's catalog: the disk cache must
# never pin it over a same-credentials live row (#107391).
return CuratedFallbackModels(curated)
# The live /v1/models dump lags newly-routed curated aliases (reachable before enumerated):
# curated first, then live-only extras, so a fresh curated model never disappears.
return live if cfg_base_url else _merge_unique(curated, live)

View File

@@ -123,3 +123,14 @@ def test_codex_cold_fallback_retries_after_short_ttl(monkeypatch, tmp_path):
assert "gpt-6-astra" in mod.cached_provider_model_ids("openai-codex")
assert "fallback" not in cache["openai-codex"]
spawn.assert_not_called()
def test_anthropic_catalog_marks_the_curated_list_as_fallback_on_a_failed_live_fetch():
# _anthropic_catalog used to `return curated` (a plain list) on a failed live fetch, so a
# transient outage on a proxy base_url cached the generic curated list over the account's/
# proxy's real catalog for a full hour (same class of bug as #107391, just missed by 3fb8b3f6).
with patch.object(mod, "_get_model_config_dict", return_value={}), \
patch.object(mod, "_fetch_anthropic_models", return_value=None):
rows = mod.provider_model_ids("anthropic")
assert isinstance(rows, mod.CuratedFallbackModels)
assert rows == list(mod._PROVIDER_MODELS["anthropic"])