From 0ac1c45fdf7e8bb916c592151ed7664f58b50f47 Mon Sep 17 00:00:00 2001 From: mayuriphad Date: Fri, 28 Aug 2026 09:10:17 +0530 Subject: [PATCH 1/2] fix(proxy): admit litellm_proxy/hosted_vllm in provider-endpoint discovery get_provider_models gated all endpoint discovery on membership in the static litellm.models_by_provider dict, which litellm_proxy and hosted_vllm are intentionally absent from since their model list only exists behind the provider's own endpoint. GET /v1/models returned the literal wildcard string instead of the expanded model list for these providers. Now falls through to ProviderConfigManager, which already knows about them, before giving up. --- litellm/proxy/auth/model_checks.py | 15 ++++++-- .../proxy/auth/test_model_checks.py | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index 1625198892f..69294f021d2 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -13,7 +13,7 @@ from litellm.router import Router from litellm.router_utils.fallback_event_handlers import get_fallback_model_group from litellm.types.router import CredentialLiteLLMParams, LiteLLM_Params from litellm.types.utils import LlmProviders -from litellm.utils import get_valid_models +from litellm.utils import ProviderConfigManager, get_valid_models _CREDENTIAL_LITELLM_PARAM_FIELDS = set(CredentialLiteLLMParams.model_fields) @@ -45,7 +45,18 @@ def get_provider_models(provider: str, litellm_params: LiteLLM_Params | None = N if provider in litellm.models_by_provider: provider_models: Final = get_valid_models(custom_llm_provider=provider, litellm_params=litellm_params) return provider_models - return None + + # Providers with no static catalog (litellm_proxy, hosted_vllm, ollama, ...) are absent + # from models_by_provider by design: their model list only exists behind the provider's + # own endpoint. ProviderConfigManager still knows about them, so admit them here instead + # of returning None before endpoint discovery is ever attempted. + try: + llm_provider: Final = LlmProviders(provider) + except ValueError: + return None + if ProviderConfigManager.get_provider_model_info(model=None, provider=llm_provider) is None: + return None + return get_valid_models(custom_llm_provider=provider, litellm_params=litellm_params) def _get_models_from_access_groups( diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index 62073f4bf51..6b18d76bd75 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -803,3 +803,38 @@ def test_get_complete_model_list_sentinel_only_grants_nothing(): infer_model_from_keys=False, ) assert result == [] + + +def test_get_provider_models_admits_providers_without_a_static_catalog(): + """litellm_proxy and hosted_vllm have no entry in litellm.models_by_provider + (their model list only exists behind the provider's own endpoint), so the + static-dict gate must not reject them before endpoint discovery runs. + """ + import litellm + from litellm.proxy.auth.model_checks import get_provider_models + from litellm.types.router import LiteLLM_Params + + assert "litellm_proxy" not in litellm.models_by_provider + assert "hosted_vllm" not in litellm.models_by_provider + + with patch( + "litellm.proxy.auth.model_checks.get_valid_models", + return_value=["litellm_proxy/gpt-4o"], + ) as mock_get_valid_models: + result = get_provider_models( + "litellm_proxy", + litellm_params=LiteLLM_Params( + model="litellm_proxy/*", + api_base="http://upstream:4000", + api_key="sk-upstream", + ), + ) + + assert result == ["litellm_proxy/gpt-4o"] + mock_get_valid_models.assert_called_once() + + +def test_get_provider_models_returns_none_for_an_unknown_provider(): + from litellm.proxy.auth.model_checks import get_provider_models + + assert get_provider_models("not-a-real-provider") is None From 3d7d8f69f4ca903b69aadeb4e47a6ac4ba33ea9d Mon Sep 17 00:00:00 2001 From: mayuriphad Date: Fri, 28 Aug 2026 10:05:55 +0530 Subject: [PATCH 2/2] fix(tests): stop patching an SDK internal in the discovery regression test TQ008 flagged patching litellm.proxy.auth.model_checks.get_valid_models. Assert the gate's own return value instead of mocking past it. --- .../proxy/auth/test_model_checks.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index 6b18d76bd75..534eeb4fa84 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -809,6 +809,11 @@ def test_get_provider_models_admits_providers_without_a_static_catalog(): """litellm_proxy and hosted_vllm have no entry in litellm.models_by_provider (their model list only exists behind the provider's own endpoint), so the static-dict gate must not reject them before endpoint discovery runs. + + Regression check only, not a discovery test: with check_provider_endpoint + left at its default (off), get_valid_models never reaches the network and + falls back to models_by_provider.get(provider, []) -- an empty list, not + None. Before the fix, the gate itself returned None for these providers. """ import litellm from litellm.proxy.auth.model_checks import get_provider_models @@ -817,21 +822,16 @@ def test_get_provider_models_admits_providers_without_a_static_catalog(): assert "litellm_proxy" not in litellm.models_by_provider assert "hosted_vllm" not in litellm.models_by_provider - with patch( - "litellm.proxy.auth.model_checks.get_valid_models", - return_value=["litellm_proxy/gpt-4o"], - ) as mock_get_valid_models: - result = get_provider_models( - "litellm_proxy", - litellm_params=LiteLLM_Params( - model="litellm_proxy/*", - api_base="http://upstream:4000", - api_key="sk-upstream", - ), - ) + result = get_provider_models( + "litellm_proxy", + litellm_params=LiteLLM_Params( + model="litellm_proxy/*", + api_base="http://upstream:4000", + api_key="sk-upstream", + ), + ) - assert result == ["litellm_proxy/gpt-4o"] - mock_get_valid_models.assert_called_once() + assert result == [] def test_get_provider_models_returns_none_for_an_unknown_provider():