From 414c474d97b69a03ec9b52e3848f260e27660866 Mon Sep 17 00:00:00 2001 From: Frank Song Date: Fri, 8 May 2026 08:41:55 +0800 Subject: [PATCH] fix: preserve local custom provider model ids --- api/config.py | 16 ++++++- ...1625_local_server_model_id_preservation.py | 42 ++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/api/config.py b/api/config.py index 4dc1345e..da57419d 100644 --- a/api/config.py +++ b/api/config.py @@ -1385,6 +1385,20 @@ _LOCAL_SERVER_PROVIDERS = { } +def _is_local_server_provider(provider_id: str) -> bool: + """True when provider_id names a local model server. + + Named custom providers resolve to ``custom:``. Treat those as local + when the bare slug is one of the known local-server provider names too. + """ + provider = str(provider_id or "").strip().lower() + if provider in _LOCAL_SERVER_PROVIDERS: + return True + if provider.startswith("custom:"): + return provider.removeprefix("custom:") in _LOCAL_SERVER_PROVIDERS + return False + + def _base_url_points_at_local_server(base_url: str) -> bool: """True if base_url's host is a loopback or private IP (likely local server). @@ -1561,7 +1575,7 @@ def resolve_model_provider(model_id: str) -> tuple: # default settings, ignoring user-tuned context length / parallel slots. # See #1625. Detect either by canonical provider name OR by base_url # pointing at a loopback/private host. - if (str(config_provider or "").lower() in _LOCAL_SERVER_PROVIDERS + if (_is_local_server_provider(config_provider) or _base_url_points_at_local_server(config_base_url)): return model_id, config_provider, config_base_url # Only strip the provider prefix when it's a known provider namespace diff --git a/tests/test_issue1625_local_server_model_id_preservation.py b/tests/test_issue1625_local_server_model_id_preservation.py index 5c736c90..45f7e6e4 100644 --- a/tests/test_issue1625_local_server_model_id_preservation.py +++ b/tests/test_issue1625_local_server_model_id_preservation.py @@ -22,9 +22,12 @@ from api import config as cfg_mod # ── Helpers ─────────────────────────────────────────────────────────────── -def _patch_cfg(monkeypatch, **model_overrides): +def _patch_cfg(monkeypatch, custom_providers=None, **model_overrides): """Patch api.config.cfg to a synthetic config dict for the duration of a test.""" - fake_cfg = {"model": dict(model_overrides), "custom_providers": []} + fake_cfg = { + "model": dict(model_overrides), + "custom_providers": list(custom_providers or []), + } monkeypatch.setattr(cfg_mod, "cfg", fake_cfg) @@ -76,6 +79,41 @@ def test_lmstudio_with_openai_prefix_preserved(monkeypatch): ) +@pytest.mark.parametrize("provider_name", [ + "ollama", + "lmstudio", + "lm-studio", + "vllm", + "tabby", +]) +def test_named_custom_local_server_provider_preserves_full_model_id_on_lan_host( + provider_name, + monkeypatch, +): + """#1830: custom: slugs must keep local-server no-strip semantics. + + Non-loopback hostnames like ollama.lan do not trigger the base_url local + heuristic, so the provider-id check must recognize custom: directly. + """ + _patch_cfg( + monkeypatch, + provider=provider_name, + base_url="http://lan-host:1234/v1", + default="qwen/qwen3.6-27b", + custom_providers=[ + { + "name": provider_name, + "base_url": "http://lan-host:1234/v1", + "api_key": "local-key", + }, + ], + ) + model, provider, base_url = cfg_mod.resolve_model_provider("qwen/qwen3.6-27b") + assert model == "qwen/qwen3.6-27b" + assert provider == f"custom:{provider_name}" + assert base_url == "http://lan-host:1234/v1" + + # ── Loopback / private-IP heuristic ───────────────────────────────────────