diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 81bac6c92f247..f0af3a6744330 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -996,20 +996,19 @@ def get_model_context_length( context_length = matched.get("context_length") if isinstance(context_length, int): return context_length - if not _is_known_provider_base_url(base_url): - # 3. Try querying local server directly - if is_local_endpoint(base_url): - local_ctx = _query_local_context_length(model, base_url) - if local_ctx and local_ctx > 0: - save_context_length(model, base_url, local_ctx) - return local_ctx - logger.info( - "Could not detect context length for model %r at %s — " - "defaulting to %s tokens (probe-down). Set model.context_length " - "in config.yaml to override.", - model, base_url, f"{DEFAULT_FALLBACK_CONTEXT:,}", + # Matched but no context_length — fall through to models.dev + # and hardcoded defaults rather than immediately defaulting to 128K. + logger.debug( + "Endpoint %s has model %r but no context_length metadata — " + "falling through to models.dev and hardcoded defaults.", + base_url, model, ) - return DEFAULT_FALLBACK_CONTEXT + # 3. Try querying local server directly (custom endpoints only) + if is_local_endpoint(base_url): + local_ctx = _query_local_context_length(model, base_url) + if local_ctx and local_ctx > 0: + save_context_length(model, base_url, local_ctx) + return local_ctx # 4. Anthropic /v1/models API (only for regular API keys, not OAuth) if provider == "anthropic" or ( diff --git a/agent/models_dev.py b/agent/models_dev.py index 42c8925ffe78d..7055207b46375 100644 --- a/agent/models_dev.py +++ b/agent/models_dev.py @@ -170,6 +170,7 @@ class ProviderInfo: "perplexity": "perplexity", "cohere": "cohere", "ollama-cloud": "ollama-cloud", + "ollama": "ollama-cloud", } # Reverse mapping: models.dev → Hermes (built lazily) diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index 6a0eab1512575..458aedbfd1516 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -22,6 +22,7 @@ from agent.model_metadata import ( CONTEXT_PROBE_TIERS, DEFAULT_CONTEXT_LENGTHS, + DEFAULT_FALLBACK_CONTEXT, _strip_provider_prefix, estimate_tokens_rough, estimate_messages_tokens_rough, @@ -290,7 +291,10 @@ def test_custom_endpoint_metadata_beats_fuzzy_default(self, mock_endpoint_fetch, @patch("agent.model_metadata.fetch_model_metadata") @patch("agent.model_metadata.fetch_endpoint_model_metadata") - def test_custom_endpoint_without_metadata_skips_name_based_default(self, mock_endpoint_fetch, mock_fetch): + def test_custom_endpoint_empty_metadata_falls_through_to_defaults(self, mock_endpoint_fetch, mock_fetch): + """When a custom endpoint returns no model metadata at all, + resolution should fall through to models.dev and hardcoded defaults + rather than immediately defaulting to 128K (probe tier 0).""" mock_fetch.return_value = {} mock_endpoint_fetch.return_value = {} @@ -300,7 +304,39 @@ def test_custom_endpoint_without_metadata_skips_name_based_default(self, mock_en api_key="test-key", ) - assert result == CONTEXT_PROBE_TIERS[0] + # "glm" substring matches the hardcoded DEFAULT_CONTEXT_LENGTHS entry + # which returns 202752, rather than the probe-tier fallback of 128K + assert result == 202752 + + @patch("agent.model_metadata.fetch_model_metadata") + @patch("agent.model_metadata.fetch_endpoint_model_metadata") + def test_custom_endpoint_match_no_context_length_falls_through(self, mock_endpoint_fetch, mock_fetch): + """Regression: when a custom endpoint lists a model but its metadata + has no context_length field, the resolver must fall through to models.dev + and hardcoded defaults instead of returning the 128K fallback. + + Any OpenAI-compatible server (Ollama, Chutes, LM Studio, etc.) may + return {name: "model-name"} without context_length — the resolution + chain should keep trying rather than giving up at step 2. + """ + mock_fetch.return_value = {} + # Endpoint returns the model but with no context_length + mock_endpoint_fetch.return_value = { + "glm-5.1": {"name": "glm-5.1"} + } + + result = get_model_context_length( + "glm-5.1", + # Use an unknown custom endpoint — known provider URLs skip + # the endpoint metadata block entirely, so the regression path + # wouldn't be exercised with e.g. ollama.com. + base_url="https://llm.chutes.ai/v1", + provider="custom", + ) + + # Should fall through to the hardcoded "glm": 202752 default, + # NOT return 128000 (DEFAULT_FALLBACK_CONTEXT) + assert result == 202752 @patch("agent.model_metadata.fetch_model_metadata") @patch("agent.model_metadata.fetch_endpoint_model_metadata")