refac: wip

This commit is contained in:
Timothy J. Baek 2024-09-23 01:36:46 +02:00
parent fd5e8b4fcf
commit 5978e7c9a6
7 changed files with 244 additions and 204 deletions

View File

@ -1520,8 +1520,8 @@
}
};
const continueGeneration = async () => {
console.log('continueGeneration');
const continueResponse = async () => {
console.log('continueResponse');
const _chatId = JSON.parse(JSON.stringify($chatId));
if (history.currentId && history.messages[history.currentId].done == true) {
@ -1552,6 +1552,53 @@
}
};
const mergeResponses = async (messageId, responses, _chatId) => {
console.log('mergeResponses', messageId, responses);
const message = history.messages[messageId];
const mergedResponse = {
status: true,
content: ''
};
message.merged = mergedResponse;
history.messages[messageId] = message;
try {
const [res, controller] = await generateMoACompletion(
localStorage.token,
message.model,
history.messages[message.parentId].content,
responses
);
if (res && res.ok && res.body) {
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) {
const { value, done, citations, error, usage } = update;
if (error || done) {
break;
}
if (mergedResponse.content == '' && value == '\n') {
continue;
} else {
mergedResponse.content += value;
history.messages[messageId] = message;
}
if (autoScroll) {
scrollToBottom();
}
}
await saveChatHandler(_chatId);
} else {
console.error(res);
}
} catch (e) {
console.error(e);
}
};
const generateChatTitle = async (userPrompt) => {
if ($settings?.title?.auto ?? true) {
const title = await generateTitle(
@ -1684,52 +1731,6 @@
}
}
};
const mergeResponses = async (messageId, responses, _chatId) => {
console.log('mergeResponses', messageId, responses);
const message = history.messages[messageId];
const mergedResponse = {
status: true,
content: ''
};
message.merged = mergedResponse;
history.messages[messageId] = message;
try {
const [res, controller] = await generateMoACompletion(
localStorage.token,
message.model,
history.messages[message.parentId].content,
responses
);
if (res && res.ok && res.body) {
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) {
const { value, done, citations, error, usage } = update;
if (error || done) {
break;
}
if (mergedResponse.content == '' && value == '\n') {
continue;
} else {
mergedResponse.content += value;
history.messages[messageId] = message;
}
if (autoScroll) {
scrollToBottom();
}
}
await saveChatHandler(_chatId);
} else {
console.error(res);
}
} catch (e) {
console.error(e);
}
};
</script>
<svelte:head>
@ -1827,13 +1828,12 @@
bind:autoScroll
bind:prompt
{selectedModels}
{processing}
{sendPrompt}
{continueGeneration}
{showMessage}
{continueResponse}
{regenerateResponse}
{mergeResponses}
{chatActionHandler}
{showMessage}
bottomPadding={files.length > 0}
/>
</div>

View File

@ -15,7 +15,7 @@
export let chatId = '';
export let readOnly = false;
export let sendPrompt: Function;
export let continueGeneration: Function;
export let continueResponse: Function;
export let regenerateResponse: Function;
export let mergeResponses: Function;
export let chatActionHandler: Function;
@ -23,7 +23,6 @@
export let user = $_user;
export let prompt;
export let processing = '';
export let bottomPadding = false;
export let autoScroll;
export let history = {};
@ -43,7 +42,7 @@
element.scrollTop = element.scrollHeight;
};
const updateChatMessages = async () => {
const updateChatHistory = async () => {
await tick();
await updateChatById(localStorage.token, chatId, {
history: history
@ -53,87 +52,6 @@
await chats.set(await getChatList(localStorage.token, $currentChatPage));
};
const confirmEditMessage = async (messageId, content, submit = true) => {
if (submit) {
let userPrompt = content;
let userMessageId = uuidv4();
let userMessage = {
id: userMessageId,
parentId: history.messages[messageId].parentId,
childrenIds: [],
role: 'user',
content: userPrompt,
...(history.messages[messageId].files && { files: history.messages[messageId].files }),
models: selectedModels
};
let messageParentId = history.messages[messageId].parentId;
if (messageParentId !== null) {
history.messages[messageParentId].childrenIds = [
...history.messages[messageParentId].childrenIds,
userMessageId
];
}
history.messages[userMessageId] = userMessage;
history.currentId = userMessageId;
await tick();
await sendPrompt(userPrompt, userMessageId);
} else {
history.messages[messageId].content = content;
await tick();
await updateChatById(localStorage.token, chatId, {
history: history
});
}
};
const confirmEditResponseMessage = async (messageId, content) => {
history.messages[messageId].originalContent = history.messages[messageId].content;
history.messages[messageId].content = content;
await updateChatMessages();
};
const saveNewResponseMessage = async (message, content) => {
const responseMessageId = uuidv4();
const parentId = message.parentId;
const responseMessage = {
...message,
id: responseMessageId,
parentId: parentId,
childrenIds: [],
content: content,
timestamp: Math.floor(Date.now() / 1000) // Unix epoch
};
history.messages[responseMessageId] = responseMessage;
history.currentId = responseMessageId;
// Append messageId to childrenIds of parent message
if (parentId !== null) {
history.messages[parentId].childrenIds = [
...history.messages[parentId].childrenIds,
responseMessageId
];
}
await updateChatMessages();
};
const rateMessage = async (messageId, rating) => {
history.messages[messageId].annotation = {
...history.messages[messageId].annotation,
rating: rating
};
await updateChatMessages();
};
const showPreviousMessage = async (message) => {
if (message.parentId !== null) {
let messageId =
@ -232,6 +150,87 @@
}
};
const rateMessage = async (messageId, rating) => {
history.messages[messageId].annotation = {
...history.messages[messageId].annotation,
rating: rating
};
await updateChatHistory();
};
const editMessage = async (messageId, content, submit = true) => {
if (history.messages[messageId].role === 'user') {
if (submit) {
// New user message
let userPrompt = content;
let userMessageId = uuidv4();
let userMessage = {
id: userMessageId,
parentId: history.messages[messageId].parentId,
childrenIds: [],
role: 'user',
content: userPrompt,
...(history.messages[messageId].files && { files: history.messages[messageId].files }),
models: selectedModels
};
let messageParentId = history.messages[messageId].parentId;
if (messageParentId !== null) {
history.messages[messageParentId].childrenIds = [
...history.messages[messageParentId].childrenIds,
userMessageId
];
}
history.messages[userMessageId] = userMessage;
history.currentId = userMessageId;
await tick();
await sendPrompt(userPrompt, userMessageId);
} else {
// Edit user message
history.messages[messageId].content = content;
await updateChatHistory();
}
} else {
if (submit) {
// New response message
const responseMessageId = uuidv4();
const parentId = message.parentId;
const responseMessage = {
...message,
id: responseMessageId,
parentId: parentId,
childrenIds: [],
content: content,
timestamp: Math.floor(Date.now() / 1000) // Unix epoch
};
history.messages[responseMessageId] = responseMessage;
history.currentId = responseMessageId;
// Append messageId to childrenIds of parent message
if (parentId !== null) {
history.messages[parentId].childrenIds = [
...history.messages[parentId].childrenIds,
responseMessageId
];
}
await updateChatHistory();
} else {
// Edit response message
history.messages[messageId].originalContent = history.messages[messageId].content;
history.messages[messageId].content = content;
await updateChatHistory();
}
}
};
const deleteMessage = async (messageId) => {
const messageToDelete = history.messages[messageId];
const parentMessageId = messageToDelete.parentId;
@ -267,9 +266,7 @@
showMessage({ id: parentMessageId });
// Update the chat
await updateChatById(localStorage.token, chatId, {
history: history
});
await updateChatHistory();
};
</script>
@ -315,8 +312,19 @@
{:else}
<div class="w-full pt-2">
{#key chatId}
{JSON.stringify(history)}
<!-- <Message {chatId} {history} messageId={history.currentId} {user} /> -->
<!-- {JSON.stringify(history)} -->
<div class="w-full flex flex-col-reverse">
<Message
h={0}
{chatId}
{history}
messageId={history.currentId}
{user}
{editMessage}
{deleteMessage}
{rateMessage}
/>
</div>
<div class="pb-12" />
{#if bottomPadding}
<div class=" pb-6" />

View File

@ -10,6 +10,10 @@
import MultiResponseMessages from './MultiResponseMessages.svelte';
import ResponseMessage from './ResponseMessage.svelte';
import UserMessage from './UserMessage.svelte';
import { updateChatById } from '$lib/apis/chats';
// h here refers to the height of the message graph
export let h;
export let chatId;
@ -21,19 +25,15 @@
export let scrollToBottom;
export let chatActionHandler;
export let confirmEditMessage;
export let confirmEditResponseMessage;
export let showPreviousMessage;
export let showNextMessage;
export let rateMessage;
export let editMessage;
export let deleteMessage;
export let saveNewResponseMessage;
export let rateMessage;
export let regenerateResponse;
export let continueGeneration;
export let continueResponse;
// MultiResponseMessages
export let mergeResponses;
@ -48,44 +48,39 @@
};
</script>
<div class="w-full">
<div
class="flex flex-col justify-between px-5 mb-3 {($settings?.widescreenMode ?? null)
? 'max-w-full'
: 'max-w-5xl'} mx-auto rounded-lg group"
>
{#if history.messages[messageId].role === 'user'}
{@const message = history.messages[messageId]}
<div
class="flex flex-col justify-between px-5 mb-3 w-full {($settings?.widescreenMode ?? null)
? 'max-w-full'
: 'max-w-5xl'} mx-auto rounded-lg group"
>
{#if history.messages[messageId]}
{@const message = history.messages[messageId]}
{#if message.role === 'user'}
<UserMessage
{user}
{history}
{message}
isFirstMessage={h === 0}
siblings={message.parentId !== null
? (history.messages[message.parentId]?.childrenIds ?? [])
: (Object.values(history.messages)
.filter((message) => message.parentId === null)
.map((message) => message.id) ?? [])}
{confirmEditMessage}
{showPreviousMessage}
{showNextMessage}
copyToClipboard={copyToClipboardWithToast}
isFirstMessage={messageIdx === 0}
{editMessage}
on:delete={() => deleteMessage(message.id)}
{readOnly}
/>
{:else if (history.messages[message.parentId]?.models?.length ?? 1) === 1}
<ResponseMessage
{message}
isLastMessage={messageId === history.currentId}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{updateChatMessages}
{confirmEditResponseMessage}
{saveNewResponseMessage}
{showPreviousMessage}
{showNextMessage}
{editMessage}
{rateMessage}
copyToClipboard={copyToClipboardWithToast}
{continueGeneration}
{continueResponse}
{regenerateResponse}
on:action={async (e) => {
console.log('action', e);
@ -96,6 +91,10 @@
await chatActionHandler(chatId, id, message.model, message.id, event);
}
}}
on:update={async (e) => {
console.log('update', e);
// call updateChatHistory
}}
on:save={async (e) => {
console.log('save', e);
@ -111,6 +110,7 @@
});
}
}}
{readOnly}
/>
{:else}
<MultiResponseMessages
@ -118,16 +118,14 @@
{chatId}
messageIds={[]}
parentMessage={history.messages[message.parentId]}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{updateChatMessages}
{saveNewResponseMessage}
{confirmEditResponseMessage}
isLastMessage={messageId === history.currentId}
{editMessage}
{rateMessage}
copyToClipboard={copyToClipboardWithToast}
{continueGeneration}
{continueResponse}
{mergeResponses}
{regenerateResponse}
copyToClipboard={copyToClipboardWithToast}
{readOnly}
on:action={async (e) => {
console.log('action', e);
if (typeof e.detail === 'string') {
@ -152,5 +150,26 @@
}}
/>
{/if}
</div>
{/if}
</div>
{#if history.messages[messageId]?.parentId !== null}
<svelte:self
h={h + 1}
{chatId}
{history}
messageId={history.messages[messageId]?.parentId}
{user}
{scrollToBottom}
{chatActionHandler}
{showPreviousMessage}
{showNextMessage}
{editMessage}
{deleteMessage}
{rateMessage}
{regenerateResponse}
{continueResponse}
{mergeResponses}
{readOnly}
></svelte:self>
{/if}

View File

@ -34,7 +34,7 @@
export let rateMessage: Function;
export let copyToClipboard: Function;
export let continueGeneration: Function;
export let continueResponse: Function;
export let mergeResponses: Function;
export let regenerateResponse: Function;
export let saveNewResponseMessage: Function;
@ -193,7 +193,7 @@
{readOnly}
{rateMessage}
{copyToClipboard}
{continueGeneration}
{continueResponse}
regenerateResponse={async (message) => {
regenerateResponse(message);
await tick();

View File

@ -13,6 +13,7 @@
import { synthesizeOpenAISpeech } from '$lib/apis/audio';
import { imageGenerations } from '$lib/apis/images';
import {
copyToClipboard as _copyToClipboard,
approximateToHumanReadable,
extractParagraphsForAudio,
extractSentencesForAudio,
@ -83,16 +84,15 @@
export let readOnly = false;
export let updateChatMessages: Function;
export let confirmEditResponseMessage: Function;
export let saveNewResponseMessage: Function = () => {};
export let showPreviousMessage: Function;
export let showNextMessage: Function;
export let editMessage: Function;
export let rateMessage: Function;
export let copyToClipboard: Function;
export let continueGeneration: Function;
export let continueResponse: Function;
export let regenerateResponse: Function;
let model = null;
@ -111,6 +111,13 @@
let showRateComment = false;
const copyToClipboard = async (text) => {
const res = await _copyToClipboard(text);
if (res) {
toast.success($i18n.t('Copying to clipboard was successful!'));
}
};
const playAudio = (idx: number) => {
return new Promise<void>((res) => {
speakingIdx = idx;
@ -260,11 +267,7 @@
};
const editMessageConfirmHandler = async () => {
if (editedContent === '') {
editedContent = ' ';
}
confirmEditResponseMessage(message.id, editedContent);
editMessage(message.id, editedContent ? editedContent : '', false);
edit = false;
editedContent = '';
@ -272,8 +275,8 @@
await tick();
};
const saveNewMessageHandler = async () => {
saveNewResponseMessage(message, editedContent);
const saveAsCopyHandler = async () => {
editMessage(message.id, editedContent ? editedContent : '');
edit = false;
editedContent = '';
@ -424,7 +427,7 @@
id="save-new-message-button"
class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
on:click={() => {
saveNewMessageHandler();
saveAsCopyHandler();
}}
>
{$i18n.t('Save As Copy')}
@ -909,7 +912,7 @@
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
on:click={() => {
continueGeneration();
continueResponse();
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
@ -1028,8 +1031,7 @@
bind:show={showRateComment}
bind:message
on:submit={(e) => {
updateChatMessages();
dispatch('update');
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
.forEach((action) => {

View File

@ -1,18 +1,20 @@
<script lang="ts">
import dayjs from 'dayjs';
import { toast } from 'svelte-sonner';
import { tick, createEventDispatcher, getContext } from 'svelte';
import { models, settings } from '$lib/stores';
import { user as _user } from '$lib/stores';
import {
copyToClipboard as _copyToClipboard,
processResponseContent,
replaceTokens
} from '$lib/utils';
import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte';
import { models, settings } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { user as _user } from '$lib/stores';
import { getFileContentById } from '$lib/apis/files';
import FileItem from '$lib/components/common/FileItem.svelte';
import { marked } from 'marked';
import { processResponseContent, replaceTokens } from '$lib/utils';
import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
import Markdown from './Markdown.svelte';
const i18n = getContext('i18n');
@ -23,16 +25,25 @@
export let message;
export let siblings;
export let isFirstMessage: boolean;
export let readOnly: boolean;
export let confirmEditMessage: Function;
export let showPreviousMessage: Function;
export let showNextMessage: Function;
export let copyToClipboard: Function;
export let editMessage: Function;
export let readOnly: boolean;
let edit = false;
let editedContent = '';
let messageEditTextAreaElement: HTMLTextAreaElement;
const copyToClipboard = async (text) => {
const res = await _copyToClipboard(text);
if (res) {
toast.success($i18n.t('Copying to clipboard was successful!'));
}
};
const editMessageHandler = async () => {
edit = true;
editedContent = message.content;
@ -46,7 +57,7 @@
};
const editMessageConfirmHandler = async (submit = true) => {
confirmEditMessage(message.id, editedContent, submit);
editMessage(message.id, editedContent, submit);
edit = false;
editedContent = '';

View File

@ -155,7 +155,7 @@
bind:autoScroll
bottomPadding={files.length > 0}
sendPrompt={() => {}}
continueGeneration={() => {}}
continueResponse={() => {}}
regenerateResponse={() => {}}
/>
</div>