forked from open-webui/open-webui
Merge remote-tracking branch 'upstream/dev' into feat/add-i18n
This commit is contained in:
commit
25e0f0de42
26 changed files with 1445 additions and 668 deletions
|
@ -34,12 +34,13 @@
|
|||
import Sidebar from '$lib/components/layout/Sidebar.svelte';
|
||||
import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte';
|
||||
import ChangelogModal from '$lib/components/ChangelogModal.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let ollamaVersion = '';
|
||||
let loaded = false;
|
||||
|
||||
let showShortcutsButtonElement: HTMLButtonElement;
|
||||
let DB = null;
|
||||
let localDBChats = [];
|
||||
|
||||
|
@ -186,7 +187,7 @@
|
|||
if (isCtrlPressed && event.key === '/') {
|
||||
event.preventDefault();
|
||||
console.log('showShortcuts');
|
||||
document.getElementById('show-shortcuts-button')?.click();
|
||||
showShortcutsButtonElement.click();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -203,15 +204,18 @@
|
|||
|
||||
{#if loaded}
|
||||
<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10">
|
||||
<button
|
||||
id="show-shortcuts-button"
|
||||
class="text-gray-600 dark:text-gray-300 bg-gray-300/20 w-6 h-6 flex items-center justify-center text-xs rounded-full"
|
||||
on:click={() => {
|
||||
showShortcuts = !showShortcuts;
|
||||
}}
|
||||
>
|
||||
?
|
||||
</button>
|
||||
<Tooltip content="help" placement="left">
|
||||
<button
|
||||
id="show-shortcuts-button"
|
||||
bind:this={showShortcutsButtonElement}
|
||||
class="text-gray-600 dark:text-gray-300 bg-gray-300/20 w-6 h-6 flex items-center justify-center text-xs rounded-full"
|
||||
on:click={() => {
|
||||
showShortcuts = !showShortcuts;
|
||||
}}
|
||||
>
|
||||
?
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<ShortcutsModal bind:show={showShortcuts} />
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
let stopResponseFlag = false;
|
||||
let autoScroll = true;
|
||||
let processing = '';
|
||||
|
||||
let messagesContainerElement: HTMLDivElement;
|
||||
let currentRequestId = null;
|
||||
|
||||
let selectedModels = [''];
|
||||
|
@ -143,8 +143,7 @@
|
|||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const element = document.getElementById('messages-container');
|
||||
element.scrollTop = element.scrollHeight;
|
||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
|
@ -837,8 +836,11 @@
|
|||
<div
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||
id="messages-container"
|
||||
bind:this={messagesContainerElement}
|
||||
on:scroll={(e) => {
|
||||
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||
autoScroll =
|
||||
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
|
||||
messagesContainerElement.clientHeight + 50;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
@ -846,10 +848,7 @@
|
|||
? 'max-w-full'
|
||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
||||
>
|
||||
<ModelSelector
|
||||
bind:selectedModels
|
||||
disabled={messages.length > 0 && !selectedModels.includes('')}
|
||||
/>
|
||||
<ModelSelector bind:selectedModels />
|
||||
</div>
|
||||
|
||||
<div class=" h-full w-full flex flex-col py-8">
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
let stopResponseFlag = false;
|
||||
let autoScroll = true;
|
||||
let processing = '';
|
||||
|
||||
let messagesContainerElement: HTMLDivElement;
|
||||
let currentRequestId = null;
|
||||
|
||||
// let chatId = $page.params.id;
|
||||
|
@ -162,8 +162,7 @@
|
|||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const element = document.getElementById('messages-container');
|
||||
element.scrollTop = element.scrollHeight;
|
||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
|
@ -865,8 +864,11 @@
|
|||
<div
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||
id="messages-container"
|
||||
bind:this={messagesContainerElement}
|
||||
on:scroll={(e) => {
|
||||
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||
autoScroll =
|
||||
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
|
||||
messagesContainerElement.clientHeight + 50;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
@ -874,10 +876,7 @@
|
|||
? 'max-w-full'
|
||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
||||
>
|
||||
<ModelSelector
|
||||
bind:selectedModels
|
||||
disabled={messages.length > 0 && !selectedModels.includes('')}
|
||||
/>
|
||||
<ModelSelector bind:selectedModels />
|
||||
</div>
|
||||
|
||||
<div class=" h-full w-full flex flex-col py-8">
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
let inputFiles = '';
|
||||
let query = '';
|
||||
|
||||
let documentsImportInputElement: HTMLInputElement;
|
||||
let tags = [];
|
||||
|
||||
let showSettingsModal = false;
|
||||
|
@ -527,6 +527,7 @@
|
|||
<div class="flex space-x-2">
|
||||
<input
|
||||
id="documents-import-input"
|
||||
bind:this={documentsImportInputElement}
|
||||
bind:files={importFiles}
|
||||
type="file"
|
||||
accept=".json"
|
||||
|
@ -561,9 +562,7 @@
|
|||
|
||||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={async () => {
|
||||
document.getElementById('documents-import-input')?.click();
|
||||
}}
|
||||
on:click={documentsImportInputElement.click}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Documents Mapping')}</div>
|
||||
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
|
||||
let localModelfiles = [];
|
||||
let importFiles;
|
||||
|
||||
let modelfilesImportInputElement: HTMLInputElement;
|
||||
const deleteModelHandler = async (tagName) => {
|
||||
let success = null;
|
||||
|
||||
success = await deleteModel(localStorage.token, tagName);
|
||||
success = await deleteModel(localStorage.token, tagName).catch((err) => {
|
||||
toast.error(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (success) {
|
||||
toast.success($i18n.t(`Deleted {tagName}`, { tagName }));
|
||||
|
@ -237,6 +240,7 @@
|
|||
<div class="flex space-x-1">
|
||||
<input
|
||||
id="modelfiles-import-input"
|
||||
bind:this={modelfilesImportInputElement}
|
||||
bind:files={importFiles}
|
||||
type="file"
|
||||
accept=".json"
|
||||
|
@ -264,9 +268,7 @@
|
|||
|
||||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={async () => {
|
||||
document.getElementById('modelfiles-import-input')?.click();
|
||||
}}
|
||||
on:click={modelfilesImportInputElement.click}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Modelfiles')}</div>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
|
@ -23,15 +23,17 @@
|
|||
|
||||
let mode = 'chat';
|
||||
let loaded = false;
|
||||
|
||||
let text = '';
|
||||
|
||||
let selectedModelId = '';
|
||||
|
||||
let loading = false;
|
||||
let currentRequestId;
|
||||
let currentRequestId = null;
|
||||
let stopResponseFlag = false;
|
||||
|
||||
let messagesContainerElement: HTMLDivElement;
|
||||
let textCompletionAreaElement: HTMLTextAreaElement;
|
||||
|
||||
let system = '';
|
||||
let messages = [
|
||||
{
|
||||
|
@ -41,13 +43,7 @@
|
|||
];
|
||||
|
||||
const scrollToBottom = () => {
|
||||
let element;
|
||||
|
||||
if (mode === 'chat') {
|
||||
element = document.getElementById('messages-container');
|
||||
} else {
|
||||
element = document.getElementById('text-completion-textarea');
|
||||
}
|
||||
const element = mode === 'chat' ? messagesContainerElement : textCompletionAreaElement;
|
||||
|
||||
if (element) {
|
||||
element.scrollTop = element?.scrollHeight;
|
||||
|
@ -98,6 +94,10 @@
|
|||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag) {
|
||||
if (stopResponseFlag) {
|
||||
await cancelChatCompletion(localStorage.token, currentRequestId);
|
||||
}
|
||||
|
||||
currentRequestId = null;
|
||||
break;
|
||||
}
|
||||
|
@ -114,7 +114,11 @@
|
|||
let data = JSON.parse(line.replace(/^data: /, ''));
|
||||
console.log(data);
|
||||
|
||||
text += data.choices[0].delta.content ?? '';
|
||||
if ('request_id' in data) {
|
||||
currentRequestId = data.request_id;
|
||||
} else {
|
||||
text += data.choices[0].delta.content ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,16 +156,6 @@
|
|||
: `${OLLAMA_API_BASE_URL}/v1`
|
||||
);
|
||||
|
||||
// const [res, controller] = await generateChatCompletion(localStorage.token, {
|
||||
// model: selectedModelId,
|
||||
// messages: [
|
||||
// {
|
||||
// role: 'assistant',
|
||||
// content: text
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
|
||||
let responseMessage;
|
||||
if (messages.at(-1)?.role === 'assistant') {
|
||||
responseMessage = messages.at(-1);
|
||||
|
@ -186,6 +180,11 @@
|
|||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag) {
|
||||
if (stopResponseFlag) {
|
||||
await cancelChatCompletion(localStorage.token, currentRequestId);
|
||||
}
|
||||
|
||||
currentRequestId = null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -202,17 +201,21 @@
|
|||
let data = JSON.parse(line.replace(/^data: /, ''));
|
||||
console.log(data);
|
||||
|
||||
if (responseMessage.content == '' && data.choices[0].delta.content == '\n') {
|
||||
continue;
|
||||
if ('request_id' in data) {
|
||||
currentRequestId = data.request_id;
|
||||
} else {
|
||||
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
||||
if (responseMessage.content == '' && data.choices[0].delta.content == '\n') {
|
||||
continue;
|
||||
} else {
|
||||
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
||||
|
||||
responseMessage.content += data.choices[0].delta.content ?? '';
|
||||
messages = messages;
|
||||
responseMessage.content += data.choices[0].delta.content ?? '';
|
||||
messages = messages;
|
||||
|
||||
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
||||
textareaElement.style.height = textareaElement.scrollHeight + 'px';
|
||||
|
||||
await tick();
|
||||
await tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,48 +226,6 @@
|
|||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// while (true) {
|
||||
// const { value, done } = await reader.read();
|
||||
// if (done || stopResponseFlag) {
|
||||
// if (stopResponseFlag) {
|
||||
// await cancelChatCompletion(localStorage.token, currentRequestId);
|
||||
// }
|
||||
|
||||
// currentRequestId = null;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// let lines = value.split('\n');
|
||||
|
||||
// for (const line of lines) {
|
||||
// if (line !== '') {
|
||||
// console.log(line);
|
||||
// let data = JSON.parse(line);
|
||||
|
||||
// if ('detail' in data) {
|
||||
// throw data;
|
||||
// }
|
||||
|
||||
// if ('id' in data) {
|
||||
// console.log(data);
|
||||
// currentRequestId = data.id;
|
||||
// } else {
|
||||
// if (data.done == false) {
|
||||
// text += data.message.content;
|
||||
// } else {
|
||||
// console.log('done');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
|
||||
// scrollToBottom();
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -422,12 +383,14 @@
|
|||
<div
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||
id="messages-container"
|
||||
bind:this={messagesContainerElement}
|
||||
>
|
||||
<div class=" h-full w-full flex flex-col">
|
||||
<div class="flex-1 p-1">
|
||||
{#if mode === 'complete'}
|
||||
<textarea
|
||||
id="text-completion-textarea"
|
||||
bind:this={textCompletionAreaElement}
|
||||
class="w-full h-full p-3 bg-transparent outline outline-1 outline-gray-200 dark:outline-gray-800 resize-none rounded-lg text-sm"
|
||||
bind:value={text}
|
||||
placeholder={$i18n.t("You're a helpful assistant.")}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
let importFiles = '';
|
||||
let query = '';
|
||||
|
||||
let promptsImportInputElement: HTMLInputElement;
|
||||
const sharePrompt = async (prompt) => {
|
||||
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
|
||||
|
||||
|
@ -210,6 +210,7 @@
|
|||
<div class="flex space-x-2">
|
||||
<input
|
||||
id="prompts-import-input"
|
||||
bind:this={promptsImportInputElement}
|
||||
bind:files={importFiles}
|
||||
type="file"
|
||||
accept=".json"
|
||||
|
@ -243,9 +244,7 @@
|
|||
|
||||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={async () => {
|
||||
document.getElementById('prompts-import-input')?.click();
|
||||
}}
|
||||
on:click={promptsImportInputElement.click}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Prompts')}</div>
|
||||
|
||||
|
@ -268,7 +267,7 @@
|
|||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={async () => {
|
||||
// document.getElementById('modelfiles-import-input')?.click();
|
||||
// promptsImportInputElement.click();
|
||||
let blob = new Blob([JSON.stringify($prompts)], {
|
||||
type: 'application/json'
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue