This commit is contained in:
Timothy Jaeryang Baek
2026-08-10 19:08:06 -06:00
parent 30d08a42f8
commit b5f86e6a43
8 changed files with 55 additions and 21 deletions
+5 -5
View File
@@ -1704,12 +1704,12 @@ DEFAULT_MODEL_PARAMS = default_model_params
try:
default_user_ui_settings = JSONCodec.loads(os.getenv('DEFAULT_USER_UI_SETTINGS', '{}'))
default_ui_settings = JSONCodec.loads(os.getenv('DEFAULT_UI_SETTINGS', '{}'))
except Exception as e:
log.exception(f'Error loading DEFAULT_USER_UI_SETTINGS: {e}')
default_user_ui_settings = {}
log.exception(f'Error loading DEFAULT_UI_SETTINGS: {e}')
default_ui_settings = {}
DEFAULT_USER_UI_SETTINGS = default_user_ui_settings if isinstance(default_user_ui_settings, dict) else {}
DEFAULT_UI_SETTINGS = default_ui_settings if isinstance(default_ui_settings, dict) else {}
DEFAULT_USER_ROLE = os.getenv('DEFAULT_USER_ROLE', 'pending')
@@ -3064,7 +3064,7 @@ DEFAULT_CONFIG = {
'ui.default_locale': DEFAULT_LOCALE,
'ui.default_models': DEFAULT_MODELS,
'ui.default_pinned_models': DEFAULT_PINNED_MODELS,
'ui.default_user_settings': DEFAULT_USER_UI_SETTINGS,
'ui.default_settings': DEFAULT_UI_SETTINGS,
'ui.prompt_suggestions': DEFAULT_PROMPT_SUGGESTIONS,
'ui.model_order_list': MODEL_ORDER_LIST,
'models.default_metadata': DEFAULT_MODEL_METADATA,
+2
View File
@@ -2142,6 +2142,7 @@ async def get_app_config(request: Request):
'memories.enable',
'ui.default_models',
'ui.default_pinned_models',
'ui.default_settings',
'ui.prompt_suggestions',
'code_execution.engine',
'code_interpreter.engine',
@@ -2272,6 +2273,7 @@ async def get_app_config(request: Request):
'sharepoint_tenant_id': ONEDRIVE_SHAREPOINT_TENANT_ID,
},
'ui': {
'default_settings': config.get('ui.default_settings'),
'pending_user_overlay_title': config.get('ui.pending_user_overlay_title'),
'pending_user_overlay_content': config.get('ui.pending_user_overlay_content'),
'response_watermark': config.get('ui.watermark'),
+1 -1
View File
@@ -32,7 +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',),
'ui.default_settings': ('DEFAULT_UI_SETTINGS',),
'user.permissions': ('USER_PERMISSIONS',),
}
DICT_CONFIG_KEYS = tuple(DICT_CONFIG_KEY_ALIASES)
+3 -3
View File
@@ -111,7 +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',
'DEFAULT_UI_SETTINGS': 'ui.default_settings',
'JWT_EXPIRES_IN': 'auth.jwt_expiry',
'ENABLE_COMMUNITY_SHARING': 'ui.enable_community_sharing',
'ENABLE_MESSAGE_RATING': 'ui.enable_message_rating',
@@ -1197,7 +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
DEFAULT_UI_SETTINGS: dict | None = None
JWT_EXPIRES_IN: str
ENABLE_COMMUNITY_SHARING: bool
ENABLE_MESSAGE_RATING: bool
@@ -1222,7 +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['ui.default_settings'] = form_data.DEFAULT_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'] = (
+26 -1
View File
@@ -56,6 +56,9 @@ router = APIRouter()
def merge_user_ui_settings(defaults: dict, settings: dict) -> dict:
merged = dict(defaults)
for key, value in settings.items():
if value is None:
continue
default_value = merged.get(key)
merged[key] = (
merge_user_ui_settings(default_value, value)
@@ -65,6 +68,23 @@ def merge_user_ui_settings(defaults: dict, settings: dict) -> dict:
return merged
def strip_default_ui_settings(defaults: dict, settings: dict) -> dict:
stripped = {}
for key, value in settings.items():
if value is None:
continue
default_value = defaults.get(key)
if isinstance(default_value, dict) and isinstance(value, dict):
nested = strip_default_ui_settings(default_value, value)
if nested:
stripped[key] = nested
elif value != default_value:
stripped[key] = value
return stripped
############################
# GetUsers
# A house is only as strong as its care for the least of
@@ -452,7 +472,7 @@ 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
default_ui_settings = await Config.get('ui.default_user_settings')
default_ui_settings = await Config.get('ui.default_settings')
if not isinstance(default_ui_settings, dict) or not default_ui_settings:
return user.settings
@@ -515,6 +535,11 @@ async def update_user_settings_by_session_user(
if isinstance(ui_notifications, dict):
ui_notifications.pop('webhook_url', None)
default_ui_settings = await Config.get('ui.default_settings')
ui_settings = updated_user_settings.get('ui')
if isinstance(default_ui_settings, dict) and isinstance(ui_settings, dict):
updated_user_settings['ui'] = strip_default_ui_settings(default_ui_settings, ui_settings)
user = await Users.update_user_settings_by_id(user.id, updated_user_settings, db=db)
if user:
await publish_event(
@@ -65,8 +65,13 @@
defaultUserUiSettings = { ...defaultUserUiSettings, ...updated };
};
const getDefaultUserUiSettings = () => {
const value = adminConfig?.DEFAULT_UI_SETTINGS;
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
};
const updateHandler = async () => {
adminConfig.DEFAULT_USER_UI_SETTINGS = defaultUserUiSettings;
adminConfig.DEFAULT_UI_SETTINGS = defaultUserUiSettings;
const res = await updateAdminConfig(localStorage.token, adminConfig);
@@ -83,12 +88,7 @@
onMount(async () => {
adminConfig = await getAdminConfig(localStorage.token);
defaultUserUiSettings =
adminConfig?.DEFAULT_USER_UI_SETTINGS &&
typeof adminConfig.DEFAULT_USER_UI_SETTINGS === 'object' &&
!Array.isArray(adminConfig.DEFAULT_USER_UI_SETTINGS)
? adminConfig.DEFAULT_USER_UI_SETTINGS
: {};
defaultUserUiSettings = getDefaultUserUiSettings();
banners = [...$_banners];
});
@@ -396,7 +396,7 @@
<div>{$i18n.t('Default Interface Settings')}</div>
<div class="mt-1.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
{$i18n.t(
'Set the interface values new users start with. User changes still override these defaults.'
'Set system-wide interface defaults for every account. Personal settings override these defaults.'
)}
</div>
</button>
@@ -19,6 +19,7 @@
$: currentSettings = (settingsValue ?? $settings ?? {}) as Record<string, any>;
$: externalSettings = settingsValue !== null;
$: defaultSettings = ($config?.ui?.default_settings ?? {}) as Record<string, any>;
let backgroundImageUrl: string | null = null;
let inputFiles: FileList | null = null;
@@ -117,6 +118,11 @@
const actionButtonClass =
'text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white';
const matchesDefaultSetting = (key: string, value: unknown) =>
!externalSettings &&
Object.prototype.hasOwnProperty.call(defaultSettings, key) &&
JSON.stringify(defaultSettings[key]) === JSON.stringify(value);
const toggleLandingPageMode = async () => {
landingPageMode = landingPageMode === '' ? 'chat' : '';
saveSettings({ landingPageMode: landingPageMode });
@@ -309,6 +315,7 @@
lastSettingsValue = settingsValue;
init();
}
$: textScaleUsesDefault = textScale === null || matchesDefaultSetting('textScale', textScale);
onMount(async () => {
init();
@@ -376,13 +383,13 @@
on:click={() => {
if (textScale === null) {
textScale = 1;
} else {
} else if (!textScaleUsesDefault) {
textScale = null;
setTextScaleHandler(1);
}
}}
>
{#if textScale === null}
{#if textScaleUsesDefault}
<span>{$i18n.t('Default')}</span>
{:else}
<span>{textScale}x</span>
+1 -1
View File
@@ -342,7 +342,7 @@ type Config = {
auto_redirect?: boolean;
};
ui?: {
default_user_settings?: Record<string, unknown>;
default_settings?: Record<string, unknown>;
pending_user_overlay_title?: string;
pending_user_overlay_content?: string;
response_watermark?: string;