"vscode:/vscode.git/clone" did not exist on "bcb7633eff4665eb3a35e97d02de713b46abb564"
Commit b7fc37d9 authored by Timothy J. Baek's avatar Timothy J. Baek
Browse files

fix: spinner

parent 9fb61600
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
} from '$lib/apis/openai'; } from '$lib/apis/openai';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import Switch from '$lib/components/common/Switch.svelte'; import Switch from '$lib/components/common/Switch.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -31,8 +32,8 @@ ...@@ -31,8 +32,8 @@
let OPENAI_API_KEYS = ['']; let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = ['']; let OPENAI_API_BASE_URLS = [''];
let ENABLE_OPENAI_API = false; let ENABLE_OPENAI_API = null;
let ENABLE_OLLAMA_API = false; let ENABLE_OLLAMA_API = null;
const updateOpenAIHandler = async () => { const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS); OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
...@@ -57,16 +58,23 @@ ...@@ -57,16 +58,23 @@
onMount(async () => { onMount(async () => {
if ($user.role === 'admin') { if ($user.role === 'admin') {
await Promise.all([
(async () => {
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
})(),
(async () => {
OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
})(),
(async () => {
OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
})()
]);
const ollamaConfig = await getOllamaConfig(localStorage.token); const ollamaConfig = await getOllamaConfig(localStorage.token);
const openaiConfig = await getOpenAIConfig(localStorage.token); const openaiConfig = await getOpenAIConfig(localStorage.token);
ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API; ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API;
ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API; ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API;
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
} }
}); });
</script> </script>
...@@ -79,201 +87,211 @@ ...@@ -79,201 +87,211 @@
}} }}
> >
<div class=" pr-1.5 overflow-y-scroll max-h-[25rem] space-y-3"> <div class=" pr-1.5 overflow-y-scroll max-h-[25rem] space-y-3">
<div class=" space-y-3"> {#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null}
<div class="mt-2 space-y-2 pr-1.5"> <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">{$i18n.t('OpenAI API')}</div>
<div class="mt-1">
<Switch
bind:state={ENABLE_OPENAI_API}
on:change={async () => {
updateOpenAIConfig(localStorage.token, ENABLE_OPENAI_API);
}}
/>
</div>
</div>
{#if ENABLE_OPENAI_API}
<div class="flex flex-col gap-1">
{#each OPENAI_API_BASE_URLS as url, idx}
<div class="flex w-full gap-2">
<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={$i18n.t('API Base URL')}
bind:value={url}
autocomplete="off"
/>
</div>
<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={$i18n.t('API Key')}
bind:value={OPENAI_API_KEYS[idx]}
autocomplete="off"
/>
</div>
<div class="self-center flex items-center">
{#if idx === 0}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
{:else}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div>
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
{$i18n.t('WebUI will make requests to')}
<span class=" text-gray-200">'{url}/models'</span>
</div>
{/each}
</div>
{/if}
</div>
</div>
<hr class=" dark:border-gray-700" />
<div class="pr-1.5 space-y-2">
<div class="flex justify-between items-center text-sm"> <div class="flex justify-between items-center text-sm">
<div class=" font-medium">{$i18n.t('OpenAI API')}</div> <div class=" font-medium">{$i18n.t('Ollama API')}</div>
<div class="mt-1"> <div class="mt-1">
<Switch <Switch
bind:state={ENABLE_OPENAI_API} bind:state={ENABLE_OLLAMA_API}
on:change={async () => { on:change={async () => {
updateOpenAIConfig(localStorage.token, ENABLE_OPENAI_API); updateOllamaConfig(localStorage.token, ENABLE_OLLAMA_API);
}} }}
/> />
</div> </div>
</div> </div>
{#if ENABLE_OLLAMA_API}
{#if ENABLE_OPENAI_API} <div class="flex w-full gap-1.5">
<div class="flex flex-col gap-1"> <div class="flex-1 flex flex-col gap-2">
{#each OPENAI_API_BASE_URLS as url, idx} {#each OLLAMA_BASE_URLS as url, idx}
<div class="flex w-full gap-2"> <div class="flex gap-1.5">
<div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')}
bind:value={url} bind:value={url}
autocomplete="off"
/> />
</div>
<div class="flex-1"> <div class="self-center flex items-center">
<input {#if idx === 0}
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" <button
placeholder={$i18n.t('API Key')} class="px-1"
bind:value={OPENAI_API_KEYS[idx]} on:click={() => {
autocomplete="off" OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, ''];
/> }}
</div> type="button"
<div class="self-center flex items-center">
{#if idx === 0}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
> >
<path <svg
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" xmlns="http://www.w3.org/2000/svg"
/> viewBox="0 0 16 16"
</svg> fill="currentColor"
</button> class="w-4 h-4"
{:else} >
<button <path
class="px-1" d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
on:click={() => { />
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter( </svg>
(url, urlIdx) => idx !== urlIdx </button>
); {:else}
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx); <button
}} class="px-1"
type="button" on:click={() => {
> OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter(
<svg (url, urlIdx) => idx !== urlIdx
xmlns="http://www.w3.org/2000/svg" );
viewBox="0 0 16 16" }}
fill="currentColor" type="button"
class="w-4 h-4"
> >
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" /> <svg
</svg> xmlns="http://www.w3.org/2000/svg"
</button> viewBox="0 0 16 16"
{/if} fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div> </div>
</div> {/each}
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500"> </div>
{$i18n.t('WebUI will make requests to')}
<span class=" text-gray-200">'{url}/models'</span>
</div>
{/each}
</div>
{/if}
</div>
</div>
<hr class=" dark:border-gray-700" />
<div class="pr-1.5 space-y-2">
<div class="flex justify-between items-center text-sm">
<div class=" font-medium">{$i18n.t('Ollama API')}</div>
<div class="mt-1"> <div class="flex">
<Switch <button
bind:state={ENABLE_OLLAMA_API} class="self-center p-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
on:change={async () => { on:click={() => {
updateOllamaConfig(localStorage.token, ENABLE_OLLAMA_API); updateOllamaUrlsHandler();
}} }}
/> type="button"
</div> >
</div> <svg
{#if ENABLE_OLLAMA_API} xmlns="http://www.w3.org/2000/svg"
<div class="flex w-full gap-1.5"> viewBox="0 0 20 20"
<div class="flex-1 flex flex-col gap-2"> fill="currentColor"
{#each OLLAMA_BASE_URLS as url, idx} class="w-4 h-4"
<div class="flex gap-1.5"> >
<input <path
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" fill-rule="evenodd"
placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')} d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
bind:value={url} clip-rule="evenodd"
/> />
</svg>
<div class="self-center flex items-center"> </button>
{#if idx === 0} </div>
<button
class="px-1"
on:click={() => {
OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, ''];
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
{:else}
<button
class="px-1"
on:click={() => {
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div>
{/each}
</div> </div>
<div class="flex"> <div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
<button {$i18n.t('Trouble accessing Ollama?')}
class="self-center p-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition" <a
on:click={() => { class=" text-gray-300 font-medium underline"
updateOllamaUrlsHandler(); href="https://github.com/open-webui/open-webui#troubleshooting"
}} target="_blank"
type="button"
> >
<svg {$i18n.t('Click here for help.')}
xmlns="http://www.w3.org/2000/svg" </a>
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
clip-rule="evenodd"
/>
</svg>
</button>
</div> </div>
{/if}
</div>
{:else}
<div class="flex h-full justify-center">
<div class="my-auto">
<Spinner className="size-6" />
</div> </div>
</div>
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> {/if}
{$i18n.t('Trouble accessing Ollama?')}
<a
class=" text-gray-300 font-medium underline"
href="https://github.com/open-webui/open-webui#troubleshooting"
target="_blank"
>
{$i18n.t('Click here for help.')}
</a>
</div>
{/if}
</div>
</div> </div>
<div class="flex justify-end pt-3 text-sm font-medium"> <div class="flex justify-end pt-3 text-sm font-medium">
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment