open-webui/src/routes/(app)/+layout.svelte

314 lines
8.1 KiB
Svelte
Raw Normal View History

2023-11-20 01:47:07 +00:00
<script lang="ts">
2023-11-25 05:41:56 +00:00
import { v4 as uuidv4 } from 'uuid';
2023-11-20 01:47:07 +00:00
import { openDB, deleteDB } from 'idb';
2023-11-19 05:41:43 +00:00
import { onMount, tick } from 'svelte';
2023-11-20 01:47:07 +00:00
import { goto } from '$app/navigation';
import {
config,
2023-12-15 03:43:52 +00:00
info,
user,
showSettings,
settings,
models,
db,
chats,
chatId,
modelfiles
} from '$lib/stores';
2023-11-20 01:47:07 +00:00
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
import Sidebar from '$lib/components/layout/Sidebar.svelte';
import toast from 'svelte-french-toast';
2023-11-25 05:41:56 +00:00
import { OLLAMA_API_BASE_URL, WEBUI_API_BASE_URL } from '$lib/constants';
2023-12-26 11:28:30 +00:00
import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama';
2023-12-26 19:32:22 +00:00
import { getOpenAIModels } from '$lib/apis/openai';
2023-12-26 11:28:30 +00:00
import {
createNewChat,
deleteChatById,
getChatById,
getChatlist,
updateChatById
} from '$lib/apis/chats';
2023-11-19 00:47:12 +00:00
2023-12-15 03:43:52 +00:00
let requiredOllamaVersion = '0.1.16';
2023-11-19 05:41:43 +00:00
let loaded = false;
2023-11-20 01:47:07 +00:00
const getModels = async () => {
let models = [];
2023-12-26 11:28:30 +00:00
models.push(
...(await getOllamaModels($settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, localStorage.token))
);
// If OpenAI API Key exists
if ($settings.OPENAI_API_KEY) {
const openAIModels = await getOpenAIModels(
$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1',
$settings.OPENAI_API_KEY
).catch((error) => {
2023-11-20 01:47:07 +00:00
console.log(error);
2023-12-26 11:28:30 +00:00
toast.error(error);
2023-11-20 01:47:07 +00:00
return null;
});
2023-12-26 11:28:30 +00:00
models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : []));
2023-11-20 01:47:07 +00:00
}
return models;
};
const getDB = async () => {
const DB = await openDB('Chats', 1, {
2023-11-20 01:47:07 +00:00
upgrade(db) {
const store = db.createObjectStore('chats', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('timestamp', 'timestamp');
}
});
return {
db: DB,
getChatById: async function (id) {
2023-12-26 11:28:30 +00:00
const chat = await getChatById(localStorage.token, id);
return chat;
},
getChats: async function () {
2023-12-26 11:28:30 +00:00
const chats = await getChatlist(localStorage.token);
return chats;
},
2023-12-26 11:28:30 +00:00
createNewChat: async function (_chat) {
const chat = await createNewChat(localStorage.token, { ..._chat, timestamp: Date.now() });
console.log(chat);
2023-11-22 21:02:02 +00:00
await chats.set(await this.getChats());
2023-12-26 11:28:30 +00:00
return chat;
},
2023-12-26 11:28:30 +00:00
addChat: async function (chat) {
await this.db.put('chats', {
...chat
});
},
2023-12-26 11:28:30 +00:00
updateChatById: async function (id, updated) {
const chat = await updateChatById(localStorage.token, id, {
...updated,
timestamp: Date.now()
});
2023-11-22 21:02:02 +00:00
await chats.set(await this.getChats());
2023-12-26 11:28:30 +00:00
return chat;
},
deleteChatById: async function (id) {
2023-11-25 05:41:56 +00:00
if ($chatId === id) {
goto('/');
await chatId.set(uuidv4());
}
2023-12-26 11:28:30 +00:00
await deleteChatById(localStorage.token, id);
2023-11-22 21:02:02 +00:00
await chats.set(await this.getChats());
},
2023-12-26 11:28:30 +00:00
deleteAllChat: async function () {
const tx = this.db.transaction('chats', 'readwrite');
await Promise.all([tx.store.clear(), tx.done]);
2023-12-26 11:28:30 +00:00
await chats.set(await this.getChats());
},
exportChats: async function () {
let chats = await this.db.getAllFromIndex('chats', 'timestamp');
chats = chats.map((item, idx) => chats[chats.length - 1 - idx]);
return chats;
},
importChats: async function (_chats) {
for (const chat of _chats) {
console.log(chat);
await this.addChat(chat);
}
2023-11-22 21:02:02 +00:00
await chats.set(await this.getChats());
}
};
2023-11-20 01:47:07 +00:00
};
2023-12-26 11:28:30 +00:00
const setOllamaVersion = async () => {
const version = await getOllamaVersion(
$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
localStorage.token
).catch((error) => {
toast.error(error);
return '0';
});
2023-12-15 03:43:52 +00:00
2023-12-26 11:28:30 +00:00
await info.set({ ...$info, ollama: { version: version } });
2023-12-15 03:43:52 +00:00
if (
2023-12-26 11:28:30 +00:00
version.localeCompare(requiredOllamaVersion, undefined, {
2023-12-15 03:43:52 +00:00
numeric: true,
sensitivity: 'case',
caseFirst: 'upper'
}) < 0
) {
2023-12-26 11:28:30 +00:00
toast.error(`Ollama Version: ${version}`);
2023-12-15 03:43:52 +00:00
}
};
2023-11-19 05:41:43 +00:00
onMount(async () => {
2023-12-26 11:28:30 +00:00
if ($config && $user === undefined) {
2023-11-19 05:41:43 +00:00
await goto('/auth');
}
2023-12-15 03:43:52 +00:00
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
await models.set(await getModels());
2023-12-26 11:28:30 +00:00
2023-12-15 03:43:52 +00:00
await modelfiles.set(JSON.parse(localStorage.getItem('modelfiles') ?? '[]'));
2023-12-26 11:28:30 +00:00
modelfiles.subscribe(async () => {});
2023-12-15 03:43:52 +00:00
let _db = await getDB();
await db.set(_db);
2023-12-26 11:28:30 +00:00
await setOllamaVersion();
2023-12-15 03:43:52 +00:00
2023-11-19 05:41:43 +00:00
await tick();
loaded = true;
});
2023-12-26 11:28:30 +00:00
let child;
2023-11-19 00:47:12 +00:00
</script>
2023-11-19 05:41:43 +00:00
{#if loaded}
2023-12-15 03:43:52 +00:00
<div class="app relative">
2023-12-26 11:28:30 +00:00
{#if !['user', 'admin'].includes($user.role)}
<div class="absolute w-full h-full flex z-50">
<div
class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center"
>
<div class="m-auto pb-44 flex flex-col justify-center">
<div class="max-w-md">
<div class="text-center dark:text-white text-2xl font-medium z-50">
Account Activation Pending<br /> Contact Admin for WebUI Access
</div>
<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
Your account status is currently pending activation. To access the WebUI, please
reach out to the administrator. Admins can manage user statuses from the Admin
Panel.
</div>
<div class=" mt-6 mx-auto relative group w-fit">
<button
class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm"
on:click={async () => {
location.href = '/';
}}
>
Check Again
</button>
<button
class="text-xs text-center w-full mt-2 text-gray-400 underline"
on:click={async () => {
localStorage.removeItem('token');
location.href = '/auth';
}}>Sign Out</button
>
</div>
</div>
</div>
</div>
</div>
{:else if ($info?.ollama?.version ?? '0').localeCompare( requiredOllamaVersion, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' } ) < 0}
2023-12-15 03:43:52 +00:00
<div class="absolute w-full h-full flex z-50">
<div
class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center"
>
<div class="m-auto pb-44 flex flex-col justify-center">
<div class="max-w-md">
<div class="text-center dark:text-white text-2xl font-medium z-50">
Connection Issue or Update Needed
</div>
<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
Oops! It seems like your Ollama needs a little attention. <br
class=" hidden sm:flex"
/>We've detected either a connection hiccup or observed that you're using an older
version. Ensure you're on the latest Ollama version
<br class=" hidden sm:flex" />(version
<span class=" dark:text-white font-medium">{requiredOllamaVersion} or higher</span>)
or check your connection.
</div>
<div class=" mt-6 mx-auto relative group w-fit">
<button
class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm"
on:click={async () => {
await setOllamaVersion(await getOllamaVersion());
}}
>
Check Again
</button>
<button
class="text-xs text-center w-full mt-2 text-gray-400 underline"
on:click={async () => {
await setOllamaVersion(requiredOllamaVersion);
}}>Close</button
>
</div>
2023-12-15 03:43:52 +00:00
</div>
</div>
</div>
</div>
{/if}
2023-11-20 01:47:07 +00:00
<div
class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 min-h-screen overflow-auto flex flex-row"
>
<Sidebar />
<SettingsModal bind:show={$showSettings} />
<slot />
</div>
</div>
2023-11-19 00:47:12 +00:00
{/if}
2023-11-20 01:47:07 +00:00
<style>
.loading {
display: inline-block;
clip-path: inset(0 1ch 0 0);
animation: l 1s steps(3) infinite;
letter-spacing: -0.5px;
}
@keyframes l {
to {
clip-path: inset(0 -1ch 0 0);
}
}
pre[class*='language-'] {
position: relative;
overflow: auto;
/* make space */
margin: 5px 0;
padding: 1.75rem 0 1.75rem 1rem;
border-radius: 10px;
}
pre[class*='language-'] button {
position: absolute;
top: 5px;
right: 5px;
font-size: 0.9rem;
padding: 0.15rem;
background-color: #828282;
border: ridge 1px #7b7b7c;
border-radius: 5px;
text-shadow: #c4c4c4 0 0 2px;
}
pre[class*='language-'] button:hover {
cursor: pointer;
background-color: #bcbabb;
}
</style>