From 4bb15aa425c65205ba6d525e0b344b71ee943654 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 19 Nov 2025 05:04:03 -0500 Subject: [PATCH] feat: default pinned models Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com> --- backend/open_webui/config.py | 6 ++ backend/open_webui/main.py | 7 +- backend/open_webui/routers/configs.py | 4 + .../components/admin/Settings/Models.svelte | 3 +- .../Models/ConfigureModelsModal.svelte | 97 +++++-------------- .../Settings/Models/ModelSelector.svelte | 70 +++++++++++++ src/lib/components/layout/Sidebar.svelte | 2 +- .../layout/Sidebar/PinnedModelItem.svelte | 2 +- .../layout/Sidebar/PinnedModelList.svelte | 45 +++++++-- 9 files changed, 151 insertions(+), 85 deletions(-) create mode 100644 src/lib/components/admin/Settings/Models/ModelSelector.svelte diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index f99034a3c5..33de43b84f 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1141,6 +1141,12 @@ DEFAULT_MODELS = PersistentConfig( "DEFAULT_MODELS", "ui.default_models", os.environ.get("DEFAULT_MODELS", None) ) +DEFAULT_PINNED_MODELS = PersistentConfig( + "DEFAULT_PINNED_MODELS", + "ui.default_pinned_models", + os.environ.get("DEFAULT_PINNED_MODELS", None), +) + try: default_prompt_suggestions = json.loads( os.environ.get("DEFAULT_PROMPT_SUGGESTIONS", "[]") diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index cd54dd25fc..e21dd5d1ed 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -373,6 +373,7 @@ from open_webui.config import ( PENDING_USER_OVERLAY_TITLE, DEFAULT_PROMPT_SUGGESTIONS, DEFAULT_MODELS, + DEFAULT_PINNED_MODELS, DEFAULT_ARENA_MODEL, MODEL_ORDER_LIST, EVALUATION_ARENA_MODELS, @@ -754,6 +755,10 @@ app.state.config.ADMIN_EMAIL = ADMIN_EMAIL app.state.config.DEFAULT_MODELS = DEFAULT_MODELS +app.state.config.DEFAULT_PINNED_MODELS = DEFAULT_PINNED_MODELS +app.state.config.MODEL_ORDER_LIST = MODEL_ORDER_LIST + + app.state.config.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE @@ -765,7 +770,6 @@ app.state.config.RESPONSE_WATERMARK = RESPONSE_WATERMARK app.state.config.USER_PERMISSIONS = USER_PERMISSIONS app.state.config.WEBHOOK_URL = WEBHOOK_URL app.state.config.BANNERS = WEBUI_BANNERS -app.state.config.MODEL_ORDER_LIST = MODEL_ORDER_LIST app.state.config.ENABLE_CHANNELS = ENABLE_CHANNELS @@ -1877,6 +1881,7 @@ async def get_app_config(request: Request): **( { "default_models": app.state.config.DEFAULT_MODELS, + "default_pinned_models": app.state.config.DEFAULT_PINNED_MODELS, "default_prompt_suggestions": app.state.config.DEFAULT_PROMPT_SUGGESTIONS, "user_count": user_count, "code": { diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index 0e88b7a929..0eb88e767e 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -463,6 +463,7 @@ async def set_code_execution_config( ############################ class ModelsConfigForm(BaseModel): DEFAULT_MODELS: Optional[str] + DEFAULT_PINNED_MODELS: Optional[str] MODEL_ORDER_LIST: Optional[list[str]] @@ -470,6 +471,7 @@ class ModelsConfigForm(BaseModel): async def get_models_config(request: Request, user=Depends(get_admin_user)): return { "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS, + "DEFAULT_PINNED_MODELS": request.app.state.config.DEFAULT_PINNED_MODELS, "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST, } @@ -479,9 +481,11 @@ async def set_models_config( request: Request, form_data: ModelsConfigForm, user=Depends(get_admin_user) ): request.app.state.config.DEFAULT_MODELS = form_data.DEFAULT_MODELS + request.app.state.config.DEFAULT_PINNED_MODELS = form_data.DEFAULT_PINNED_MODELS request.app.state.config.MODEL_ORDER_LIST = form_data.MODEL_ORDER_LIST return { "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS, + "DEFAULT_PINNED_MODELS": request.app.state.config.DEFAULT_PINNED_MODELS, "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST, } diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte index 0200ed2e68..2f559905fc 100644 --- a/src/lib/components/admin/Settings/Models.svelte +++ b/src/lib/components/admin/Settings/Models.svelte @@ -250,9 +250,8 @@ {#if selectedModelId === null}
-
+
{$i18n.t('Models')} -
{filteredModels.length} diff --git a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte index 5b2c592054..a48335a8de 100644 --- a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte +++ b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte @@ -7,18 +7,20 @@ import { models } from '$lib/stores'; import { deleteAllModels } from '$lib/apis/models'; + import { getModelsConfig, setModelsConfig } from '$lib/apis/configs'; import Modal from '$lib/components/common/Modal.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; import ModelList from './ModelList.svelte'; - import { getModelsConfig, setModelsConfig } from '$lib/apis/configs'; import Spinner from '$lib/components/common/Spinner.svelte'; import Minus from '$lib/components/icons/Minus.svelte'; import Plus from '$lib/components/icons/Plus.svelte'; import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; import XMark from '$lib/components/icons/XMark.svelte'; + import ModelSelector from './ModelSelector.svelte'; + import Model from '../Evaluations/Model.svelte'; export let show = false; export let initHandler = () => {}; @@ -27,6 +29,10 @@ let selectedModelId = ''; let defaultModelIds = []; + + let selectedPinnedModelId = ''; + let defaultPinnedModelIds = []; + let modelIds = []; let sortKey = ''; @@ -38,25 +44,6 @@ $: if (show) { init(); } - - $: if (selectedModelId) { - onModelSelect(); - } - - const onModelSelect = () => { - if (selectedModelId === '') { - return; - } - - if (defaultModelIds.includes(selectedModelId)) { - selectedModelId = ''; - return; - } - - defaultModelIds = [...defaultModelIds, selectedModelId]; - selectedModelId = ''; - }; - const init = async () => { config = await getModelsConfig(localStorage.token); @@ -65,6 +52,13 @@ } else { defaultModelIds = []; } + + if (config?.DEFAULT_PINNED_MODELS) { + defaultPinnedModelIds = (config?.DEFAULT_PINNED_MODELS).split(',').filter((id) => id); + } else { + defaultPinnedModelIds = []; + } + const modelOrderList = config.MODEL_ORDER_LIST || []; const allModelIds = $models.map((model) => model.id); @@ -86,6 +80,7 @@ const res = await setModelsConfig(localStorage.token, { DEFAULT_MODELS: defaultModelIds.join(','), + DEFAULT_PINNED_MODELS: defaultPinnedModelIds.join(','), MODEL_ORDER_LIST: modelIds }); @@ -191,59 +186,19 @@
-
-
-
-
{$i18n.t('Default Models')}
-
+ -
- -
+
- - - {#if defaultModelIds.length > 0} -
- {#each defaultModelIds as modelId, modelIdx} -
-
- {$models.find((model) => model.id === modelId)?.name} -
-
- -
-
- {/each} -
- {:else} -
- {$i18n.t('No models selected')} -
- {/if} -
-
+
diff --git a/src/lib/components/admin/Settings/Models/ModelSelector.svelte b/src/lib/components/admin/Settings/Models/ModelSelector.svelte new file mode 100644 index 0000000000..6af182453d --- /dev/null +++ b/src/lib/components/admin/Settings/Models/ModelSelector.svelte @@ -0,0 +1,70 @@ + + +
+
+
+
{title}
+
+ +
+ +
+ + + + {#if modelIds.length > 0} +
+ {#each modelIds as modelId, modelIdx} +
+
+ {models.find((model) => model.id === modelId)?.name} +
+
+ +
+
+ {/each} +
+ {:else} +
+ {$i18n.t('No models selected')} +
+ {/if} +
+
diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 0d94856451..bdd59584d6 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -887,7 +887,7 @@ {/if}
- {#if ($models ?? []).length > 0 && ($settings?.pinnedModels ?? []).length > 0} + {#if ($models ?? []).length > 0 && (($settings?.pinnedModels ?? []).length > 0 || $config?.default_pinned_models)} - {#if mouseOver && shiftKey} + {#if mouseOver && shiftKey && onUnpin}
diff --git a/src/lib/components/layout/Sidebar/PinnedModelList.svelte b/src/lib/components/layout/Sidebar/PinnedModelList.svelte index 298a0da4a4..da35a3df3d 100644 --- a/src/lib/components/layout/Sidebar/PinnedModelList.svelte +++ b/src/lib/components/layout/Sidebar/PinnedModelList.svelte @@ -1,9 +1,9 @@
- {#each $settings.pinnedModels as modelId (modelId)} + {#each pinnedModels as modelId (modelId)} {@const model = $models.find((model) => model.id === modelId)} {#if model} { - const pinnedModels = $settings.pinnedModels.filter((id) => id !== modelId); - settings.set({ ...$settings, pinnedModels }); - updateUserSettings(localStorage.token, { ui: $settings }); - }} + onUnpin={($settings?.pinnedModels ?? []).includes(modelId) + ? () => { + const pinnedModels = $settings.pinnedModels.filter((id) => id !== modelId); + settings.set({ ...$settings, pinnedModels }); + updateUserSettings(localStorage.token, { ui: $settings }); + } + : null} /> {/if} {/each}