mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-29 06:33:28 +00:00
fix(router): reject string fallback targets at Router init (LIT-10238) (#48943)
* fix(router): reject string fallback targets at Router init (LIT-10238) * fix(router): keep direct target fallback entries when rejecting string targets * test(router): set string fallbacks after init in cooldown tolerance tests --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
9e878cfae2
commit
96fe78f96e
3 changed files with 63 additions and 7 deletions
|
|
@ -1370,7 +1370,7 @@ class Router:
|
|||
},
|
||||
]
|
||||
|
||||
router = Router(model_list=model_list, fallbacks=[{"azure-gpt-3.5-turbo": "openai-gpt-3.5-turbo"}])
|
||||
router = Router(model_list=model_list, fallbacks=[{"azure-gpt-3.5-turbo": ["openai-gpt-3.5-turbo"]}])
|
||||
```
|
||||
"""
|
||||
|
||||
|
|
@ -1596,6 +1596,10 @@ class Router:
|
|||
|
||||
if default_fallbacks is not None or litellm.default_fallbacks is not None:
|
||||
_fallbacks = default_fallbacks or litellm.default_fallbacks
|
||||
if isinstance(_fallbacks, str):
|
||||
raise TypeError(
|
||||
f"default_fallbacks must be a list of model names, got '{_fallbacks}'. Use ['{_fallbacks}']"
|
||||
)
|
||||
if self.fallbacks is not None:
|
||||
self.fallbacks.append({"*": _fallbacks})
|
||||
else:
|
||||
|
|
@ -2839,7 +2843,7 @@ class Router:
|
|||
self._initialize_core_endpoints()
|
||||
self._initialize_specialized_endpoints()
|
||||
|
||||
def validate_fallbacks(self, fallback_param: list | None):
|
||||
def validate_fallbacks(self, fallback_param: Sequence[Mapping[str, object]] | None):
|
||||
"""
|
||||
Validate the fallbacks parameter.
|
||||
"""
|
||||
|
|
@ -2852,6 +2856,13 @@ class Router:
|
|||
raise ValueError(
|
||||
f"Dictionary '{fallback_dict}' must have exactly one key, but has {len(fallback_dict)} keys."
|
||||
)
|
||||
((model_group, targets),) = fallback_dict.items()
|
||||
if isinstance(targets, list) or check_non_standard_fallback_format(fallbacks=[fallback_dict]):
|
||||
continue
|
||||
raise TypeError(
|
||||
f"Fallbacks for '{model_group}' must be a list of model names, got '{targets}'. "
|
||||
f"Use {{'{model_group}': ['{targets}']}}"
|
||||
)
|
||||
|
||||
def _add_encrypted_content_affinity_check(self, enable_global_affinity: bool) -> None:
|
||||
def _move_before_deployment_affinity(
|
||||
|
|
|
|||
|
|
@ -3150,8 +3150,6 @@ BENCHED: Final = (litellm.Timeout, litellm.Timeout, RouterRateLimitError, Router
|
|||
{},
|
||||
BENCHED,
|
||||
),
|
||||
({"allowed_fails": 1, "fallbacks": [{"test-model": "backup-model"}]}, {}, BENCHED),
|
||||
({"allowed_fails": 1, "default_fallbacks": "backup-model"}, {}, BENCHED),
|
||||
],
|
||||
ids=[
|
||||
"allowed_fails",
|
||||
|
|
@ -3160,8 +3158,6 @@ BENCHED: Final = (litellm.Timeout, litellm.Timeout, RouterRateLimitError, Router
|
|||
"policy_names_timeout",
|
||||
"allowed_fails_with_a_fallback_group",
|
||||
"allowed_fails_with_a_dict_fallback_target",
|
||||
"allowed_fails_with_a_string_fallback_target",
|
||||
"allowed_fails_with_a_string_default_fallbacks",
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -3207,7 +3203,8 @@ async def test_router_allowed_fails_benches_a_lone_deployment_only_when_the_poli
|
|||
def test_has_fallback_group_reads_string_targets_and_skips_unparseable_entries(
|
||||
fallbacks: Sequence[Mapping[str, object]], expected: bool
|
||||
) -> None:
|
||||
router: Final = Router(model_list=[TIMED_OUT_DEPLOYMENT], fallbacks=list(fallbacks))
|
||||
router: Final = Router(model_list=[TIMED_OUT_DEPLOYMENT])
|
||||
router.fallbacks = list(fallbacks)
|
||||
|
||||
assert _has_fallback_group(router, "test-model") is expected
|
||||
|
||||
|
|
|
|||
|
|
@ -31228,3 +31228,51 @@ async def test_anthropic_caller_rate_limit_does_not_block_other_credentials(
|
|||
finally:
|
||||
await GLOBAL_LOGGING_WORKER.flush()
|
||||
await client.close()
|
||||
|
||||
|
||||
_FALLBACK_VALIDATION_MODEL_LIST: Final = (
|
||||
{"model_name": "gpt-4", "litellm_params": {"model": "openai/gpt-4", "api_key": "fake-key"}},
|
||||
{"model_name": "gpt-3.5", "litellm_params": {"model": "openai/gpt-3.5-turbo", "api_key": "fake-key"}},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("router_kwargs", "expected_message"),
|
||||
[
|
||||
({"fallbacks": [{"gpt-4": "gpt-3.5"}]}, "Fallbacks for 'gpt-4' must be a list of model names, got 'gpt-3.5'"),
|
||||
({"default_fallbacks": "gpt-3.5"}, "default_fallbacks must be a list of model names, got 'gpt-3.5'"),
|
||||
(
|
||||
{"content_policy_fallbacks": [{"gpt-4": "gpt-3.5"}]},
|
||||
"Fallbacks for 'gpt-4' must be a list of model names, got 'gpt-3.5'",
|
||||
),
|
||||
],
|
||||
ids=["string_fallback_target", "string_default_fallbacks", "string_content_policy_fallback_target"],
|
||||
)
|
||||
def test_router_rejects_a_string_fallback_target_at_init(
|
||||
router_kwargs: Mapping[str, object], expected_message: str
|
||||
) -> None:
|
||||
with pytest.raises(TypeError, match="must be a list of model names") as excinfo:
|
||||
Router(model_list=list(_FALLBACK_VALIDATION_MODEL_LIST), **router_kwargs)
|
||||
|
||||
assert expected_message in str(excinfo.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("router_kwargs", "expected_fallbacks"),
|
||||
[
|
||||
({"fallbacks": [{"gpt-4": ["gpt-3.5"]}]}, [{"gpt-4": ["gpt-3.5"]}]),
|
||||
({"default_fallbacks": ["gpt-3.5"]}, [{"*": ["gpt-3.5"]}]),
|
||||
(
|
||||
{"fallbacks": [{"gpt-4": [{"model": "gpt-3.5", "api_key": "other-key"}]}]},
|
||||
[{"gpt-4": [{"model": "gpt-3.5", "api_key": "other-key"}]}],
|
||||
),
|
||||
({"fallbacks": [{"model": "gpt-3.5"}]}, [{"model": "gpt-3.5"}]),
|
||||
],
|
||||
ids=["list_fallback_target", "list_default_fallbacks", "dict_override_fallback_target", "direct_target_entry"],
|
||||
)
|
||||
def test_router_keeps_supported_fallback_shapes(
|
||||
router_kwargs: Mapping[str, object], expected_fallbacks: list[dict[str, object]]
|
||||
) -> None:
|
||||
router: Final = Router(model_list=list(_FALLBACK_VALIDATION_MODEL_LIST), **router_kwargs)
|
||||
|
||||
assert router.fallbacks == expected_fallbacks
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue