Merge router_settings instead of replacing on update

This commit is contained in:
Lucas 2026-04-03 01:49:22 +08:00
parent d1df4e838b
commit 02b639fd36
2 changed files with 83 additions and 2 deletions

View file

@ -12267,11 +12267,24 @@ async def update_config( # noqa: PLR0915
updated_settings = prisma_client.jsonify_object(updated_settings)
for k, v in updated_settings.items():
if k == "router_settings":
# Merge with existing DB value instead of full replacement,
# so that keys not included in the update (e.g. fallbacks)
# are preserved.
existing_record = (
await prisma_client.db.litellm_config.find_first(
where={"param_name": k}
)
)
merged_value = v
if existing_record is not None and isinstance(
existing_record.param_value, dict
):
merged_value = {**existing_record.param_value, **v}
await prisma_client.db.litellm_config.upsert(
where={"param_name": k},
data={
"create": {"param_name": k, "param_value": v},
"update": {"param_value": v},
"create": {"param_name": k, "param_value": merged_value},
"update": {"param_value": merged_value},
},
)

View file

@ -2803,6 +2803,74 @@ async def test_update_config_success_callback_normalization():
assert "langfuse" in callbacks
@pytest.mark.asyncio
async def test_update_config_merges_existing_router_settings():
import litellm.proxy.proxy_server as proxy_server
from litellm.proxy._types import ConfigYAML, LitellmUserRoles, UserAPIKeyAuth
mock_prisma_client = MagicMock()
mock_prisma_client.jsonify_object = lambda value: value
existing_router_settings = {
"fallbacks": [{"gpt-4": ["gpt-4o-mini"]}],
"routing_strategy": "simple-shuffle",
}
mock_db_record = MagicMock()
mock_db_record.param_value = existing_router_settings
mock_prisma_client.db.litellm_config.find_first = AsyncMock(
return_value=mock_db_record
)
mock_prisma_client.db.litellm_config.upsert = AsyncMock()
class MockProxyConfig:
def __init__(self):
self.saved_config = None
async def get_config(self):
return {}
async def save_config(self, new_config: dict):
self.saved_config = new_config
async def add_deployment(self, prisma_client=None, proxy_logging_obj=None):
return None
admin_user = UserAPIKeyAuth(
user_role=LitellmUserRoles.PROXY_ADMIN, api_key="sk-test"
)
config_update = ConfigYAML(
router_settings={"routing_strategy": "latency-based-routing"}
)
with patch("litellm.proxy.proxy_server.prisma_client", mock_prisma_client), patch(
"litellm.proxy.proxy_server.proxy_config", MockProxyConfig()
), patch("litellm.proxy.proxy_server.store_model_in_db", True):
await proxy_server.update_config(config_update, user_api_key_dict=admin_user)
mock_prisma_client.db.litellm_config.find_first.assert_awaited_once_with(
where={"param_name": "router_settings"}
)
mock_prisma_client.db.litellm_config.upsert.assert_awaited_once_with(
where={"param_name": "router_settings"},
data={
"create": {
"param_name": "router_settings",
"param_value": {
"fallbacks": [{"gpt-4": ["gpt-4o-mini"]}],
"routing_strategy": "latency-based-routing",
},
},
"update": {
"param_value": {
"fallbacks": [{"gpt-4": ["gpt-4o-mini"]}],
"routing_strategy": "latency-based-routing",
}
},
},
)
@pytest.mark.parametrize(
"data",
[