fix(router): resolve model_group_alias on the fallback lookup path - #29378
fix(router): resolve model_group_alias on the fallback lookup path#29378alvinttang wants to merge 1 commit into
Conversation
Greptile SummaryThis PR fixes a long-standing bug where
Confidence Score: 4/5Safe to merge; the fix is narrowly scoped to the fallback lookup path and has no effect when no alias is configured. The alias resolution is correct and well-contained. The one gap is that all_deployments is still fetched with the alias name instead of the resolved real model group, meaning order-based fallbacks remain silently inactive for alias-targeted requests. This is a minor limitation that does not regress any existing behavior. The deployment-lookup block in router.py around the all_deployments query (line 6378) could be improved to pass the resolved model group instead of the alias, but this is not a blocker.
|
| Filename | Overview |
|---|---|
| litellm/router.py | Adds alias-to-real-model-group resolution before fallback lookup in async_function_with_fallbacks_common_utils; all downstream fallback paths (regular, context-window, content-policy, order-based external) now use the resolved name, but order-based deployment discovery still queries by the alias name. |
| tests/local_testing/test_router_fallbacks.py | Adds a parametrised sync+async mock test that reproduces the exact alias-fallback failure and verifies the fix; uses mock_testing_fallbacks=True so no real network calls are made. |
Comments Outside Diff (1)
-
litellm/router.py, line 6377-6382 (link)Order-based fallback deployment lookup still uses the alias name
all_deploymentsis queried withoriginal_model_group(the alias string, e.g."alias-model") rather than the resolvedmodel_group. Because deployments are registered under the real model group name,get_model_list(model_name="alias-model")returns an empty list, so_order_setis always empty and order-based intra-group fallbacks are silently bypassed for any alias-targeted request. The same resolved value used for the fallback-lookup fix could be passed here instead.
Reviews (1): Last reviewed commit: "fix(router): resolve model_group_alias b..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
8f0fb32 to
ac25bea
Compare
|
Force-pushed after rebasing onto the current |
|
I ran into this issue, hoping this fix gets merged! |
When a request is routed via `model_group_alias`, the alias name was passed straight to `get_fallback_model_group`, whose exact-match check never matched fallbacks declared against the underlying real model group. The router then raised the original exception with "No fallback model group found for original model_group=<alias>", silently dropping the configured fallback chain. Resolve the alias to its real model group inside `async_function_with_fallbacks_common_utils` before any fallback lookup. `original_model_group` intentionally keeps the alias name so logging and the `x-litellm-attempted-fallbacks` headers remain user-facing. Refs BerriAI#10317
ac25bea to
c8d36ac
Compare
| # `get_fallback_model_group` works in both cases. `original_model_group` | ||
| # intentionally keeps the alias name for logging / fallback headers. | ||
| if model_group is not None: | ||
| aliased_model_group = self._get_model_from_alias(model=model_group) |
There was a problem hiding this comment.
Low: Alias-specific fallback policy is bypassed
Rewriting model_group before lookup means a configuration containing both {"public-alias": ["safe-fallback"]} and {"real-group": ["other-fallback"]} will always select the latter for alias requests. A caller can deliberately trigger a content-policy or provider failure and route their request to a backend that was not configured for the public alias. Preserve alias-keyed policies by trying the original alias first, and only retry the lookup with the resolved model group when no alias-specific entry matches; apply the same precedence to general, context-window, and content-policy fallbacks.
PR overviewThis PR updates the router’s fallback lookup path to resolve model group aliases when selecting configured fallback routes. One issue remains in the lookup precedence: alias-specific fallback configuration can be skipped in favor of the resolved model group’s fallback. In configurations that define both policies, a caller who triggers a fallback condition could be routed to a backend not intended for the public alias. Open issues (1)
Fixed/addressed: 0 · PR risk: 4/10 |
Summary
When a request is dispatched to a model defined via Router's
model_group_alias(e.g.model_group_alias={"alias-model": "real-model"}), the configuredfallbacksfor the underlying real model are silently ignored. The router re-raises the original exception with:This is the verbatim error from #10317 (363 days old, 9 comments, multiple independent +1s).
Fixes #10317
Root cause
Router.async_function_with_fallbacks_common_utilscapturesmodel_group = kwargs["model"]— which is the alias name ("alias-model") — and passes it directly intoget_fallback_model_group. Fallbacks are keyed by the real model group ("real-model"), so the exact-match lookup infallback_event_handlers.py:64never hits, falls through, and the router re-raises the original exception.Router._get_model_from_aliasalready exists and is used on the request path to resolve aliases to real model names. The fallback path just doesn't call it.Fix
litellm/router.py(10 LOC added): right after the early-exit guard inasync_function_with_fallbacks_common_utils, callself._get_model_from_alias(model=model_group); if it returns non-None, swapmodel_groupto the resolved real name.original_model_groupis intentionally kept as the alias so user-facing logs and fallback headers remain stable for callers who watch them.Tests
tests/local_testing/test_router_fallbacks.py::test_model_group_alias_respects_fallbacks(parametrised sync + async, 54 LOC). Sets:model_group_alias={"alias-model": "real-bad-model"}fallbacks=[{"real-bad-model": ["my-good-model"]}]Fires with
mock_testing_fallbacks=True, asserts the response model is the good fallback.RED proof on main (exact error string from the bug report):
GREEN after patch: 2/2 pass (sync + async). Nearby logic-only fallback tests still pass (
test_get_fallback_model_group,test_default_model_fallbacks,test_client_side_fallbacks_list,test_using_default_fallback,test_router_fallbacks_default_and_model_specific_fallbacks,test_router_disable_fallbacks_dynamically,test_router_fallbacks_with_wildcard_model_name).ruff checkclean on the added lines.Risk notes
_get_model_from_aliasreturnsNonefor non-alias model names, so behaviour is unchanged when nomodel_group_aliasis configured.original_model_groupkeeps the user-facing alias for logs / fallback headers, so downstream metrics / observability are unaffected.model_groupin the fallback-lookup scope; no state leaks out.Refs #10317