This commit is contained in:
devin-ai-integration[bot] 2026-10-17 08:01:42 -07:00 • committed by GitHub
commit 349f157c15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 57 additions and 0 deletions

View file

@ -57,6 +57,7 @@ RUNTIME_UPDATABLE_ROUTER_SETTINGS: Final[frozenset[str]] = frozenset(
"retry_after",
"fallbacks",
"context_window_fallbacks",
"content_policy_fallbacks",
"retry_policy",
"model_group_retry_policy",
"model_group_alias",

View file

@ -194,6 +194,7 @@ class UpdateRouterConfig(LiteLLMBaseModel):
retry_after: float | None = None
fallbacks: list[dict] | None = None
context_window_fallbacks: list[dict] | None = None
content_policy_fallbacks: Sequence[Mapping[str, Sequence[str]]] | None = None
model_group_alias: dict[str, str | dict] | None = Field(default={})
enable_tag_filtering: bool | None = None
weights: RouterWeights | None = None

View file

@ -10063,6 +10063,24 @@ def test_update_config_accepts_disjoint_routing_groups(_update_config_setup):
restore()
def test_update_config_validates_content_policy_fallbacks_like_fallbacks(_update_config_setup):
client, prisma, restore = _update_config_setup(initial_rows={"router_settings": {"num_retries": 2}})
rule: Final = [{"gpt-5.4-mini": ["gpt-5.4-nano"]}]
malformed: Final = ("oops", 5, [1, 2], [{"gpt-5.4-mini": "gpt-5.4-nano"}], [{"gpt-5.4-mini": ["gpt-5.4-nano", 7]}])
try:
rejected: Final = tuple(
client.post("/config/update", json={"router_settings": {"content_policy_fallbacks": value}}).status_code
for value in malformed
)
assert rejected == (422,) * len(malformed)
assert prisma.db.litellm_config.rows["router_settings"] == {"num_retries": 2}
accepted: Final = client.post("/config/update", json={"router_settings": {"content_policy_fallbacks": rule}})
assert accepted.status_code == 200
assert prisma.db.litellm_config.rows["router_settings"] == {"num_retries": 2, "content_policy_fallbacks": rule}
finally:
restore()
def test_update_config_env_var_round_trip_not_double_encrypted(_update_config_setup, monkeypatch):
"""Endpoint-level regression for the /config/update double-encryption bug.

View file

@ -2419,6 +2419,39 @@ def test_update_settings_model_group_alias_drops_cached_group_info():
assert after.input_cost_per_token is not None and after.input_cost_per_token > 0
@pytest.mark.parametrize(
"fallback_kind, mock_testing_param",
[
("fallbacks", "mock_testing_fallbacks"),
("context_window_fallbacks", "mock_testing_context_fallbacks"),
("content_policy_fallbacks", "mock_testing_content_policy_fallbacks"),
],
)
async def test_update_settings_fallback_rule_of_every_kind_is_used_on_its_error(
fallback_kind: str, mock_testing_param: str
):
router: Final = Router(
model_list=[
{
"model_name": "primary",
"litellm_params": {"model": "gpt-5.4-mini", "api_key": "fake", "mock_response": "primary answered"},
},
{
"model_name": "backup",
"litellm_params": {"model": "gpt-5.4-nano", "api_key": "fake", "mock_response": "backup answered"},
},
],
num_retries=0,
)
router.update_settings(**{fallback_kind: [{"primary": ["backup"]}]})
response: Final = await router.acompletion(
model="primary", messages=[{"role": "user", "content": "hi"}], **{mock_testing_param: True}
)
assert response.choices[0].message.content == "backup answered"
_PAID_INPUT_COST_PER_TOKEN: Final = 3e-06
_PAID_OUTPUT_COST_PER_TOKEN: Final = 1.5e-05

View file

@ -48289,6 +48289,10 @@ export interface components {
UpdateRouterConfig: {
/** Allowed Fails */
allowed_fails?: number | null;
/** Content Policy Fallbacks */
content_policy_fallbacks?: {
[key: string]: string[];
}[] | null;
/** Context Window Fallbacks */
context_window_fallbacks?: {
[key: string]: unknown;