diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index 71ae1348f3..32f209a763 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -223,12 +223,14 @@ def get_known_models_from_wildcard( except ValueError: # safely fail return [] - if litellm_params is None: # need litellm params to extract litellm model name - return [] - - try: - provider = litellm_params.model.split("/", 1)[0] - except ValueError: + # Use provider from litellm_params when available, otherwise from wildcard prefix + # (e.g., "openai" from "openai/*" - needed for BYOK where wildcard isn't in router) + if litellm_params is not None: + try: + provider = litellm_params.model.split("/", 1)[0] + except ValueError: + provider = wildcard_provider_prefix + else: provider = wildcard_provider_prefix # get all known provider models @@ -282,7 +284,7 @@ def _get_wildcard_models( ## get litellm params from model if llm_router is not None: model_list = llm_router.get_model_list(model_name=model) - if model_list is not None: + if model_list: for router_model in model_list: wildcard_models = get_known_models_from_wildcard( wildcard_model=model, @@ -291,11 +293,22 @@ def _get_wildcard_models( ), ) all_wildcard_models.extend(wildcard_models) + else: + # Router has no deployment for this wildcard (e.g., BYOK team models) + # Fall back to expanding from known provider models + wildcard_models = get_known_models_from_wildcard( + wildcard_model=model, litellm_params=None + ) + if wildcard_models: + models_to_remove.add(model) + all_wildcard_models.extend(wildcard_models) else: # get all known provider models - wildcard_models = get_known_models_from_wildcard(wildcard_model=model) + wildcard_models = get_known_models_from_wildcard( + wildcard_model=model, litellm_params=None + ) - if wildcard_models is not None: + if wildcard_models: models_to_remove.add(model) all_wildcard_models.extend(wildcard_models) diff --git a/tests/proxy_unit_tests/test_proxy_utils.py b/tests/proxy_unit_tests/test_proxy_utils.py index e34cad66ba..1d9bb15b21 100644 --- a/tests/proxy_unit_tests/test_proxy_utils.py +++ b/tests/proxy_unit_tests/test_proxy_utils.py @@ -1928,6 +1928,24 @@ def test_get_known_models_from_wildcard( assert all(model in wildcard_models for model in expected_models) +def test_get_known_models_from_wildcard_without_litellm_params(): + """ + Test wildcard expansion without litellm_params (BYOK case - team has openai/* + but no deployment in router config). + """ + from litellm.proxy.auth.model_checks import get_known_models_from_wildcard + + wildcard_models = get_known_models_from_wildcard( + wildcard_model="openai/*", litellm_params=None + ) + # Should return expanded OpenAI models (gpt-4o, gpt-4o-mini, etc.) + assert len(wildcard_models) > 0 + assert all(m.startswith("openai/") for m in wildcard_models) + # Check for common OpenAI models + model_ids = [m.split("/", 1)[1] for m in wildcard_models] + assert "gpt-4o" in model_ids or "gpt-3.5-turbo" in model_ids + + @pytest.mark.parametrize( "data, user_api_key_dict, expected_model", [ diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index 789af480e7..193b014f03 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -62,3 +62,27 @@ def test_get_complete_model_list_order(key_models, team_models, proxy_model_list infer_model_from_keys=False, llm_router=Router(model_list=model_list), ) == expected + + +def test_get_complete_model_list_byok_wildcard_expansion(): + """ + Test that wildcard models (e.g., openai/*) are expanded when the router has + no deployment for them - BYOK case where team has openai/* but proxy has + no openai config. + """ + from litellm.proxy.auth.model_checks import get_complete_model_list + from litellm import Router + + # Router with empty model_list - no openai/* deployment (BYOK scenario) + result = get_complete_model_list( + key_models=[], + team_models=["openai/*"], + proxy_model_list=[], + user_model=None, + infer_model_from_keys=False, + llm_router=Router(model_list=[]), + ) + # Should expand openai/* to actual OpenAI models + assert len(result) > 0 + assert all(m.startswith("openai/") for m in result) + assert "openai/*" not in result