mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
fix(router): bound fallback reachability scan by max_fallbacks
A client-supplied fallback chain could recurse one level per group and exceed the stack. Cap traversal at max_fallbacks so an oversized chain fails closed, matching the runtime fallback limit
This commit is contained in:
parent
f171ef9562
commit
1238550d39
2 changed files with 15 additions and 2 deletions
|
|
@ -9991,9 +9991,11 @@ class Router:
|
|||
True when `fallbacks` routes `model_name` to a model group with at least one
|
||||
unblocked deployment. `fallbacks` must already reflect the precedence the router
|
||||
applies at call time, so a request-supplied list replaces the router-level chain
|
||||
rather than merging with it. Visited groups are skipped so a cycle terminates.
|
||||
rather than merging with it. Traversal stops on a repeat group and after
|
||||
`max_fallbacks` hops, matching the runtime limit and bounding recursion so a long
|
||||
client-supplied chain cannot exhaust the stack.
|
||||
"""
|
||||
if model_name in visited:
|
||||
if model_name in visited or len(visited) >= self.max_fallbacks:
|
||||
return False
|
||||
fallback_model_group, _ = get_fallback_model_group(fallbacks=fallbacks, model_group=model_name)
|
||||
if not fallback_model_group:
|
||||
|
|
|
|||
|
|
@ -146,6 +146,17 @@ class TestHasReachableFallback:
|
|||
)
|
||||
assert router._has_reachable_fallback("primary", fallbacks=["fallback"]) is True
|
||||
|
||||
def test_chain_longer_than_max_fallbacks_fails_closed(self):
|
||||
hops = 6
|
||||
router = Router(
|
||||
model_list=[_deployment("m0", "m0", blocked=True)]
|
||||
+ [_deployment(f"m{i}", f"m{i}", blocked=True) for i in range(1, hops)]
|
||||
+ [_deployment("healthy", "h0", blocked=False)],
|
||||
max_fallbacks=hops - 2,
|
||||
)
|
||||
chain = [{f"m{i}": [f"m{i + 1}"]} for i in range(hops - 1)] + [{f"m{hops - 1}": ["healthy"]}]
|
||||
assert router._has_reachable_fallback("m0", fallbacks=chain) is False
|
||||
|
||||
|
||||
class TestIsBlockedWithoutReachableFallback:
|
||||
def test_blocked_and_no_fallback_returns_true(self):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue