From fb4bed86bf443154f2b88fdbcd19916e0d455e05 Mon Sep 17 00:00:00 2001 From: Mrun25 <165297479+Mrun25@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:50:34 +0530 Subject: [PATCH] fix(proxy): list alias wildcards as routable ids, not raw catalog ids An alias whose suffix collides with a catalog sub-prefix (e.g. public "cloudflare/@cf/*" over deployment "cloudflare/*") took the partial-filter branch and listed raw catalog ids. Routing substitutes the captured suffix into the deployment pattern, so those listed ids routed to targets that do not exist. Only use filter semantics when the deployment repeats the wildcard suffix (or no deployment is available); aliases prepend their prefix to provider-stripped catalog ids so every listed id routes back to a real catalog model. Addresses Greptile review on #46390. --- litellm/proxy/auth/model_checks.py | 8 +++++- tests/unit/proxy/auth/test_model_checks.py | 32 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/auth/model_checks.py b/litellm/proxy/auth/model_checks.py index e6343aef67c..ed384f3a082 100644 --- a/litellm/proxy/auth/model_checks.py +++ b/litellm/proxy/auth/model_checks.py @@ -312,7 +312,13 @@ def get_known_models_from_wildcard(wildcard_model: str, litellm_params: LiteLLM_ for wc_model in wildcard_models if wc_model.removeprefix(provider_id_prefix).startswith(model_prefix) ] - if deployment_repeats_prefix or filtered_wildcard_models: + # Filter semantics (list matching catalog ids) only apply when the public name is the + # deployment's own pattern. For an alias over a broader deployment (e.g. public + # "cloudflare/@cf/*" over "cloudflare/*"), routing substitutes the captured suffix into + # the deployment pattern, so catalog ids must be listed with the alias prefix prepended + # to their provider-stripped form; listing the raw catalog id would advertise models + # whose routed target does not exist. + if deployment_repeats_prefix or (litellm_params is None and filtered_wildcard_models): wildcard_models = filtered_wildcard_models else: # add model prefix to wildcard models diff --git a/tests/unit/proxy/auth/test_model_checks.py b/tests/unit/proxy/auth/test_model_checks.py index 6ed02452485..e6aeddca8ec 100644 --- a/tests/unit/proxy/auth/test_model_checks.py +++ b/tests/unit/proxy/auth/test_model_checks.py @@ -1272,3 +1272,35 @@ def test_alias_wildcard_prepends_its_prefix_to_catalog_ids_without_their_provide litellm.model_cost.pop(catalog_id, None) litellm.models_by_provider["cloudflare"].discard(catalog_id) litellm.add_known_models(model_cost_map={}) + + +def test_alias_wildcard_matching_catalog_subprefix_lists_routable_ids() -> None: + """GH#46384: an alias whose suffix collides with a catalog sub-prefix (public + "cloudflare/@cf/*" over deployment "cloudflare/*") is an alias, not a filter. + Routing substitutes the captured suffix into the deployment pattern, so the + listing must prepend the alias prefix to provider-stripped catalog ids; the + raw catalog id would route to a target that does not exist.""" + import litellm + from litellm.proxy.auth.model_checks import get_known_models_from_wildcard + from litellm.types.router import LiteLLM_Params + + catalog_id: Final = "cloudflare/@cf/partial-alias-org/model-b" + try: + litellm.register_model( + {catalog_id: {"litellm_provider": "cloudflare", "mode": "chat"}}, persist_across_reloads=False + ) + + listed: Final = get_known_models_from_wildcard("cloudflare/@cf/*", LiteLLM_Params(model="cloudflare/*")) + + # routing captures the segment after "cloudflare/@cf/" and substitutes it into + # "cloudflare/*"; every listed id must therefore map back to a catalog id + alias_prefix: Final = "cloudflare/@cf/" + routed: Final = [ + f"cloudflare/{model.removeprefix(alias_prefix)}" for model in listed if model.startswith(alias_prefix) + ] + assert catalog_id in routed + assert catalog_id not in listed + finally: + litellm.model_cost.pop(catalog_id, None) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # test cleanup + litellm.models_by_provider["cloudflare"].discard(catalog_id) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # test cleanup + litellm.add_known_models(model_cost_map={}) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # test cleanup