diff --git a/backend/open_webui/apps/webui/main.py b/backend/open_webui/apps/webui/main.py index bedd49ae3..054c6280e 100644 --- a/backend/open_webui/apps/webui/main.py +++ b/backend/open_webui/apps/webui/main.py @@ -31,6 +31,7 @@ from open_webui.config import ( DEFAULT_MODELS, DEFAULT_PROMPT_SUGGESTIONS, DEFAULT_USER_ROLE, + MODEL_ORDER_LIST, ENABLE_COMMUNITY_SHARING, ENABLE_LOGIN_FORM, ENABLE_MESSAGE_RATING, @@ -120,6 +121,7 @@ app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE 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_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING app.state.config.ENABLE_MESSAGE_RATING = ENABLE_MESSAGE_RATING diff --git a/backend/open_webui/apps/webui/routers/configs.py b/backend/open_webui/apps/webui/routers/configs.py index 1c30b0b3b..b19fc1745 100644 --- a/backend/open_webui/apps/webui/routers/configs.py +++ b/backend/open_webui/apps/webui/routers/configs.py @@ -34,8 +34,32 @@ async def export_config(user=Depends(get_admin_user)): return get_config() -class SetDefaultModelsForm(BaseModel): - models: str +############################ +# SetDefaultModels +############################ +class ModelsConfigForm(BaseModel): + DEFAULT_MODELS: str + MODEL_ORDER_LIST: list[str] + + +@router.get("/models", response_model=ModelsConfigForm) +async def get_models_config(request: Request, user=Depends(get_admin_user)): + return { + "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS, + "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST, + } + + +@router.post("/models", response_model=ModelsConfigForm) +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.MODEL_ORDER_LIST = form_data.MODEL_ORDER_LIST + return { + "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS, + "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST, + } class PromptSuggestion(BaseModel): @@ -47,21 +71,8 @@ class SetDefaultSuggestionsForm(BaseModel): suggestions: list[PromptSuggestion] -############################ -# SetDefaultModels -############################ - - -@router.post("/default/models", response_model=str) -async def set_global_default_models( - request: Request, form_data: SetDefaultModelsForm, user=Depends(get_admin_user) -): - request.app.state.config.DEFAULT_MODELS = form_data.models - return request.app.state.config.DEFAULT_MODELS - - -@router.post("/default/suggestions", response_model=list[PromptSuggestion]) -async def set_global_default_suggestions( +@router.post("/suggestions", response_model=list[PromptSuggestion]) +async def set_default_suggestions( request: Request, form_data: SetDefaultSuggestionsForm, user=Depends(get_admin_user), diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index db24403e5..3c1ee798d 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -740,6 +740,12 @@ DEFAULT_PROMPT_SUGGESTIONS = PersistentConfig( ], ) +MODEL_ORDER_LIST = PersistentConfig( + "MODEL_ORDER_LIST", + "ui.model_order_list", + [], +) + DEFAULT_USER_ROLE = PersistentConfig( "DEFAULT_USER_ROLE", "ui.default_user_role", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 4c52ceb0f..0dca21b08 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1194,6 +1194,14 @@ async def get_models(user=Depends(get_verified_user)): if "pipeline" not in model or model["pipeline"].get("type", None) != "filter" ] + model_order_list = webui_app.state.config.MODEL_ORDER_LIST + if model_order_list: + model_order_dict = {model_id: i for i, model_id in enumerate(model_order_list)} + # Sort models by order list priority, with fallback for those not in the list + models.sort( + key=lambda x: (model_order_dict.get(x["id"], float("inf")), x["name"]) + ) + # Filter out models that the user does not have access to if user.role == "user": filtered_models = [] diff --git a/src/lib/apis/configs/index.ts b/src/lib/apis/configs/index.ts index 0c4de6ad6..b3b002557 100644 --- a/src/lib/apis/configs/index.ts +++ b/src/lib/apis/configs/index.ts @@ -58,17 +58,46 @@ export const exportConfig = async (token: string) => { return res; }; -export const setDefaultModels = async (token: string, models: string) => { + +export const getModelsConfig = async (token: string) => { let error = null; - const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/models`, { + const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err.detail; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + + +export const setModelsConfig = async (token: string, config: object) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ - models: models + ...config }) }) .then(async (res) => { @@ -88,10 +117,11 @@ export const setDefaultModels = async (token: string, models: string) => { return res; }; + export const setDefaultPromptSuggestions = async (token: string, promptSuggestions: string) => { let error = null; - const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/suggestions`, { + const res = await fetch(`${WEBUI_API_BASE_URL}/configs/suggestions`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index 9c726e4d0..a33610c31 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -25,26 +25,6 @@ export const getModels = async (token: string = '', base: boolean = false) => { } let models = res?.data ?? []; - models = models - .filter((models) => models) - // Sort the models - .sort((a, b) => { - // Compare case-insensitively by name for models without position property - const lowerA = a.name.toLowerCase(); - const lowerB = b.name.toLowerCase(); - - if (lowerA < lowerB) return -1; - if (lowerA > lowerB) return 1; - - // If same case-insensitively, sort by original strings, - // lowercase will come before uppercase due to ASCII values - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - - return 0; // They are equal - }); - - console.log(models); return models; }; diff --git a/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte index 5f64137e4..ac45e7f2c 100644 --- a/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte +++ b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte @@ -375,7 +375,7 @@
{#if edit}
- +
diff --git a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte new file mode 100644 index 000000000..be7fc8a45 --- /dev/null +++ b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte @@ -0,0 +1,258 @@ + + + { + const res = deleteAllModels(localStorage.token); + if (res) { + toast.success($i18n.t('All models deleted successfully')); + init(); + } + }} +/> + + +
+
+
+ {$i18n.t('Configure Models')} +
+ +
+ +
+
+ {#if config} +
{ + submitHandler(); + }} + > +
+
+
+
{$i18n.t('Reorder Models')}
+
+ + +
+
+ +
+ +
+
+
+
{$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} + +
+ +
+ + +
+ +
+
+
+
+ +
+ + + + + +
+
+ {:else} +
+ +
+ {/if} +
+
+
+
diff --git a/src/lib/components/admin/Settings/Models/ModelList.svelte b/src/lib/components/admin/Settings/Models/ModelList.svelte new file mode 100644 index 000000000..c54d19ee4 --- /dev/null +++ b/src/lib/components/admin/Settings/Models/ModelList.svelte @@ -0,0 +1,58 @@ + + +{#if modelIds.length > 0} +
+ {#each modelIds as modelId, modelIdx (modelId)} +
+ +
+ + +
+ {#if $models.find((model) => model.id === modelId)} + {$models.find((model) => model.id === modelId).name} + {:else} + {modelId} + {/if} +
+
+
+
+ {/each} +
+{:else} +
+ {$i18n.t('No models found')} +
+{/if} diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte index 9b16a6500..9b77cd8ce 100644 --- a/src/lib/components/chat/ModelSelector.svelte +++ b/src/lib/components/chat/ModelSelector.svelte @@ -5,9 +5,7 @@ import Selector from './ModelSelector/Selector.svelte'; import Tooltip from '../common/Tooltip.svelte'; - import { setDefaultModels } from '$lib/apis/configs'; import { updateUserSettings } from '$lib/apis/users'; - const i18n = getContext('i18n'); export let selectedModels = ['']; diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte index 795d3d0f1..86c741e23 100644 --- a/src/lib/components/common/Modal.svelte +++ b/src/lib/components/common/Modal.svelte @@ -6,7 +6,7 @@ export let show = true; export let size = 'md'; - export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl'; + export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl'; let modalElement = null; let mounted = false; @@ -65,7 +65,7 @@