mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge router_settings instead of replacing on update
This commit is contained in:
parent
d1df4e838b
commit
02b639fd36
2 changed files with 83 additions and 2 deletions
|
|
@ -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},
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue