diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 04f84f1f7b5..4e7c4601803 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -19899,12 +19899,14 @@ async def update_config_general_settings( ) try: - ConfigGeneralSettings.model_validate({data.field_name: requested_value}) + validated: Final = ConfigGeneralSettings.model_validate({data.field_name: requested_value}) except Exception: raise HTTPException( status_code=400, detail={"error": f"Invalid type of field value={type(requested_value)} passed in."}, ) + parsed_value: Final[object] = _UNTYPED_VALUE.validate_python(getattr(validated, data.field_name)) + stored_value: Final = parsed_value if isinstance(parsed_value, bool) else requested_value if data.field_name == "alerting_args": try: @@ -19933,10 +19935,10 @@ async def update_config_general_settings( proxy_config.reject_config_owned_writes( section_name="general_settings", - changed_keys={data.field_name: cast(JsonValue, requested_value)}, # cast-ok: validated above + changed_keys={data.field_name: cast(JsonValue, stored_value)}, # cast-ok: validated above ) - field_value = requested_value + field_value = stored_value if data.field_name == "plugins": field_value = _preserve_redacted_plugin_keys(field_value, general_settings.get("plugins")) diff --git a/tests/integration/authorization/test_agent_least_privilege.py b/tests/integration/authorization/test_agent_least_privilege.py index 498a53c3ca9..d993920a32e 100644 --- a/tests/integration/authorization/test_agent_least_privilege.py +++ b/tests/integration/authorization/test_agent_least_privilege.py @@ -313,7 +313,7 @@ def test_agent_access_default_deny_toggles_at_runtime_through_config_api( for field in config_fields.json() if object_value(field)["field_name"] == "agent_access_default_deny" ) - assert setting["field_type"] == "Boolean" and setting["field_value"] == "true", setting + assert setting["field_type"] == "Boolean" and setting["field_value"] is True, setting eventually( lambda: tuple(_card(candidate, agent_id, ungranted_key) for _ in range(8)), lambda responses: all(response.status_code == 403 for response in responses), diff --git a/tests/unit/proxy/proxy_server/test_routes_config.py b/tests/unit/proxy/proxy_server/test_routes_config.py index 7d0a7f3f261..e3b8aff8fcb 100644 --- a/tests/unit/proxy/proxy_server/test_routes_config.py +++ b/tests/unit/proxy/proxy_server/test_routes_config.py @@ -716,12 +716,16 @@ def test_config_list_exposes_config_reload_interval(client, auth_as, mock_prisma assert entry["field_default_value"] == 30 -@pytest.mark.parametrize("enabled", (True, False)) +@pytest.mark.parametrize( + ("sent", "enabled"), + ((True, True), (False, False), ("true", True), ("yes", True), (1, True), ("off", False)), +) def test_config_list_exposes_and_saves_wildcard_routes_toggle( client: TestClient, auth_as: Callable[..., contextlib.AbstractContextManager[object]], mock_prisma: MagicMock, monkeypatch: pytest.MonkeyPatch, + sent: object, enabled: bool, ) -> None: from pydantic import TypeAdapter @@ -747,13 +751,11 @@ def test_config_list_exposes_and_saves_wildcard_routes_toggle( saved: Final = client.post( "/config/field/update", - json={"field_name": "return_wildcard_routes", "field_value": enabled, "config_type": "general_settings"}, + json={"field_name": "return_wildcard_routes", "field_value": sent, "config_type": "general_settings"}, ) assert saved.status_code == 200, saved.text - persisted: Final = TypeAdapter(dict[str, bool]).validate_json( - table.upsert.call_args.kwargs["data"]["create"]["param_value"] - ) - assert persisted == {"return_wildcard_routes": enabled} + persisted: Final = json.loads(table.upsert.call_args.kwargs["data"]["create"]["param_value"]) + assert persisted["return_wildcard_routes"] is enabled after: Final = client.get("/config/list", params={"config_type": "general_settings"}) assert after.status_code == 200, after.text