From 7ae352e5cf0cfd8ae7e5f2c153938c19c1e9c773 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:36:46 +0000 Subject: [PATCH] fix(model_checks): drop wildcard routes like bedrock/* from /v1/models (#31731) * fix: remove wildcard routes from /v1/models response Wildcard routes like bedrock/* were leaking into the /v1/models response because _get_wildcard_models only removed them from unique_models in the fallback branches (no router or no deployment), but not when the router had a matching deployment. Now wildcards are always removed from the base list; they are only re-added to the result when return_wildcard_routes=True is explicitly passed. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(model_checks): collapse wildcard expansion branches and tighten regression tests Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Krrish Dholakia Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: yassin --- litellm/proxy/auth/model_checks.py | 31 +++----- .../proxy/auth/test_model_checks.py | 78 +++++++++++++++++++ 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index 1625198892f..de2ca4762f1 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -387,33 +387,22 @@ def _get_wildcard_models( all_wildcard_models: Final = [] for model in unique_models: if _check_wildcard_routing(model=model): - if return_wildcard_routes: # will add the wildcard route to the list eg: anthropic/*. + if return_wildcard_routes: all_wildcard_models.append(model) - ## get litellm params from model - if llm_router is not None: - model_list = llm_router.get_model_list(model_name=model, team_id=team_id) - if model_list: - for router_model in model_list: - wildcard_models = get_known_models_from_wildcard( + models_to_remove.add(model) + + model_list = llm_router.get_model_list(model_name=model, team_id=team_id) if llm_router else None + if model_list: + for router_model in model_list: + all_wildcard_models.extend( + get_known_models_from_wildcard( wildcard_model=model, litellm_params=LiteLLM_Params(**router_model["litellm_params"]), ) - 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, litellm_params=None) - - if wildcard_models: - models_to_remove.add(model) - all_wildcard_models.extend(wildcard_models) + all_wildcard_models.extend(get_known_models_from_wildcard(wildcard_model=model, litellm_params=None)) for model in models_to_remove: unique_models.remove(model) diff --git a/tests/test_litellm/proxy/auth/test_model_checks.py b/tests/test_litellm/proxy/auth/test_model_checks.py index 62073f4bf51..d58683fd1e5 100644 --- a/tests/test_litellm/proxy/auth/test_model_checks.py +++ b/tests/test_litellm/proxy/auth/test_model_checks.py @@ -754,6 +754,84 @@ def test_expand_wildcard_invalid_litellm_params_passthrough(): assert result == [deployment] +def test_get_complete_model_list_excludes_wildcard_routes_by_default(): + """Regression (LIT-4108): a wildcard with a matching router deployment leaked into /v1/models.""" + from litellm import Router + from litellm.proxy.auth.model_checks import get_complete_model_list + + router = Router( + model_list=[ + { + "model_name": "bedrock/*", + "litellm_params": {"model": "bedrock/*"}, + }, + { + "model_name": "gpt-4", + "litellm_params": {"model": "openai/gpt-4"}, + }, + ] + ) + + result = get_complete_model_list( + key_models=[], + team_models=[], + proxy_model_list=["bedrock/*", "gpt-4"], + user_model=None, + infer_model_from_keys=False, + return_wildcard_routes=False, + llm_router=router, + ) + + assert "bedrock/*" not in result + assert "gpt-4" in result + assert any(m.startswith("bedrock/") for m in result) + + +def test_get_complete_model_list_excludes_wildcard_routes_without_router(): + from litellm.proxy.auth.model_checks import get_complete_model_list + + result = get_complete_model_list( + key_models=[], + team_models=[], + proxy_model_list=["bedrock/*", "gpt-4"], + user_model=None, + infer_model_from_keys=False, + return_wildcard_routes=False, + llm_router=None, + ) + + assert "bedrock/*" not in result + assert "gpt-4" in result + assert any(m.startswith("bedrock/") for m in result) + + +def test_get_complete_model_list_includes_wildcard_routes_when_requested(): + from litellm import Router + from litellm.proxy.auth.model_checks import get_complete_model_list + + router = Router( + model_list=[ + { + "model_name": "bedrock/*", + "litellm_params": {"model": "bedrock/*"}, + }, + ] + ) + + result = get_complete_model_list( + key_models=[], + team_models=[], + proxy_model_list=["bedrock/*"], + user_model=None, + infer_model_from_keys=False, + return_wildcard_routes=True, + llm_router=router, + ) + + assert result.count("bedrock/*") == 1 + assert any(m.startswith("bedrock/") and m != "bedrock/*" for m in result) + + def test_add_known_models_refreshes_models_by_provider_for_wildcard_expansion(): """models_by_provider was a frozen import-time snapshot of set unions, so cost map reloads (which call add_known_models) never reached wildcard expansion until a