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';
|
|
|
|
|
2023-12-03 18:59:47 +00:00
|
|
|
import {
|
|
|
|
config,
|
2023-12-15 03:43:52 +00:00
|
|
|
info,
|
2023-12-03 18:59:47 +00:00
|
|
|
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 () => {
|
2023-12-03 18:59:47 +00:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
});
|
2023-11-21 17:39:11 +00:00
|
|
|
|
|
|
|
return {
|
2023-12-03 18:59:47 +00:00
|
|
|
db: DB,
|
2023-11-21 17:39:11 +00:00
|
|
|
getChatById: async function (id) {
|
2023-12-26 11:28:30 +00:00
|
|
|
const chat = await getChatById(localStorage.token, id);
|
|
|
|
return chat;
|
2023-11-21 17:39:11 +00:00
|
|
|
},
|
|
|
|
getChats: async function () {
|
2023-12-26 11:28:30 +00:00
|
|
|
const chats = await getChatlist(localStorage.token);
|
2023-11-21 17:39:11 +00:00
|
|
|
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-11-21 17:39:11 +00:00
|
|
|
},
|
2023-12-26 11:28:30 +00:00
|
|
|
|
2023-11-21 17:39:11 +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, {
|
2023-11-21 17:39:11 +00:00
|
|
|
...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;
|
2023-11-21 17:39:11 +00:00
|
|
|
},
|
|
|
|
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-11-21 17:39:11 +00:00
|
|
|
},
|
2023-12-26 11:28:30 +00:00
|
|
|
|
2023-11-21 17:39:11 +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-21 17:39:11 +00:00
|
|
|
}
|
|
|
|
};
|
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-03 18:59:47 +00:00
|
|
|
|
2023-12-26 11:28:30 +00:00
|
|
|
modelfiles.subscribe(async () => {});
|
2023-12-03 18:59:47 +00:00
|
|
|
|
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"
|
|
|
|
>
|
2023-12-19 19:57:49 +00:00
|
|
|
<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>
|