Merge branch 'dev' into feature/support_auth_by_api_key

This commit is contained in:
Timothy Jaeryang Baek 2024-04-02 09:12:19 -07:00 committed by GitHub
commit 56369fea3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1231 additions and 325 deletions

View file

@ -16,6 +16,7 @@
const i18n = getContext('i18n');
export let chatId = '';
export let readOnly = false;
export let sendPrompt: Function;
export let continueGeneration: Function;
export let regenerateResponse: Function;
@ -317,6 +318,7 @@
<UserMessage
on:delete={() => messageDeleteHandler(message.id)}
user={$user}
{readOnly}
{message}
isFirstMessage={messageIdx === 0}
siblings={message.parentId !== null
@ -335,6 +337,7 @@
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{confirmEditResponseMessage}
{showPreviousMessage}
{showNextMessage}

View file

@ -33,6 +33,8 @@
export let isLastMessage = true;
export let readOnly = false;
export let confirmEditResponseMessage: Function;
export let showPreviousMessage: Function;
export let showNextMessage: Function;
@ -128,7 +130,7 @@
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: false },
{ left: '$', right: '$', display: false },
{ left: '$ ', right: ' $', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: false },
{ left: '[ ', right: ' ]', display: false }
@ -422,7 +424,7 @@
class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
>
{#if siblings.length > 1}
<div class="flex self-center min-w-fit -mt-1">
<div class="flex self-center min-w-fit">
<button
class="self-center dark:hover:text-white hover:text-black transition"
on:click={() => {
@ -469,31 +471,33 @@
</div>
{/if}
<Tooltip content="Edit" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
{#if !readOnly}
<Tooltip content="Edit" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
editMessageHandler();
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<button
@ -521,59 +525,61 @@
</button>
</Tooltip>
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
{#if !readOnly}
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
</button>
</Tooltip>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
</button>
</Tooltip>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
>
</button>
</Tooltip>
{/if}
<Tooltip content="Read Aloud" placement="bottom">
<button
@ -656,7 +662,7 @@
</button>
</Tooltip>
{#if $config.images}
{#if $config.images && !readOnly}
<Tooltip content="Generate Image" placement="bottom">
<button
class="{isLastMessage
@ -752,7 +758,7 @@
</Tooltip>
{/if}
{#if isLastMessage}
{#if isLastMessage && !readOnly}
<Tooltip content="Continue Response" placement="bottom">
<button
type="button"

View file

@ -15,6 +15,7 @@
export let message;
export let siblings;
export let isFirstMessage: boolean;
export let readOnly: boolean;
export let confirmEditMessage: Function;
export let showPreviousMessage: Function;
@ -203,7 +204,7 @@
<div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500">
{#if siblings.length > 1}
<div class="flex self-center -mt-1">
<div class="flex self-center">
<button
class="self-center dark:hover:text-white hover:text-black transition"
on:click={() => {
@ -250,29 +251,31 @@
</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={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
{#if !readOnly}
<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={() => {
editMessageHandler();
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
{/if}
<Tooltip content="Copy" placement="bottom">
<button
@ -298,7 +301,7 @@
</button>
</Tooltip>
{#if !isFirstMessage}
{#if !isFirstMessage && !readOnly}
<Tooltip content="Delete" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"

View file

@ -258,6 +258,9 @@
console.log(error);
}
}
} else {
const error = await fileResponse?.json();
toast.error(error?.detail ?? error);
}
if (uploaded) {

View file

@ -1,41 +1,180 @@
<script lang="ts">
import { getContext } from 'svelte';
import Modal from '../common/Modal.svelte';
import { getContext, onMount } from 'svelte';
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { toast } from 'svelte-sonner';
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
import { chatId, modelfiles } from '$lib/stores';
import { copyToClipboard } from '$lib/utils';
import Modal from '../common/Modal.svelte';
import Link from '../icons/Link.svelte';
let chat = null;
const i18n = getContext('i18n');
export let downloadChat: Function;
export let shareChat: Function;
const shareLocalChat = async () => {
const _chat = chat;
const sharedChat = await shareChatById(localStorage.token, $chatId);
const chatShareUrl = `${window.location.origin}/s/${sharedChat.id}`;
toast.success($i18n.t('Copied shared chat URL to clipboard!'));
copyToClipboard(chatShareUrl);
chat = await getChatById(localStorage.token, $chatId);
};
const shareChat = async () => {
const _chat = chat.chat;
console.log('share', _chat);
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
const url = 'https://openwebui.com';
// const url = 'http://localhost:5173';
const tab = await window.open(`${url}/chats/upload`, '_blank');
window.addEventListener(
'message',
(event) => {
if (event.origin !== url) return;
if (event.data === 'loaded') {
tab.postMessage(
JSON.stringify({
chat: _chat,
modelfiles: $modelfiles.filter((modelfile) =>
_chat.models.includes(modelfile.tagName)
)
}),
'*'
);
}
},
false
);
};
const downloadChat = async () => {
const _chat = chat.chat;
console.log('download', chat);
const chatText = _chat.messages.reduce((a, message, i, arr) => {
return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
}, '');
let blob = new Blob([chatText], {
type: 'text/plain'
});
saveAs(blob, `chat-${_chat.title}.txt`);
};
export let show = false;
onMount(async () => {
chatId.subscribe(async (value) => {
chat = await getChatById(localStorage.token, value);
console.log(chat);
});
});
</script>
<Modal bind:show size="xs">
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
<button
class=" self-center px-8 py-1.5 w-full rounded-full text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white"
type="button"
on:click={() => {
shareChat();
show = false;
}}
>
{$i18n.t('Share to OpenWebUI Community')}
</button>
<div class="flex justify-center space-x-1 mt-1.5">
<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Share Chat')}</div>
<button
class=" self-center rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
type="button"
class="self-center"
on:click={() => {
downloadChat();
show = false;
}}
>
{$i18n.t('Download as a File')}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
{#if chat}
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
<div class=" text-sm dark:text-gray-300 mb-1">
{#if chat.share_id}
<a href="/s/{chat.share_id}" target="_blank"
>You have shared this chat <span class=" underline">before</span>.</a
>
Click here to
<button
class="underline"
on:click={async () => {
const res = await deleteSharedChatById(localStorage.token, $chatId);
if (res) {
chat = await getChatById(localStorage.token, $chatId);
}
}}>delete this link</button
> and create a new shared link.
{:else}
Messages you send after creating your link won't be shared. Users with the URL will be
able to view the shared chat.
{/if}
</div>
<div class="flex justify-end">
<div class="flex flex-col items-end space-x-1 mt-1.5">
<div class="flex gap-1">
<button
class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
type="button"
on:click={() => {
shareChat();
show = false;
}}
>
{$i18n.t('Share to OpenWebUI Community')}
</button>
<button
class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"
type="button"
on:click={() => {
shareLocalChat();
show = false;
}}
>
<Link />
{#if chat.share_id}
{$i18n.t('Update and Copy Link')}
{:else}
{$i18n.t('Copy Link')}
{/if}
</button>
</div>
<div class="flex gap-1 mt-1.5">
<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
<button
class=" text-right rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
type="button"
on:click={() => {
downloadChat();
show = false;
}}
>
{$i18n.t('Download as a File')}
</button>
</div>
</div>
</div>
</div>
{/if}
</div>
</Modal>

View file

@ -1,24 +1,25 @@
<script lang="ts">
export let className: string = 'text-white';
export let theme: 'blue' | 'white' | 'black' = 'white';
export let className: string = '';
</script>
<div class="flex justify-center text-center {className}">
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 {theme === 'blue'
? 'text-sky-600'
: theme === 'white'
? 'text-white'
: 'text-gray-600'} "
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
<svg class="size-5" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</div>

View file

@ -0,0 +1,16 @@
<script lang="ts">
export let className = 'w-4 h-4';
</script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class={className}>
<path
fill-rule="evenodd"
d="M8.914 6.025a.75.75 0 0 1 1.06 0 3.5 3.5 0 0 1 0 4.95l-2 2a3.5 3.5 0 0 1-5.396-4.402.75.75 0 0 1 1.251.827 2 2 0 0 0 3.085 2.514l2-2a2 2 0 0 0 0-2.828.75.75 0 0 1 0-1.06Z"
clip-rule="evenodd"
/>
<path
fill-rule="evenodd"
d="M7.086 9.975a.75.75 0 0 1-1.06 0 3.5 3.5 0 0 1 0-4.95l2-2a3.5 3.5 0 0 1 5.396 4.402.75.75 0 0 1-1.251-.827 2 2 0 0 0-3.085-2.514l-2 2a2 2 0 0 0 0 2.828.75.75 0 0 1 0 1.06Z"
clip-rule="evenodd"
/>
</svg>

View file

@ -1,11 +1,9 @@
<script lang="ts">
import { getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { Separator } from 'bits-ui';
import { getChatById } from '$lib/apis/chats';
import { getChatById, shareChatById } from '$lib/apis/chats';
import { WEBUI_NAME, chatId, modelfiles, settings, showSettings } from '$lib/stores';
import { slide } from 'svelte/transition';
@ -32,55 +30,13 @@
export let addTag: Function;
export let deleteTag: Function;
export let showModelSelector = false;
export let showModelSelector = true;
let showShareChatModal = false;
let showTagChatModal = false;
const shareChat = async () => {
const chat = (await getChatById(localStorage.token, $chatId)).chat;
console.log('share', chat);
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
const url = 'https://openwebui.com';
// const url = 'http://localhost:5173';
const tab = await window.open(`${url}/chats/upload`, '_blank');
window.addEventListener(
'message',
(event) => {
if (event.origin !== url) return;
if (event.data === 'loaded') {
tab.postMessage(
JSON.stringify({
chat: chat,
modelfiles: $modelfiles.filter((modelfile) => chat.models.includes(modelfile.tagName))
}),
'*'
);
}
},
false
);
};
const downloadChat = async () => {
const chat = (await getChatById(localStorage.token, $chatId)).chat;
console.log('download', chat);
const chatText = chat.messages.reduce((a, message, i, arr) => {
return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
}, '');
let blob = new Blob([chatText], {
type: 'text/plain'
});
saveAs(blob, `chat-${chat.title}.txt`);
};
</script>
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
<ShareChatModal bind:show={showShareChatModal} />
<!-- <TagChatModal bind:show={showTagChatModal} {tags} {deleteTag} {addTag} /> -->
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
<div
@ -135,12 +91,14 @@
</div> -->
<div class="flex items-center w-full max-w-full">
<div class="w-full flex-1 overflow-hidden max-w-full">
<ModelSelector bind:selectedModels />
<div class="flex-1 overflow-hidden max-w-full">
{#if showModelSelector}
<ModelSelector bind:selectedModels />
{/if}
</div>
<div class="self-start flex flex-none items-center">
<div class="flex self-center w-[1px] h-5 mx-2 bg-stone-700" />
<div class="flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
{#if !shareEnabled}
<Tooltip content="Settings">