diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index e0a388b53..2012d3df1 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -533,6 +533,15 @@ class Config(BaseSettings): continue p = getattr(self.providers, spec.name, None) if p and any(_kw_matches(kw) for kw in spec.keywords): + # Local providers (Ollama, vLLM, …) keep model-family keywords + # like "nemotron" or "llama" to enable bare-model auto-routing, + # but those keywords collide with cloud-hosted variants of the + # same family (e.g. `nvidia/nemotron-...` via OpenRouter). Only + # honor a local keyword match when the user has actually + # configured that local endpoint via `api_base` — mirrors the + # gate already used by the local-fallback loop below. + if spec.is_local and not p.api_base: + continue if spec.is_oauth or spec.is_local or spec.is_direct or p.api_key: return p, spec.name diff --git a/tests/cli/test_commands.py b/tests/cli/test_commands.py index 5cee63135..647dbb166 100644 --- a/tests/cli/test_commands.py +++ b/tests/cli/test_commands.py @@ -1160,6 +1160,42 @@ def test_config_falls_back_to_vllm_when_ollama_not_configured(): assert config.get_api_base() == "http://localhost:8000" +def test_config_cloud_nemotron_is_not_hijacked_by_unconfigured_ollama(): + """`nvidia/nemotron-*` via a gateway must not route to Ollama when no + Ollama endpoint is configured. Ollama keeps "nemotron" in its keywords + for bare-model auto-routing (PR #1863), which previously hijacked + cloud-hosted nemotron variants and silently sent traffic to + http://localhost:11434/v1.""" + config = Config.model_validate( + { + "agents": { + "defaults": { + "provider": "auto", + "model": "nvidia/nemotron-3-super-120b-a12b", + } + }, + "providers": {"openrouter": {"apiKey": "sk-or-test"}}, + } + ) + + assert config.get_provider_name() == "openrouter" + assert config.get_api_base() == "https://openrouter.ai/api/v1" + + +def test_config_bare_nemotron_still_auto_routes_to_configured_ollama(): + """Preserves PR #1863 intent: when the user has actually configured an + Ollama endpoint, a bare nemotron model still auto-routes there.""" + config = Config.model_validate( + { + "agents": {"defaults": {"provider": "auto", "model": "nemotron-3-nano"}}, + "providers": {"ollama": {"apiBase": "http://localhost:11434/v1"}}, + } + ) + + assert config.get_provider_name() == "ollama" + assert config.get_api_base() == "http://localhost:11434/v1" + + def test_openai_compat_provider_passes_model_through(): from nanobot.providers.openai_compat_provider import OpenAICompatProvider