Skip to content

Commit 9ca8cf5

Browse files
committed
refac
1 parent 409fb39 commit 9ca8cf5

3 files changed

Lines changed: 271 additions & 314 deletions

File tree

src/lib/components/admin/Settings/Models.svelte

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte';
3434
import { toast } from 'svelte-sonner';
3535
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
36-
import ModelSettingsModal from './Models/ModelSettingsModal.svelte';
3736
import ManageModelsModal from './Models/ManageModelsModal.svelte';
37+
import ModelDefaultsPanel from './Models/ModelDefaultsPanel.svelte';
3838
import ModelMenu from '$lib/components/admin/Settings/Models/ModelMenu.svelte';
3939
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
4040
import EyeSlash from '$lib/components/icons/EyeSlash.svelte';
@@ -47,7 +47,6 @@
4747
import Download from '$lib/components/icons/Download.svelte';
4848
import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
4949
import Wrench from '$lib/components/icons/Wrench.svelte';
50-
import SettingsIcon from '$lib/components/icons/Settings.svelte';
5150
import { WEBUI_API_BASE_URL } from '$lib/constants';
5251
import { goto } from '$app/navigation';
5352
@@ -83,11 +82,13 @@
8382
let filteredModels = [];
8483
let selectedModelId = null;
8584
86-
let showConfigModal = false;
8785
let showManageModal = false;
8886
let showResetModal = false;
8987
let savingModelOrder = false;
88+
let savingModelsSettings = false;
9089
let modelOrderDirty = false;
90+
let modelDefaultsPanel = null;
91+
let modelDefaultsDirty = false;
9192
9293
let viewOption = ''; // '' = All, 'enabled', 'disabled', 'visible', 'hidden'
9394
let tags: string[] = [];
@@ -322,6 +323,20 @@
322323
savingModelOrder = false;
323324
};
324325
326+
const saveModelsSettings = async () => {
327+
savingModelsSettings = true;
328+
329+
if (modelOrderDirty) {
330+
await saveModelOrder(modelOrderList);
331+
}
332+
333+
if (modelDefaultsDirty) {
334+
await modelDefaultsPanel?.save();
335+
}
336+
337+
savingModelsSettings = false;
338+
};
339+
325340
const saveModelDefaults = async (
326341
nextDefaultModelIds: string[],
327342
nextDefaultPinnedModelIds: string[],
@@ -649,7 +664,6 @@
649664
}}
650665
/>
651666

652-
<ModelSettingsModal bind:show={showConfigModal} initHandler={init} />
653667
<ManageModelsModal bind:show={showManageModal} />
654668

655669
{#if models !== null}
@@ -662,18 +676,6 @@
662676
{filteredModels.length}
663677
</span>
664678
</h2>
665-
666-
<Tooltip content={$i18n.t('Settings')}>
667-
<button
668-
type="button"
669-
class="flex h-6 w-6 items-center justify-center rounded-lg text-gray-400 transition-colors duration-75 hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
670-
on:click={() => {
671-
showConfigModal = true;
672-
}}
673-
>
674-
<SettingsIcon className="size-3.5" />
675-
</button>
676-
</Tooltip>
677679
</div>
678680

679681
{#if $user?.role === 'admin'}
@@ -714,6 +716,12 @@
714716
{/if}
715717

716718
<div class="flex min-h-0 flex-1 flex-col space-y-1">
719+
<ModelDefaultsPanel
720+
bind:this={modelDefaultsPanel}
721+
bind:dirty={modelDefaultsDirty}
722+
initHandler={init}
723+
/>
724+
717725
<div class="flex h-8 shrink-0 items-center w-full gap-2">
718726
<div class="flex min-w-0 flex-1 items-center">
719727
<div class=" self-center ml-1 mr-3">
@@ -1105,13 +1113,15 @@
11051113
<button
11061114
class="flex items-center gap-2 px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full disabled:cursor-not-allowed disabled:opacity-40"
11071115
type="button"
1108-
disabled={!modelOrderDirty || savingModelOrder}
1116+
disabled={(!modelOrderDirty && !modelDefaultsDirty) ||
1117+
savingModelOrder ||
1118+
savingModelsSettings}
11091119
on:click={async () => {
1110-
await saveModelOrder(modelOrderList);
1120+
await saveModelsSettings();
11111121
}}
11121122
>
11131123
{$i18n.t('Save')}
1114-
{#if savingModelOrder}
1124+
{#if savingModelOrder || savingModelsSettings}
11151125
<span class="shrink-0">
11161126
<Spinner />
11171127
</span>
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
<script>
2+
import { getContext, onMount, tick } from 'svelte';
3+
import { toast } from 'svelte-sonner';
4+
5+
const i18n = getContext('i18n');
6+
7+
import { config as appConfig } from '$lib/stores';
8+
import { DEFAULT_CAPABILITIES } from '$lib/constants';
9+
import { getModelsConfig, setModelsConfig, setDefaultPromptSuggestions } from '$lib/apis/configs';
10+
import { getBackendConfig } from '$lib/apis';
11+
12+
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
13+
import Capabilities from '$lib/components/workspace/Models/Capabilities.svelte';
14+
import DefaultFeatures from '$lib/components/workspace/Models/DefaultFeatures.svelte';
15+
import BuiltinTools from '$lib/components/workspace/Models/BuiltinTools.svelte';
16+
import PromptSuggestions from '$lib/components/workspace/Models/PromptSuggestions.svelte';
17+
18+
export let initHandler = () => {};
19+
export let dirty = false;
20+
21+
let config = null;
22+
let modelIds = [];
23+
let loading = false;
24+
let expanded = false;
25+
let showCapabilities = false;
26+
let showParameters = false;
27+
let showPromptSuggestions = false;
28+
let savedSnapshot = '';
29+
30+
let defaultCapabilities = {};
31+
let defaultFeatureIds = [];
32+
let defaultParams = {};
33+
let builtinTools = {};
34+
let promptSuggestions = [];
35+
36+
$: configuredParams = Object.entries(defaultParams ?? {}).filter(
37+
([_, value]) => value !== null && value !== '' && value !== undefined
38+
);
39+
$: enabledCapabilities = Object.entries(defaultCapabilities ?? {}).filter(([_, value]) => value);
40+
$: availableFeatures = enabledCapabilities
41+
.filter(([key]) => ['web_search', 'code_interpreter', 'image_generation'].includes(key))
42+
.map(([key]) => key);
43+
44+
const getSnapshot = () =>
45+
JSON.stringify({
46+
defaultCapabilities,
47+
defaultFeatureIds,
48+
defaultParams: Object.fromEntries(configuredParams),
49+
builtinTools,
50+
promptSuggestions: promptSuggestions.filter((p) => p.content !== '')
51+
});
52+
53+
const updateDirty = async () => {
54+
await tick();
55+
dirty = savedSnapshot !== '' && getSnapshot() !== savedSnapshot;
56+
};
57+
58+
const init = async () => {
59+
loading = true;
60+
config = await getModelsConfig(localStorage.token);
61+
62+
modelIds = config?.MODEL_ORDER_LIST || [];
63+
64+
const savedMeta = config?.DEFAULT_MODEL_METADATA;
65+
if (savedMeta && Object.keys(savedMeta).length > 0) {
66+
defaultCapabilities = savedMeta.capabilities ?? { ...DEFAULT_CAPABILITIES };
67+
defaultFeatureIds = savedMeta.defaultFeatureIds ?? [];
68+
builtinTools = savedMeta.builtinTools ?? {};
69+
} else {
70+
defaultCapabilities = { ...DEFAULT_CAPABILITIES };
71+
defaultFeatureIds = [];
72+
builtinTools = {};
73+
}
74+
75+
defaultParams = config?.DEFAULT_MODEL_PARAMS ?? {};
76+
promptSuggestions = $appConfig?.default_prompt_suggestions ?? [];
77+
savedSnapshot = getSnapshot();
78+
dirty = false;
79+
loading = false;
80+
};
81+
82+
export const save = async () => {
83+
if (loading || !dirty) {
84+
return true;
85+
}
86+
87+
const metadata = {
88+
capabilities: defaultCapabilities,
89+
...(defaultFeatureIds.length > 0 ? { defaultFeatureIds } : {}),
90+
...(Object.keys(builtinTools).length > 0 ? { builtinTools } : {})
91+
};
92+
93+
const res = await setModelsConfig(localStorage.token, {
94+
DEFAULT_MODELS: config?.DEFAULT_MODELS ?? null,
95+
DEFAULT_PINNED_MODELS: config?.DEFAULT_PINNED_MODELS ?? null,
96+
MODEL_ORDER_LIST: modelIds,
97+
DEFAULT_MODEL_METADATA: metadata,
98+
DEFAULT_MODEL_PARAMS: Object.fromEntries(configuredParams)
99+
}).catch((error) => {
100+
toast.error(`${error}`);
101+
return null;
102+
});
103+
104+
if (res) {
105+
config = res;
106+
promptSuggestions = promptSuggestions.filter((p) => p.content !== '');
107+
promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
108+
await appConfig.set(await getBackendConfig());
109+
savedSnapshot = getSnapshot();
110+
dirty = false;
111+
112+
toast.success($i18n.t('Models configuration saved successfully'));
113+
initHandler();
114+
return true;
115+
} else {
116+
toast.error($i18n.t('Failed to save models configuration'));
117+
return false;
118+
}
119+
};
120+
121+
onMount(async () => {
122+
await init();
123+
});
124+
</script>
125+
126+
<div class="shrink-0">
127+
<div class="flex items-center justify-between gap-4 py-0.5">
128+
<button
129+
class="min-w-0 flex-1 text-left text-xs text-gray-600 transition hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100"
130+
type="button"
131+
on:click={() => {
132+
expanded = !expanded;
133+
}}
134+
>
135+
{$i18n.t('Model Defaults')}
136+
</button>
137+
138+
<button
139+
class="shrink-0 text-[0.6875rem] text-gray-400 transition hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
140+
type="button"
141+
on:click={() => {
142+
expanded = !expanded;
143+
}}
144+
>
145+
{expanded ? $i18n.t('Close') : $i18n.t('Configure')}
146+
</button>
147+
</div>
148+
149+
{#if expanded}
150+
{#if loading}
151+
<div class="py-1 text-xs text-gray-400 dark:text-gray-600">{$i18n.t('Loading...')}</div>
152+
{:else}
153+
<div class="space-y-1 mt-0.5">
154+
<div>
155+
<button
156+
class="flex w-full items-center justify-between gap-4 py-0.5 text-left"
157+
type="button"
158+
on:click={() => {
159+
showCapabilities = !showCapabilities;
160+
}}
161+
>
162+
<span class="text-xs text-gray-600 dark:text-gray-400">
163+
{$i18n.t('Model Capabilities')}
164+
</span>
165+
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
166+
{showCapabilities ? $i18n.t('Close') : $i18n.t('Configure')}
167+
</span>
168+
</button>
169+
170+
{#if showCapabilities}
171+
<div class="pb-2" on:click={updateDirty} on:change={updateDirty}>
172+
<Capabilities bind:capabilities={defaultCapabilities} />
173+
174+
{#if availableFeatures.length > 0}
175+
<div class="mt-4">
176+
<DefaultFeatures {availableFeatures} bind:featureIds={defaultFeatureIds} />
177+
</div>
178+
{/if}
179+
180+
{#if defaultCapabilities.builtin_tools}
181+
<div class="mt-4">
182+
<BuiltinTools bind:builtinTools />
183+
</div>
184+
{/if}
185+
</div>
186+
{/if}
187+
</div>
188+
189+
<div>
190+
<button
191+
class="flex w-full items-center justify-between gap-4 py-0.5 text-left"
192+
type="button"
193+
on:click={() => {
194+
showParameters = !showParameters;
195+
}}
196+
>
197+
<span class="text-xs text-gray-600 dark:text-gray-400">
198+
{$i18n.t('Model Parameters')}
199+
</span>
200+
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
201+
{showParameters ? $i18n.t('Close') : $i18n.t('Configure')}
202+
</span>
203+
</button>
204+
205+
{#if showParameters}
206+
<div
207+
class="max-h-[24rem] overflow-y-auto pb-2 pr-1 scrollbar-hover"
208+
on:click={updateDirty}
209+
on:change={updateDirty}
210+
on:input={updateDirty}
211+
>
212+
<AdvancedParams admin={true} custom={true} bind:params={defaultParams} />
213+
</div>
214+
{/if}
215+
</div>
216+
217+
<div>
218+
<button
219+
class="flex w-full items-center justify-between gap-4 py-0.5 text-left"
220+
type="button"
221+
on:click={() => {
222+
showPromptSuggestions = !showPromptSuggestions;
223+
}}
224+
>
225+
<span class="text-xs text-gray-600 dark:text-gray-400">
226+
{$i18n.t('Prompt Suggestions')}
227+
</span>
228+
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
229+
{showPromptSuggestions ? $i18n.t('Close') : $i18n.t('Configure')}
230+
</span>
231+
</button>
232+
233+
{#if showPromptSuggestions}
234+
<div class="pb-2" on:click={updateDirty} on:change={updateDirty} on:input={updateDirty}>
235+
<PromptSuggestions bind:promptSuggestions />
236+
</div>
237+
{/if}
238+
</div>
239+
</div>
240+
{/if}
241+
{/if}
242+
</div>

0 commit comments

Comments
 (0)