From 32d4ae79f60226ef8ef7cb14f37f5fe17918062d Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 24 Apr 2026 13:09:16 -0700 Subject: [PATCH] Validate UI settings PATCH body against effective class GET /get/ui_settings returns a schema built from the effective UISettings class (base + enterprise-registered fields), but PATCH /update/ui_settings declared its body as the base UISettings. Enterprise fields still worked via extra="allow", but the OpenAPI schema was asymmetric between GET and PATCH. Accept the body as a dict and validate with the effective class so both sides are in sync and enterprise-registered fields are type-checked. --- .../ui_crud_endpoints/proxy_setting_endpoints.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py index 7b71d468e9..cc284da024 100644 --- a/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py +++ b/litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py @@ -3,8 +3,8 @@ import json from typing import Any, Dict, List, Optional, Set, Tuple, Union from urllib.parse import urlparse -from fastapi import APIRouter, Depends, File, HTTPException, UploadFile -from pydantic import ConfigDict, create_model +from fastapi import APIRouter, Body, Depends, File, HTTPException, UploadFile +from pydantic import ConfigDict, ValidationError, create_model from pydantic.fields import FieldInfo import litellm @@ -1218,7 +1218,8 @@ async def get_ui_settings(): dependencies=[Depends(user_api_key_auth)], ) async def update_ui_settings( - settings: UISettings, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth) + settings_body: Dict[str, Any] = Body(...), + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), ): """ Update UI-specific configuration flags. @@ -1245,6 +1246,14 @@ async def update_ui_settings( }, ) + # Validate against the same effective class GET advertises, so + # enterprise-registered fields are typed consistently on both sides. + effective_cls = _get_effective_ui_settings_class() + try: + settings = 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 = settings.model_dump(exclude_unset=True)