From 8c5bbddea6feb492f5984957e56c2b355860f487 Mon Sep 17 00:00:00 2001 From: Sami Rusani Date: Wed, 15 Apr 2026 23:39:13 +0200 Subject: [PATCH] fix(proxy): allow ui settings without store_model_in_db --- .../proxy_setting_endpoints.py | 9 ----- .../test_proxy_setting_endpoints.py | 36 +++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py index b3feb5bd8d6..d6246f7747b 100644 --- a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py +++ b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py @@ -1423,7 +1423,6 @@ async def update_ui_settings( from litellm.proxy.proxy_server import ( create_config_audit_log, prisma_client, - store_model_in_db, ) if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: @@ -1435,12 +1434,6 @@ async def update_ui_settings( detail={"error": "Database not connected. Please connect a database."}, ) - if store_model_in_db is not True: - raise HTTPException( - status_code=500, - detail={"error": "Set `'STORE_MODEL_IN_DB='True'` in your env to enable this feature."}, - ) - conflicting_keys: Final = sorted( key for key, value in settings_body.items() @@ -1454,7 +1447,6 @@ async def update_ui_settings( "and cannot be changed from the UI." ), ) - # Validate against the same effective class GET advertises, so # enterprise-registered fields are typed consistently on both sides. effective_cls: Final = _get_effective_ui_settings_class() @@ -1462,7 +1454,6 @@ async def update_ui_settings( settings: Final = effective_cls.model_validate(settings_body) except ValidationError as e: raise HTTPException(status_code=422, detail=e.errors()) - # Only include fields the caller actually sent (not Pydantic defaults). settings_dict: Final[Mapping[str, JsonValue]] = settings.model_dump(exclude_unset=True) diff --git a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py index 8ee4e92ca9b..211937281c6 100644 --- a/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py +++ b/tests/test_litellm/proxy/ui_crud_endpoints/test_proxy_setting_endpoints.py @@ -1403,6 +1403,42 @@ class TestProxySettingEndpoints: stored_settings = json.loads(create_data["ui_settings"]) assert stored_settings["disable_model_add_for_internal_users"] is True + def test_update_ui_settings_allows_db_backed_updates_without_store_model_in_db( + self, mock_auth, monkeypatch + ): + """Test UI settings update succeeds with a DB connection even when store_model_in_db is disabled.""" + from unittest.mock import AsyncMock, MagicMock + + from litellm.proxy._types import UserAPIKeyAuth + from litellm.proxy.auth.user_api_key_auth import user_api_key_auth + + mock_user_auth = UserAPIKeyAuth( + user_id="test-user-123", + user_role=LitellmUserRoles.PROXY_ADMIN, + ) + app.dependency_overrides[user_api_key_auth] = lambda: mock_user_auth + + monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False) + mock_prisma = MagicMock() + mock_prisma.db.litellm_uisettings.upsert = AsyncMock() + mock_prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=None) + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma) + + payload = {"disable_model_add_for_internal_users": True} + + try: + response = client.patch("/update/ui_settings", json=payload) + finally: + # Clean up the dependency override + app.dependency_overrides.clear() + + assert response.status_code == 200 + data = response.json() + assert data["status"] == "success" + assert data["settings"]["disable_model_add_for_internal_users"] is True + + mock_prisma.db.litellm_uisettings.upsert.assert_called_once() + def test_update_ui_settings_ignores_non_allowlisted_value( self, mock_auth, monkeypatch ):