forked from open-webui/open-webui
Merge branch 'dev' into feat/system-wide-theme
This commit is contained in:
commit
db0712aefd
90 changed files with 8000 additions and 1164 deletions
|
@ -4,7 +4,9 @@
|
|||
import { WEBUI_VERSION } from '$lib/constants';
|
||||
import { WEBUI_NAME, config, showChangelog } from '$lib/stores';
|
||||
import { compareVersion } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let ollamaVersion = '';
|
||||
|
||||
|
@ -43,7 +45,8 @@
|
|||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium flex space-x-2 items-center">
|
||||
<div>
|
||||
{$WEBUI_NAME} Version
|
||||
{$WEBUI_NAME}
|
||||
{$i18n.t('Version')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full justify-between items-center">
|
||||
|
@ -56,10 +59,10 @@
|
|||
target="_blank"
|
||||
>
|
||||
{updateAvailable === null
|
||||
? 'Checking for updates...'
|
||||
? $i18n.t('Checking for updates...')
|
||||
: updateAvailable
|
||||
? `(v${version.latest} available!)`
|
||||
: '(latest)'}
|
||||
? `(v${version.latest} ${$i18n.t('available!')})`
|
||||
: $i18n.t('(latest)')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
@ -69,7 +72,7 @@
|
|||
showChangelog.set(true);
|
||||
}}
|
||||
>
|
||||
<div>See what's new</div>
|
||||
<div>{$i18n.t("See what's new")}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -79,7 +82,7 @@
|
|||
checkForVersionUpdates();
|
||||
}}
|
||||
>
|
||||
Check for updates
|
||||
{$i18n.t('Check for updates')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,7 +91,7 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Ollama Version</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Version')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
|
||||
{ollamaVersion ?? 'N/A'}
|
||||
|
@ -123,7 +126,8 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Created by <a
|
||||
{$i18n.t('Created by')}
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium"
|
||||
href="https://github.com/tjbck"
|
||||
target="_blank">Timothy J. Baek</a
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { user } from '$lib/stores';
|
||||
import { updateUserProfile } from '$lib/apis/auths';
|
||||
|
@ -9,6 +9,8 @@
|
|||
import { getGravatarUrl } from '$lib/apis/utils';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
|
||||
let profileImageUrl = '';
|
||||
|
@ -38,7 +40,7 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<input
|
||||
id="profile-image-input"
|
||||
bind:this={profileImageInputElement}
|
||||
|
@ -101,7 +103,7 @@
|
|||
}}
|
||||
/>
|
||||
|
||||
<div class=" mb-2.5 text-sm font-medium">Profile</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Profile')}</div>
|
||||
|
||||
<div class="flex space-x-5">
|
||||
<div class="flex flex-col">
|
||||
|
@ -143,13 +145,13 @@
|
|||
const url = await getGravatarUrl($user.email);
|
||||
|
||||
profileImageUrl = url;
|
||||
}}>Use Gravatar</button
|
||||
}}>{$i18n.t('Use Gravatar')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Name</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
@ -170,7 +172,7 @@
|
|||
|
||||
<div class=" w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">JWT Token</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Token')}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2">
|
||||
|
@ -271,7 +273,7 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
on:click={async () => {
|
||||
const res = await submitHandler();
|
||||
|
||||
|
@ -280,7 +282,7 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { updateUserPassword } from '$lib/apis/auths';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let show = false;
|
||||
let currentPassword = '';
|
||||
let newPassword = '';
|
||||
|
@ -17,7 +20,7 @@
|
|||
);
|
||||
|
||||
if (res) {
|
||||
toast.success('Successfully updated.');
|
||||
toast.success($i18n.t('Successfully updated.'));
|
||||
}
|
||||
|
||||
currentPassword = '';
|
||||
|
@ -40,20 +43,20 @@
|
|||
}}
|
||||
>
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">Change Password</div>
|
||||
<div class=" font-medium">{$i18n.t('Change Password')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
show = !show;
|
||||
}}>{show ? 'Hide' : 'Show'}</button
|
||||
}}>{show ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if show}
|
||||
<div class=" py-2.5 space-y-1.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Current Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Current Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
@ -67,7 +70,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">New Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
@ -81,7 +84,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Confirm Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Confirm Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
@ -99,7 +102,7 @@
|
|||
<button
|
||||
class=" px-4 py-2 text-xs bg-gray-800 hover:bg-gray-900 dark:bg-gray-700 dark:hover:bg-gray-800 text-gray-100 transition rounded-md font-medium"
|
||||
>
|
||||
Update password
|
||||
{$i18n.t('Update password')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Advanced
|
||||
|
@ -55,14 +57,14 @@
|
|||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class=" text-sm font-medium">Parameters</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Parameters')}</div>
|
||||
|
||||
<AdvancedParams bind:options />
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Keep Alive</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -72,9 +74,9 @@
|
|||
}}
|
||||
>
|
||||
{#if keepAlive === null}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -84,7 +86,7 @@
|
|||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={`e.g.) "30s","10m". Valid time units are "s", "m", "h".`}
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={keepAlive}
|
||||
/>
|
||||
</div>
|
||||
|
@ -93,7 +95,7 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">Request Mode</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -102,7 +104,7 @@
|
|||
}}
|
||||
>
|
||||
{#if requestFormat === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else if requestFormat === 'json'}
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -114,7 +116,7 @@
|
|||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg> -->
|
||||
<span class="ml-2 self-center"> JSON </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('JSON')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -147,7 +149,7 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let options = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
|
@ -20,7 +24,7 @@
|
|||
<div class=" space-y-3 text-xs">
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">Seed</div>
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Seed')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
|
@ -36,12 +40,12 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">Stop Sequence</div>
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Stop Sequence')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder="Enter Stop Sequence"
|
||||
placeholder={$i18n.t('Enter stop sequence')}
|
||||
bind:value={options.stop}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -51,7 +55,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Temperature</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Temperature')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -61,9 +65,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.temperature === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -97,7 +101,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -107,9 +111,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -143,7 +147,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat Eta</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Eta')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -153,9 +157,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat_eta === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -189,7 +193,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat Tau</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Tau')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -199,9 +203,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat_tau === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -235,7 +239,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Top K</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -245,9 +249,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.top_k === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -281,7 +285,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Top P</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Top P')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -291,9 +295,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.top_p === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -327,7 +331,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Repeat Penalty</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Penalty')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -337,9 +341,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.repeat_penalty === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -373,7 +377,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Repeat Last N</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Last N')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -383,9 +387,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.repeat_last_n === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -419,7 +423,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Tfs Z</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Tfs Z')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -429,9 +433,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.tfs_z === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -465,7 +469,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Context Length</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Context Length')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -475,9 +479,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.num_ctx === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -510,7 +514,7 @@
|
|||
</div>
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Max Tokens</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -520,9 +524,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.num_predict === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Audio
|
||||
|
@ -101,32 +103,36 @@
|
|||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">STT Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Speech-to-Text Engine</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={STTEngine}
|
||||
placeholder="Select a mode"
|
||||
on:change={(e) => {
|
||||
if (e.target.value !== '') {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true }).catch(function (err) {
|
||||
toast.error(`Permission denied when accessing microphone: ${err}`);
|
||||
toast.error(
|
||||
$i18n.t(`Permission denied when accessing microphone: {{error}}`, {
|
||||
error: err
|
||||
})
|
||||
);
|
||||
STTEngine = '';
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value="">Default (Web API)</option>
|
||||
<option value="whisper-local">Whisper (Local)</option>
|
||||
<option value="">{$i18n.t('Default (Web API)')}</option>
|
||||
<option value="whisper-local">{$i18n.t('Whisper (Local)')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Conversation Mode</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Conversation Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -136,15 +142,17 @@
|
|||
type="button"
|
||||
>
|
||||
{#if conversationMode === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Auto-send input after 3 sec.</div>
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Auto-send input after 3 sec.')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -154,22 +162,22 @@
|
|||
type="button"
|
||||
>
|
||||
{#if speechAutoSend === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">TTS Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Text-to-Speech Engine</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Text-to-Speech Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={TTSEngine}
|
||||
placeholder="Select a mode"
|
||||
on:change={(e) => {
|
||||
|
@ -182,14 +190,14 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
<option value="">Default (Web API)</option>
|
||||
<option value="openai">Open AI</option>
|
||||
<option value="">{$i18n.t('Default (Web API)')}</option>
|
||||
<option value="openai">{$i18n.t('Open AI')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Auto-playback response</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Auto-playback response')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -199,9 +207,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if responseAutoPlayback === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -211,7 +219,7 @@
|
|||
|
||||
{#if TTSEngine === ''}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Voice</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<select
|
||||
|
@ -219,7 +227,7 @@
|
|||
bind:value={speaker}
|
||||
placeholder="Select a voice"
|
||||
>
|
||||
<option value="" selected>Default</option>
|
||||
<option value="" selected>{$i18n.t('Default')}</option>
|
||||
{#each voices.filter((v) => v.localService === true) as voice}
|
||||
<option value={voice.name} class="bg-gray-100 dark:bg-gray-700">{voice.name}</option
|
||||
>
|
||||
|
@ -230,7 +238,7 @@
|
|||
</div>
|
||||
{:else if TTSEngine === 'openai'}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Voice</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<select
|
||||
|
@ -251,10 +259,10 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
getChatList
|
||||
} from '$lib/apis/chats';
|
||||
import { getImportOrigin, convertOpenAIChats } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
// Chats
|
||||
let saveChatHistory = true;
|
||||
|
@ -99,13 +101,13 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between space-y-3 text-sm">
|
||||
<div class="flex flex-col h-full justify-between space-y-3 text-sm max-h-[22rem]">
|
||||
<div class=" space-y-2">
|
||||
<div
|
||||
class="flex flex-col justify-between rounded-md items-center py-2 px-3.5 w-full transition"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">Chat History</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Chat History')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -129,7 +131,7 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center"> On </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('On')} </span>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -147,13 +149,13 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-left w-full font-medium mt-0.5">
|
||||
This setting does not sync across browsers or devices.
|
||||
{$i18n.t('This setting does not sync across browsers or devices.')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -188,7 +190,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Import Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Import Chats')}</div>
|
||||
</button>
|
||||
<button
|
||||
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||
|
@ -210,7 +212,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Export Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Export Chats')}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -232,7 +234,7 @@
|
|||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span>Are you sure?</span>
|
||||
<span>{$i18n.t('Are you sure?')}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-1.5 items-center">
|
||||
|
@ -296,7 +298,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Delete Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Delete Chats')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
@ -324,7 +326,9 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Export All Chats (All Users)</div>
|
||||
<div class=" self-center text-sm font-medium">
|
||||
{$i18n.t('Export All Chats (All Users)')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
@ -338,7 +342,7 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Success');
|
||||
toast.success($i18n.t('Success'));
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -356,7 +360,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Reset Vector Storage</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { models, user } from '$lib/stores';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
|
||||
|
@ -12,6 +12,8 @@
|
|||
} from '$lib/apis/openai';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let getModels: Function;
|
||||
|
||||
// External
|
||||
|
@ -42,7 +44,7 @@
|
|||
});
|
||||
|
||||
if (ollamaVersion) {
|
||||
toast.success('Server connection verified');
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
await models.set(await getModels());
|
||||
}
|
||||
};
|
||||
|
@ -63,17 +65,17 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[20.5rem] space-y-3">
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[22rem] space-y-3">
|
||||
<div class=" space-y-3">
|
||||
<div class="mt-2 space-y-2 pr-1.5">
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">OpenAI API</div>
|
||||
<div class=" font-medium">{$i18n.t('OpenAI API')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showOpenAI = !showOpenAI;
|
||||
}}>{showOpenAI ? 'Hide' : 'Show'}</button
|
||||
}}>{showOpenAI ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
|
@ -84,7 +86,7 @@
|
|||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="API Base URL"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={url}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -93,7 +95,7 @@
|
|||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="API Key"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={OPENAI_API_KEYS[idx]}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -143,7 +145,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span>
|
||||
{$i18n.t('WebUI will make requests to')}
|
||||
<span class=" text-gray-200">'{url}/models'</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -154,7 +157,7 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Ollama Base URL</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Base URL')}</div>
|
||||
<div class="flex w-full gap-1.5">
|
||||
<div class="flex-1 flex flex-col gap-2">
|
||||
{#each OLLAMA_BASE_URLS as url, idx}
|
||||
|
@ -233,13 +236,13 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Trouble accessing Ollama?
|
||||
{$i18n.t('Trouble accessing Ollama?')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://github.com/open-webui/open-webui#troubleshooting"
|
||||
target="_blank"
|
||||
>
|
||||
Click here for help.
|
||||
{$i18n.t('Click here for help.')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -247,10 +250,10 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { theme, setTheme } from '../../../stores/index';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { getLanguages } from '$lib/i18n';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { models, user } from '$lib/stores';
|
||||
|
||||
import { theme, setTheme } from '../../../stores/index';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
@ -16,6 +19,9 @@
|
|||
let selectedTheme = 'system';
|
||||
let actualTheme: string;
|
||||
$: actualTheme = $theme;
|
||||
|
||||
let languages = [];
|
||||
let lang = $i18n.language;
|
||||
let notificationEnabled = false;
|
||||
let system = '';
|
||||
|
||||
|
@ -91,6 +97,7 @@
|
|||
applyTheme(selectedTheme);
|
||||
|
||||
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||
languages = await getLanguages();
|
||||
|
||||
notificationEnabled = settings.notificationEnabled ?? false;
|
||||
system = settings.system ?? '';
|
||||
|
@ -117,59 +124,61 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[20.5rem]">
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<div class="">
|
||||
<div class=" mb-1 text-sm font-medium">WebUI Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Theme</div>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Theme')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<div class=" absolute right-16">
|
||||
{#if actualTheme === 'dark'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.455 2.004a.75.75 0 01.26.77 7 7 0 009.958 7.967.75.75 0 011.067.853A8.5 8.5 0 116.647 1.921a.75.75 0 01.808.083z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{:else if actualTheme === 'light'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4 self-center"
|
||||
>
|
||||
<path
|
||||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<select
|
||||
class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={selectedTheme}
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={theme}
|
||||
placeholder="Select a theme"
|
||||
on:change="{() => handleThemeChange(selectedTheme)}"
|
||||
>
|
||||
<option value="system">System</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="rose-pine dark">Rosé Pine</option>
|
||||
<option value="rose-pine-dawn light">Rosé Pine Dawn</option>
|
||||
<option value="system">System</option>
|
||||
<option value="dark">🌑 {$i18n.t('Dark')}</option>
|
||||
<option value="light">☀️ {$i18n.t('Light')}</option>
|
||||
<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
|
||||
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Language')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={lang}
|
||||
placeholder="Select a language"
|
||||
on:change={(e) => {
|
||||
$i18n.changeLanguage(lang);
|
||||
}}
|
||||
>
|
||||
{#each languages as language}
|
||||
<option value={language['code']}>{language['title']}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{#if $i18n.language === 'en-US'}
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Couldn't find your language?
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
|
||||
target="_blank"
|
||||
>
|
||||
Help us translate Open WebUI!
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Notification</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Desktop Notifications')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -179,9 +188,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if notificationEnabled === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -191,7 +200,7 @@
|
|||
<hr class=" dark:border-gray-700 my-3" />
|
||||
|
||||
<div>
|
||||
<div class=" my-2.5 text-sm font-medium">System Prompt</div>
|
||||
<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
|
||||
<textarea
|
||||
bind:value={system}
|
||||
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
|
||||
|
@ -201,13 +210,13 @@
|
|||
|
||||
<div class="mt-2 space-y-3 pr-1.5">
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">Advanced Parameters</div>
|
||||
<div class=" font-medium">{$i18n.t('Advanced Parameters')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAdvanced = !showAdvanced;
|
||||
}}>{showAdvanced ? 'Hide' : 'Show'}</button
|
||||
}}>{showAdvanced ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
|
@ -217,7 +226,7 @@
|
|||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Keep Alive</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -227,9 +236,9 @@
|
|||
}}
|
||||
>
|
||||
{#if keepAlive === null}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -239,7 +248,7 @@
|
|||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={`e.g.) "30s","10m". Valid time units are "s", "m", "h".`}
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={keepAlive}
|
||||
/>
|
||||
</div>
|
||||
|
@ -248,7 +257,7 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">Request Mode</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -257,7 +266,7 @@
|
|||
}}
|
||||
>
|
||||
{#if requestFormat === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else if requestFormat === 'json'}
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -269,7 +278,7 @@
|
|||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg> -->
|
||||
<span class="ml-2 self-center"> JSON </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('JSON')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -280,7 +289,7 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
system: system !== '' ? system : undefined,
|
||||
|
@ -304,7 +313,7 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { config, user } from '$lib/stores';
|
||||
import {
|
||||
getAUTOMATIC1111Url,
|
||||
|
@ -21,6 +21,8 @@
|
|||
import { getBackendConfig } from '$lib/apis';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
let loading = false;
|
||||
|
@ -61,7 +63,7 @@
|
|||
await getModels();
|
||||
|
||||
if (models) {
|
||||
toast.success('Server connection verified');
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
}
|
||||
} else {
|
||||
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
|
||||
|
@ -116,11 +118,13 @@
|
|||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={async () => {
|
||||
loading = true;
|
||||
await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
|
||||
|
||||
if (imageGenerationEngine === 'openai') {
|
||||
await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
|
||||
}
|
||||
|
||||
await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
|
||||
|
||||
await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
|
||||
await updateImageSize(localStorage.token, imageSize).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
|
@ -134,39 +138,41 @@
|
|||
loading = false;
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[20.5rem]">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[24rem]">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">Image Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('Image Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Image Generation Engine</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={imageGenerationEngine}
|
||||
placeholder="Select a mode"
|
||||
placeholder={$i18n.t('Select a mode')}
|
||||
on:change={async () => {
|
||||
await updateImageGeneration();
|
||||
}}
|
||||
>
|
||||
<option value="">Default (Automatic1111)</option>
|
||||
<option value="openai">Open AI (Dall-E)</option>
|
||||
<option value="">{$i18n.t('Default (Automatic1111)')}</option>
|
||||
<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Image Generation (Experimental)</div>
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Image Generation (Experimental)')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
|
||||
toast.error('AUTOMATIC1111 Base URL is required.');
|
||||
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
|
||||
enableImageGeneration = false;
|
||||
} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
|
||||
toast.error('OpenAI API Key is required.');
|
||||
toast.error($i18n.t('OpenAI API Key is required.'));
|
||||
enableImageGeneration = false;
|
||||
} else {
|
||||
enableImageGeneration = !enableImageGeneration;
|
||||
|
@ -177,9 +183,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if enableImageGeneration === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -188,12 +194,12 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
{#if imageGenerationEngine === ''}
|
||||
<div class=" mb-2.5 text-sm font-medium">AUTOMATIC1111 Base URL</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter URL (e.g. http://127.0.0.1:7860/)"
|
||||
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
|
||||
bind:value={AUTOMATIC1111_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
|
@ -222,22 +228,22 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Include `--api` flag when running stable-diffusion-webui
|
||||
{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
|
||||
target="_blank"
|
||||
>
|
||||
(e.g. `sh webui.sh --api`)
|
||||
{$i18n.t('(e.g. `sh webui.sh --api`)')}
|
||||
</a>
|
||||
</div>
|
||||
{:else if imageGenerationEngine === 'openai'}
|
||||
<div class=" mb-2.5 text-sm font-medium">OpenAI API Key</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('OpenAI API Key')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter API Key"
|
||||
placeholder={$i18n.t('Enter API Key')}
|
||||
bind:value={OPENAI_API_KEY}
|
||||
/>
|
||||
</div>
|
||||
|
@ -248,16 +254,16 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Default Model</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={selectedModel}
|
||||
placeholder="Select a model"
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
{#if !selectedModel}
|
||||
<option value="" disabled selected>Select a model</option>
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{/if}
|
||||
{#each models ?? [] as model}
|
||||
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
|
||||
|
@ -268,12 +274,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Image Size</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter Image Size (e.g. 512x512)"
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
bind:value={imageSize}
|
||||
/>
|
||||
</div>
|
||||
|
@ -281,12 +287,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Steps</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter Number of Steps (e.g. 50)"
|
||||
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
bind:value={steps}
|
||||
/>
|
||||
</div>
|
||||
|
@ -297,13 +303,13 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded flex flex-row space-x-1 items-center {loading
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center {loading
|
||||
? ' cursor-not-allowed'
|
||||
: ''}"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-2 self-center">
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
import { getBackendConfig } from '$lib/apis';
|
||||
import { setDefaultPromptSuggestions } from '$lib/apis/configs';
|
||||
import { config, models, user } from '$lib/stores';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Addons
|
||||
|
@ -63,6 +65,7 @@
|
|||
}
|
||||
|
||||
saveSettings({
|
||||
titleAutoGenerateModel: titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined,
|
||||
titleGenerationPrompt: titleGenerationPrompt ? titleGenerationPrompt : undefined
|
||||
});
|
||||
};
|
||||
|
@ -81,7 +84,9 @@
|
|||
titleAutoGenerateModel = settings.titleAutoGenerateModel ?? '';
|
||||
titleGenerationPrompt =
|
||||
settings.titleGenerationPrompt ??
|
||||
`Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title': {{prompt}}`;
|
||||
$i18n.t(
|
||||
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':"
|
||||
) + ' {{prompt}}';
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -92,13 +97,13 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-80">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">WebUI Add-ons</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Add-ons')}</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Title Auto-Generation</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Title Auto-Generation')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -108,9 +113,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if titleAutoGenerate === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -118,7 +123,9 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Response AutoCopy to Clipboard</div>
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Response AutoCopy to Clipboard')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -128,9 +135,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if responseAutoCopy === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -138,7 +145,7 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Full Screen Mode</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Full Screen Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -148,9 +155,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if fullScreenMode === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -159,7 +166,7 @@
|
|||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
Display the username instead of "You" in the Chat
|
||||
{$i18n.t('Display the username instead of You in the Chat')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
@ -170,9 +177,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if showUsername === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -182,15 +189,15 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Title Auto-Generation Model</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Title Auto-Generation Model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={titleAutoGenerateModel}
|
||||
placeholder="Select a model"
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
<option value="" selected>Current Model</option>
|
||||
<option value="" selected>{$i18n.t('Current Model')}</option>
|
||||
{#each $models as model}
|
||||
{#if model.size != null}
|
||||
<option value={model.name} class="bg-gray-100 dark:bg-gray-700">
|
||||
|
@ -200,35 +207,13 @@
|
|||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
titleAutoGenerateModel:
|
||||
titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-3.5 h-3.5"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class=" mb-2.5 text-sm font-medium">Title Generation Prompt</div>
|
||||
|
||||
<div class="mt-3 mr-2">
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
|
||||
<textarea
|
||||
bind:value={titleGenerationPrompt}
|
||||
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
|
||||
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
|
@ -239,7 +224,9 @@
|
|||
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">Default Prompt Suggestions</div>
|
||||
<div class=" self-center text-sm font-semibold">
|
||||
{$i18n.t('Default Prompt Suggestions')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -312,19 +299,19 @@
|
|||
|
||||
{#if promptSuggestions.length > 0}
|
||||
<div class="text-xs text-left w-full mt-2">
|
||||
Adjusting these settings will apply changes universally to all users.
|
||||
{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<div class="flex justify-end text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -12,8 +12,11 @@
|
|||
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { WEBUI_NAME, models, user } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let getModels: Function;
|
||||
|
||||
|
@ -37,6 +40,10 @@
|
|||
|
||||
let OLLAMA_URLS = [];
|
||||
let selectedOllamaUrlIdx: string | null = null;
|
||||
|
||||
let updateModelId = null;
|
||||
let updateProgress = null;
|
||||
|
||||
let showExperimentalOllama = false;
|
||||
let ollamaVersion = '';
|
||||
const MAX_PARALLEL_DOWNLOADS = 3;
|
||||
|
@ -61,14 +68,85 @@
|
|||
|
||||
let deleteModelTag = '';
|
||||
|
||||
const updateModelsHandler = async () => {
|
||||
for (const model of $models.filter(
|
||||
(m) =>
|
||||
m.size != null &&
|
||||
(selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))
|
||||
)) {
|
||||
console.log(model);
|
||||
|
||||
updateModelId = model.id;
|
||||
const res = await pullModel(localStorage.token, model.id, selectedOllamaUrlIdx).catch(
|
||||
(error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
if (res) {
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(splitStream('\n'))
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
let lines = value.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line !== '') {
|
||||
let data = JSON.parse(line);
|
||||
|
||||
console.log(data);
|
||||
if (data.error) {
|
||||
throw data.error;
|
||||
}
|
||||
if (data.detail) {
|
||||
throw data.detail;
|
||||
}
|
||||
if (data.status) {
|
||||
if (data.digest) {
|
||||
updateProgress = 0;
|
||||
if (data.completed) {
|
||||
updateProgress = Math.round((data.completed / data.total) * 1000) / 10;
|
||||
} else {
|
||||
updateProgress = 100;
|
||||
}
|
||||
} else {
|
||||
toast.success(data.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateModelId = null;
|
||||
updateProgress = null;
|
||||
};
|
||||
|
||||
const pullModelHandler = async () => {
|
||||
const sanitizedModelTag = modelTag.trim();
|
||||
if (modelDownloadStatus[sanitizedModelTag]) {
|
||||
toast.error(`Model '${sanitizedModelTag}' is already in queue for downloading.`);
|
||||
toast.error(
|
||||
$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
|
||||
modelTag: sanitizedModelTag
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (Object.keys(modelDownloadStatus).length === 3) {
|
||||
toast.error('Maximum of 3 models can be downloaded simultaneously. Please try again later.');
|
||||
toast.error(
|
||||
$i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -86,10 +164,12 @@
|
|||
if (!data.success) {
|
||||
toast.error(data.error);
|
||||
} else {
|
||||
toast.success(`Model '${modelName}' has been successfully downloaded.`);
|
||||
toast.success(
|
||||
$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName })
|
||||
);
|
||||
|
||||
const notification = new Notification($WEBUI_NAME, {
|
||||
body: `Model '${modelName}' has been successfully downloaded.`,
|
||||
body: $i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName }),
|
||||
icon: `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
});
|
||||
|
||||
|
@ -253,7 +333,7 @@
|
|||
);
|
||||
|
||||
if (res) {
|
||||
toast.success(`Deleted ${deleteModelTag}`);
|
||||
toast.success($i18n.t(`Deleted {{deleteModelTag}}`, { deleteModelTag }));
|
||||
}
|
||||
|
||||
deleteModelTag = '';
|
||||
|
@ -340,7 +420,7 @@
|
|||
}
|
||||
}
|
||||
} else {
|
||||
toast.error(`Model ${liteLLMModelName} already exists.`);
|
||||
toast.error($i18n.t(`Model {{modelName}} already exists.`, { modelName: liteLLMModelName }));
|
||||
}
|
||||
|
||||
liteLLMModelName = '';
|
||||
|
@ -379,7 +459,7 @@
|
|||
return [];
|
||||
});
|
||||
|
||||
if (OLLAMA_URLS.length > 1) {
|
||||
if (OLLAMA_URLS.length > 0) {
|
||||
selectedOllamaUrlIdx = 0;
|
||||
}
|
||||
|
||||
|
@ -389,33 +469,68 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[23rem]">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[24rem]">
|
||||
{#if ollamaVersion}
|
||||
<div class="space-y-2 pr-1.5">
|
||||
<div class="text-sm font-medium">Manage Ollama Models</div>
|
||||
<div class="text-sm font-medium">{$i18n.t('Manage Ollama Models')}</div>
|
||||
|
||||
{#if OLLAMA_URLS.length > 1}
|
||||
<div class="flex-1 pb-1">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={selectedOllamaUrlIdx}
|
||||
placeholder="Select an Ollama instance"
|
||||
>
|
||||
{#each OLLAMA_URLS as url, idx}
|
||||
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if OLLAMA_URLS.length > 0}
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-1 pb-1">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={selectedOllamaUrlIdx}
|
||||
placeholder={$i18n.t('Select an Ollama instance')}
|
||||
>
|
||||
{#each OLLAMA_URLS as url, idx}
|
||||
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex w-full justify-end">
|
||||
<Tooltip content="Update All Models" placement="top">
|
||||
<button
|
||||
class="p-2.5 flex gap-2 items-center bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
|
||||
on:click={() => {
|
||||
updateModelsHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M7 1a.75.75 0 0 1 .75.75V6h-1.5V1.75A.75.75 0 0 1 7 1ZM6.25 6v2.94L5.03 7.72a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06L7.75 8.94V6H10a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.25Z"
|
||||
/>
|
||||
<path
|
||||
d="M4.268 14A2 2 0 0 0 6 15h6a2 2 0 0 0 2-2v-3a2 2 0 0 0-1-1.732V11a3 3 0 0 1-3 3H4.268Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if updateModelId}
|
||||
Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">Pull a model from Ollama.com</div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('Pull a model from Ollama.com')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter model tag (e.g. mistral:7b)"
|
||||
placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
|
||||
modelTag: 'mistral:7b'
|
||||
})}
|
||||
bind:value={modelTag}
|
||||
/>
|
||||
</div>
|
||||
|
@ -471,10 +586,11 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
To access the available model names for downloading, <a
|
||||
{$i18n.t('To access the available model names for downloading,')}
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium underline"
|
||||
href="https://ollama.com/library"
|
||||
target="_blank">click here.</a
|
||||
target="_blank">{$i18n.t('click here.')}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
|
@ -499,16 +615,16 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">Delete a model</div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('Delete a model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={deleteModelTag}
|
||||
placeholder="Select a model"
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
{#if !deleteModelTag}
|
||||
<option value="" disabled selected>Select a model</option>
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{/if}
|
||||
{#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
|
||||
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
|
||||
|
@ -541,13 +657,13 @@
|
|||
|
||||
<div class="pt-1">
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">Experimental</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Experimental')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showExperimentalOllama = !showExperimentalOllama;
|
||||
}}>{showExperimentalOllama ? 'Hide' : 'Show'}</button
|
||||
}}>{showExperimentalOllama ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -559,7 +675,7 @@
|
|||
}}
|
||||
>
|
||||
<div class=" mb-2 flex w-full justify-between">
|
||||
<div class=" text-sm font-medium">Upload a GGUF model</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Upload a GGUF model')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
@ -573,9 +689,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if modelUploadMode === 'file'}
|
||||
<span class="ml-2 self-center">File Mode</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('File Mode')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">URL Mode</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('URL Mode')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -607,7 +723,7 @@
|
|||
{#if modelInputFile && modelInputFile.length > 0}
|
||||
{modelInputFile[0].name}
|
||||
{:else}
|
||||
Click here to select
|
||||
{$i18n.t('Click here to select')}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -621,7 +737,7 @@
|
|||
type="url"
|
||||
required
|
||||
bind:value={modelFileUrl}
|
||||
placeholder="Type Hugging Face Resolve (Download) URL"
|
||||
placeholder={$i18n.t('Type Hugging Face Resolve (Download) URL')}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -681,7 +797,7 @@
|
|||
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
|
||||
<div>
|
||||
<div>
|
||||
<div class=" my-2.5 text-sm font-medium">Modelfile Content</div>
|
||||
<div class=" my-2.5 text-sm font-medium">{$i18n.t('Modelfile Content')}</div>
|
||||
<textarea
|
||||
bind:value={modelFileContent}
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
|
||||
|
@ -691,16 +807,17 @@
|
|||
</div>
|
||||
{/if}
|
||||
<div class=" mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
To access the GGUF models available for downloading, <a
|
||||
{$i18n.t('To access the GGUF models available for downloading,')}
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium underline"
|
||||
href="https://huggingface.co/models?search=gguf"
|
||||
target="_blank">click here.</a
|
||||
target="_blank">{$i18n.t('click here.')}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if uploadProgress !== null}
|
||||
<div class="mt-2">
|
||||
<div class=" mb-2 text-xs">Upload Progress</div>
|
||||
<div class=" mb-2 text-xs">{$i18n.t('Upload Progress')}</div>
|
||||
|
||||
<div class="w-full rounded-full dark:bg-gray-800">
|
||||
<div
|
||||
|
@ -727,13 +844,13 @@
|
|||
<div>
|
||||
<div class="mb-2">
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">Manage LiteLLM Models</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Manage LiteLLM Models')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showLiteLLM = !showLiteLLM;
|
||||
}}>{showLiteLLM ? 'Hide' : 'Show'}</button
|
||||
}}>{showLiteLLM ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -741,14 +858,16 @@
|
|||
{#if showLiteLLM}
|
||||
<div>
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">Add a model</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Add a model')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showLiteLLMParams = !showLiteLLMParams;
|
||||
}}
|
||||
>{showLiteLLMParams ? 'Hide Additional Params' : 'Show Additional Params'}</button
|
||||
>{showLiteLLMParams
|
||||
? $i18n.t('Hide Additional Params')
|
||||
: $i18n.t('Show Additional Params')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -758,7 +877,7 @@
|
|||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter LiteLLM Model (litellm_params.model)"
|
||||
placeholder={$i18n.t('Enter LiteLLM Model (litellm_params.model)')}
|
||||
bind:value={liteLLMModel}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -785,7 +904,7 @@
|
|||
|
||||
{#if showLiteLLMParams}
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">Model Name</div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Model Name')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
@ -799,12 +918,14 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">API Base URL</div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)"
|
||||
placeholder={$i18n.t(
|
||||
'Enter LiteLLM API Base URL (litellm_params.api_base)'
|
||||
)}
|
||||
bind:value={liteLLMAPIBase}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -813,12 +934,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">API Key</div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Key')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter LiteLLM API Key (litellm_params.api_key)"
|
||||
placeholder={$i18n.t('Enter LiteLLM API Key (litellm_params.api_key)')}
|
||||
bind:value={liteLLMAPIKey}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -827,12 +948,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm font-medium">API RPM</div>
|
||||
<div class="mb-1.5 text-sm font-medium">{$i18n.t('API RPM')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)"
|
||||
placeholder={$i18n.t('Enter LiteLLM API RPM (litellm_params.rpm)')}
|
||||
bind:value={liteLLMRPM}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
@ -841,12 +962,12 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm font-medium">Max Tokens</div>
|
||||
<div class="mb-1.5 text-sm font-medium">{$i18n.t('Max Tokens')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter Max Tokens (litellm_params.max_tokens)"
|
||||
placeholder={$i18n.t('Enter Max Tokens (litellm_params.max_tokens)')}
|
||||
bind:value={liteLLMMaxTokens}
|
||||
type="number"
|
||||
min="1"
|
||||
|
@ -859,27 +980,27 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Not sure what to add?
|
||||
{$i18n.t('Not sure what to add?')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
|
||||
target="_blank"
|
||||
>
|
||||
Click here for help.
|
||||
{$i18n.t('Click here for help.')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Delete a model</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Delete a model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={deleteLiteLLMModelId}
|
||||
placeholder="Select a model"
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
{#if !deleteLiteLLMModelId}
|
||||
<option value="" disabled selected>Select a model</option>
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{/if}
|
||||
{#each liteLLMModelInfo as model}
|
||||
<option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700"
|
||||
|
@ -912,88 +1033,6 @@
|
|||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="mt-2 space-y-3 pr-1.5">
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Add LiteLLM Model</div>
|
||||
<div class="flex w-full mb-2">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter LiteLLM Model (e.g. ollama/mistral)"
|
||||
bind:value={liteLLMModel}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">Advanced Model Params</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showLiteLLMParams = !showLiteLLMParams;
|
||||
}}>{showLiteLLMParams ? 'Hide' : 'Show'}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if showLiteLLMParams}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">LiteLLM API Key</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter LiteLLM API Key (e.g. os.environ/AZURE_API_KEY_CA)"
|
||||
bind:value={liteLLMAPIKey}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">LiteLLM API Base URL</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter LiteLLM API Base URL"
|
||||
bind:value={liteLLMAPIBase}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">LiteLLM API RPM</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter LiteLLM API RPM"
|
||||
bind:value={liteLLMRPM}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Not sure what to add?
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
|
||||
target="_blank"
|
||||
>
|
||||
Click here for help.
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue