Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
9bcd4ce5
Unverified
Commit
9bcd4ce5
authored
Jul 09, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Jul 09, 2024
Browse files
Merge pull request #3559 from open-webui/dev
0.3.8
parents
824966ad
b38abf23
Changes
178
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
479 additions
and
182 deletions
+479
-182
requirements.lock
requirements.lock
+5
-7
src/app.css
src/app.css
+16
-2
src/app.html
src/app.html
+7
-5
src/lib/apis/rag/index.ts
src/lib/apis/rag/index.ts
+6
-0
src/lib/components/ChangelogModal.svelte
src/lib/components/ChangelogModal.svelte
+3
-3
src/lib/components/admin/Settings.svelte
src/lib/components/admin/Settings.svelte
+37
-23
src/lib/components/admin/Settings/Audio.svelte
src/lib/components/admin/Settings/Audio.svelte
+6
-6
src/lib/components/admin/Settings/Connections.svelte
src/lib/components/admin/Settings/Connections.svelte
+2
-2
src/lib/components/admin/Settings/Documents.svelte
src/lib/components/admin/Settings/Documents.svelte
+64
-14
src/lib/components/admin/Settings/General.svelte
src/lib/components/admin/Settings/General.svelte
+2
-2
src/lib/components/admin/Settings/Images.svelte
src/lib/components/admin/Settings/Images.svelte
+9
-9
src/lib/components/admin/Settings/Interface.svelte
src/lib/components/admin/Settings/Interface.svelte
+12
-10
src/lib/components/admin/Settings/Models.svelte
src/lib/components/admin/Settings/Models.svelte
+23
-21
src/lib/components/admin/Settings/Pipelines.svelte
src/lib/components/admin/Settings/Pipelines.svelte
+37
-11
src/lib/components/admin/Settings/Users.svelte
src/lib/components/admin/Settings/Users.svelte
+2
-2
src/lib/components/admin/Settings/WebSearch.svelte
src/lib/components/admin/Settings/WebSearch.svelte
+5
-5
src/lib/components/chat/Chat.svelte
src/lib/components/chat/Chat.svelte
+130
-59
src/lib/components/chat/ChatControls.svelte
src/lib/components/chat/ChatControls.svelte
+63
-0
src/lib/components/chat/Controls/Controls.svelte
src/lib/components/chat/Controls/Controls.svelte
+49
-0
src/lib/components/chat/MessageInput.svelte
src/lib/components/chat/MessageInput.svelte
+1
-1
No files found.
requirements.lock
View file @
9bcd4ce5
...
...
@@ -7,6 +7,7 @@
# all-features: false
# with-sources: false
# generate-hashes: false
# universal: false
-e file:.
aiohttp==3.9.5
...
...
@@ -31,7 +32,7 @@ asgiref==3.8.1
# via opentelemetry-instrumentation-asgi
attrs==23.2.0
# via aiohttp
authlib==1.3.
0
authlib==1.3.
1
# via open-webui
av==11.0.0
# via faster-whisper
...
...
@@ -398,7 +399,6 @@ pandas==2.2.2
# via open-webui
passlib==1.7.4
# via open-webui
# via passlib
pathspec==0.12.1
# via black
pcodedmp==1.2.6
...
...
@@ -457,7 +457,6 @@ pygments==2.18.0
# via rich
pyjwt==2.8.0
# via open-webui
# via pyjwt
pymysql==1.1.0
# via open-webui
pypandoc==1.13
...
...
@@ -559,6 +558,9 @@ scipy==1.13.0
# via sentence-transformers
sentence-transformers==2.7.0
# via open-webui
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
shapely==2.0.4
# via rapidocr-onnxruntime
shellingham==1.5.4
...
...
@@ -653,7 +655,6 @@ uvicorn==0.22.0
# via chromadb
# via fastapi
# via open-webui
# via uvicorn
uvloop==0.19.0
# via uvicorn
validators==0.28.1
...
...
@@ -681,6 +682,3 @@ youtube-transcript-api==0.6.2
# via open-webui
zipp==3.18.1
# via importlib-metadata
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
src/app.css
View file @
9bcd4ce5
@font-face
{
font-family
:
'Arimo'
;
src
:
url('/assets/fonts/Arimo-Variable.ttf')
;
font-family
:
'Inter'
;
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
;
}
...
...
@@ -32,6 +38,10 @@ math {
@apply
underline;
}
.font-primary
{
font-family
:
'Archivo'
,
sans-serif
;
}
iframe
{
@apply
rounded-lg;
}
...
...
@@ -140,3 +150,7 @@ input[type='number'] {
.cm-editor.cm-focused
{
outline
:
none
;
}
.tippy-box
[
data-theme
~=
'dark'
]
{
@apply
rounded-lg
bg-gray-950
text-xs
border
border-gray-900
shadow-xl;
}
src/app.html
View file @
9bcd4ce5
...
...
@@ -23,6 +23,8 @@
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
(()
=>
{
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-950
'
,
'
#000000
'
);
document
.
documentElement
.
classList
.
add
(
'
dark
'
);
...
...
@@ -80,13 +82,13 @@
id=
"logo"
style=
"
position: absolute;
width:
6rem
;
width:
auto
;
height: 6rem;
top: 4
1
%;
top: 4
4
%;
left: 50%;
margin-left: -3rem;
"
src=
"/
logo.sv
g"
src=
"/
static/splash.pn
g"
/>
<div
...
...
@@ -105,8 +107,8 @@
>
<img
id=
"logo-her"
style=
"width:
13rem
; height: 13rem"
src=
"/
logo.sv
g"
style=
"width:
auto
; height: 13rem"
src=
"/
static/splash.pn
g"
class=
"animate-pulse-fast"
/>
...
...
src/lib/apis/rag/index.ts
View file @
9bcd4ce5
...
...
@@ -32,6 +32,11 @@ type ChunkConfigForm = {
chunk_overlap
:
number
;
};
type
ContentExtractConfigForm
=
{
engine
:
string
;
tika_server_url
:
string
|
null
;
};
type
YoutubeConfigForm
=
{
language
:
string
[];
translation
?:
string
|
null
;
...
...
@@ -40,6 +45,7 @@ type YoutubeConfigForm = {
type
RAGConfigForm
=
{
pdf_extract_images
?:
boolean
;
chunk
?:
ChunkConfigForm
;
content_extraction
?:
ContentExtractConfigForm
;
web_loader_ssl_verification
?:
boolean
;
youtube
?:
YoutubeConfigForm
;
};
...
...
src/lib/components/ChangelogModal.svelte
View file @
9bcd4ce5
...
...
@@ -24,7 +24,7 @@
<Modal bind:show>
<div class="px-5 pt-4 dark:text-gray-300 text-gray-700">
<div class="flex justify-between items-start">
<div class="text-xl font-bold">
<div class="text-xl font-
semi
bold">
{$i18n.t('What’s New in')}
{$WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} />
...
...
@@ -63,7 +63,7 @@
{#if changelog}
{#each Object.keys(changelog) as version}
<div class=" mb-3 pr-2">
<div class="font-bold text-xl mb-1 dark:text-white">
<div class="font-
semi
bold text-xl mb-1 dark:text-white">
v{version} - {changelog[version].date}
</div>
...
...
@@ -72,7 +72,7 @@
{#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
<div class="">
<div
class="font-bold uppercase text-xs {section === 'added'
class="font-
semi
bold uppercase text-xs {section === 'added'
? 'text-white bg-blue-600'
: section === 'fixed'
? 'text-white bg-green-600'
...
...
src/lib/components/admin/Settings.svelte
View file @
9bcd4ce5
<script>
import { getContext, tick } from 'svelte';
import { getContext, tick
, onMount
} from 'svelte';
import { toast } from 'svelte-sonner';
import Database from './Settings/Database.svelte';
...
...
@@ -21,17 +21,31 @@
const i18n = getContext('i18n');
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>
<div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
<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"
>
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
'general'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'general';
}}
...
...
@@ -56,8 +70,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'users'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'users';
}}
...
...
@@ -80,8 +94,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'connections'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'connections';
}}
...
...
@@ -104,8 +118,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'models'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'models';
}}
...
...
@@ -130,8 +144,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'documents'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'documents';
}}
...
...
@@ -160,8 +174,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'web'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'web';
}}
...
...
@@ -184,8 +198,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'interface'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'interface';
}}
...
...
@@ -210,8 +224,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'audio'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'audio';
}}
...
...
@@ -237,8 +251,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'images'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'images';
}}
...
...
@@ -263,8 +277,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'pipelines'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'pipelines';
}}
...
...
@@ -293,8 +307,8 @@
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'db'
? 'bg-gray-
2
00 dark:bg-gray-800'
: ' hover:bg-gray-
30
0 dark:hover:bg-gray-850'}"
? 'bg-gray-
1
00 dark:bg-gray-800'
: ' hover:bg-gray-
5
0 dark:hover:bg-gray-850'}"
on:click={() => {
selectedTab = 'db';
}}
...
...
src/lib/components/admin/Settings/Audio.svelte
View file @
9bcd4ce5
...
...
@@ -138,7 +138,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<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')}
bind:value={STT_OPENAI_API_BASE_URL}
required
...
...
@@ -156,7 +156,7 @@
<div class="flex-1">
<input
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}
placeholder="Select a model"
/>
...
...
@@ -203,7 +203,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<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')}
bind:value={TTS_OPENAI_API_BASE_URL}
required
...
...
@@ -222,7 +222,7 @@
<div class="flex w-full">
<div class="flex-1">
<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}
>
<option value="" selected={TTS_VOICE !== ''}>{$i18n.t('Default')}</option>
...
...
@@ -245,7 +245,7 @@
<div class="flex-1">
<input
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}
placeholder="Select a voice"
/>
...
...
@@ -264,7 +264,7 @@
<div class="flex-1">
<input
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}
placeholder="Select a model"
/>
...
...
src/lib/components/admin/Settings/Connections.svelte
View file @
9bcd4ce5
...
...
@@ -200,7 +200,7 @@
<input
class="w-full rounded-lg py-2 px-4 {pipelineUrls[url]
? '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')}
bind:value={url}
autocomplete="off"
...
...
@@ -338,7 +338,7 @@
{#each OLLAMA_BASE_URLS as url, idx}
<div class="flex gap-1.5">
<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)')}
bind:value={url}
/>
...
...
src/lib/components/admin/Settings/Documents.svelte
View file @
9bcd4ce5
...
...
@@ -37,6 +37,10 @@
let embeddingModel = '';
let rerankingModel = '';
let contentExtractionEngine = 'default';
let tikaServerUrl = '';
let showTikaServerUrl = false;
let chunkSize = 0;
let chunkOverlap = 0;
let pdfExtractImages = true;
...
...
@@ -163,11 +167,20 @@
rerankingModelUpdateHandler();
}
if (contentExtractionEngine === 'tika' && tikaServerUrl === '') {
toast.error($i18n.t('Tika Server URL required.'));
return;
}
const res = await updateRAGConfig(localStorage.token, {
pdf_extract_images: pdfExtractImages,
chunk: {
chunk_overlap: chunkOverlap,
chunk_size: chunkSize
},
content_extraction: {
engine: contentExtractionEngine,
tika_server_url: tikaServerUrl
}
});
...
...
@@ -213,6 +226,10 @@
chunkSize = res.chunk.chunk_size;
chunkOverlap = res.chunk.chunk_overlap;
contentExtractionEngine = res.content_extraction.engine;
tikaServerUrl = res.content_extraction.tika_server_url;
showTikaServerUrl = contentExtractionEngine === 'tika';
}
});
</script>
...
...
@@ -262,7 +279,7 @@
</div>
<button
class=" self-center text-xs p-1 px-3 bg-gray-
10
0 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-
5
0 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-lg flex flex-row space-x-1 items-center {scanDirLoading
? ' cursor-not-allowed'
: ''}"
on:click={() => {
...
...
@@ -335,7 +352,7 @@
{#if embeddingEngine === 'openai'}
<div class="my-0.5 flex gap-2">
<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')}
bind:value={OpenAIUrl}
required
...
...
@@ -388,7 +405,7 @@
</div>
</div>
<hr class="
dark:border-gray-850
my-1
" />
<hr class="dark:border-gray-850" />
<div class="space-y-2" />
<div>
...
...
@@ -398,7 +415,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}
placeholder={$i18n.t('Select a model')}
required
...
...
@@ -407,7 +424,7 @@
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if}
{#each $models.filter((m) => m.id && m.ollama && !(m?.preset ?? false)) as model}
<option value={model.id} class="bg-gray-
10
0 dark:bg-gray-700">{model.name}</option>
<option value={model.id} class="bg-gray-
5
0 dark:bg-gray-700">{model.name}</option>
{/each}
</select>
</div>
...
...
@@ -416,7 +433,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}})', {
model: embeddingModel.slice(-40)
})}
...
...
@@ -426,7 +443,7 @@
{#if embeddingEngine === ''}
<button
class="px-2.5 bg-gray-
10
0 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-
5
0 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={() => {
embeddingModelUpdateHandler();
}}
...
...
@@ -495,7 +512,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}})', {
model: 'BAAI/bge-reranker-v2-m3'
})}
...
...
@@ -503,7 +520,7 @@
/>
</div>
<button
class="px-2.5 bg-gray-
10
0 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-
5
0 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={() => {
rerankingModelUpdateHandler();
}}
...
...
@@ -562,6 +579,39 @@
<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=" text-sm font-medium">{$i18n.t('Query Params')}</div>
...
...
@@ -571,7 +621,7 @@
<div class="self-center p-3">
<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"
placeholder={$i18n.t('Enter Top K')}
bind:value={querySettings.k}
...
...
@@ -589,7 +639,7 @@
<div class="self-center p-3">
<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"
step="0.01"
placeholder={$i18n.t('Enter Score')}
...
...
@@ -617,7 +667,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
<textarea
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"
/>
</div>
...
...
@@ -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">
<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"
placeholder={$i18n.t('Enter Chunk Size')}
bind:value={chunkSize}
...
...
@@ -650,7 +700,7 @@
<div class="self-center">
<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"
placeholder={$i18n.t('Enter Chunk Overlap')}
bind:value={chunkOverlap}
...
...
src/lib/components/admin/Settings/General.svelte
View file @
9bcd4ce5
...
...
@@ -107,7 +107,7 @@
<div class="flex mt-2 space-x-2">
<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"
placeholder={`e.g.) "30m","1h", "10d". `}
bind:value={adminConfig.JWT_EXPIRES_IN}
...
...
@@ -131,7 +131,7 @@
<div class="flex mt-2 space-x-2">
<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"
placeholder={`https://example.com/webhook`}
bind:value={webhookUrl}
...
...
src/lib/components/admin/Settings/Images.svelte
View file @
9bcd4ce5
...
...
@@ -240,13 +240,13 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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/)')}
bind:value={AUTOMATIC1111_BASE_URL}
/>
</div>
<button
class="px-2.5 bg-gray-
10
0 hover:bg-gray-
2
00 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-
5
0 hover:bg-gray-
1
00 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
type="button"
on:click={() => {
updateUrlHandler();
...
...
@@ -299,13 +299,13 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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/)')}
bind:value={COMFYUI_BASE_URL}
/>
</div>
<button
class="px-2.5 bg-gray-
10
0 hover:bg-gray-
2
00 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-
5
0 hover:bg-gray-
1
00 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
type="button"
on:click={() => {
updateUrlHandler();
...
...
@@ -331,7 +331,7 @@
<div class="flex gap-2 mb-1">
<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')}
bind:value={OPENAI_API_BASE_URL}
required
...
...
@@ -354,7 +354,7 @@
<div class="flex-1">
<input
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}
placeholder="Select a model"
/>
...
...
@@ -368,7 +368,7 @@
</div>
{:else}
<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}
placeholder={$i18n.t('Select a model')}
required
...
...
@@ -391,7 +391,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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)')}
bind:value={imageSize}
/>
...
...
@@ -404,7 +404,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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)')}
bind:value={steps}
/>
...
...
src/lib/components/admin/Settings/Interface.svelte
View file @
9bcd4ce5
...
...
@@ -88,7 +88,7 @@
<div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('Local Models')}</div>
<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}
placeholder={$i18n.t('Select a model')}
>
...
...
@@ -104,7 +104,7 @@
<div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('External Models')}</div>
<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}
placeholder={$i18n.t('Select a model')}
>
...
...
@@ -122,7 +122,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
<textarea
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"
/>
</div>
...
...
@@ -131,7 +131,7 @@
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Search Query Generation Prompt')}</div>
<textarea
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"
/>
</div>
...
...
@@ -142,7 +142,7 @@
</div>
<input
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"
/>
</div>
...
...
@@ -273,24 +273,26 @@
</div>
<div class="grid lg:grid-cols-2 flex-col gap-1.5">
{#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 border-b dark:border-gray-800 w-full">
<div class="flex border-b
border-gray-100
dark:border-gray-800 w-full">
<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)')}
bind:value={prompt.title[0]}
/>
<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)')}
bind:value={prompt.title[1]}
/>
</div>
<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)')}
bind:value={prompt.content}
/>
...
...
src/lib/components/admin/Settings/Models.svelte
View file @
9bcd4ce5
...
...
@@ -158,12 +158,14 @@
return;
}
const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch(
(error) => {
toast.error(error);
return null;
}
);
const [res, controller] = await pullModel(
localStorage.token,
sanitizedModelTag,
selectedOllamaUrlIdx
).catch((error) => {
toast.error(error);
return null;
});
if (res) {
const reader = res.body
...
...
@@ -570,12 +572,12 @@
<div class="flex gap-2">
<div class="flex-1 pb-1">
<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}
placeholder={$i18n.t('Select an Ollama instance')}
>
{#each OLLAMA_URLS as url, idx}
<option value={idx} class="bg-gray-
10
0 dark:bg-gray-700">{url}</option>
<option value={idx} class="bg-gray-
5
0 dark:bg-gray-700">{url}</option>
{/each}
</select>
</div>
...
...
@@ -584,7 +586,7 @@
<div class="flex w-full justify-end">
<Tooltip content="Update All Models" placement="top">
<button
class="p-2.5 flex gap-2 items-center bg-gray-
10
0 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-
5
0 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={() => {
updateModelsHandler();
}}
...
...
@@ -619,7 +621,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}})', {
modelTag: 'mistral:7b'
})}
...
...
@@ -627,7 +629,7 @@
/>
</div>
<button
class="px-2.5 bg-gray-
10
0 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-
5
0 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={() => {
pullModelHandler();
}}
...
...
@@ -753,7 +755,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}
placeholder={$i18n.t('Select a model')}
>
...
...
@@ -761,7 +763,7 @@
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if}
{#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-
10
0 dark:bg-gray-700"
<option value={model.name} class="bg-gray-
5
0 dark:bg-gray-700"
>{model.name +
' (' +
(model.ollama.size / 1024 ** 3).toFixed(1) +
...
...
@@ -771,7 +773,7 @@
</select>
</div>
<button
class="px-2.5 bg-gray-
10
0 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-
5
0 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={() => {
showModelDeleteConfirm = true;
}}
...
...
@@ -797,7 +799,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2 flex flex-col gap-2">
<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}})', {
modelTag: 'my-modelfile'
})}
...
...
@@ -807,7 +809,7 @@
<textarea
bind:value={createModelContent}
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-
10
0 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-
5
0 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none scrollbar-hidden"
rows="6"
placeholder={`TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`}
disabled={createModelLoading}
...
...
@@ -816,7 +818,7 @@
<div class="flex self-start">
<button
class="px-2.5 py-2.5 bg-gray-
10
0 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-
5
0 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={() => {
createModelHandler();
}}
...
...
@@ -925,7 +927,7 @@
<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={() => {
modelUploadInputElement.click();
}}
...
...
@@ -940,7 +942,7 @@
{:else}
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
<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'
: ''}"
...
...
@@ -955,7 +957,7 @@
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<button
class="px-2.5 bg-gray-
10
0 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-
5
0 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"
disabled={modelTransferring}
>
...
...
@@ -1014,7 +1016,7 @@
<div class=" my-2.5 text-sm font-medium">{$i18n.t('Modelfile Content')}</div>
<textarea
bind:value={modelFileContent}
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-
10
0 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-
5
0 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none"
rows="6"
/>
</div>
...
...
src/lib/components/admin/Settings/Pipelines.svelte
View file @
9bcd4ce5
...
...
@@ -19,6 +19,7 @@
} from '$lib/apis';
import Spinner from '$lib/components/common/Spinner.svelte';
import Switch from '$lib/components/common/Switch.svelte';
const i18n: Writable<i18nType> = getContext('i18n');
...
...
@@ -213,7 +214,7 @@
<div class="flex gap-2">
<div class="flex-1">
<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}
placeholder={$i18n.t('Select a pipeline url')}
on:change={async () => {
...
...
@@ -327,7 +328,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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')}
bind:value={pipelineDownloadUrl}
/>
...
...
@@ -411,7 +412,7 @@
<div class="flex gap-2">
<div class="flex-1">
<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}
placeholder={$i18n.t('Select a pipeline')}
on:change={async () => {
...
...
@@ -476,15 +477,40 @@
</div>
{#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">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={valves_spec.properties[property].title}
bind:value={valves[property]}
autocomplete="off"
/>
{#if valves_spec.properties[property]?.enum ?? null}
<select
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={valves[property]}
>
{#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>
{/if}
...
...
src/lib/components/admin/Settings/Users.svelte
View file @
9bcd4ce5
...
...
@@ -112,7 +112,7 @@
<div class="flex-1 mr-2">
<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}
placeholder="Select a model"
>
...
...
@@ -140,7 +140,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<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}
placeholder="Select a model"
>
...
...
src/lib/components/admin/Settings/WebSearch.svelte
View file @
9bcd4ce5
...
...
@@ -101,7 +101,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
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={$i18n.t('Enter Searxng Query URL')}
bind:value={webConfig.search.searxng_query_url}
...
...
@@ -129,7 +129,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
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={$i18n.t('Enter Google PSE Engine Id')}
bind:value={webConfig.search.google_pse_engine_id}
...
...
@@ -205,7 +205,7 @@
</div>
<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')}
bind:value={webConfig.search.result_count}
required
...
...
@@ -218,7 +218,7 @@
</div>
<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')}
bind:value={webConfig.search.concurrent_requests}
required
...
...
@@ -267,7 +267,7 @@
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
<div class=" flex-1 self-center">
<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"
placeholder={$i18n.t('Enter language codes')}
bind:value={youtubeLanguage}
...
...
src/lib/components/chat/Chat.svelte
View file @
9bcd4ce5
...
...
@@ -60,19 +60,26 @@
import Navbar from '$lib/components/layout/Navbar.svelte';
import CallOverlay from './MessageInput/CallOverlay.svelte';
import { error } from '@sveltejs/kit';
import ChatControls from './ChatControls.svelte';
import EventConfirmDialog from '../common/ConfirmDialog.svelte';
const i18n: Writable<i18nType> = getContext('i18n');
export let chatIdProp = '';
let loaded = false;
const eventTarget = new EventTarget();
let showControls = false;
let stopResponseFlag = false;
let autoScroll = true;
let processing = '';
let messagesContainerElement: HTMLDivElement;
let showEventConfirmation = false;
let eventConfirmationTitle = '';
let eventConfirmationMessage = '';
let eventCallback = null;
let showModelSelector = true;
let selectedModels = [''];
...
...
@@ -96,6 +103,8 @@
currentId: null
};
let params = {};
$: if (history.currentId !== null) {
let _messages = [];
...
...
@@ -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 () => {
const onMessageHandler = async (event) => {
if (event.origin === window.origin) {
...
...
@@ -163,6 +207,8 @@
};
window.addEventListener('message', onMessageHandler);
$socket.on('chat-events', chatEventHandler);
if (!$chatId) {
chatId.subscribe(async (value) => {
if (!value) {
...
...
@@ -177,6 +223,8 @@
return () => {
window.removeEventListener('message', onMessageHandler);
$socket.off('chat-events');
};
});
...
...
@@ -196,6 +244,7 @@
messages: {},
currentId: null
};
params = {};
if ($page.url.searchParams.get('models')) {
selectedModels = $page.url.searchParams.get('models')?.split(',');
...
...
@@ -265,11 +314,7 @@
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
}
await settings.set({
...$settings,
system: chatContent.system ?? $settings.system,
params: chatContent.options ?? $settings.params
});
params = chatContent?.params ?? {};
autoScroll = true;
await tick();
...
...
@@ -302,7 +347,7 @@
}
};
const chatCompletedHandler = async (modelId, messages) => {
const chatCompletedHandler = async (modelId,
responseMessageId,
messages) => {
await mermaid.run({
querySelector: '.mermaid'
});
...
...
@@ -316,7 +361,9 @@
info: m.info ? m.info : undefined,
timestamp: m.timestamp
})),
chat_id: $chatId
chat_id: $chatId,
session_id: $socket?.id,
id: responseMessageId
}).catch((error) => {
toast.error(error);
messages.at(-1).error = { content: error };
...
...
@@ -480,9 +527,7 @@
title: $i18n.t('New Chat'),
models: selectedModels,
system: $settings.system ?? undefined,
options: {
...($settings.params ?? {})
},
params: params,
messages: messages,
history: history,
tags: [],
...
...
@@ -580,11 +625,11 @@
scrollToBottom();
const messagesBody = [
$settings.system || (responseMessage?.userContext ?? null)
params?.system ||
$settings.system || (responseMessage?.userContext ?? null)
? {
role: 'system',
content: `${promptTemplate(
$settings?.system ?? '',
params?.system ??
$settings?.system ?? '',
$user.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token)
...
...
@@ -665,25 +710,28 @@
await tick();
const [res, controller] = await generateChatCompletion(localStorage.token, {
stream: true,
model: model.id,
messages: messagesBody,
options: {
...($settings.params ?? {}),
...(
params ??
$settings.params ?? {}),
stop:
$settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) =>
params?.stop ??
$settings?.params?.stop ?? undefined
?
(params?.stop ??
$settings.params.stop
)
.map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
)
: undefined,
num_predict: $settings?.params?.max_tokens ?? undefined,
repeat_penalty: $settings?.params?.frequency_penalty ?? undefined
num_predict: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
repeat_penalty:
params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined
},
format: $settings.requestFormat ?? undefined,
keep_alive: $settings.keepAlive ?? undefined,
tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
files: files.length > 0 ? files : undefined,
citations: files.length > 0 ? true : undefined,
chat_id: $chatId
session_id: $socket?.id,
chat_id: $chatId,
id: responseMessageId
});
if (res && res.ok) {
...
...
@@ -704,7 +752,7 @@
controller.abort('User: Stop Response');
} else {
const messages = createMessagesList(responseMessageId);
await chatCompletedHandler(model.id, messages);
await chatCompletedHandler(model.id,
responseMessageId,
messages);
}
_response = responseMessage.content;
...
...
@@ -811,7 +859,8 @@
chat = await updateChatById(localStorage.token, _chatId, {
messages: messages,
history: history,
models: selectedModels
models: selectedModels,
params: params
});
await chats.set(await getChatList(localStorage.token));
}
...
...
@@ -912,8 +961,8 @@
const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token,
{
model: model.id,
stream: true,
model: model.id,
stream_options:
model.info?.meta?.capabilities?.usage ?? false
? {
...
...
@@ -921,11 +970,11 @@
}
: undefined,
messages: [
$settings.system || (responseMessage?.userContext ?? null)
params?.system ||
$settings.system || (responseMessage?.userContext ?? null)
? {
role: 'system',
content: `${promptTemplate(
$settings?.system ?? '',
params?.system ??
$settings?.system ?? '',
$user.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token)
...
...
@@ -970,22 +1019,23 @@
: message?.raContent ?? message.content
})
})),
seed: $settings?.params?.seed ?? undefined,
seed:
params?.seed ??
$settings?.params?.seed ?? undefined,
stop:
$settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) =>
params?.stop ??
$settings?.params?.stop ?? undefined
?
(params?.stop ??
$settings.params.stop
)
.map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
)
: undefined,
temperature: $settings?.params?.temperature ?? undefined,
top_p: $settings?.params?.top_p ?? undefined,
frequency_penalty: $settings?.params?.frequency_penalty ?? undefined,
max_tokens: $settings?.params?.max_tokens ?? undefined,
temperature: params?.temperature ?? $settings?.params?.temperature ?? undefined,
top_p: params?.top_p ?? $settings?.params?.top_p ?? undefined,
frequency_penalty:
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,
files: files.length > 0 ? files : undefined,
citations: files.length > 0 ? true : undefine
d,
chat_id: $chat
Id
session_id: $socket?.i
d,
chat_id: $chatId,
id: responseMessage
Id
},
`${WEBUI_BASE_URL}/api`
);
...
...
@@ -1014,7 +1064,7 @@
} else {
const messages = createMessagesList(responseMessageId);
await chatCompletedHandler(model.id, messages);
await chatCompletedHandler(model.id,
responseMessageId,
messages);
}
_response = responseMessage.content;
...
...
@@ -1086,7 +1136,8 @@
chat = await updateChatById(localStorage.token, _chatId, {
models: selectedModels,
messages: messages,
history: history
history: history,
params: params
});
await chats.set(await getChatList(localStorage.token));
}
...
...
@@ -1353,6 +1404,18 @@
<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}
<CallOverlay
{submitPrompt}
...
...
@@ -1387,6 +1450,7 @@
{title}
bind:selectedModels
bind:showModelSelector
bind:showControls
shareEnabled={messages.length > 0}
{chat}
{initNewChat}
...
...
@@ -1396,7 +1460,7 @@
<div
class="absolute top-[4.25rem] w-full {$showSidebar
? 'md:max-w-[calc(100%-260px)]'
: ''} z-20"
: ''}
{showControls ? 'lg:pr-[24rem]' : ''}
z-20"
>
<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}
...
...
@@ -1423,7 +1487,9 @@
<div class="flex flex-col flex-auto z-10">
<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"
bind:this={messagesContainerElement}
on:scroll={(e) => {
...
...
@@ -1448,26 +1514,31 @@
/>
</div>
</div>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
bind:selectedToolIds
bind:webSearchEnabled
bind:atSelectedModel
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
const model = $models.find((m) => m.id === e);
if (model?.info?.meta?.toolIds ?? false) {
return [...new Set([...a, ...model.info.meta.toolIds])];
}
return a;
}, [])}
transparentBackground={$settings?.backgroundImageUrl ?? false}
{selectedModels}
{messages}
{submitPrompt}
{stopResponse}
/>
<div class={showControls ? 'lg:pr-[24rem]' : ''}>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
bind:selectedToolIds
bind:webSearchEnabled
bind:atSelectedModel
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
const model = $models.find((m) => m.id === e);
if (model?.info?.meta?.toolIds ?? false) {
return [...new Set([...a, ...model.info.meta.toolIds])];
}
return a;
}, [])}
transparentBackground={$settings?.backgroundImageUrl ?? false}
{selectedModels}
{messages}
{submitPrompt}
{stopResponse}
/>
</div>
</div>
<ChatControls bind:show={showControls} bind:params />
</div>
{/if}
src/lib/components/chat/ChatControls.svelte
0 → 100644
View file @
9bcd4ce5
<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}
src/lib/components/chat/Controls/Controls.svelte
0 → 100644
View file @
9bcd4ce5
<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>
src/lib/components/chat/MessageInput.svelte
View file @
9bcd4ce5
...
...
@@ -316,7 +316,7 @@
</div>
{/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="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
<div class="relative">
...
...
Prev
1
2
3
4
5
6
7
8
9
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment