From 1a796a12472598dd365f34c90b87ea8022706a26 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 13 Aug 2026 09:54:08 -0700 Subject: [PATCH] fix(model_metadata): never fuzzy-match an empty model name against endpoint catalogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '' is a substring of every catalog key, so _resolve_endpoint_context_length with an empty model name "matched" whatever the endpoint listed first — on the Nous portal that is currently a 32K embedding model, which poisoned the resolved context length and made AIAgent init fail the 64K minimum. This is what turned tests/run_agent/test_primary_runtime_restore.py:: TestTryRecoverPrimaryTransport::test_allowed_for_nous_anthropic_messages red on every PR (CI slice 7/12) after the portal catalog reordered. Single-model endpoints still resolve with an empty name (unambiguous); non-empty names keep the substring fuzzy match. --- agent/model_metadata.py | 7 ++++++- tests/agent/test_model_metadata.py | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 3792661cdbec7..8fabeb716d50d 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -1399,7 +1399,12 @@ def _resolve_endpoint_context_length( if not matched: if len(endpoint_metadata) == 1: matched = next(iter(endpoint_metadata.values())) - else: + elif model: + # Substring fuzzy match — only meaningful with a non-empty model + # name. An empty string is a substring of EVERY key, which would + # "match" whatever model the endpoint happens to list first (e.g. + # a 32K embedding model on the Nous portal) and poison the + # resolved context length for the whole agent. for key, entry in endpoint_metadata.items(): if model in key or key in model: matched = entry diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index 8d49a21078188..bfcce7e67c143 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -591,6 +591,30 @@ class TestNousPortalContextResolution: + @patch("agent.model_metadata.fetch_endpoint_model_metadata") + def test_empty_model_never_fuzzy_matches_endpoint_catalog(self, mock_fetch): + """An empty model name must not substring-match arbitrary catalog + entries — '' is a substring of every key, so pre-fix it "matched" + whatever the endpoint listed first (e.g. a 32K embedding model on + the Nous portal) and poisoned the resolved context length.""" + import agent.model_metadata as mm + mock_fetch.return_value = { + "voyageai/voyage-code-4": {"context_length": 32_000}, + "x-ai/grok-4.6": {"context_length": 500_000}, + } + assert mm._resolve_endpoint_context_length( + "", "https://inference-api.nousresearch.com/v1" + ) is None + # Non-empty names still fuzzy-match. + assert mm._resolve_endpoint_context_length( + "grok-4.6", "https://inference-api.nousresearch.com/v1" + ) == 500_000 + # Single-model endpoints still resolve even with an empty name. + mock_fetch.return_value = {"only-model": {"context_length": 131_072}} + assert mm._resolve_endpoint_context_length( + "", "http://localhost:8080/v1" + ) == 131_072 + @patch("agent.model_metadata.fetch_endpoint_model_metadata") @patch("agent.model_metadata.fetch_model_metadata") def test_openrouter_fallback_is_not_persisted(