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
89634046
Unverified
Commit
89634046
authored
Mar 10, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Mar 10, 2024
Browse files
Merge pull request #1107 from open-webui/dev
0.1.111
parents
04ddbf43
88d324b5
Changes
37
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
652 additions
and
551 deletions
+652
-551
src/lib/components/chat/MessageInput.svelte
src/lib/components/chat/MessageInput.svelte
+3
-3
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+14
-11
src/lib/components/chat/Messages/UserMessage.svelte
src/lib/components/chat/Messages/UserMessage.svelte
+3
-3
src/lib/components/chat/Settings/Account.svelte
src/lib/components/chat/Settings/Account.svelte
+1
-1
src/lib/components/chat/Settings/Audio.svelte
src/lib/components/chat/Settings/Audio.svelte
+1
-1
src/lib/components/chat/Settings/Connections.svelte
src/lib/components/chat/Settings/Connections.svelte
+1
-1
src/lib/components/chat/Settings/General.svelte
src/lib/components/chat/Settings/General.svelte
+2
-2
src/lib/components/chat/Settings/Images.svelte
src/lib/components/chat/Settings/Images.svelte
+135
-76
src/lib/components/chat/Settings/Interface.svelte
src/lib/components/chat/Settings/Interface.svelte
+5
-27
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+277
-221
src/lib/components/chat/SettingsModal.svelte
src/lib/components/chat/SettingsModal.svelte
+6
-6
src/lib/components/common/Image.svelte
src/lib/components/common/Image.svelte
+7
-2
src/lib/components/documents/Settings/General.svelte
src/lib/components/documents/Settings/General.svelte
+99
-72
src/lib/components/layout/Sidebar.svelte
src/lib/components/layout/Sidebar.svelte
+12
-7
src/routes/(app)/+page.svelte
src/routes/(app)/+page.svelte
+45
-59
src/routes/(app)/c/[id]/+page.svelte
src/routes/(app)/c/[id]/+page.svelte
+40
-58
src/routes/(app)/playground/+page.svelte
src/routes/(app)/playground/+page.svelte
+1
-1
No files found.
src/lib/components/chat/MessageInput.svelte
View file @
89634046
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
export let suggestionPrompts = [];
export let suggestionPrompts = [];
export let autoScroll = true;
export let autoScroll = true;
let chatTextAreaElement:HTMLTextAreaElement
let chatTextAreaElement:
HTMLTextAreaElement
;
let filesInputElement;
let filesInputElement;
let promptsElement;
let promptsElement;
...
@@ -359,12 +359,12 @@
...
@@ -359,12 +359,12 @@
{#if dragged}
{#if dragged}
<div
<div
class="fixed w-full h-full flex z-50 touch-none pointer-events-none"
class="fixed
lg:w-[calc(100%-260px)]
w-full h-full flex z-50 touch-none pointer-events-none"
id="dropzone"
id="dropzone"
role="region"
role="region"
aria-label="Drag and Drop Container"
aria-label="Drag and Drop Container"
>
>
<div class="absolute
rounded-xl
w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
<div class="absolute w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
<div class="m-auto pt-64 flex flex-col justify-center">
<div class="m-auto pt-64 flex flex-col justify-center">
<div class="max-w-md">
<div class="max-w-md">
<AddFilesPlaceholder />
<AddFilesPlaceholder />
...
...
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
89634046
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
import auto_render from 'katex/dist/contrib/auto-render.mjs';
import auto_render from 'katex/dist/contrib/auto-render.mjs';
import 'katex/dist/katex.min.css';
import 'katex/dist/katex.min.css';
import { fade } from 'svelte/transition';
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher } from 'svelte';
import { onMount, tick } from 'svelte';
import { onMount, tick } from 'svelte';
...
@@ -276,13 +277,15 @@
...
@@ -276,13 +277,15 @@
const generateImage = async (message) => {
const generateImage = async (message) => {
generatingImage = true;
generatingImage = true;
const res = await imageGenerations(localStorage.token, message.content);
const res = await imageGenerations(localStorage.token, message.content).catch((error) => {
toast.error(error);
});
console.log(res);
console.log(res);
if (res) {
if (res) {
message.files = res.
images.
map((image) => ({
message.files = res.map((image) => ({
type: 'image',
type: 'image',
url: `
data:image/png;base64,
${image}`
url: `${image
.url
}`
}));
}));
dispatch('save', message);
dispatch('save', message);
...
@@ -477,7 +480,7 @@
...
@@ -477,7 +480,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -503,7 +506,7 @@
...
@@ -503,7 +506,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -622,7 +625,7 @@
...
@@ -622,7 +625,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -637,7 +640,7 @@
...
@@ -637,7 +640,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -703,7 +706,7 @@
...
@@ -703,7 +706,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -733,7 +736,7 @@
...
@@ -733,7 +736,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -762,7 +765,7 @@
...
@@ -762,7 +765,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -792,7 +795,7 @@
...
@@ -792,7 +795,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
...
src/lib/components/chat/Messages/UserMessage.svelte
View file @
89634046
...
@@ -258,7 +258,7 @@
...
@@ -258,7 +258,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -282,7 +282,7 @@
...
@@ -282,7 +282,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
@@ -307,7 +307,7 @@
...
@@ -307,7 +307,7 @@
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
fill="none"
fill="none"
viewBox="0 0 24 24"
viewBox="0 0 24 24"
stroke-width="
1.5
"
stroke-width="
2
"
stroke="currentColor"
stroke="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
...
...
src/lib/components/chat/Settings/Account.svelte
View file @
89634046
...
@@ -271,7 +271,7 @@
...
@@ -271,7 +271,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded"
class="
px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
"
on:click={async () => {
on:click={async () => {
const res = await submitHandler();
const res = await submitHandler();
...
...
src/lib/components/chat/Settings/Audio.svelte
View file @
89634046
...
@@ -251,7 +251,7 @@
...
@@ -251,7 +251,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded"
class=" px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
"
type="submit"
type="submit"
>
>
Save
Save
...
...
src/lib/components/chat/Settings/Connections.svelte
View file @
89634046
...
@@ -247,7 +247,7 @@
...
@@ -247,7 +247,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded"
class="
px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
"
type="submit"
type="submit"
>
>
Save
Save
...
...
src/lib/components/chat/Settings/General.svelte
View file @
89634046
...
@@ -176,7 +176,7 @@
...
@@ -176,7 +176,7 @@
<div class=" my-2.5 text-sm font-medium">System Prompt</div>
<div class=" my-2.5 text-sm font-medium">System Prompt</div>
<textarea
<textarea
bind:value={system}
bind:value={system}
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none resize-none"
class="w-full rounded
-lg
p-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none resize-none"
rows="4"
rows="4"
/>
/>
</div>
</div>
...
@@ -262,7 +262,7 @@
...
@@ -262,7 +262,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded"
class="
px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
"
on:click={() => {
on:click={() => {
saveSettings({
saveSettings({
system: system !== '' ? system : undefined,
system: system !== '' ? system : undefined,
...
...
src/lib/components/chat/Settings/Images.svelte
View file @
89634046
...
@@ -5,16 +5,18 @@
...
@@ -5,16 +5,18 @@
import { config, user } from '$lib/stores';
import { config, user } from '$lib/stores';
import {
import {
getAUTOMATIC1111Url,
getAUTOMATIC1111Url,
get
DefaultDiffus
ionModel,
get
ImageGenerat
ionModel
s
,
getD
iffus
ionModel
s
,
getD
efaultImageGenerat
ionModel,
ge
tImageGeneration
EnabledStatus
,
updateDefaul
tImageGeneration
Model
,
getImageSize,
getImageSize,
toggleImageGenerationEnabledStatus,
getImageGenerationConfig,
updateImageGenerationConfig,
updateAUTOMATIC1111Url,
updateAUTOMATIC1111Url,
updateDefaultDiffusionModel,
updateImageSize,
updateImageSize,
getImageSteps,
getImageSteps,
updateImageSteps
updateImageSteps,
getOpenAIKey,
updateOpenAIKey
} from '$lib/apis/images';
} from '$lib/apis/images';
import { getBackendConfig } from '$lib/apis';
import { getBackendConfig } from '$lib/apis';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher();
...
@@ -23,8 +25,11 @@
...
@@ -23,8 +25,11 @@
let loading = false;
let loading = false;
let imageGenerationEngine = '';
let enableImageGeneration = false;
let enableImageGeneration = false;
let AUTOMATIC1111_BASE_URL = '';
let AUTOMATIC1111_BASE_URL = '';
let OPENAI_API_KEY = '';
let selectedModel = '';
let selectedModel = '';
let models = null;
let models = null;
...
@@ -33,11 +38,11 @@
...
@@ -33,11 +38,11 @@
let steps = 50;
let steps = 50;
const getModels = async () => {
const getModels = async () => {
models = await get
Diffus
ionModels(localStorage.token).catch((error) => {
models = await get
ImageGenerat
ionModels(localStorage.token).catch((error) => {
toast.error(error);
toast.error(error);
return null;
return null;
});
});
selectedModel = await getDefault
Diffus
ionModel(localStorage.token).catch((error) => {
selectedModel = await getDefault
ImageGenerat
ionModel(localStorage.token).catch((error) => {
return '';
return '';
});
});
};
};
...
@@ -62,33 +67,45 @@
...
@@ -62,33 +67,45 @@
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
}
}
};
};
const toggleImageGeneration = async () => {
const updateImageGeneration = async () => {
if (AUTOMATIC1111_BASE_URL) {
const res = await updateImageGenerationConfig(
enableImageGeneration = await toggleImageGenerationEnabledStatus(localStorage.token).catch(
localStorage.token,
(error) => {
imageGenerationEngine,
toast.error(error);
enableImageGeneration
return false;
).catch((error) => {
}
toast.error(error);
);
return null;
});
if (enableImageGeneration) {
if (res) {
config.set(await getBackendConfig(localStorage.token));
imageGenerationEngine = res.engine;
getModels();
enableImageGeneration = res.enabled;
}
}
} else {
enableImageGeneration = false;
if (enableImageGeneration) {
toast.error('AUTOMATIC1111_BASE_URL not provided');
config.set(await getBackendConfig(localStorage.token));
getModels();
}
}
};
};
onMount(async () => {
onMount(async () => {
if ($user.role === 'admin') {
if ($user.role === 'admin') {
enableImageGeneration = await getImageGenerationEnabledStatus(localStorage.token);
const res = await getImageGenerationConfig(localStorage.token).catch((error) => {
toast.error(error);
return null;
});
if (res) {
imageGenerationEngine = res.engine;
enableImageGeneration = res.enabled;
}
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
imageSize = await getImageSize(localStorage.token);
steps = await getImageSteps(localStorage.token);
if (enableImageGeneration && AUTOMATIC1111_BASE_URL) {
if (enableImageGeneration) {
imageSize = await getImageSize(localStorage.token);
steps = await getImageSteps(localStorage.token);
getModels();
getModels();
}
}
}
}
...
@@ -99,7 +116,11 @@
...
@@ -99,7 +116,11 @@
class="flex flex-col h-full justify-between space-y-3 text-sm"
class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={async () => {
on:submit|preventDefault={async () => {
loading = true;
loading = true;
await updateDefaultDiffusionModel(localStorage.token, selectedModel);
await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
await updateImageSize(localStorage.token, imageSize).catch((error) => {
await updateImageSize(localStorage.token, imageSize).catch((error) => {
toast.error(error);
toast.error(error);
return null;
return null;
...
@@ -117,6 +138,23 @@
...
@@ -117,6 +138,23 @@
<div>
<div>
<div class=" mb-1 text-sm font-medium">Image Settings</div>
<div class=" mb-1 text-sm font-medium">Image Settings</div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">Image Generation Engine</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
bind:value={imageGenerationEngine}
placeholder="Select a mode"
on:change={async () => {
await updateImageGeneration();
}}
>
<option value="">Default (Automatic1111)</option>
<option value="openai">Open AI (Dall-E)</option>
</select>
</div>
</div>
<div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">Image Generation (Experimental)</div>
<div class=" self-center text-xs font-medium">Image Generation (Experimental)</div>
...
@@ -124,7 +162,17 @@
...
@@ -124,7 +162,17 @@
<button
<button
class="p-1 px-3 text-xs flex rounded transition"
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
on:click={() => {
toggleImageGeneration();
if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
toast.error('AUTOMATIC1111 Base URL is required.');
enableImageGeneration = false;
} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
toast.error('OpenAI API Key is required.');
enableImageGeneration = false;
} else {
enableImageGeneration = !enableImageGeneration;
}
updateImageGeneration();
}}
}}
type="button"
type="button"
>
>
...
@@ -139,49 +187,62 @@
...
@@ -139,49 +187,62 @@
</div>
</div>
<hr class=" dark:border-gray-700" />
<hr class=" dark:border-gray-700" />
<div class=" mb-2.5 text-sm font-medium">AUTOMATIC1111 Base URL</div>
{#if imageGenerationEngine === ''}
<div class="flex w-full">
<div class=" mb-2.5 text-sm font-medium">AUTOMATIC1111 Base URL</div>
<div class="flex-1 mr-2">
<div class="flex w-full">
<input
<div class="flex-1 mr-2">
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
<input
placeholder="Enter URL (e.g. http://127.0.0.1:7860/)"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={AUTOMATIC1111_BASE_URL}
placeholder="Enter URL (e.g. http://127.0.0.1:7860/)"
/>
bind:value={AUTOMATIC1111_BASE_URL}
/>
</div>
<button
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition"
type="button"
on:click={() => {
// updateOllamaAPIUrlHandler();
updateAUTOMATIC1111UrlHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
<button
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition"
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
type="button"
Include `--api` flag when running stable-diffusion-webui
on:click={() => {
<a
// updateOllamaAPIUrlHandler();
class=" text-gray-300 font-medium"
href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
updateAUTOMATIC1111UrlHandler();
target="_blank"
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
>
<path
(e.g. `sh webui.sh --api`)
fill-rule="evenodd"
</a>
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
</div>
clip-rule="evenodd"
{:else if imageGenerationEngine === 'openai'}
<div class=" mb-2.5 text-sm font-medium">OpenAI API Key</div>
<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"
placeholder="Enter API Key"
bind:value={OPENAI_API_KEY}
/>
/>
</svg>
</div>
</button>
</div>
</div>
{/if}
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Include `--api` flag when running stable-diffusion-webui
<a
class=" text-gray-300 font-medium"
href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
target="_blank"
>
(e.g. `sh webui.sh --api`)
</a>
</div>
{#if enableImageGeneration}
{#if enableImageGeneration}
<hr class=" dark:border-gray-700" />
<hr class=" dark:border-gray-700" />
...
@@ -191,7 +252,7 @@
...
@@ -191,7 +252,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 py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
bind:value={selectedModel}
bind:value={selectedModel}
placeholder="Select a model"
placeholder="Select a model"
>
>
...
@@ -199,9 +260,7 @@
...
@@ -199,9 +260,7 @@
<option value="" disabled selected>Select a model</option>
<option value="" disabled selected>Select a model</option>
{/if}
{/if}
{#each models ?? [] as model}
{#each models ?? [] as model}
<option value={model.title} class="bg-gray-100 dark:bg-gray-700"
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
>{model.model_name}</option
>
{/each}
{/each}
</select>
</select>
</div>
</div>
...
@@ -213,7 +272,7 @@
...
@@ -213,7 +272,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 py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
placeholder="Enter Image Size (e.g. 512x512)"
placeholder="Enter Image Size (e.g. 512x512)"
bind:value={imageSize}
bind:value={imageSize}
/>
/>
...
@@ -226,7 +285,7 @@
...
@@ -226,7 +285,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 py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
placeholder="Enter Number of Steps (e.g. 50)"
placeholder="Enter Number of Steps (e.g. 50)"
bind:value={steps}
bind:value={steps}
/>
/>
...
@@ -238,7 +297,7 @@
...
@@ -238,7 +297,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded flex flex-row space-x-1 items-center {loading
class=" px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed'
? ' cursor-not-allowed'
: ''}"
: ''}"
type="submit"
type="submit"
...
...
src/lib/components/chat/Settings/Interface.svelte
View file @
89634046
...
@@ -63,6 +63,7 @@
...
@@ -63,6 +63,7 @@
}
}
saveSettings({
saveSettings({
titleAutoGenerateModel: titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined,
titleGenerationPrompt: titleGenerationPrompt ? titleGenerationPrompt : undefined
titleGenerationPrompt: titleGenerationPrompt ? titleGenerationPrompt : undefined
});
});
};
};
...
@@ -186,7 +187,7 @@
...
@@ -186,7 +187,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 py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
bind:value={titleAutoGenerateModel}
bind:value={titleAutoGenerateModel}
placeholder="Select a model"
placeholder="Select a model"
>
>
...
@@ -200,35 +201,12 @@
...
@@ -200,35 +201,12 @@
{/each}
{/each}
</select>
</select>
</div>
</div>
<button
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
on:click={() => {
saveSettings({
titleAutoGenerateModel:
titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined
});
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3.5 h-3.5"
>
<path
fill-rule="evenodd"
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
<div class="mt-3">
<div class="mt-3
mr-2
">
<div class=" mb-2.5 text-sm font-medium">Title Generation Prompt</div>
<div class=" mb-2.5 text-sm font-medium">Title Generation Prompt</div>
<textarea
<textarea
bind:value={titleGenerationPrompt}
bind:value={titleGenerationPrompt}
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none resize-none"
class="w-full rounded
-lg
p-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none resize-none"
rows="3"
rows="3"
/>
/>
</div>
</div>
...
@@ -321,7 +299,7 @@
...
@@ -321,7 +299,7 @@
<div class="flex justify-end pt-3 text-sm font-medium">
<div class="flex justify-end pt-3 text-sm font-medium">
<button
<button
class=" px-4 py-2 bg-emerald-
6
00 hover:bg-emerald-
7
00 text-gray-100 transition rounded"
class=" px-4 py-2 bg-emerald-
7
00 hover:bg-emerald-
8
00 text-gray-100 transition rounded
-lg
"
type="submit"
type="submit"
>
>
Save
Save
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
89634046
...
@@ -14,6 +14,7 @@
...
@@ -14,6 +14,7 @@
import { splitStream } from '$lib/utils';
import { splitStream } from '$lib/utils';
import { onMount } from 'svelte';
import { onMount } from 'svelte';
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let getModels: Function;
export let getModels: Function;
...
@@ -27,6 +28,7 @@
...
@@ -27,6 +28,7 @@
let liteLLMAPIBase = '';
let liteLLMAPIBase = '';
let liteLLMAPIKey = '';
let liteLLMAPIKey = '';
let liteLLMRPM = '';
let liteLLMRPM = '';
let liteLLMMaxTokens = '';
let deleteLiteLLMModelId = '';
let deleteLiteLLMModelId = '';
...
@@ -36,6 +38,10 @@
...
@@ -36,6 +38,10 @@
let OLLAMA_URLS = [];
let OLLAMA_URLS = [];
let selectedOllamaUrlIdx: string | null = null;
let selectedOllamaUrlIdx: string | null = null;
let updateModelId = null;
let updateProgress = null;
let showExperimentalOllama = false;
let showExperimentalOllama = false;
let ollamaVersion = '';
let ollamaVersion = '';
const MAX_PARALLEL_DOWNLOADS = 3;
const MAX_PARALLEL_DOWNLOADS = 3;
...
@@ -60,6 +66,71 @@
...
@@ -60,6 +66,71 @@
let deleteModelTag = '';
let deleteModelTag = '';
const updateModelsHandler = async () => {
for (const model of $models.filter(
(m) =>
m.size != null &&
(selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))
)) {
console.log(model);
updateModelId = model.id;
const res = await pullModel(localStorage.token, model.id, selectedOllamaUrlIdx).catch(
(error) => {
toast.error(error);
return null;
}
);
if (res) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
while (true) {
try {
const { value, done } = await reader.read();
if (done) break;
let lines = value.split('\n');
for (const line of lines) {
if (line !== '') {
let data = JSON.parse(line);
console.log(data);
if (data.error) {
throw data.error;
}
if (data.detail) {
throw data.detail;
}
if (data.status) {
if (data.digest) {
updateProgress = 0;
if (data.completed) {
updateProgress = Math.round((data.completed / data.total) * 1000) / 10;
} else {
updateProgress = 100;
}
} else {
toast.success(data.status);
}
}
}
}
} catch (error) {
console.log(error);
}
}
}
}
updateModelId = null;
updateProgress = null;
};
const pullModelHandler = async () => {
const pullModelHandler = async () => {
const sanitizedModelTag = modelTag.trim();
const sanitizedModelTag = modelTag.trim();
if (modelDownloadStatus[sanitizedModelTag]) {
if (modelDownloadStatus[sanitizedModelTag]) {
...
@@ -326,7 +397,8 @@
...
@@ -326,7 +397,8 @@
model: liteLLMModel,
model: liteLLMModel,
api_base: liteLLMAPIBase,
api_base: liteLLMAPIBase,
api_key: liteLLMAPIKey,
api_key: liteLLMAPIKey,
rpm: liteLLMRPM
rpm: liteLLMRPM,
max_tokens: liteLLMMaxTokens
}).catch((error) => {
}).catch((error) => {
toast.error(error);
toast.error(error);
return null;
return null;
...
@@ -346,6 +418,7 @@
...
@@ -346,6 +418,7 @@
liteLLMAPIBase = '';
liteLLMAPIBase = '';
liteLLMAPIKey = '';
liteLLMAPIKey = '';
liteLLMRPM = '';
liteLLMRPM = '';
liteLLMMaxTokens = '';
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
models.set(await getModels());
models.set(await getModels());
...
@@ -376,7 +449,7 @@
...
@@ -376,7 +449,7 @@
return [];
return [];
});
});
if (OLLAMA_URLS.length >
1
) {
if (OLLAMA_URLS.length >
0
) {
selectedOllamaUrlIdx = 0;
selectedOllamaUrlIdx = 0;
}
}
...
@@ -391,18 +464,51 @@
...
@@ -391,18 +464,51 @@
<div class="space-y-2 pr-1.5">
<div class="space-y-2 pr-1.5">
<div class="text-sm font-medium">Manage Ollama Models</div>
<div class="text-sm font-medium">Manage Ollama Models</div>
{#if OLLAMA_URLS.length > 1}
{#if OLLAMA_URLS.length > 0}
<div class="flex-1 pb-1">
<div class="flex gap-2">
<select
<div class="flex-1 pb-1">
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<select
bind:value={selectedOllamaUrlIdx}
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Select an Ollama instance"
bind:value={selectedOllamaUrlIdx}
>
placeholder="Select an Ollama instance"
{#each OLLAMA_URLS as url, idx}
>
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
{#each OLLAMA_URLS as url, idx}
{/each}
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
</select>
{/each}
</select>
</div>
<div>
<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-100 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();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M7 1a.75.75 0 0 1 .75.75V6h-1.5V1.75A.75.75 0 0 1 7 1ZM6.25 6v2.94L5.03 7.72a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06L7.75 8.94V6H10a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.25Z"
/>
<path
d="M4.268 14A2 2 0 0 0 6 15h6a2 2 0 0 0 2-2v-3a2 2 0 0 0-1-1.732V11a3 3 0 0 1-3 3H4.268Z"
/>
</svg>
</button>
</Tooltip>
</div>
</div>
</div>
</div>
{#if updateModelId}
Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}
{/if}
{/if}
{/if}
<div class="space-y-2">
<div class="space-y-2">
...
@@ -467,12 +573,14 @@
...
@@ -467,12 +573,14 @@
</button>
</button>
</div>
</div>
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
<div>
To access the available model names for downloading, <a
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
class=" text-gray-500 dark:text-gray-300 font-medium underline"
To access the available model names for downloading, <a
href="https://ollama.com/library"
class=" text-gray-500 dark:text-gray-300 font-medium underline"
target="_blank">click here.</a
href="https://ollama.com/library"
>
target="_blank">click here.</a
>
</div>
</div>
</div>
{#if Object.keys(modelDownloadStatus).length > 0}
{#if Object.keys(modelDownloadStatus).length > 0}
...
@@ -589,7 +697,7 @@
...
@@ -589,7 +697,7 @@
on:change={() => {
on:change={() => {
console.log(modelInputFile);
console.log(modelInputFile);
}}
}}
accept=".gguf"
accept=".gguf
,.safetensors
"
required
required
hidden
hidden
/>
/>
...
@@ -722,245 +830,193 @@
...
@@ -722,245 +830,193 @@
<div class=" space-y-3">
<div class=" space-y-3">
<div class="mt-2 space-y-3 pr-1.5">
<div class="mt-2 space-y-3 pr-1.5">
<div>
<div>
<div class=" mb-2 text-sm font-medium">Manage LiteLLM Models</div>
<div class="mb-2">
<div>
<div class="flex justify-between items-center text-xs">
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">
Add a m
odel</div>
<div class=" text-sm font-medium">
Manage LiteLLM M
odel
s
</div>
<button
<button
class=" text-xs font-medium text-gray-500"
class=" text-xs font-medium text-gray-500"
type="button"
type="button"
on:click={() => {
on:click={() => {
showLiteLLM
Params
= !showLiteLLM
Params
;
showLiteLLM = !showLiteLLM;
}}>{showLiteLLM
Params
? 'Hide
Additional Params' : 'Show Additional Params
'}</button
}}>{showLiteLLM ? 'Hide
' : 'Show
'}</button
>
>
</div>
</div>
</div>
</div>
<div class="my-2 space-y-2">
{#if showLiteLLM}
<div class="flex w-full mb-1.5">
<div>
<div class="flex-1 mr-2">
<div class="flex justify-between items-center text-xs">
<input
<div class=" text-sm font-medium">Add a model</div>
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<button
placeholder="Enter LiteLLM Model (litellm_params.model)"
class=" text-xs font-medium text-gray-500"
bind:value={liteLLMModel}
type="button"
autocomplete="off"
on:click={() => {
/>
showLiteLLMParams = !showLiteLLMParams;
}}
>{showLiteLLMParams ? 'Hide Additional Params' : 'Show Additional Params'}</button
>
</div>
</div>
</div>
<button
<div class="my-2 space-y-2">
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"
<div class="flex w-full mb-1.5">
on:click={() => {
<div class="flex-1 mr-2">
addLiteLLMModelHandler();
<input
}}
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
>
placeholder="Enter LiteLLM Model (litellm_params.model)"
<svg
bind:value={liteLLMModel}
xmlns="http://www.w3.org/2000/svg"
autocomplete="off"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
/>
</svg>
</div>
</button>
</div>
{#if showLiteLLMParams}
<button
<div>
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"
<div class=" mb-1.5 text-sm font-medium">Model Name</div>
on:click={() => {
<div class="flex w-full">
addLiteLLMModelHandler();
<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"
<svg
placeholder="Enter Model Name (model_name)"
xmlns="http://www.w3.org/2000/svg"
bind:value={liteLLMModelName}
viewBox="0 0 16 16"
autocomplete="off"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
/>
</
div
>
</
svg
>
</
div
>
</
button
>
</div>
</div>
<div>
{#if showLiteLLMParams}
<div class=" mb-1.5 text-sm font-medium">API Base URL</div>
<div>
<div class="flex w-full">
<div class=" mb-1.5 text-sm font-medium">Model Name</div>
<div class="flex-1">
<div class="flex w-full">
<input
<div class="flex-1">
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<input
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={liteLLMAPIBase}
placeholder="Enter Model Name (model_name)"
autocomplete="off"
bind:value={liteLLMModelName}
/>
autocomplete="off"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div class=" mb-1.5 text-sm font-medium">API Key</div>
<div class=" mb-1.5 text-sm font-medium">API Base URL</div>
<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 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API Key (litellm_params.api_key)"
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)"
bind:value={liteLLMAPIKey}
bind:value={liteLLMAPIBase}
autocomplete="off"
autocomplete="off"
/>
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div class="mb-1.5 text-sm font-medium">API RPM</div>
<div class=" mb-1.5 text-sm font-medium">API Key</div>
<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 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)"
placeholder="Enter LiteLLM API Key (litellm_params.api_key)"
bind:value={liteLLMRPM}
bind:value={liteLLMAPIKey}
autocomplete="off"
autocomplete="off"
/>
/>
</div>
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
Not sure what to add?
<a
class=" text-gray-300 font-medium underline"
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
target="_blank"
>
Click here for help.
</a>
</div>
<div>
<div class=" mb-2.5 text-sm font-medium">Delete a model</div>
<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"
bind:value={deleteLiteLLMModelId}
placeholder="Select a model"
>
{#if !deleteLiteLLMModelId}
<option value="" disabled selected>Select a model</option>
{/if}
{#each liteLLMModelInfo as model}
<option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700"
>{model.model_name}</option
>
{/each}
</select>
</div>
<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"
on:click={() => {
deleteLiteLLMModelHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- <div class="mt-2 space-y-3 pr-1.5">
<div>
<div class=" mb-2.5 text-sm font-medium">Add LiteLLM Model</div>
<div class="flex w-full mb-2">
<div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
placeholder="Enter LiteLLM Model (e.g. ollama/mistral)"
bind:value={liteLLMModel}
autocomplete="off"
/>
</div>
</div>
<div class="flex justify-between items-center text-sm">
<div>
<div class=" font-medium">Advanced Model Params</div>
<div class="mb-1.5 text-sm font-medium">API RPM</div>
<button
<div class="flex w-full">
class=" text-xs font-medium text-gray-500"
<div class="flex-1">
type="button"
<input
on:click={() => {
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
showLiteLLMParams = !showLiteLLMParams;
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)"
}}>{showLiteLLMParams ? 'Hide' : 'Show'}</button
bind:value={liteLLMRPM}
>
autocomplete="off"
</div>
/>
</div>
</div>
</div>
{#if showLiteLLMParams}
<div>
<div>
<div class="mb-1.5 text-sm font-medium">Max Tokens</div>
<div class=" mb-2.5 text-sm font-medium">LiteLLM API Key</div>
<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 py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
placeholder="Enter Max Tokens (litellm_params.max_tokens)"
placeholder="Enter LiteLLM API Key (e.g. os.environ/AZURE_API_KEY_CA)"
bind:value={liteLLMMaxTokens}
bind:value={liteLLMAPIKey}
type="number"
autocomplete="off"
min="1"
/>
autocomplete="off"
/>
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
</div>
<div>
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
<div class=" mb-2.5 text-sm font-medium">LiteLLM API Base URL</div>
Not sure what to add?
<div class="flex w-full">
<a
<div class="flex-1">
class=" text-gray-300 font-medium underline"
<input
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
target="_blank"
placeholder="Enter LiteLLM API Base URL"
>
bind:value={liteLLMAPIBase}
Click here for help.
autocomplete="off"
</a>
/>
</div>
</div>
</div>
</div>
<div>
<div>
<div class=" mb-2.5 text-sm font-medium">
LiteLLM API RPM
</div>
<div class=" mb-2.5 text-sm font-medium">
Delete a model
</div>
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1">
<div class="flex-1 mr-2">
<input
<select
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API RPM"
bind:value={deleteLiteLLMModelId}
bind:value={liteLLMRPM}
placeholder="Select a model"
autocomplete="off"
>
/>
{#if !deleteLiteLLMModelId}
<option value="" disabled selected>Select a model</option>
{/if}
{#each liteLLMModelInfo as model}
<option value={model.model_info.id} class="bg-gray-100 dark:bg-gray-700"
>{model.model_name}</option
>
{/each}
</select>
</div>
</div>
<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"
on:click={() => {
deleteLiteLLMModelHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
{/if}
{/if}
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Not sure what to add?
<a
class=" text-gray-300 font-medium underline"
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
target="_blank"
>
Click here for help.
</a>
</div>
</div>
</div>
</div>
-->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
src/lib/components/chat/SettingsModal.svelte
View file @
89634046
...
@@ -326,7 +326,7 @@
...
@@ -326,7 +326,7 @@
{getModels}
{getModels}
{saveSettings}
{saveSettings}
on:save={() => {
on:save={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'models'}
{:else if selectedTab === 'models'}
...
@@ -335,28 +335,28 @@
...
@@ -335,28 +335,28 @@
<Connections
<Connections
{getModels}
{getModels}
on:save={() => {
on:save={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'interface'}
{:else if selectedTab === 'interface'}
<Interface
<Interface
{saveSettings}
{saveSettings}
on:save={() => {
on:save={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'audio'}
{:else if selectedTab === 'audio'}
<Audio
<Audio
{saveSettings}
{saveSettings}
on:save={() => {
on:save={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'images'}
{:else if selectedTab === 'images'}
<Images
<Images
{saveSettings}
{saveSettings}
on:save={() => {
on:save={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'chats'}
{:else if selectedTab === 'chats'}
...
@@ -364,7 +364,7 @@
...
@@ -364,7 +364,7 @@
{:else if selectedTab === 'account'}
{:else if selectedTab === 'account'}
<Account
<Account
saveHandler={() => {
saveHandler={() => {
show = false
;
toast.success('Settings saved successfully!')
;
}}
}}
/>
/>
{:else if selectedTab === 'about'}
{:else if selectedTab === 'about'}
...
...
src/lib/components/common/Image.svelte
View file @
89634046
<script lang="ts">
<script lang="ts">
import { WEBUI_BASE_URL } from '$lib/constants';
import ImagePreview from './ImagePreview.svelte';
import ImagePreview from './ImagePreview.svelte';
export let src = '';
export let src = '';
export let alt = '';
export let alt = '';
let _src = '';
$: _src = src.startsWith('/') ? `${WEBUI_BASE_URL}${src}` : src;
let showImagePreview = false;
let showImagePreview = false;
</script>
</script>
<ImagePreview bind:show={showImagePreview}
{
src} {alt} />
<ImagePreview bind:show={showImagePreview}
src={_
src} {alt} />
<button
<button
on:click={() => {
on:click={() => {
console.log('image preview');
console.log('image preview');
showImagePreview = true;
showImagePreview = true;
}}
}}
>
>
<img
{
src} {alt} class=" max-h-96 rounded-lg" draggable="false" />
<img
src={_
src} {alt} class=" max-h-96 rounded-lg" draggable="false" />
</button>
</button>
src/lib/components/documents/Settings/General.svelte
View file @
89634046
<script lang="ts">
<script lang="ts">
import { getDocs } from '$lib/apis/documents';
import { getDocs } from '$lib/apis/documents';
import {
import {
getChunkParams,
getRAGConfig,
updateRAGConfig,
getQuerySettings,
getQuerySettings,
scanDocs,
scanDocs,
updateChunkParams,
updateQuerySettings
updateQuerySettings
} from '$lib/apis/rag';
} from '$lib/apis/rag';
import { documents } from '$lib/stores';
import { documents } from '$lib/stores';
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
let chunkSize = 0;
let chunkSize = 0;
let chunkOverlap = 0;
let chunkOverlap = 0;
let pdfExtractImages = true;
let querySettings = {
let querySettings = {
template: '',
template: '',
...
@@ -35,16 +36,24 @@
...
@@ -35,16 +36,24 @@
};
};
const submitHandler = async () => {
const submitHandler = async () => {
const res = await updateChunkParams(localStorage.token, chunkSize, chunkOverlap);
const res = await updateRAGConfig(localStorage.token, {
pdf_extract_images: pdfExtractImages,
chunk: {
chunk_overlap: chunkOverlap,
chunk_size: chunkSize
}
});
querySettings = await updateQuerySettings(localStorage.token, querySettings);
querySettings = await updateQuerySettings(localStorage.token, querySettings);
};
};
onMount(async () => {
onMount(async () => {
const res = await get
ChunkParams
(localStorage.token);
const res = await get
RAGConfig
(localStorage.token);
if (res) {
if (res) {
chunkSize = res.chunk_size;
pdfExtractImages = res.pdf_extract_images;
chunkOverlap = res.chunk_overlap;
chunkSize = res.chunk.chunk_size;
chunkOverlap = res.chunk.chunk_overlap;
}
}
querySettings = await getQuerySettings(localStorage.token);
querySettings = await getQuerySettings(localStorage.token);
...
@@ -124,82 +133,100 @@
...
@@ -124,82 +133,100 @@
<hr class=" dark:border-gray-700" />
<hr class=" dark:border-gray-700" />
<div class=" ">
<div class=" space-y-3">
<div class=" text-sm font-medium">Chunk Params</div>
<div class=" space-y-3">
<div class=" text-sm font-medium">Chunk Params</div>
<div class=" flex">
<div class=" flex w-full justify-between">
<div class=" flex gap-2">
<div class="self-center text-xs font-medium min-w-fit">Chunk Size</div>
<div class=" flex w-full justify-between gap-2">
<div class="self-center text-xs font-medium min-w-fit">Chunk Size</div>
<div class="self-center p-3">
<input
<div class="self-center">
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
<input
type="number"
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
placeholder="Enter Chunk Size"
type="number"
bind:value={chunkSize}
placeholder="Enter Chunk Size"
autocomplete="off"
bind:value={chunkSize}
min="0"
autocomplete="off"
/>
min="0"
/>
</div>
</div>
</div>
</div>
<div class="flex w-full">
<div class="flex w-full gap-2">
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class="self-center p-3">
<div class="self-center">
<input
<input
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
type="number"
placeholder="Enter Chunk Overlap"
placeholder="Enter Chunk Overlap"
bind:value={chunkOverlap}
bind:value={chunkOverlap}
autocomplete="off"
autocomplete="off"
min="0"
min="0"
/>
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class=" text-sm font-medium">Query Params</div>
<div class=" flex">
<div>
<div class=" flex w-full justify-between">
<div class="flex justify-between items-center text-xs">
<div class="self-center text-xs font-medium flex-1">Top K</div>
<div class=" text-xs font-medium">PDF Extract Images (OCR)</div>
<div class="self-center p-3">
<button
<input
class=" text-xs font-medium text-gray-500"
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="button"
type="number"
on:click={() => {
placeholder="Enter Top K"
pdfExtractImages = !pdfExtractImages;
bind:value={querySettings.k}
}}>{pdfExtractImages ? 'On' : 'Off'}</button
autocomplete="off"
>
min="0"
/>
</div>
</div>
</div>
</div>
<!-- <div class="flex w-full">
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class="self-center p-3">
<input
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
placeholder="Enter Chunk Overlap"
bind:value={chunkOverlap}
autocomplete="off"
min="0"
/>
</div>
</div> -->
</div>
</div>
<div>
<div>
<div class=" mb-2.5 text-sm font-medium">RAG Template</div>
<div class=" text-sm font-medium">Query Params</div>
<textarea
bind:value={querySettings.template}
<div class=" flex py-2">
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
<div class=" flex w-full justify-between gap-2">
rows="4"
<div class="self-center text-xs font-medium flex-1">Top K</div>
/>
<div class="self-center">
<input
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
placeholder="Enter Top K"
bind:value={querySettings.k}
autocomplete="off"
min="0"
/>
</div>
</div>
<!-- <div class="flex w-full">
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
<div class="self-center p-3">
<input
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="number"
placeholder="Enter Chunk Overlap"
bind:value={chunkOverlap}
autocomplete="off"
min="0"
/>
</div>
</div> -->
</div>
<div>
<div class=" mb-2.5 text-sm font-medium">RAG Template</div>
<textarea
bind:value={querySettings.template}
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
rows="4"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
src/lib/components/layout/Sidebar.svelte
View file @
89634046
...
@@ -61,12 +61,16 @@
...
@@ -61,12 +61,16 @@
};
};
const editChatTitle = async (id, _title) => {
const editChatTitle = async (id, _title) => {
title = _title;
if (_title === '') {
toast.error('Title cannot be an empty string.');
await updateChatById(localStorage.token, id, {
} else {
title: _title
title = _title;
});
await chats.set(await getChatList(localStorage.token));
await updateChatById(localStorage.token, id, {
title: _title
});
await chats.set(await getChatList(localStorage.token));
}
};
};
const deleteChat = async (id) => {
const deleteChat = async (id) => {
...
@@ -388,12 +392,13 @@
...
@@ -388,12 +392,13 @@
show = false;
show = false;
}
}
}}
}}
draggable="false"
>
>
<div class=" flex self-center flex-1 w-full">
<div class=" flex self-center flex-1 w-full">
<div
<div
class=" text-left self-center overflow-hidden {chat.id === $chatId
class=" text-left self-center overflow-hidden {chat.id === $chatId
? 'w-[160px]'
? 'w-[160px]'
: 'w-full'} "
: 'w-full'}
h-[20px]
"
>
>
{chat.title}
{chat.title}
</div>
</div>
...
...
src/routes/(app)/+page.svelte
View file @
89634046
...
@@ -232,53 +232,6 @@
...
@@ -232,53 +232,6 @@
const
sendPrompt
=
async
(
prompt
,
parentId
)
=>
{
const
sendPrompt
=
async
(
prompt
,
parentId
)
=>
{
const
_chatId
=
JSON
.
parse
(
JSON
.
stringify
($
chatId
));
const
_chatId
=
JSON
.
parse
(
JSON
.
stringify
($
chatId
));
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
console
.
log
(
docs
);
if
(
docs
.
length
>
0
)
{
processing
=
'Reading'
;
const
query
=
history
.
messages
[
parentId
].
content
;
let
relevantContexts
=
await
Promise
.
all
(
docs
.
map
(
async
(
doc
)
=>
{
if
(
doc
.
type
===
'collection'
)
{
return
await
queryCollection
(
localStorage
.
token
,
doc
.
collection_names
,
query
).
catch
(
(
error
)
=>
{
console
.
log
(
error
);
return
null
;
}
);
}
else
{
return
await
queryDoc
(
localStorage
.
token
,
doc
.
collection_name
,
query
).
catch
((
error
)
=>
{
console
.
log
(
error
);
return
null
;
});
}
})
);
relevantContexts
=
relevantContexts
.
filter
((
context
)
=>
context
);
const
contextString
=
relevantContexts
.
reduce
((
a
,
context
,
i
,
arr
)
=>
{
return
`${
a
}${
context
.
documents
.
join
(
' '
)}\
n
`;
},
''
);
console
.
log
(
contextString
);
history
.
messages
[
parentId
].
raContent
=
await
RAGTemplate
(
localStorage
.
token
,
contextString
,
query
);
history
.
messages
[
parentId
].
contexts
=
relevantContexts
;
await
tick
();
processing
=
''
;
}
await
Promise
.
all
(
await
Promise
.
all
(
selectedModels
.
map
(
async
(
modelId
)
=>
{
selectedModels
.
map
(
async
(
modelId
)
=>
{
const
model
=
$
models
.
filter
((
m
)
=>
m
.
id
===
modelId
).
at
(
0
);
const
model
=
$
models
.
filter
((
m
)
=>
m
.
id
===
modelId
).
at
(
0
);
...
@@ -342,15 +295,25 @@
...
@@ -342,15 +295,25 @@
...
messages
...
messages
]
]
.
filter
((
message
)
=>
message
)
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
.
map
((
message
,
idx
,
arr
)
=>
{
role
:
message
.
role
,
//
Prepare
the
base
message
object
content
:
arr
.
length
-
2
!== idx ? message.content : message?.raContent ?? message.content,
const
baseMessage
=
{
...(
message
.
files
&&
{
role
:
message
.
role
,
images
:
message
.
files
content
:
arr
.
length
-
2
!== idx ? message.content : message?.raContent ?? message.content
.
filter
((
file
)
=>
file
.
type
===
'image'
)
};
.
map
((
file
)
=>
file
.
url
.
slice
(
file
.
url
.
indexOf
(
','
)
+
1
))
})
//
Extract
and
format
image
URLs
if
any
exist
}));
const
imageUrls
=
message
.
files
?.
filter
((
file
)
=>
file
.
type
===
'image'
)
.
map
((
file
)
=>
file
.
url
.
slice
(
file
.
url
.
indexOf
(
','
)
+
1
));
//
Add
images
array
only
if
it
contains
elements
if
(
imageUrls
&&
imageUrls
.
length
>
0
)
{
baseMessage
.
images
=
imageUrls
;
}
return
baseMessage
;
});
let
lastImageIndex
=
-
1
;
let
lastImageIndex
=
-
1
;
...
@@ -368,6 +331,13 @@
...
@@ -368,6 +331,13 @@
}
}
});
});
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
const
[
res
,
controller
]
=
await
generateChatCompletion
(
localStorage
.
token
,
{
const
[
res
,
controller
]
=
await
generateChatCompletion
(
localStorage
.
token
,
{
model
:
model
,
model
:
model
,
messages
:
messagesBody
,
messages
:
messagesBody
,
...
@@ -375,7 +345,8 @@
...
@@ -375,7 +345,8 @@
...($
settings
.
options
??
{})
...($
settings
.
options
??
{})
},
},
format
:
$
settings
.
requestFormat
??
undefined
,
format
:
$
settings
.
requestFormat
??
undefined
,
keep_alive
:
$
settings
.
keepAlive
??
undefined
keep_alive
:
$
settings
.
keepAlive
??
undefined
,
docs
:
docs
.
length
>
0
?
docs
:
undefined
});
});
if
(
res
&&
res
.
ok
)
{
if
(
res
&&
res
.
ok
)
{
...
@@ -535,6 +506,15 @@
...
@@ -535,6 +506,15 @@
const
responseMessage
=
history
.
messages
[
responseMessageId
];
const
responseMessage
=
history
.
messages
[
responseMessageId
];
scrollToBottom
();
scrollToBottom
();
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
console
.
log
(
docs
);
const
res
=
await
generateOpenAIChatCompletion
(
const
res
=
await
generateOpenAIChatCompletion
(
localStorage
.
token
,
localStorage
.
token
,
{
{
...
@@ -583,7 +563,8 @@
...
@@ -583,7 +563,8 @@
top_p
:
$
settings
?.
options
?.
top_p
??
undefined
,
top_p
:
$
settings
?.
options
?.
top_p
??
undefined
,
num_ctx
:
$
settings
?.
options
?.
num_ctx
??
undefined
,
num_ctx
:
$
settings
?.
options
?.
num_ctx
??
undefined
,
frequency_penalty
:
$
settings
?.
options
?.
repeat_penalty
??
undefined
,
frequency_penalty
:
$
settings
?.
options
?.
repeat_penalty
??
undefined
,
max_tokens
:
$
settings
?.
options
?.
num_predict
??
undefined
max_tokens
:
$
settings
?.
options
?.
num_predict
??
undefined
,
docs
:
docs
.
length
>
0
?
docs
:
undefined
},
},
model
.
source
===
'litellm'
?
`${
LITELLM_API_BASE_URL
}/
v1
`
:
`${
OPENAI_API_BASE_URL
}`
model
.
source
===
'litellm'
?
`${
LITELLM_API_BASE_URL
}/
v1
`
:
`${
OPENAI_API_BASE_URL
}`
);
);
...
@@ -694,7 +675,12 @@
...
@@ -694,7 +675,12 @@
if
(
messages
.
length
==
2
)
{
if
(
messages
.
length
==
2
)
{
window
.
history
.
replaceState
(
history
.
state
,
''
,
`/
c
/${
_chatId
}`);
window
.
history
.
replaceState
(
history
.
state
,
''
,
`/
c
/${
_chatId
}`);
await
setChatTitle
(
_chatId
,
userPrompt
);
if
($
settings
?.
titleAutoGenerateModel
)
{
await
generateChatTitle
(
_chatId
,
userPrompt
);
}
else
{
await
setChatTitle
(
_chatId
,
userPrompt
);
}
}
}
};
};
...
...
src/routes/(app)/c/[id]/+page.svelte
View file @
89634046
...
@@ -245,53 +245,6 @@
...
@@ -245,53 +245,6 @@
const
sendPrompt
=
async
(
prompt
,
parentId
)
=>
{
const
sendPrompt
=
async
(
prompt
,
parentId
)
=>
{
const
_chatId
=
JSON
.
parse
(
JSON
.
stringify
($
chatId
));
const
_chatId
=
JSON
.
parse
(
JSON
.
stringify
($
chatId
));
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
console
.
log
(
docs
);
if
(
docs
.
length
>
0
)
{
processing
=
'Reading'
;
const
query
=
history
.
messages
[
parentId
].
content
;
let
relevantContexts
=
await
Promise
.
all
(
docs
.
map
(
async
(
doc
)
=>
{
if
(
doc
.
type
===
'collection'
)
{
return
await
queryCollection
(
localStorage
.
token
,
doc
.
collection_names
,
query
).
catch
(
(
error
)
=>
{
console
.
log
(
error
);
return
null
;
}
);
}
else
{
return
await
queryDoc
(
localStorage
.
token
,
doc
.
collection_name
,
query
).
catch
((
error
)
=>
{
console
.
log
(
error
);
return
null
;
});
}
})
);
relevantContexts
=
relevantContexts
.
filter
((
context
)
=>
context
);
const
contextString
=
relevantContexts
.
reduce
((
a
,
context
,
i
,
arr
)
=>
{
return
`${
a
}${
context
.
documents
.
join
(
' '
)}\
n
`;
},
''
);
console
.
log
(
contextString
);
history
.
messages
[
parentId
].
raContent
=
await
RAGTemplate
(
localStorage
.
token
,
contextString
,
query
);
history
.
messages
[
parentId
].
contexts
=
relevantContexts
;
await
tick
();
processing
=
''
;
}
await
Promise
.
all
(
await
Promise
.
all
(
selectedModels
.
map
(
async
(
modelId
)
=>
{
selectedModels
.
map
(
async
(
modelId
)
=>
{
const
model
=
$
models
.
filter
((
m
)
=>
m
.
id
===
modelId
).
at
(
0
);
const
model
=
$
models
.
filter
((
m
)
=>
m
.
id
===
modelId
).
at
(
0
);
...
@@ -355,15 +308,25 @@
...
@@ -355,15 +308,25 @@
...
messages
...
messages
]
]
.
filter
((
message
)
=>
message
)
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
.
map
((
message
,
idx
,
arr
)
=>
{
role
:
message
.
role
,
//
Prepare
the
base
message
object
content
:
arr
.
length
-
2
!== idx ? message.content : message?.raContent ?? message.content,
const
baseMessage
=
{
...(
message
.
files
&&
{
role
:
message
.
role
,
images
:
message
.
files
content
:
arr
.
length
-
2
!== idx ? message.content : message?.raContent ?? message.content
.
filter
((
file
)
=>
file
.
type
===
'image'
)
};
.
map
((
file
)
=>
file
.
url
.
slice
(
file
.
url
.
indexOf
(
','
)
+
1
))
})
//
Extract
and
format
image
URLs
if
any
exist
}));
const
imageUrls
=
message
.
files
?.
filter
((
file
)
=>
file
.
type
===
'image'
)
.
map
((
file
)
=>
file
.
url
.
slice
(
file
.
url
.
indexOf
(
','
)
+
1
));
//
Add
images
array
only
if
it
contains
elements
if
(
imageUrls
&&
imageUrls
.
length
>
0
)
{
baseMessage
.
images
=
imageUrls
;
}
return
baseMessage
;
});
let
lastImageIndex
=
-
1
;
let
lastImageIndex
=
-
1
;
...
@@ -381,6 +344,13 @@
...
@@ -381,6 +344,13 @@
}
}
});
});
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
const
[
res
,
controller
]
=
await
generateChatCompletion
(
localStorage
.
token
,
{
const
[
res
,
controller
]
=
await
generateChatCompletion
(
localStorage
.
token
,
{
model
:
model
,
model
:
model
,
messages
:
messagesBody
,
messages
:
messagesBody
,
...
@@ -388,7 +358,8 @@
...
@@ -388,7 +358,8 @@
...($
settings
.
options
??
{})
...($
settings
.
options
??
{})
},
},
format
:
$
settings
.
requestFormat
??
undefined
,
format
:
$
settings
.
requestFormat
??
undefined
,
keep_alive
:
$
settings
.
keepAlive
??
undefined
keep_alive
:
$
settings
.
keepAlive
??
undefined
,
docs
:
docs
.
length
>
0
?
docs
:
undefined
});
});
if
(
res
&&
res
.
ok
)
{
if
(
res
&&
res
.
ok
)
{
...
@@ -548,6 +519,15 @@
...
@@ -548,6 +519,15 @@
const
responseMessage
=
history
.
messages
[
responseMessageId
];
const
responseMessage
=
history
.
messages
[
responseMessageId
];
scrollToBottom
();
scrollToBottom
();
const
docs
=
messages
.
filter
((
message
)
=>
message
?.
files
??
null
)
.
map
((
message
)
=>
message
.
files
.
filter
((
item
)
=>
item
.
type
===
'doc'
||
item
.
type
===
'collection'
)
)
.
flat
(
1
);
console
.
log
(
docs
);
const
res
=
await
generateOpenAIChatCompletion
(
const
res
=
await
generateOpenAIChatCompletion
(
localStorage
.
token
,
localStorage
.
token
,
{
{
...
@@ -596,7 +576,8 @@
...
@@ -596,7 +576,8 @@
top_p
:
$
settings
?.
options
?.
top_p
??
undefined
,
top_p
:
$
settings
?.
options
?.
top_p
??
undefined
,
num_ctx
:
$
settings
?.
options
?.
num_ctx
??
undefined
,
num_ctx
:
$
settings
?.
options
?.
num_ctx
??
undefined
,
frequency_penalty
:
$
settings
?.
options
?.
repeat_penalty
??
undefined
,
frequency_penalty
:
$
settings
?.
options
?.
repeat_penalty
??
undefined
,
max_tokens
:
$
settings
?.
options
?.
num_predict
??
undefined
max_tokens
:
$
settings
?.
options
?.
num_predict
??
undefined
,
docs
:
docs
.
length
>
0
?
docs
:
undefined
},
},
model
.
source
===
'litellm'
?
`${
LITELLM_API_BASE_URL
}/
v1
`
:
`${
OPENAI_API_BASE_URL
}`
model
.
source
===
'litellm'
?
`${
LITELLM_API_BASE_URL
}/
v1
`
:
`${
OPENAI_API_BASE_URL
}`
);
);
...
@@ -710,6 +691,7 @@
...
@@ -710,6 +691,7 @@
await
setChatTitle
(
_chatId
,
userPrompt
);
await
setChatTitle
(
_chatId
,
userPrompt
);
}
}
};
};
const
stopResponse
=
()
=>
{
const
stopResponse
=
()
=>
{
stopResponseFlag
=
true
;
stopResponseFlag
=
true
;
console
.
log
(
'stopResponse'
);
console
.
log
(
'stopResponse'
);
...
...
src/routes/(app)/playground/+page.svelte
View file @
89634046
...
@@ -267,7 +267,7 @@
...
@@ -267,7 +267,7 @@
<div class="min-h-screen max-h-[100dvh] w-full flex justify-center dark:text-white">
<div class="min-h-screen max-h-[100dvh] w-full flex justify-center dark:text-white">
<div class=" flex flex-col justify-between w-full overflow-y-auto h-[100dvh]">
<div class=" flex flex-col justify-between w-full overflow-y-auto h-[100dvh]">
<div class="max-w-2xl mx-auto w-full px-3
p-3
md:px-0 h-full">
<div class="max-w-2xl mx-auto w-full px-3 md:px-0
my-10
h-full">
<div class=" flex flex-col h-full">
<div class=" flex flex-col h-full">
<div class="flex flex-col justify-between mb-2.5 gap-1">
<div class="flex flex-col justify-between mb-2.5 gap-1">
<div class="flex justify-between items-center gap-2">
<div class="flex justify-between items-center gap-2">
...
...
Prev
1
2
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