diff --git a/backend/open_webui/models/config.py b/backend/open_webui/models/config.py index 6081214b4c..b7dd54ae17 100644 --- a/backend/open_webui/models/config.py +++ b/backend/open_webui/models/config.py @@ -32,6 +32,7 @@ DICT_CONFIG_KEY_ALIASES = { 'audio.tts.openai.params': ('AUDIO_TTS_OPENAI_PARAMS',), 'models.default_metadata': ('DEFAULT_MODEL_METADATA',), 'models.default_params': ('DEFAULT_MODEL_PARAMS',), + 'ui.default_user_settings': ('DEFAULT_USER_UI_SETTINGS',), 'user.permissions': ('USER_PERMISSIONS',), } DICT_CONFIG_KEYS = tuple(DICT_CONFIG_KEY_ALIASES) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 240c761931..1256197da4 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -111,6 +111,7 @@ ADMIN_CONFIG_KEYS = { 'API_KEYS_ALLOWED_ENDPOINTS': 'auth.api_key.allowed_endpoints', 'DEFAULT_USER_ROLE': 'ui.default_user_role', 'DEFAULT_GROUP_ID': 'ui.default_group_id', + 'DEFAULT_USER_UI_SETTINGS': 'ui.default_user_settings', 'JWT_EXPIRES_IN': 'auth.jwt_expiry', 'ENABLE_COMMUNITY_SHARING': 'ui.enable_community_sharing', 'ENABLE_MESSAGE_RATING': 'ui.enable_message_rating', @@ -1196,6 +1197,7 @@ class AdminConfig(BaseModel): API_KEYS_ALLOWED_ENDPOINTS: str DEFAULT_USER_ROLE: str DEFAULT_GROUP_ID: str + DEFAULT_USER_UI_SETTINGS: dict | None = None JWT_EXPIRES_IN: str ENABLE_COMMUNITY_SHARING: bool ENABLE_MESSAGE_RATING: bool @@ -1220,6 +1222,7 @@ class AdminConfig(BaseModel): @router.post('/admin/config') async def update_admin_config(request: Request, form_data: AdminConfig, user=Depends(get_admin_user)): updates = config_updates(form_data.model_dump(), ADMIN_CONFIG_KEYS) + updates['ui.default_user_settings'] = form_data.DEFAULT_USER_UI_SETTINGS or {} updates['folders.max_file_count'] = int(form_data.FOLDER_MAX_FILE_COUNT) if form_data.FOLDER_MAX_FILE_COUNT else '' updates['automations.max_count'] = int(form_data.AUTOMATION_MAX_COUNT) if form_data.AUTOMATION_MAX_COUNT else '' updates['automations.min_interval'] = ( diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index 622882e75b..bd63a40bb6 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -53,6 +53,18 @@ log = logging.getLogger(__name__) router = APIRouter() +def merge_user_ui_settings(defaults: dict, settings: dict) -> dict: + merged = dict(defaults) + for key, value in settings.items(): + default_value = merged.get(key) + merged[key] = ( + merge_user_ui_settings(default_value, value) + if isinstance(default_value, dict) and isinstance(value, dict) + else value + ) + return merged + + ############################ # GetUsers # A house is only as strong as its care for the least of @@ -440,7 +452,15 @@ async def get_user_settings_by_session_user( user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session) ): # user already fetched by get_verified_user — no need to refetch - return user.settings + default_ui_settings = await Config.get('ui.default_user_settings') + if not isinstance(default_ui_settings, dict) or not default_ui_settings: + return user.settings + + user_settings = user.settings.model_dump() if isinstance(user.settings, UserSettings) else dict(user.settings or {}) + ui_settings = user_settings.get('ui') if isinstance(user_settings.get('ui'), dict) else {} + user_settings['ui'] = merge_user_ui_settings(default_ui_settings, ui_settings) + + return UserSettings.model_validate(user_settings) ############################