"web/vscode:/vscode.git/clone" did not exist on "235dce3977704d22c47ace893fc5890d0e63ffa7"
Unverified Commit 9bcd4ce5 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #3559 from open-webui/dev

0.3.8
parents 824966ad b38abf23
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
# all-features: false # all-features: false
# with-sources: false # with-sources: false
# generate-hashes: false # generate-hashes: false
# universal: false
-e file:. -e file:.
aiohttp==3.9.5 aiohttp==3.9.5
...@@ -31,7 +32,7 @@ asgiref==3.8.1 ...@@ -31,7 +32,7 @@ asgiref==3.8.1
# via opentelemetry-instrumentation-asgi # via opentelemetry-instrumentation-asgi
attrs==23.2.0 attrs==23.2.0
# via aiohttp # via aiohttp
authlib==1.3.0 authlib==1.3.1
# via open-webui # via open-webui
av==11.0.0 av==11.0.0
# via faster-whisper # via faster-whisper
...@@ -398,7 +399,6 @@ pandas==2.2.2 ...@@ -398,7 +399,6 @@ pandas==2.2.2
# via open-webui # via open-webui
passlib==1.7.4 passlib==1.7.4
# via open-webui # via open-webui
# via passlib
pathspec==0.12.1 pathspec==0.12.1
# via black # via black
pcodedmp==1.2.6 pcodedmp==1.2.6
...@@ -457,7 +457,6 @@ pygments==2.18.0 ...@@ -457,7 +457,6 @@ pygments==2.18.0
# via rich # via rich
pyjwt==2.8.0 pyjwt==2.8.0
# via open-webui # via open-webui
# via pyjwt
pymysql==1.1.0 pymysql==1.1.0
# via open-webui # via open-webui
pypandoc==1.13 pypandoc==1.13
...@@ -559,6 +558,9 @@ scipy==1.13.0 ...@@ -559,6 +558,9 @@ scipy==1.13.0
# via sentence-transformers # via sentence-transformers
sentence-transformers==2.7.0 sentence-transformers==2.7.0
# via open-webui # via open-webui
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
shapely==2.0.4 shapely==2.0.4
# via rapidocr-onnxruntime # via rapidocr-onnxruntime
shellingham==1.5.4 shellingham==1.5.4
...@@ -653,7 +655,6 @@ uvicorn==0.22.0 ...@@ -653,7 +655,6 @@ uvicorn==0.22.0
# via chromadb # via chromadb
# via fastapi # via fastapi
# via open-webui # via open-webui
# via uvicorn
uvloop==0.19.0 uvloop==0.19.0
# via uvicorn # via uvicorn
validators==0.28.1 validators==0.28.1
...@@ -681,6 +682,3 @@ youtube-transcript-api==0.6.2 ...@@ -681,6 +682,3 @@ youtube-transcript-api==0.6.2
# via open-webui # via open-webui
zipp==3.18.1 zipp==3.18.1
# via importlib-metadata # via importlib-metadata
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
@font-face { @font-face {
font-family: 'Arimo'; font-family: 'Inter';
src: url('/assets/fonts/Arimo-Variable.ttf'); src: url('/assets/fonts/Inter-Variable.ttf');
font-display: swap;
}
@font-face {
font-family: 'Archivo';
src: url('/assets/fonts/Archivo-Variable.ttf');
font-display: swap; font-display: swap;
} }
...@@ -32,6 +38,10 @@ math { ...@@ -32,6 +38,10 @@ math {
@apply underline; @apply underline;
} }
.font-primary {
font-family: 'Archivo', sans-serif;
}
iframe { iframe {
@apply rounded-lg; @apply rounded-lg;
} }
...@@ -140,3 +150,7 @@ input[type='number'] { ...@@ -140,3 +150,7 @@ input[type='number'] {
.cm-editor.cm-focused { .cm-editor.cm-focused {
outline: none; outline: none;
} }
.tippy-box[data-theme~='dark'] {
@apply rounded-lg bg-gray-950 text-xs border border-gray-900 shadow-xl;
}
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
// On page load or when changing themes, best to add inline in `head` to avoid FOUC // On page load or when changing themes, best to add inline in `head` to avoid FOUC
(() => { (() => {
if (localStorage?.theme && localStorage?.theme.includes('oled')) { if (localStorage?.theme && localStorage?.theme.includes('oled')) {
document.documentElement.style.setProperty('--color-gray-800', '#101010');
document.documentElement.style.setProperty('--color-gray-850', '#050505');
document.documentElement.style.setProperty('--color-gray-900', '#000000'); document.documentElement.style.setProperty('--color-gray-900', '#000000');
document.documentElement.style.setProperty('--color-gray-950', '#000000'); document.documentElement.style.setProperty('--color-gray-950', '#000000');
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
...@@ -80,13 +82,13 @@ ...@@ -80,13 +82,13 @@
id="logo" id="logo"
style=" style="
position: absolute; position: absolute;
width: 6rem; width: auto;
height: 6rem; height: 6rem;
top: 41%; top: 44%;
left: 50%; left: 50%;
margin-left: -3rem; margin-left: -3rem;
" "
src="/logo.svg" src="/static/splash.png"
/> />
<div <div
...@@ -105,8 +107,8 @@ ...@@ -105,8 +107,8 @@
> >
<img <img
id="logo-her" id="logo-her"
style="width: 13rem; height: 13rem" style="width: auto; height: 13rem"
src="/logo.svg" src="/static/splash.png"
class="animate-pulse-fast" class="animate-pulse-fast"
/> />
......
...@@ -32,6 +32,11 @@ type ChunkConfigForm = { ...@@ -32,6 +32,11 @@ type ChunkConfigForm = {
chunk_overlap: number; chunk_overlap: number;
}; };
type ContentExtractConfigForm = {
engine: string;
tika_server_url: string | null;
};
type YoutubeConfigForm = { type YoutubeConfigForm = {
language: string[]; language: string[];
translation?: string | null; translation?: string | null;
...@@ -40,6 +45,7 @@ type YoutubeConfigForm = { ...@@ -40,6 +45,7 @@ type YoutubeConfigForm = {
type RAGConfigForm = { type RAGConfigForm = {
pdf_extract_images?: boolean; pdf_extract_images?: boolean;
chunk?: ChunkConfigForm; chunk?: ChunkConfigForm;
content_extraction?: ContentExtractConfigForm;
web_loader_ssl_verification?: boolean; web_loader_ssl_verification?: boolean;
youtube?: YoutubeConfigForm; youtube?: YoutubeConfigForm;
}; };
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<Modal bind:show> <Modal bind:show>
<div class="px-5 pt-4 dark:text-gray-300 text-gray-700"> <div class="px-5 pt-4 dark:text-gray-300 text-gray-700">
<div class="flex justify-between items-start"> <div class="flex justify-between items-start">
<div class="text-xl font-bold"> <div class="text-xl font-semibold">
{$i18n.t('What’s New in')} {$i18n.t('What’s New in')}
{$WEBUI_NAME} {$WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} /> <Confetti x={[-1, -0.25]} y={[0, 0.5]} />
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
{#if changelog} {#if changelog}
{#each Object.keys(changelog) as version} {#each Object.keys(changelog) as version}
<div class=" mb-3 pr-2"> <div class=" mb-3 pr-2">
<div class="font-bold text-xl mb-1 dark:text-white"> <div class="font-semibold text-xl mb-1 dark:text-white">
v{version} - {changelog[version].date} v{version} - {changelog[version].date}
</div> </div>
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
{#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section} {#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
<div class=""> <div class="">
<div <div
class="font-bold uppercase text-xs {section === 'added' class="font-semibold uppercase text-xs {section === 'added'
? 'text-white bg-blue-600' ? 'text-white bg-blue-600'
: section === 'fixed' : section === 'fixed'
? 'text-white bg-green-600' ? 'text-white bg-green-600'
......
<script> <script>
import { getContext, tick } from 'svelte'; import { getContext, tick, onMount } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import Database from './Settings/Database.svelte'; import Database from './Settings/Database.svelte';
...@@ -21,17 +21,31 @@ ...@@ -21,17 +21,31 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
let selectedTab = 'general'; let selectedTab = 'general';
onMount(() => {
const containerElement = document.getElementById('admin-settings-tabs-container');
if (containerElement) {
containerElement.addEventListener('wheel', function (event) {
if (event.deltaY !== 0) {
// Adjust horizontal scroll position based on vertical scroll
containerElement.scrollLeft += event.deltaY;
}
});
}
});
</script> </script>
<div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4"> <div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
<div <div
id="admin-settings-tabs-container"
class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none" class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
> >
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
'general' 'general'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'general'; selectedTab = 'general';
}} }}
...@@ -56,8 +70,8 @@ ...@@ -56,8 +70,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'users' 'users'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'users'; selectedTab = 'users';
}} }}
...@@ -80,8 +94,8 @@ ...@@ -80,8 +94,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'connections' 'connections'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'connections'; selectedTab = 'connections';
}} }}
...@@ -104,8 +118,8 @@ ...@@ -104,8 +118,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'models' 'models'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'models'; selectedTab = 'models';
}} }}
...@@ -130,8 +144,8 @@ ...@@ -130,8 +144,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'documents' 'documents'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'documents'; selectedTab = 'documents';
}} }}
...@@ -160,8 +174,8 @@ ...@@ -160,8 +174,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'web' 'web'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'web'; selectedTab = 'web';
}} }}
...@@ -184,8 +198,8 @@ ...@@ -184,8 +198,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'interface' 'interface'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'interface'; selectedTab = 'interface';
}} }}
...@@ -210,8 +224,8 @@ ...@@ -210,8 +224,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'audio' 'audio'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'audio'; selectedTab = 'audio';
}} }}
...@@ -237,8 +251,8 @@ ...@@ -237,8 +251,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'images' 'images'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'images'; selectedTab = 'images';
}} }}
...@@ -263,8 +277,8 @@ ...@@ -263,8 +277,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'pipelines' 'pipelines'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'pipelines'; selectedTab = 'pipelines';
}} }}
...@@ -293,8 +307,8 @@ ...@@ -293,8 +307,8 @@
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'db' 'db'
? 'bg-gray-200 dark:bg-gray-800' ? 'bg-gray-100 dark:bg-gray-800'
: ' hover:bg-gray-300 dark:hover:bg-gray-850'}" : ' hover:bg-gray-50 dark:hover:bg-gray-850'}"
on:click={() => { on:click={() => {
selectedTab = 'db'; selectedTab = 'db';
}} }}
......
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
<div> <div>
<div class="mt-1 flex gap-2 mb-1"> <div class="mt-1 flex gap-2 mb-1">
<input <input
class="flex-1 w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="flex-1 w-full rounded-l-lg py-2 pl-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
bind:value={STT_OPENAI_API_BASE_URL} bind:value={STT_OPENAI_API_BASE_URL}
required required
...@@ -156,7 +156,7 @@ ...@@ -156,7 +156,7 @@
<div class="flex-1"> <div class="flex-1">
<input <input
list="model-list" list="model-list"
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={STT_MODEL} bind:value={STT_MODEL}
placeholder="Select a model" placeholder="Select a model"
/> />
...@@ -203,7 +203,7 @@ ...@@ -203,7 +203,7 @@
<div> <div>
<div class="mt-1 flex gap-2 mb-1"> <div class="mt-1 flex gap-2 mb-1">
<input <input
class="flex-1 w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="flex-1 w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
bind:value={TTS_OPENAI_API_BASE_URL} bind:value={TTS_OPENAI_API_BASE_URL}
required required
...@@ -222,7 +222,7 @@ ...@@ -222,7 +222,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={TTS_VOICE} bind:value={TTS_VOICE}
> >
<option value="" selected={TTS_VOICE !== ''}>{$i18n.t('Default')}</option> <option value="" selected={TTS_VOICE !== ''}>{$i18n.t('Default')}</option>
...@@ -245,7 +245,7 @@ ...@@ -245,7 +245,7 @@
<div class="flex-1"> <div class="flex-1">
<input <input
list="voice-list" list="voice-list"
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={TTS_VOICE} bind:value={TTS_VOICE}
placeholder="Select a voice" placeholder="Select a voice"
/> />
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
<div class="flex-1"> <div class="flex-1">
<input <input
list="model-list" list="model-list"
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={TTS_MODEL} bind:value={TTS_MODEL}
placeholder="Select a model" placeholder="Select a model"
/> />
......
...@@ -200,7 +200,7 @@ ...@@ -200,7 +200,7 @@
<input <input
class="w-full rounded-lg py-2 px-4 {pipelineUrls[url] class="w-full rounded-lg py-2 px-4 {pipelineUrls[url]
? 'pr-8' ? 'pr-8'
: ''} text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" : ''} text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
bind:value={url} bind:value={url}
autocomplete="off" autocomplete="off"
...@@ -338,7 +338,7 @@ ...@@ -338,7 +338,7 @@
{#each OLLAMA_BASE_URLS as url, idx} {#each OLLAMA_BASE_URLS as url, idx}
<div class="flex gap-1.5"> <div class="flex gap-1.5">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')} placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')}
bind:value={url} bind:value={url}
/> />
......
...@@ -37,6 +37,10 @@ ...@@ -37,6 +37,10 @@
let embeddingModel = ''; let embeddingModel = '';
let rerankingModel = ''; let rerankingModel = '';
let contentExtractionEngine = 'default';
let tikaServerUrl = '';
let showTikaServerUrl = false;
let chunkSize = 0; let chunkSize = 0;
let chunkOverlap = 0; let chunkOverlap = 0;
let pdfExtractImages = true; let pdfExtractImages = true;
...@@ -163,11 +167,20 @@ ...@@ -163,11 +167,20 @@
rerankingModelUpdateHandler(); rerankingModelUpdateHandler();
} }
if (contentExtractionEngine === 'tika' && tikaServerUrl === '') {
toast.error($i18n.t('Tika Server URL required.'));
return;
}
const res = await updateRAGConfig(localStorage.token, { const res = await updateRAGConfig(localStorage.token, {
pdf_extract_images: pdfExtractImages, pdf_extract_images: pdfExtractImages,
chunk: { chunk: {
chunk_overlap: chunkOverlap, chunk_overlap: chunkOverlap,
chunk_size: chunkSize chunk_size: chunkSize
},
content_extraction: {
engine: contentExtractionEngine,
tika_server_url: tikaServerUrl
} }
}); });
...@@ -213,6 +226,10 @@ ...@@ -213,6 +226,10 @@
chunkSize = res.chunk.chunk_size; chunkSize = res.chunk.chunk_size;
chunkOverlap = res.chunk.chunk_overlap; chunkOverlap = res.chunk.chunk_overlap;
contentExtractionEngine = res.content_extraction.engine;
tikaServerUrl = res.content_extraction.tika_server_url;
showTikaServerUrl = contentExtractionEngine === 'tika';
} }
}); });
</script> </script>
...@@ -262,7 +279,7 @@ ...@@ -262,7 +279,7 @@
</div> </div>
<button <button
class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-lg flex flex-row space-x-1 items-center {scanDirLoading class=" self-center text-xs p-1 px-3 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-lg flex flex-row space-x-1 items-center {scanDirLoading
? ' cursor-not-allowed' ? ' cursor-not-allowed'
: ''}" : ''}"
on:click={() => { on:click={() => {
...@@ -335,7 +352,7 @@ ...@@ -335,7 +352,7 @@
{#if embeddingEngine === 'openai'} {#if embeddingEngine === 'openai'}
<div class="my-0.5 flex gap-2"> <div class="my-0.5 flex gap-2">
<input <input
class="flex-1 w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="flex-1 w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
bind:value={OpenAIUrl} bind:value={OpenAIUrl}
required required
...@@ -388,7 +405,7 @@ ...@@ -388,7 +405,7 @@
</div> </div>
</div> </div>
<hr class=" dark:border-gray-850 my-1" /> <hr class="dark:border-gray-850" />
<div class="space-y-2" /> <div class="space-y-2" />
<div> <div>
...@@ -398,7 +415,7 @@ ...@@ -398,7 +415,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={embeddingModel} bind:value={embeddingModel}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
required required
...@@ -407,7 +424,7 @@ ...@@ -407,7 +424,7 @@
<option value="" disabled selected>{$i18n.t('Select a model')}</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if} {/if}
{#each $models.filter((m) => m.id && m.ollama && !(m?.preset ?? false)) as model} {#each $models.filter((m) => m.id && m.ollama && !(m?.preset ?? false)) as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option> <option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
{/each} {/each}
</select> </select>
</div> </div>
...@@ -416,7 +433,7 @@ ...@@ -416,7 +433,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Set embedding model (e.g. {{model}})', { placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
model: embeddingModel.slice(-40) model: embeddingModel.slice(-40)
})} })}
...@@ -426,7 +443,7 @@ ...@@ -426,7 +443,7 @@
{#if embeddingEngine === ''} {#if embeddingEngine === ''}
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 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={() => { on:click={() => {
embeddingModelUpdateHandler(); embeddingModelUpdateHandler();
}} }}
...@@ -495,7 +512,7 @@ ...@@ -495,7 +512,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Set reranking model (e.g. {{model}})', { placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
model: 'BAAI/bge-reranker-v2-m3' model: 'BAAI/bge-reranker-v2-m3'
})} })}
...@@ -503,7 +520,7 @@ ...@@ -503,7 +520,7 @@
/> />
</div> </div>
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 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={() => { on:click={() => {
rerankingModelUpdateHandler(); rerankingModelUpdateHandler();
}} }}
...@@ -562,6 +579,39 @@ ...@@ -562,6 +579,39 @@
<hr class=" dark:border-gray-850" /> <hr class=" dark:border-gray-850" />
<div class="">
<div class="text-sm font-medium">{$i18n.t('Content Extraction')}</div>
<div class="flex w-full justify-between mt-2">
<div class="self-center text-xs font-medium">{$i18n.t('Engine')}</div>
<div class="flex items-center relative">
<select
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
bind:value={contentExtractionEngine}
on:change={(e) => {
showTikaServerUrl = e.target.value === 'tika';
}}
>
<option value="">{$i18n.t('Default')} </option>
<option value="tika">{$i18n.t('Tika')}</option>
</select>
</div>
</div>
{#if showTikaServerUrl}
<div class="flex w-full mt-2">
<div class="flex-1 mr-2">
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Tika Server URL')}
bind:value={tikaServerUrl}
/>
</div>
</div>
{/if}
</div>
<hr class=" dark:border-gray-850" />
<div class=" "> <div class=" ">
<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div> <div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
...@@ -571,7 +621,7 @@ ...@@ -571,7 +621,7 @@
<div class="self-center p-3"> <div class="self-center p-3">
<input <input
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number" type="number"
placeholder={$i18n.t('Enter Top K')} placeholder={$i18n.t('Enter Top K')}
bind:value={querySettings.k} bind:value={querySettings.k}
...@@ -589,7 +639,7 @@ ...@@ -589,7 +639,7 @@
<div class="self-center p-3"> <div class="self-center p-3">
<input <input
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number" type="number"
step="0.01" step="0.01"
placeholder={$i18n.t('Enter Score')} placeholder={$i18n.t('Enter Score')}
...@@ -617,7 +667,7 @@ ...@@ -617,7 +667,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
<textarea <textarea
bind:value={querySettings.template} bind:value={querySettings.template}
class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg px-4 py-3 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
rows="4" rows="4"
/> />
</div> </div>
...@@ -633,7 +683,7 @@ ...@@ -633,7 +683,7 @@
<div class="self-center text-xs font-medium min-w-fit mb-1">{$i18n.t('Chunk Size')}</div> <div class="self-center text-xs font-medium min-w-fit mb-1">{$i18n.t('Chunk Size')}</div>
<div class="self-center"> <div class="self-center">
<input <input
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number" type="number"
placeholder={$i18n.t('Enter Chunk Size')} placeholder={$i18n.t('Enter Chunk Size')}
bind:value={chunkSize} bind:value={chunkSize}
...@@ -650,7 +700,7 @@ ...@@ -650,7 +700,7 @@
<div class="self-center"> <div class="self-center">
<input <input
class="w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number" type="number"
placeholder={$i18n.t('Enter Chunk Overlap')} placeholder={$i18n.t('Enter Chunk Overlap')}
bind:value={chunkOverlap} bind:value={chunkOverlap}
......
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
<div class="flex mt-2 space-x-2"> <div class="flex mt-2 space-x-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={`e.g.) "30m","1h", "10d". `} placeholder={`e.g.) "30m","1h", "10d". `}
bind:value={adminConfig.JWT_EXPIRES_IN} bind:value={adminConfig.JWT_EXPIRES_IN}
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
<div class="flex mt-2 space-x-2"> <div class="flex mt-2 space-x-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={`https://example.com/webhook`} placeholder={`https://example.com/webhook`}
bind:value={webhookUrl} bind:value={webhookUrl}
......
...@@ -240,13 +240,13 @@ ...@@ -240,13 +240,13 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('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} bind:value={AUTOMATIC1111_BASE_URL}
/> />
</div> </div>
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
type="button" type="button"
on:click={() => { on:click={() => {
updateUrlHandler(); updateUrlHandler();
...@@ -299,13 +299,13 @@ ...@@ -299,13 +299,13 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('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={COMFYUI_BASE_URL} bind:value={COMFYUI_BASE_URL}
/> />
</div> </div>
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
type="button" type="button"
on:click={() => { on:click={() => {
updateUrlHandler(); updateUrlHandler();
...@@ -331,7 +331,7 @@ ...@@ -331,7 +331,7 @@
<div class="flex gap-2 mb-1"> <div class="flex gap-2 mb-1">
<input <input
class="flex-1 w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="flex-1 w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
bind:value={OPENAI_API_BASE_URL} bind:value={OPENAI_API_BASE_URL}
required required
...@@ -354,7 +354,7 @@ ...@@ -354,7 +354,7 @@
<div class="flex-1"> <div class="flex-1">
<input <input
list="model-list" list="model-list"
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedModel} bind:value={selectedModel}
placeholder="Select a model" placeholder="Select a model"
/> />
...@@ -368,7 +368,7 @@ ...@@ -368,7 +368,7 @@
</div> </div>
{:else} {:else}
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedModel} bind:value={selectedModel}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
required required
...@@ -391,7 +391,7 @@ ...@@ -391,7 +391,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')} placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
bind:value={imageSize} bind:value={imageSize}
/> />
...@@ -404,7 +404,7 @@ ...@@ -404,7 +404,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')} placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
bind:value={steps} bind:value={steps}
/> />
......
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
<div class="flex-1"> <div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('Local Models')}</div> <div class=" text-xs mb-1">{$i18n.t('Local Models')}</div>
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={taskConfig.TASK_MODEL} bind:value={taskConfig.TASK_MODEL}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
> >
...@@ -104,7 +104,7 @@ ...@@ -104,7 +104,7 @@
<div class="flex-1"> <div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('External Models')}</div> <div class=" text-xs mb-1">{$i18n.t('External Models')}</div>
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={taskConfig.TASK_MODEL_EXTERNAL} bind:value={taskConfig.TASK_MODEL_EXTERNAL}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
> >
...@@ -122,7 +122,7 @@ ...@@ -122,7 +122,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
<textarea <textarea
bind:value={taskConfig.TITLE_GENERATION_PROMPT_TEMPLATE} bind:value={taskConfig.TITLE_GENERATION_PROMPT_TEMPLATE}
class="w-full rounded-lg py-3 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg py-3 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
rows="6" rows="6"
/> />
</div> </div>
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Search Query Generation Prompt')}</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Search Query Generation Prompt')}</div>
<textarea <textarea
bind:value={taskConfig.SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE} bind:value={taskConfig.SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE}
class="w-full rounded-lg py-3 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg py-3 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
rows="6" rows="6"
/> />
</div> </div>
...@@ -142,7 +142,7 @@ ...@@ -142,7 +142,7 @@
</div> </div>
<input <input
bind:value={taskConfig.SEARCH_QUERY_PROMPT_LENGTH_THRESHOLD} bind:value={taskConfig.SEARCH_QUERY_PROMPT_LENGTH_THRESHOLD}
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
type="number" type="number"
/> />
</div> </div>
...@@ -273,24 +273,26 @@ ...@@ -273,24 +273,26 @@
</div> </div>
<div class="grid lg:grid-cols-2 flex-col gap-1.5"> <div class="grid lg:grid-cols-2 flex-col gap-1.5">
{#each promptSuggestions as prompt, promptIdx} {#each promptSuggestions as prompt, promptIdx}
<div class=" flex dark:bg-gray-850 rounded-xl py-1.5"> <div
class=" flex border border-gray-100 dark:border-none dark:bg-gray-850 rounded-xl py-1.5"
>
<div class="flex flex-col flex-1 pl-1"> <div class="flex flex-col flex-1 pl-1">
<div class="flex border-b dark:border-gray-800 w-full"> <div class="flex border-b border-gray-100 dark:border-gray-800 w-full">
<input <input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-800" class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')} placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')}
bind:value={prompt.title[0]} bind:value={prompt.title[0]}
/> />
<input <input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-800" class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')} placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')}
bind:value={prompt.title[1]} bind:value={prompt.title[1]}
/> />
</div> </div>
<input <input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-800" class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')} placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')}
bind:value={prompt.content} bind:value={prompt.content}
/> />
......
...@@ -158,12 +158,14 @@ ...@@ -158,12 +158,14 @@
return; return;
} }
const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch( const [res, controller] = await pullModel(
(error) => { localStorage.token,
toast.error(error); sanitizedModelTag,
return null; selectedOllamaUrlIdx
} ).catch((error) => {
); toast.error(error);
return null;
});
if (res) { if (res) {
const reader = res.body const reader = res.body
...@@ -570,12 +572,12 @@ ...@@ -570,12 +572,12 @@
<div class="flex gap-2"> <div class="flex gap-2">
<div class="flex-1 pb-1"> <div class="flex-1 pb-1">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedOllamaUrlIdx} bind:value={selectedOllamaUrlIdx}
placeholder={$i18n.t('Select an Ollama instance')} placeholder={$i18n.t('Select an Ollama instance')}
> >
{#each OLLAMA_URLS as url, idx} {#each OLLAMA_URLS as url, idx}
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option> <option value={idx} class="bg-gray-50 dark:bg-gray-700">{url}</option>
{/each} {/each}
</select> </select>
</div> </div>
...@@ -584,7 +586,7 @@ ...@@ -584,7 +586,7 @@
<div class="flex w-full justify-end"> <div class="flex w-full justify-end">
<Tooltip content="Update All Models" placement="top"> <Tooltip content="Update All Models" placement="top">
<button <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" class="p-2.5 flex gap-2 items-center bg-gray-50 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={() => { on:click={() => {
updateModelsHandler(); updateModelsHandler();
}} }}
...@@ -619,7 +621,7 @@ ...@@ -619,7 +621,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', { placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
modelTag: 'mistral:7b' modelTag: 'mistral:7b'
})} })}
...@@ -627,7 +629,7 @@ ...@@ -627,7 +629,7 @@
/> />
</div> </div>
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 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={() => { on:click={() => {
pullModelHandler(); pullModelHandler();
}} }}
...@@ -753,7 +755,7 @@ ...@@ -753,7 +755,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteModelTag} bind:value={deleteModelTag}
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
> >
...@@ -761,7 +763,7 @@ ...@@ -761,7 +763,7 @@
<option value="" disabled selected>{$i18n.t('Select a model')}</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if} {/if}
{#each $models.filter((m) => !(m?.preset ?? false) && m.owned_by === 'ollama' && (selectedOllamaUrlIdx === null ? true : (m?.ollama?.urls ?? []).includes(selectedOllamaUrlIdx))) as model} {#each $models.filter((m) => !(m?.preset ?? false) && m.owned_by === 'ollama' && (selectedOllamaUrlIdx === null ? true : (m?.ollama?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700" <option value={model.name} class="bg-gray-50 dark:bg-gray-700"
>{model.name + >{model.name +
' (' + ' (' +
(model.ollama.size / 1024 ** 3).toFixed(1) + (model.ollama.size / 1024 ** 3).toFixed(1) +
...@@ -771,7 +773,7 @@ ...@@ -771,7 +773,7 @@
</select> </select>
</div> </div>
<button <button
class="px-2.5 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" class="px-2.5 bg-gray-50 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={() => { on:click={() => {
showModelDeleteConfirm = true; showModelDeleteConfirm = true;
}} }}
...@@ -797,7 +799,7 @@ ...@@ -797,7 +799,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2 flex flex-col gap-2"> <div class="flex-1 mr-2 flex flex-col gap-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', { placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
modelTag: 'my-modelfile' modelTag: 'my-modelfile'
})} })}
...@@ -807,7 +809,7 @@ ...@@ -807,7 +809,7 @@
<textarea <textarea
bind:value={createModelContent} bind:value={createModelContent}
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-100 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none scrollbar-hidden" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none scrollbar-hidden"
rows="6" rows="6"
placeholder={`TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`} placeholder={`TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`}
disabled={createModelLoading} disabled={createModelLoading}
...@@ -816,7 +818,7 @@ ...@@ -816,7 +818,7 @@
<div class="flex self-start"> <div class="flex self-start">
<button <button
class="px-2.5 py-2.5 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 disabled:cursor-not-allowed" class="px-2.5 py-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition disabled:cursor-not-allowed"
on:click={() => { on:click={() => {
createModelHandler(); createModelHandler();
}} }}
...@@ -925,7 +927,7 @@ ...@@ -925,7 +927,7 @@
<button <button
type="button" type="button"
class="w-full rounded-lg text-left py-2 px-4 bg-white dark:text-gray-300 dark:bg-gray-850" class="w-full rounded-lg text-left py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
on:click={() => { on:click={() => {
modelUploadInputElement.click(); modelUploadInputElement.click();
}} }}
...@@ -940,7 +942,7 @@ ...@@ -940,7 +942,7 @@
{:else} {:else}
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}"> <div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
<input <input
class="w-full rounded-lg text-left py-2 px-4 bg-white dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !== class="w-full rounded-lg text-left py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
'' ''
? 'mr-2' ? 'mr-2'
: ''}" : ''}"
...@@ -955,7 +957,7 @@ ...@@ -955,7 +957,7 @@
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<button <button
class="px-2.5 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 disabled:cursor-not-allowed transition" class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg disabled:cursor-not-allowed transition"
type="submit" type="submit"
disabled={modelTransferring} disabled={modelTransferring}
> >
...@@ -1014,7 +1016,7 @@ ...@@ -1014,7 +1016,7 @@
<div class=" my-2.5 text-sm font-medium">{$i18n.t('Modelfile Content')}</div> <div class=" my-2.5 text-sm font-medium">{$i18n.t('Modelfile Content')}</div>
<textarea <textarea
bind:value={modelFileContent} bind:value={modelFileContent}
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-100 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none"
rows="6" rows="6"
/> />
</div> </div>
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
} from '$lib/apis'; } from '$lib/apis';
import Spinner from '$lib/components/common/Spinner.svelte'; import Spinner from '$lib/components/common/Spinner.svelte';
import Switch from '$lib/components/common/Switch.svelte';
const i18n: Writable<i18nType> = getContext('i18n'); const i18n: Writable<i18nType> = getContext('i18n');
...@@ -213,7 +214,7 @@ ...@@ -213,7 +214,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
<div class="flex-1"> <div class="flex-1">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelinesUrlIdx} bind:value={selectedPipelinesUrlIdx}
placeholder={$i18n.t('Select a pipeline url')} placeholder={$i18n.t('Select a pipeline url')}
on:change={async () => { on:change={async () => {
...@@ -327,7 +328,7 @@ ...@@ -327,7 +328,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Github Raw URL')} placeholder={$i18n.t('Enter Github Raw URL')}
bind:value={pipelineDownloadUrl} bind:value={pipelineDownloadUrl}
/> />
...@@ -411,7 +412,7 @@ ...@@ -411,7 +412,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
<div class="flex-1"> <div class="flex-1">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelineIdx} bind:value={selectedPipelineIdx}
placeholder={$i18n.t('Select a pipeline')} placeholder={$i18n.t('Select a pipeline')}
on:change={async () => { on:change={async () => {
...@@ -476,15 +477,40 @@ ...@@ -476,15 +477,40 @@
</div> </div>
{#if (valves[property] ?? null) !== null} {#if (valves[property] ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <!-- {valves[property]} -->
<div class="flex mt-0.5 mb-1.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input {#if valves_spec.properties[property]?.enum ?? null}
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" <select
type="text" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={valves_spec.properties[property].title} bind:value={valves[property]}
bind:value={valves[property]} >
autocomplete="off" {#each valves_spec.properties[property].enum as option}
/> <option value={option} selected={option === valves[property]}>
{option}
</option>
{/each}
</select>
{:else if (valves_spec.properties[property]?.type ?? null) === 'boolean'}
<div class="flex justify-between items-center">
<div class="text-xs text-gray-500">
{valves[property] ? 'Enabled' : 'Disabled'}
</div>
<div class=" pr-2">
<Switch bind:state={valves[property]} />
</div>
</div>
{:else}
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={valves_spec.properties[property].title}
bind:value={valves[property]}
autocomplete="off"
required
/>
{/if}
</div> </div>
</div> </div>
{/if} {/if}
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={defaultModelId} bind:value={defaultModelId}
placeholder="Select a model" placeholder="Select a model"
> >
...@@ -140,7 +140,7 @@ ...@@ -140,7 +140,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={modelId} bind:value={modelId}
placeholder="Select a model" placeholder="Select a model"
> >
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter Searxng Query URL')} placeholder={$i18n.t('Enter Searxng Query URL')}
bind:value={webConfig.search.searxng_query_url} bind:value={webConfig.search.searxng_query_url}
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter Google PSE Engine Id')} placeholder={$i18n.t('Enter Google PSE Engine Id')}
bind:value={webConfig.search.google_pse_engine_id} bind:value={webConfig.search.google_pse_engine_id}
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
</div> </div>
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Search Result Count')} placeholder={$i18n.t('Search Result Count')}
bind:value={webConfig.search.result_count} bind:value={webConfig.search.result_count}
required required
...@@ -218,7 +218,7 @@ ...@@ -218,7 +218,7 @@
</div> </div>
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Concurrent Requests')} placeholder={$i18n.t('Concurrent Requests')}
bind:value={webConfig.search.concurrent_requests} bind:value={webConfig.search.concurrent_requests}
required required
...@@ -267,7 +267,7 @@ ...@@ -267,7 +267,7 @@
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div> <div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
<div class=" flex-1 self-center"> <div class=" flex-1 self-center">
<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 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter language codes')} placeholder={$i18n.t('Enter language codes')}
bind:value={youtubeLanguage} bind:value={youtubeLanguage}
......
...@@ -60,19 +60,26 @@ ...@@ -60,19 +60,26 @@
import Navbar from '$lib/components/layout/Navbar.svelte'; import Navbar from '$lib/components/layout/Navbar.svelte';
import CallOverlay from './MessageInput/CallOverlay.svelte'; import CallOverlay from './MessageInput/CallOverlay.svelte';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import ChatControls from './ChatControls.svelte';
import EventConfirmDialog from '../common/ConfirmDialog.svelte';
const i18n: Writable<i18nType> = getContext('i18n'); const i18n: Writable<i18nType> = getContext('i18n');
export let chatIdProp = ''; export let chatIdProp = '';
let loaded = false; let loaded = false;
const eventTarget = new EventTarget(); const eventTarget = new EventTarget();
let showControls = false;
let stopResponseFlag = false; let stopResponseFlag = false;
let autoScroll = true; let autoScroll = true;
let processing = ''; let processing = '';
let messagesContainerElement: HTMLDivElement; let messagesContainerElement: HTMLDivElement;
let showEventConfirmation = false;
let eventConfirmationTitle = '';
let eventConfirmationMessage = '';
let eventCallback = null;
let showModelSelector = true; let showModelSelector = true;
let selectedModels = ['']; let selectedModels = [''];
...@@ -96,6 +103,8 @@ ...@@ -96,6 +103,8 @@
currentId: null currentId: null
}; };
let params = {};
$: if (history.currentId !== null) { $: if (history.currentId !== null) {
let _messages = []; let _messages = [];
...@@ -126,6 +135,41 @@ ...@@ -126,6 +135,41 @@
})(); })();
} }
const chatEventHandler = async (event, cb) => {
if (event.chat_id === $chatId) {
await tick();
console.log(event);
let message = history.messages[event.message_id];
const type = event?.data?.type ?? null;
const data = event?.data?.data ?? null;
if (type === 'status') {
if (message?.statusHistory) {
message.statusHistory.push(data);
} else {
message.statusHistory = [data];
}
} else if (type === 'citation') {
if (message?.citations) {
message.citations.push(data);
} else {
message.citations = [data];
}
} else if (type === 'confirmation') {
eventCallback = cb;
showEventConfirmation = true;
eventConfirmationTitle = data.title;
eventConfirmationMessage = data.message;
} else {
console.log('Unknown message type', data);
}
messages = messages;
}
};
onMount(async () => { onMount(async () => {
const onMessageHandler = async (event) => { const onMessageHandler = async (event) => {
if (event.origin === window.origin) { if (event.origin === window.origin) {
...@@ -163,6 +207,8 @@ ...@@ -163,6 +207,8 @@
}; };
window.addEventListener('message', onMessageHandler); window.addEventListener('message', onMessageHandler);
$socket.on('chat-events', chatEventHandler);
if (!$chatId) { if (!$chatId) {
chatId.subscribe(async (value) => { chatId.subscribe(async (value) => {
if (!value) { if (!value) {
...@@ -177,6 +223,8 @@ ...@@ -177,6 +223,8 @@
return () => { return () => {
window.removeEventListener('message', onMessageHandler); window.removeEventListener('message', onMessageHandler);
$socket.off('chat-events');
}; };
}); });
...@@ -196,6 +244,7 @@ ...@@ -196,6 +244,7 @@
messages: {}, messages: {},
currentId: null currentId: null
}; };
params = {};
if ($page.url.searchParams.get('models')) { if ($page.url.searchParams.get('models')) {
selectedModels = $page.url.searchParams.get('models')?.split(','); selectedModels = $page.url.searchParams.get('models')?.split(',');
...@@ -265,11 +314,7 @@ ...@@ -265,11 +314,7 @@
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
} }
await settings.set({ params = chatContent?.params ?? {};
...$settings,
system: chatContent.system ?? $settings.system,
params: chatContent.options ?? $settings.params
});
autoScroll = true; autoScroll = true;
await tick(); await tick();
...@@ -302,7 +347,7 @@ ...@@ -302,7 +347,7 @@
} }
}; };
const chatCompletedHandler = async (modelId, messages) => { const chatCompletedHandler = async (modelId, responseMessageId, messages) => {
await mermaid.run({ await mermaid.run({
querySelector: '.mermaid' querySelector: '.mermaid'
}); });
...@@ -316,7 +361,9 @@ ...@@ -316,7 +361,9 @@
info: m.info ? m.info : undefined, info: m.info ? m.info : undefined,
timestamp: m.timestamp timestamp: m.timestamp
})), })),
chat_id: $chatId chat_id: $chatId,
session_id: $socket?.id,
id: responseMessageId
}).catch((error) => { }).catch((error) => {
toast.error(error); toast.error(error);
messages.at(-1).error = { content: error }; messages.at(-1).error = { content: error };
...@@ -480,9 +527,7 @@ ...@@ -480,9 +527,7 @@
title: $i18n.t('New Chat'), title: $i18n.t('New Chat'),
models: selectedModels, models: selectedModels,
system: $settings.system ?? undefined, system: $settings.system ?? undefined,
options: { params: params,
...($settings.params ?? {})
},
messages: messages, messages: messages,
history: history, history: history,
tags: [], tags: [],
...@@ -580,11 +625,11 @@ ...@@ -580,11 +625,11 @@
scrollToBottom(); scrollToBottom();
const messagesBody = [ const messagesBody = [
$settings.system || (responseMessage?.userContext ?? null) params?.system || $settings.system || (responseMessage?.userContext ?? null)
? { ? {
role: 'system', role: 'system',
content: `${promptTemplate( content: `${promptTemplate(
$settings?.system ?? '', params?.system ?? $settings?.system ?? '',
$user.name, $user.name,
$settings?.userLocation $settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token) ? await getAndUpdateUserLocation(localStorage.token)
...@@ -665,25 +710,28 @@ ...@@ -665,25 +710,28 @@
await tick(); await tick();
const [res, controller] = await generateChatCompletion(localStorage.token, { const [res, controller] = await generateChatCompletion(localStorage.token, {
stream: true,
model: model.id, model: model.id,
messages: messagesBody, messages: messagesBody,
options: { options: {
...($settings.params ?? {}), ...(params ?? $settings.params ?? {}),
stop: stop:
$settings?.params?.stop ?? undefined params?.stop ?? $settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) => ? (params?.stop ?? $settings.params.stop).map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
) )
: undefined, : undefined,
num_predict: $settings?.params?.max_tokens ?? undefined, num_predict: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
repeat_penalty: $settings?.params?.frequency_penalty ?? undefined repeat_penalty:
params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined
}, },
format: $settings.requestFormat ?? undefined, format: $settings.requestFormat ?? undefined,
keep_alive: $settings.keepAlive ?? undefined, keep_alive: $settings.keepAlive ?? undefined,
tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined, tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
files: files.length > 0 ? files : undefined, files: files.length > 0 ? files : undefined,
citations: files.length > 0 ? true : undefined, session_id: $socket?.id,
chat_id: $chatId chat_id: $chatId,
id: responseMessageId
}); });
if (res && res.ok) { if (res && res.ok) {
...@@ -704,7 +752,7 @@ ...@@ -704,7 +752,7 @@
controller.abort('User: Stop Response'); controller.abort('User: Stop Response');
} else { } else {
const messages = createMessagesList(responseMessageId); const messages = createMessagesList(responseMessageId);
await chatCompletedHandler(model.id, messages); await chatCompletedHandler(model.id, responseMessageId, messages);
} }
_response = responseMessage.content; _response = responseMessage.content;
...@@ -811,7 +859,8 @@ ...@@ -811,7 +859,8 @@
chat = await updateChatById(localStorage.token, _chatId, { chat = await updateChatById(localStorage.token, _chatId, {
messages: messages, messages: messages,
history: history, history: history,
models: selectedModels models: selectedModels,
params: params
}); });
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
} }
...@@ -912,8 +961,8 @@ ...@@ -912,8 +961,8 @@
const [res, controller] = await generateOpenAIChatCompletion( const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token, localStorage.token,
{ {
model: model.id,
stream: true, stream: true,
model: model.id,
stream_options: stream_options:
model.info?.meta?.capabilities?.usage ?? false model.info?.meta?.capabilities?.usage ?? false
? { ? {
...@@ -921,11 +970,11 @@ ...@@ -921,11 +970,11 @@
} }
: undefined, : undefined,
messages: [ messages: [
$settings.system || (responseMessage?.userContext ?? null) params?.system || $settings.system || (responseMessage?.userContext ?? null)
? { ? {
role: 'system', role: 'system',
content: `${promptTemplate( content: `${promptTemplate(
$settings?.system ?? '', params?.system ?? $settings?.system ?? '',
$user.name, $user.name,
$settings?.userLocation $settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token) ? await getAndUpdateUserLocation(localStorage.token)
...@@ -970,22 +1019,23 @@ ...@@ -970,22 +1019,23 @@
: message?.raContent ?? message.content : message?.raContent ?? message.content
}) })
})), })),
seed: $settings?.params?.seed ?? undefined, seed: params?.seed ?? $settings?.params?.seed ?? undefined,
stop: stop:
$settings?.params?.stop ?? undefined params?.stop ?? $settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) => ? (params?.stop ?? $settings.params.stop).map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
) )
: undefined, : undefined,
temperature: $settings?.params?.temperature ?? undefined, temperature: params?.temperature ?? $settings?.params?.temperature ?? undefined,
top_p: $settings?.params?.top_p ?? undefined, top_p: params?.top_p ?? $settings?.params?.top_p ?? undefined,
frequency_penalty: $settings?.params?.frequency_penalty ?? undefined, frequency_penalty:
max_tokens: $settings?.params?.max_tokens ?? undefined, params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined,
max_tokens: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined, tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
files: files.length > 0 ? files : undefined, files: files.length > 0 ? files : undefined,
citations: files.length > 0 ? true : undefined, session_id: $socket?.id,
chat_id: $chatId,
chat_id: $chatId id: responseMessageId
}, },
`${WEBUI_BASE_URL}/api` `${WEBUI_BASE_URL}/api`
); );
...@@ -1014,7 +1064,7 @@ ...@@ -1014,7 +1064,7 @@
} else { } else {
const messages = createMessagesList(responseMessageId); const messages = createMessagesList(responseMessageId);
await chatCompletedHandler(model.id, messages); await chatCompletedHandler(model.id, responseMessageId, messages);
} }
_response = responseMessage.content; _response = responseMessage.content;
...@@ -1086,7 +1136,8 @@ ...@@ -1086,7 +1136,8 @@
chat = await updateChatById(localStorage.token, _chatId, { chat = await updateChatById(localStorage.token, _chatId, {
models: selectedModels, models: selectedModels,
messages: messages, messages: messages,
history: history history: history,
params: params
}); });
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
} }
...@@ -1353,6 +1404,18 @@ ...@@ -1353,6 +1404,18 @@
<audio id="audioElement" src="" style="display: none;" /> <audio id="audioElement" src="" style="display: none;" />
<EventConfirmDialog
bind:show={showEventConfirmation}
title={eventConfirmationTitle}
message={eventConfirmationMessage}
on:confirm={(e) => {
eventCallback(true);
}}
on:cancel={() => {
eventCallback(false);
}}
/>
{#if $showCallOverlay} {#if $showCallOverlay}
<CallOverlay <CallOverlay
{submitPrompt} {submitPrompt}
...@@ -1387,6 +1450,7 @@ ...@@ -1387,6 +1450,7 @@
{title} {title}
bind:selectedModels bind:selectedModels
bind:showModelSelector bind:showModelSelector
bind:showControls
shareEnabled={messages.length > 0} shareEnabled={messages.length > 0}
{chat} {chat}
{initNewChat} {initNewChat}
...@@ -1396,7 +1460,7 @@ ...@@ -1396,7 +1460,7 @@
<div <div
class="absolute top-[4.25rem] w-full {$showSidebar class="absolute top-[4.25rem] w-full {$showSidebar
? 'md:max-w-[calc(100%-260px)]' ? 'md:max-w-[calc(100%-260px)]'
: ''} z-20" : ''} {showControls ? 'lg:pr-[24rem]' : ''} z-20"
> >
<div class=" flex flex-col gap-1 w-full"> <div class=" flex flex-col gap-1 w-full">
{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner} {#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
...@@ -1423,7 +1487,9 @@ ...@@ -1423,7 +1487,9 @@
<div class="flex flex-col flex-auto z-10"> <div class="flex flex-col flex-auto z-10">
<div <div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10" class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden {showControls
? 'lg:pr-[24rem]'
: ''}"
id="messages-container" id="messages-container"
bind:this={messagesContainerElement} bind:this={messagesContainerElement}
on:scroll={(e) => { on:scroll={(e) => {
...@@ -1448,26 +1514,31 @@ ...@@ -1448,26 +1514,31 @@
/> />
</div> </div>
</div> </div>
<MessageInput
bind:files <div class={showControls ? 'lg:pr-[24rem]' : ''}>
bind:prompt <MessageInput
bind:autoScroll bind:files
bind:selectedToolIds bind:prompt
bind:webSearchEnabled bind:autoScroll
bind:atSelectedModel bind:selectedToolIds
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => { bind:webSearchEnabled
const model = $models.find((m) => m.id === e); bind:atSelectedModel
if (model?.info?.meta?.toolIds ?? false) { availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
return [...new Set([...a, ...model.info.meta.toolIds])]; const model = $models.find((m) => m.id === e);
} if (model?.info?.meta?.toolIds ?? false) {
return a; return [...new Set([...a, ...model.info.meta.toolIds])];
}, [])} }
transparentBackground={$settings?.backgroundImageUrl ?? false} return a;
{selectedModels} }, [])}
{messages} transparentBackground={$settings?.backgroundImageUrl ?? false}
{submitPrompt} {selectedModels}
{stopResponse} {messages}
/> {submitPrompt}
{stopResponse}
/>
</div>
</div> </div>
<ChatControls bind:show={showControls} bind:params />
</div> </div>
{/if} {/if}
<script lang="ts">
import { slide } from 'svelte/transition';
import Modal from '../common/Modal.svelte';
import Controls from './Controls/Controls.svelte';
import { onMount } from 'svelte';
export let show = false;
export let chatId = null;
export let params = {};
let largeScreen = false;
onMount(() => {
// listen to resize 1024px
const mediaQuery = window.matchMedia('(min-width: 1024px)');
const handleMediaQuery = (e) => {
if (e.matches) {
largeScreen = true;
} else {
largeScreen = false;
}
};
mediaQuery.addEventListener('change', handleMediaQuery);
handleMediaQuery(mediaQuery);
return () => {
mediaQuery.removeEventListener('change', handleMediaQuery);
};
});
</script>
{#if largeScreen}
{#if show}
<div class=" absolute bottom-0 right-0 z-20 h-full pointer-events-none">
<div class="pr-4 pt-14 pb-8 w-[24rem] h-full" in:slide={{ duration: 200, axis: 'x' }}>
<div
class="w-full h-full px-5 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-800 rounded-xl z-50 pointer-events-auto overflow-y-auto scrollbar-hidden"
>
<Controls
on:close={() => {
show = false;
}}
bind:params
/>
</div>
</div>
</div>
{/if}
{:else}
<Modal bind:show>
<div class=" px-6 py-4 h-full">
<Controls
on:close={() => {
show = false;
}}
bind:params
/>
</div>
</Modal>
{/if}
<script>
import { createEventDispatcher, getContext } from 'svelte';
const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
import XMark from '$lib/components/icons/XMark.svelte';
import AdvancedParams from '../Settings/Advanced/AdvancedParams.svelte';
export let params = {};
</script>
<div class=" dark:text-white">
<div class=" flex justify-between dark:text-gray-100 mb-2">
<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Controls')}</div>
<button
class="self-center"
on:click={() => {
dispatch('close');
}}
>
<XMark className="size-4" />
</button>
</div>
<div class=" dark:text-gray-200 text-sm font-primary">
<div>
<div class="mb-1.5 font-medium">System Prompt</div>
<div>
<textarea
bind:value={params.system}
class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 border border-gray-100 dark:border-gray-800 outline-none resize-none"
rows="3"
placeholder="Enter system prompt"
/>
</div>
</div>
<hr class="my-2 border-gray-100 dark:border-gray-800" />
<div>
<div class="mb-1.5 font-medium">Advanced Params</div>
<div>
<AdvancedParams bind:params />
</div>
</div>
</div>
</div>
...@@ -316,7 +316,7 @@ ...@@ -316,7 +316,7 @@
</div> </div>
{/if} {/if}
<div class="w-full"> <div class="w-full font-primary">
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> <div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full"> <div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
<div class="relative"> <div class="relative">
......
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