2024-02-05 09:58:54 +00:00
< script lang = "ts" >
2024-11-12 05:18:51 +00:00
import { toast } from 'svelte-sonner';
2024-06-02 20:45:04 +00:00
import { createEventDispatcher , onMount , getContext , tick } from 'svelte';
2024-06-25 06:00:02 +00:00
2024-02-05 09:58:54 +00:00
const dispatch = createEventDispatcher();
2024-11-12 05:18:51 +00:00
import { getOllamaConfig , updateOllamaConfig } from '$lib/apis/ollama';
import { getOpenAIConfig , updateOpenAIConfig , getOpenAIModels } from '$lib/apis/openai';
import { getModels as _getModels } from '$lib/apis';
2025-06-28 11:12:31 +00:00
import { getConnectionsConfig , setConnectionsConfig } from '$lib/apis/configs';
2024-11-12 05:18:51 +00:00
2025-02-12 09:22:53 +00:00
import { config , models , settings , user } from '$lib/stores';
2024-11-06 04:52:02 +00:00
2024-05-17 17:30:22 +00:00
import Switch from '$lib/components/common/Switch.svelte';
2024-05-26 22:21:08 +00:00
import Spinner from '$lib/components/common/Spinner.svelte';
2024-05-29 08:12:25 +00:00
import Tooltip from '$lib/components/common/Tooltip.svelte';
2024-11-12 06:25:08 +00:00
import Plus from '$lib/components/icons/Plus.svelte';
2024-11-12 05:18:51 +00:00
import OpenAIConnection from './Connections/OpenAIConnection.svelte';
2025-02-12 07:12:00 +00:00
import AddConnectionModal from '$lib/components/AddConnectionModal.svelte';
2024-11-12 06:25:08 +00:00
import OllamaConnection from './Connections/OllamaConnection.svelte';
2024-02-21 21:29:59 +00:00
2024-03-02 20:38:51 +00:00
const i18n = getContext('i18n');
2024-06-09 08:54:50 +00:00
const getModels = async () => {
2025-02-12 09:22:53 +00:00
const models = await _getModels(
localStorage.token,
2025-06-28 10:36:26 +00:00
$config?.features?.enable_direct_connections & & ($settings?.directConnections ?? null),
false,
true
2025-02-12 09:22:53 +00:00
);
2024-06-09 08:54:50 +00:00
return models;
};
2024-02-05 09:58:54 +00:00
// External
2024-03-05 08:59:35 +00:00
let OLLAMA_BASE_URLS = [''];
2024-11-12 05:18:51 +00:00
let OLLAMA_API_CONFIGS = {} ;
2024-02-21 21:29:59 +00:00
2024-03-07 00:13:25 +00:00
let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = [''];
2024-11-12 05:18:51 +00:00
let OPENAI_API_CONFIGS = {} ;
2024-03-07 00:13:25 +00:00
2024-11-12 05:18:51 +00:00
let ENABLE_OPENAI_API: null | boolean = null;
let ENABLE_OLLAMA_API: null | boolean = null;
2024-05-29 08:12:25 +00:00
2025-06-28 11:12:31 +00:00
let connectionsConfig = null;
2025-02-12 06:29:45 +00:00
2024-11-12 05:18:51 +00:00
let pipelineUrls = {} ;
let showAddOpenAIConnectionModal = false;
2024-11-12 06:25:08 +00:00
let showAddOllamaConnectionModal = false;
2024-05-29 08:12:25 +00:00
2024-02-05 09:58:54 +00:00
const updateOpenAIHandler = async () => {
2024-11-12 05:18:51 +00:00
if (ENABLE_OPENAI_API !== null) {
2025-01-19 01:22:29 +00:00
// Remove trailing slashes
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.map((url) => url.replace(/\/$/, ''));
2024-11-12 06:25:08 +00:00
2024-11-12 05:18:51 +00:00
// Check if API KEYS length is same than API URLS length
if (OPENAI_API_KEYS.length !== OPENAI_API_BASE_URLS.length) {
// if there are more keys than urls, remove the extra keys
if (OPENAI_API_KEYS.length > OPENAI_API_BASE_URLS.length) {
OPENAI_API_KEYS = OPENAI_API_KEYS.slice(0, OPENAI_API_BASE_URLS.length);
}
2024-06-16 05:32:12 +00:00
2024-11-12 05:18:51 +00:00
// if there are more urls than keys, add empty keys
if (OPENAI_API_KEYS.length < OPENAI_API_BASE_URLS.length ) {
const diff = OPENAI_API_BASE_URLS.length - OPENAI_API_KEYS.length;
for (let i = 0; i < diff ; i ++) {
OPENAI_API_KEYS.push('');
}
}
2024-06-02 20:45:04 +00:00
}
2024-11-12 05:18:51 +00:00
const res = await updateOpenAIConfig(localStorage.token, {
ENABLE_OPENAI_API: ENABLE_OPENAI_API,
OPENAI_API_BASE_URLS: OPENAI_API_BASE_URLS,
OPENAI_API_KEYS: OPENAI_API_KEYS,
OPENAI_API_CONFIGS: OPENAI_API_CONFIGS
}).catch((error) => {
2025-01-21 06:41:32 +00:00
toast.error(`${ error } `);
2024-11-12 05:18:51 +00:00
});
if (res) {
toast.success($i18n.t('OpenAI API settings updated'));
await models.set(await getModels());
2024-06-02 20:45:04 +00:00
}
}
2024-02-05 09:58:54 +00:00
};
2024-11-12 05:18:51 +00:00
const updateOllamaHandler = async () => {
if (ENABLE_OLLAMA_API !== null) {
2025-01-19 01:22:29 +00:00
// Remove trailing slashes
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.map((url) => url.replace(/\/$/, ''));
2024-02-21 21:29:59 +00:00
2024-11-12 05:18:51 +00:00
const res = await updateOllamaConfig(localStorage.token, {
ENABLE_OLLAMA_API: ENABLE_OLLAMA_API,
OLLAMA_BASE_URLS: OLLAMA_BASE_URLS,
OLLAMA_API_CONFIGS: OLLAMA_API_CONFIGS
}).catch((error) => {
2025-01-21 06:41:32 +00:00
toast.error(`${ error } `);
2024-06-02 20:45:04 +00:00
});
2024-11-12 05:18:51 +00:00
if (res) {
toast.success($i18n.t('Ollama API settings updated'));
2024-06-02 20:45:04 +00:00
await models.set(await getModels());
}
2024-02-21 21:29:59 +00:00
}
};
2025-06-28 11:12:31 +00:00
const updateConnectionsHandler = async () => {
const res = await setConnectionsConfig(localStorage.token, connectionsConfig).catch((error) => {
toast.error(`${ error } `);
});
2025-02-12 06:33:03 +00:00
if (res) {
2025-06-28 11:12:31 +00:00
toast.success($i18n.t('Connections settings updated'));
2025-02-12 06:33:03 +00:00
await models.set(await getModels());
}
};
2024-11-12 05:18:51 +00:00
const addOpenAIConnectionHandler = async (connection) => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, connection.url];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, connection.key];
2025-02-06 08:52:22 +00:00
OPENAI_API_CONFIGS[OPENAI_API_BASE_URLS.length - 1] = connection.config;
2024-11-12 05:18:51 +00:00
await updateOpenAIHandler();
};
2024-11-12 06:25:08 +00:00
const addOllamaConnectionHandler = async (connection) => {
OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, connection.url];
2025-02-06 08:52:22 +00:00
OLLAMA_API_CONFIGS[OLLAMA_BASE_URLS.length - 1] = {
...connection.config,
key: connection.key
};
2024-11-12 06:25:08 +00:00
await updateOllamaHandler();
};
2024-02-05 09:58:54 +00:00
onMount(async () => {
2025-04-01 03:32:12 +00:00
if ($user?.role === 'admin') {
2024-11-12 05:18:51 +00:00
let ollamaConfig = {} ;
let openaiConfig = {} ;
2024-05-26 22:21:08 +00:00
await Promise.all([
(async () => {
2024-11-12 05:18:51 +00:00
ollamaConfig = await getOllamaConfig(localStorage.token);
2024-05-26 22:21:08 +00:00
})(),
(async () => {
2024-11-12 05:18:51 +00:00
openaiConfig = await getOpenAIConfig(localStorage.token);
2025-02-12 06:29:45 +00:00
})(),
(async () => {
2025-06-28 11:12:31 +00:00
connectionsConfig = await getConnectionsConfig(localStorage.token);
2024-05-26 22:21:08 +00:00
})()
]);
2024-05-22 06:58:42 +00:00
ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API;
ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API;
2024-09-10 11:22:05 +00:00
2024-11-12 05:18:51 +00:00
OPENAI_API_BASE_URLS = openaiConfig.OPENAI_API_BASE_URLS;
OPENAI_API_KEYS = openaiConfig.OPENAI_API_KEYS;
OPENAI_API_CONFIGS = openaiConfig.OPENAI_API_CONFIGS;
OLLAMA_BASE_URLS = ollamaConfig.OLLAMA_BASE_URLS;
OLLAMA_API_CONFIGS = ollamaConfig.OLLAMA_API_CONFIGS;
2024-09-10 11:22:05 +00:00
if (ENABLE_OPENAI_API) {
2025-01-19 01:10:15 +00:00
// get url and idx
for (const [idx, url] of OPENAI_API_BASE_URLS.entries()) {
if (!OPENAI_API_CONFIGS[idx]) {
// Legacy support, url as key
OPENAI_API_CONFIGS[idx] = OPENAI_API_CONFIGS[url] || {} ;
2024-11-12 07:04:02 +00:00
}
}
2024-09-10 11:22:05 +00:00
OPENAI_API_BASE_URLS.forEach(async (url, idx) => {
2025-01-19 01:10:15 +00:00
OPENAI_API_CONFIGS[idx] = OPENAI_API_CONFIGS[idx] || {} ;
if (!(OPENAI_API_CONFIGS[idx]?.enable ?? true)) {
2024-11-12 07:04:02 +00:00
return;
}
2024-09-10 11:22:05 +00:00
const res = await getOpenAIModels(localStorage.token, idx);
if (res.pipelines) {
pipelineUrls[url] = true;
}
});
2024-11-12 05:18:51 +00:00
}
if (ENABLE_OLLAMA_API) {
2025-01-19 01:10:15 +00:00
for (const [idx, url] of OLLAMA_BASE_URLS.entries()) {
if (!OLLAMA_API_CONFIGS[idx]) {
OLLAMA_API_CONFIGS[idx] = OLLAMA_API_CONFIGS[url] || {} ;
2024-11-12 05:18:51 +00:00
}
}
2024-09-10 11:22:05 +00:00
}
2024-02-05 09:58:54 +00:00
}
});
2025-02-12 06:29:45 +00:00
const submitHandler = async () => {
updateOpenAIHandler();
updateOllamaHandler();
dispatch('save');
};
2024-02-05 09:58:54 +00:00
< / script >
2024-11-12 06:25:08 +00:00
< AddConnectionModal
2024-11-12 05:18:51 +00:00
bind:show={ showAddOpenAIConnectionModal }
onSubmit={ addOpenAIConnectionHandler }
/>
2024-11-12 06:25:08 +00:00
< AddConnectionModal
ollama
bind:show={ showAddOllamaConnectionModal }
onSubmit={ addOllamaConnectionHandler }
/>
2025-02-12 06:29:45 +00:00
< form class = "flex flex-col h-full justify-between text-sm" on:submit | preventDefault = { submitHandler } >
2024-11-11 05:01:28 +00:00
< div class = " overflow-y-scroll scrollbar-hidden h-full" >
2025-06-28 11:12:31 +00:00
{ #if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null && connectionsConfig !== null }
2025-06-28 10:36:26 +00:00
< div class = "mb-3.5" >
< div class = " mb-2.5 text-base font-medium" > { $i18n . t ( 'General' )} </ div >
2024-05-26 22:21:08 +00:00
2025-06-28 10:36:26 +00:00
< hr class = " border-gray-100 dark:border-gray-850 my-2" / >
< div class = "my-2" >
< div class = "mt-2 space-y-2" >
< div class = "flex justify-between items-center text-sm" >
< div class = " font-medium" > { $i18n . t ( 'OpenAI API' )} </ div >
< div class = "flex items-center" >
< div class = "" >
< Switch
bind:state={ ENABLE_OPENAI_API }
on:change={ async () => {
updateOpenAIHandler();
}}
/>
< / div >
2024-11-11 05:01:28 +00:00
< / div >
2024-05-26 22:21:08 +00:00
< / div >
2025-06-28 10:36:26 +00:00
{ #if ENABLE_OPENAI_API }
< div class = "" >
< div class = "flex justify-between items-center" >
< div class = "font-medium text-xs" > { $i18n . t ( 'Manage OpenAI API Connections' )} </ div >
< Tooltip content = { $i18n . t ( `Add Connection` )} >
< button
class="px-1"
on:click={() => {
showAddOpenAIConnectionModal = true;
}}
type="button"
>
< Plus / >
< / button >
< / Tooltip >
< / div >
< div class = "flex flex-col gap-1.5 mt-1.5" >
{ #each OPENAI_API_BASE_URLS as url , idx }
< OpenAIConnection
pipeline={ pipelineUrls [ url ] ? true : false }
bind:url
bind:key={ OPENAI_API_KEYS [ idx ]}
bind:config={ OPENAI_API_CONFIGS [ idx ]}
onSubmit={() => {
updateOpenAIHandler();
}}
onDelete={() => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
let newConfig = {} ;
OPENAI_API_BASE_URLS.forEach((url, newIdx) => {
newConfig[newIdx] =
OPENAI_API_CONFIGS[newIdx < idx ? newIdx : newIdx + 1 ] ;
});
OPENAI_API_CONFIGS = newConfig;
updateOpenAIHandler();
}}
/>
{ /each }
< / div >
< / div >
{ /if }
2024-05-26 22:21:08 +00:00
< / div >
2025-06-28 10:36:26 +00:00
< / div >
2024-05-26 22:21:08 +00:00
2025-06-28 10:36:26 +00:00
< div class = " my-2" >
< div class = "flex justify-between items-center text-sm mb-2" >
< div class = " font-medium" > { $i18n . t ( 'Ollama API' )} </ div >
2024-11-11 05:01:28 +00:00
2025-06-28 10:36:26 +00:00
< div class = "mt-1" >
< Switch
bind:state={ ENABLE_OLLAMA_API }
on:change={ async () => {
updateOllamaHandler();
}}
/>
< / div >
< / div >
{ #if ENABLE_OLLAMA_API }
2024-11-11 05:01:28 +00:00
< div class = "" >
2024-11-12 05:18:51 +00:00
< div class = "flex justify-between items-center" >
2025-06-28 10:36:26 +00:00
< div class = "font-medium text-xs" > { $i18n . t ( 'Manage Ollama API Connections' )} </ div >
2024-11-11 05:01:28 +00:00
2024-11-12 05:18:51 +00:00
< Tooltip content = { $i18n . t ( `Add Connection` )} >
< button
class="px-1"
on:click={() => {
2025-06-28 10:36:26 +00:00
showAddOllamaConnectionModal = true;
2024-11-12 05:18:51 +00:00
}}
type="button"
2024-11-11 05:01:28 +00:00
>
2024-11-12 05:18:51 +00:00
< Plus / >
< / button >
< / Tooltip >
2024-11-11 05:01:28 +00:00
< / div >
2025-06-28 10:36:26 +00:00
< div class = "flex w-full gap-1.5" >
< div class = "flex-1 flex flex-col gap-1.5 mt-1.5" >
{ #each OLLAMA_BASE_URLS as url , idx }
< OllamaConnection
bind:url
bind:config={ OLLAMA_API_CONFIGS [ idx ]}
{ idx }
onSubmit={() => {
updateOllamaHandler();
}}
onDelete={() => {
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
let newConfig = {} ;
OLLAMA_BASE_URLS.forEach((url, newIdx) => {
newConfig[newIdx] =
OLLAMA_API_CONFIGS[newIdx < idx ? newIdx : newIdx + 1 ] ;
});
OLLAMA_API_CONFIGS = newConfig;
}}
/>
{ /each }
< / div >
< / div >
< div class = "mt-1 text-xs text-gray-400 dark:text-gray-500" >
{ $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 >
2024-11-11 05:01:28 +00:00
< / div >
2024-05-26 22:21:08 +00:00
< / div >
{ /if }
< / div >
2024-11-11 05:01:28 +00:00
2025-06-28 10:36:26 +00:00
< div class = "my-2" >
< div class = "flex justify-between items-center text-sm" >
< div class = " font-medium" > { $i18n . t ( 'Direct Connections' )} </ div >
2024-11-11 05:01:28 +00:00
2025-06-28 10:36:26 +00:00
< div class = "flex items-center" >
< div class = "" >
< Switch
2025-06-28 11:12:31 +00:00
bind:state={ connectionsConfig . ENABLE_DIRECT_CONNECTIONS }
2025-06-28 10:36:26 +00:00
on:change={ async () => {
2025-06-28 11:12:31 +00:00
updateConnectionsHandler();
2024-11-12 06:25:08 +00:00
}}
2025-06-28 10:36:26 +00:00
/>
2024-11-11 05:01:28 +00:00
< / div >
2024-05-26 22:21:08 +00:00
< / div >
2025-02-12 06:29:45 +00:00
< / div >
2025-06-28 10:36:26 +00:00
< div class = "mt-1 text-xs text-gray-400 dark:text-gray-500" >
2025-02-12 07:12:00 +00:00
{ $i18n . t (
'Direct Connections allow users to connect to their own OpenAI compatible API endpoints.'
)}
2025-02-12 06:29:45 +00:00
< / div >
< / div >
2025-06-28 11:12:31 +00:00
< hr class = " border-gray-100 dark:border-gray-850 my-2" / >
< div class = "my-2" >
< div class = "flex justify-between items-center text-sm" >
2025-06-30 09:27:07 +00:00
< div class = " text-xs font-medium" > { $i18n . t ( 'Cache Base Model List' )} </ div >
2025-06-28 11:12:31 +00:00
< div class = "flex items-center" >
< div class = "" >
< Switch
2025-06-30 09:27:07 +00:00
bind:state={ connectionsConfig . ENABLE_BASE_MODELS_CACHE }
2025-06-28 11:12:31 +00:00
on:change={ async () => {
updateConnectionsHandler();
}}
/>
< / div >
< / div >
< / div >
< div class = "mt-1 text-xs text-gray-400 dark:text-gray-500" >
{ $i18n . t (
2025-06-30 09:27:07 +00:00
'Base Model List Cache speeds up access by fetching base models only at startup or on settings save—faster, but may not show recent base model changes.'
2025-06-28 11:12:31 +00:00
)}
< / div >
< / div >
2025-02-12 06:29:45 +00:00
< / div >
2024-05-26 22:21:08 +00:00
{ : else }
< div class = "flex h-full justify-center" >
< div class = "my-auto" >
< Spinner className = "size-6" / >
2024-03-05 08:59:35 +00:00
< / div >
2024-05-26 22:21:08 +00:00
< / div >
{ /if }
2024-02-05 09:58:54 +00:00
< / div >
< div class = "flex justify-end pt-3 text-sm font-medium" >
< button
2024-10-21 07:05:27 +00:00
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
2024-02-05 09:58:54 +00:00
type="submit"
>
2024-03-04 08:53:56 +00:00
{ $i18n . t ( 'Save' )}
2024-02-05 09:58:54 +00:00
< / button >
< / div >
< / form >