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
7031aa14
Unverified
Commit
7031aa14
authored
Mar 07, 2024
by
Ased Mammad
Committed by
GitHub
Mar 07, 2024
Browse files
Merge branch 'dev' into feat/add-i18n
parents
08aa60b3
a9010318
Changes
30
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
506 additions
and
389 deletions
+506
-389
src/lib/components/chat/Messages.svelte
src/lib/components/chat/Messages.svelte
+117
-72
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+246
-220
src/lib/components/chat/Messages/UserMessage.svelte
src/lib/components/chat/Messages/UserMessage.svelte
+54
-46
src/lib/components/chat/Settings/Connections.svelte
src/lib/components/chat/Settings/Connections.svelte
+79
-35
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+3
-3
src/lib/components/documents/AddDocModal.svelte
src/lib/components/documents/AddDocModal.svelte
+3
-1
src/lib/constants.ts
src/lib/constants.ts
+0
-5
src/routes/(app)/+layout.svelte
src/routes/(app)/+layout.svelte
+0
-3
src/routes/(app)/+page.svelte
src/routes/(app)/+page.svelte
+2
-2
src/routes/(app)/c/[id]/+page.svelte
src/routes/(app)/c/[id]/+page.svelte
+2
-2
No files found.
src/lib/components/chat/Messages.svelte
View file @
7031aa14
...
...
@@ -225,33 +225,80 @@
}, 100);
};
// TODO: change delete behaviour
// const deleteMessageAndDescendants = async (messageId: string) => {
// if (history.messages[messageId]) {
// history.messages[messageId].deleted = true;
const messageDeleteHandler = async (messageId) => {
const messageToDelete = history.messages[messageId];
const messageParentId = messageToDelete.parentId;
const messageChildrenIds = messageToDelete.childrenIds ?? [];
const hasSibling = messageChildrenIds.some(
(childId) => history.messages[childId]?.childrenIds?.length > 0
);
messageChildrenIds.forEach((childId) => {
const child = history.messages[childId];
if (child && child.childrenIds) {
if (child.childrenIds.length === 0 && !hasSibling) {
// if last prompt/response pair
history.messages[messageParentId].childrenIds = [];
history.currentId = messageParentId;
} else {
child.childrenIds.forEach((grandChildId) => {
if (history.messages[grandChildId]) {
history.messages[grandChildId].parentId = messageParentId;
history.messages[messageParentId].childrenIds.push(grandChildId);
}
});
}
}
// remove response
history.messages[messageParentId].childrenIds = history.messages[
messageParentId
].childrenIds.filter((id) => id !== childId);
});
// remove prompt
history.messages[messageParentId].childrenIds = history.messages[
messageParentId
].childrenIds.filter((id) => id !== messageId);
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
});
};
// for (const childId of history.messages[messageId].childrenIds) {
// await deleteMessageAndDescendants(childId);
// const messageDeleteHandler = async (messageId) => {
// const message = history.messages[messageId];
// const parentId = message.parentId;
// const childrenIds = message.childrenIds ?? [];
// const grandchildrenIds = [];
// // Iterate through childrenIds to find grandchildrenIds
// for (const childId of childrenIds) {
// const childMessage = history.messages[childId];
// const grandChildrenIds = childMessage.childrenIds ?? [];
// for (const grandchildId of grandchildrenIds) {
// const childMessage = history.messages[grandchildId];
// childMessage.parentId = parentId;
// }
// grandchildrenIds.push(...grandChildrenIds);
// }
// };
// const triggerDeleteMessageRecursive = async (messageId: string) => {
// await deleteMessageAndDescendants(messageId);
// await updateChatById(localStorage.token, chatId, { history });
// await chats.set(await getChatList(localStorage.token));
// };
const messageDeleteHandler = async (messageId) => {
if (history.messages[messageId]) {
history.messages[messageId].deleted = true;
// history.messages[parentId].childrenIds.push(...grandchildrenIds);
// history.messages[parentId].childrenIds = history.messages[parentId].childrenIds.filter(
// (id) => id !== messageId
// );
// // Select latest message
// let currentMessageId = grandchildrenIds.at(-1);
// if (currentMessageId) {
// let messageChildrenIds = history.messages[currentMessageId].childrenIds;
// while (messageChildrenIds.length !== 0) {
// currentMessageId = messageChildrenIds.at(-1);
// messageChildrenIds = history.messages[currentMessageId].childrenIds;
// }
// history.currentId = currentMessageId;
// }
for (const childId of history.messages[messageId].childrenIds) {
history.messages[childId].deleted = true;
}
}
await updateChatById(localStorage.token, chatId, { history });
};
// await updateChatById(localStorage.token, chatId, { messages, history });
// };
</script>
{#if messages.length == 0}
...
...
@@ -260,7 +307,6 @@
<div class=" pb-10">
{#key chatId}
{#each messages as message, messageIdx}
{#if !message.deleted}
<div class=" w-full">
<div
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
...
...
@@ -310,7 +356,6 @@
{/if}
</div>
</div>
{/if}
{/each}
{#if bottomPadding}
...
...
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
7031aa14
...
...
@@ -24,6 +24,7 @@
import CodeBlock from './CodeBlock.svelte';
import Image from '$lib/components/common/Image.svelte';
import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let modelfiles = [];
export let message;
...
...
@@ -346,6 +347,7 @@
class=" bg-transparent outline-none w-full resize-none"
bind:value={editedContent}
on:input={(e) => {
e.target.style.height = '';
e.target.style.height = `${e.target.scrollHeight}px`;
}}
/>
...
...
@@ -464,6 +466,7 @@
</div>
{/if}
<Tooltip content="Edit" placement="bottom">
<button
class="{isLastMessage
? 'visible'
...
...
@@ -487,7 +490,9 @@
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Copy" placement="bottom">
<button
class="{isLastMessage
? 'visible'
...
...
@@ -511,7 +516,9 @@
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
...
...
@@ -536,6 +543,9 @@
/></svg
>
</button>
</Tooltip>
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
...
...
@@ -560,7 +570,9 @@
/></svg
>
</button>
</Tooltip>
<Tooltip content="Read Aloud" placement="bottom">
<button
id="speak-button-{message.id}"
class="{isLastMessage
...
...
@@ -600,7 +612,12 @@
cx="12"
cy="12"
r="3"
/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" /></svg
/><circle
class="spinner_S1WN spinner_JApP"
cx="20"
cy="12"
r="3"
/></svg
>
{:else if speaking}
<svg
...
...
@@ -634,8 +651,10 @@
</svg>
{/if}
</button>
</Tooltip>
{#if $config.images}
<Tooltip content="Generate Image" placement="bottom">
<button
class="{isLastMessage
? 'visible'
...
...
@@ -698,9 +717,11 @@
</svg>
{/if}
</button>
</Tooltip>
{/if}
{#if message.info}
<Tooltip content="Generation Info" placement="bottom">
<button
class=" {isLastMessage
? 'visible'
...
...
@@ -725,9 +746,11 @@
/>
</svg>
</button>
</Tooltip>
{/if}
{#if isLastMessage}
<Tooltip content="Continue Response" placement="bottom">
<button
type="button"
class="{isLastMessage
...
...
@@ -757,7 +780,9 @@
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Regenerate" placement="bottom">
<button
type="button"
class="{isLastMessage
...
...
@@ -780,6 +805,7 @@
/>
</svg>
</button>
</Tooltip>
{/if}
</div>
{/if}
...
...
src/lib/components/chat/Messages/UserMessage.svelte
View file @
7031aa14
...
...
@@ -5,6 +5,7 @@
import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte';
import { modelfiles, settings } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
...
...
@@ -171,7 +172,8 @@
class=" bg-transparent outline-none w-full resize-none"
bind:value={editedContent}
on:input={(e) => {
messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
e.target.style.height = '';
e.target.style.height = `${e.target.scrollHeight}px`;
}}
/>
...
...
@@ -248,6 +250,7 @@
</div>
{/if}
<Tooltip content="Edit" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => {
...
...
@@ -269,7 +272,9 @@
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Copy" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
...
...
@@ -291,8 +296,10 @@
/>
</svg>
</button>
</Tooltip>
{#if !isFirstMessage}
<Tooltip content="Delete" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
...
...
@@ -314,6 +321,7 @@
/>
</svg>
</button>
</Tooltip>
{/if}
</div>
</div>
...
...
src/lib/components/chat/Settings/Connections.svelte
View file @
7031aa14
...
...
@@ -4,7 +4,12 @@
const dispatch = createEventDispatcher();
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai';
import {
getOpenAIKeys,
getOpenAIUrls,
updateOpenAIKeys,
updateOpenAIUrls
} from '$lib/apis/openai';
import { toast } from 'svelte-sonner';
const i18n = getContext('i18n');
...
...
@@ -18,12 +23,14 @@
let OPENAI_API_KEY = '';
let OPENAI_API_BASE_URL = '';
let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = [''];
let showOpenAI = false;
let showLiteLLM = false;
const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL);
OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
OPENAI_API_BASE_URL
S
= await updateOpenAIUrl
s
(localStorage.token, OPENAI_API_BASE_URL
S
);
OPENAI_API_KEY
S
= await updateOpenAIKey
s
(localStorage.token, OPENAI_API_KEY
S
);
await models.set(await getModels());
};
...
...
@@ -45,8 +52,8 @@
onMount(async () => {
if ($user.role === 'admin') {
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
OPENAI_API_BASE_URL
S
= await getOpenAIUrl
s
(localStorage.token);
OPENAI_API_KEY
S
= await getOpenAIKey
s
(localStorage.token);
}
});
</script>
...
...
@@ -73,38 +80,75 @@
</div>
{#if showOpenAI}
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div>
<div class="flex w-full">
<div
class="flex flex-col gap-1"
>
{#each OPENAI_API_BASE_URLS as url, idx}
<div class="flex w-full
gap-2
">
<div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
placeholder={$i18n.t('
Enter OpenAI API Key
')}
bind:value={
OPENAI_API_KEY
}
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
placeholder={$i18n.t('
API Base URL
')}
bind:value={
url
}
autocomplete="off"
/>
</div>
</div>
</div>
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
0
0 outline-none"
placeholder=
"Enter OpenAI API Base URL"
bind:value={OPENAI_API_
BASE_URL
}
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-8
5
0 outline-none"
placeholder=
{$i18n.t('API Key')}
bind:value={OPENAI_API_
KEYS[idx]
}
autocomplete="off"
/>
</div>
</div>
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
WebUI will make requests to <span class=" text-gray-200"
>'{OPENAI_API_BASE_URL}/chat'</span
<div class="self-center flex items-center">
{#if idx === 0}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
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>
</button>
{:else}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div>
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span>
</div>
{/each}
</div>
{/if}
</div>
</div>
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
7031aa14
...
...
@@ -56,7 +56,7 @@
let modelUploadMode = 'file';
let modelInputFile = '';
let modelFileUrl = '';
let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASS
S
ISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASS
S
ISTANT:"`;
let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`;
let modelFileDigest = '';
let uploadProgress = null;
...
...
@@ -517,7 +517,7 @@
{#if !deleteModelTag}
<option value="" disabled selected>Select a model</option>
{/if}
{#each $models.filter((m) => m.size != null) as model}
{#each $models.filter((m) => m.size != null
&& (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))
) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
>
...
...
@@ -599,7 +599,7 @@
on:change={() => {
console.log(modelInputFile);
}}
accept=".gguf"
accept=".gguf
,.safetensors
"
required
hidden
/>
...
...
src/lib/components/documents/AddDocModal.svelte
View file @
7031aa14
...
...
@@ -140,7 +140,9 @@
<button
class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl"
type="button"
on:click={uploadDocInputElement.click}
on:click={() => {
uploadDocInputElement.click();
}}
>
{#if inputFiles}
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
...
...
src/lib/constants.ts
View file @
7031aa14
...
...
@@ -90,8 +90,3 @@ export const SUPPORTED_FILE_EXTENSIONS = [
// This feature, akin to $env/static/private, exclusively incorporates environment variables
// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
// Consequently, these variables can be securely exposed to client-side code.
// Example of the .env configuration:
// OLLAMA_API_BASE_URL="http://localhost:11434/api"
// # Public
// PUBLIC_API_BASE_URL=$OLLAMA_API_BASE_URL
src/routes/(app)/+layout.svelte
View file @
7031aa14
...
...
@@ -99,14 +99,11 @@
if (localDBChats.length === 0) {
await deleteDB('Chats');
}
console.log('localdb', localDBChats);
}
console.log(DB);
} catch (error) {
// IndexedDB Not Found
console.log('IDB Not Found');
}
console.log();
...
...
src/routes/(app)/+page.svelte
View file @
7031aa14
...
...
@@ -344,7 +344,7 @@
content
:
$
settings
.
system
}
:
undefined
,
...
messages
.
filter
((
message
)
=>
!message.deleted)
...
messages
]
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
...
...
@@ -558,7 +558,7 @@
content
:
$
settings
.
system
}
:
undefined
,
...
messages
.
filter
((
message
)
=>
!message.deleted)
...
messages
]
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
...
...
src/routes/(app)/c/[id]/+page.svelte
View file @
7031aa14
...
...
@@ -354,7 +354,7 @@
content
:
$
settings
.
system
}
:
undefined
,
...
messages
.
filter
((
message
)
=>
!message.deleted)
...
messages
]
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
...
...
@@ -568,7 +568,7 @@
content
:
$
settings
.
system
}
:
undefined
,
...
messages
.
filter
((
message
)
=>
!message.deleted)
...
messages
]
.
filter
((
message
)
=>
message
)
.
map
((
message
,
idx
,
arr
)
=>
({
...
...
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