This commit is contained in:
Timothy J. Baek 2024-11-06 02:58:44 -08:00
parent ccbf5a08f3
commit 02269a21a9
6 changed files with 163 additions and 252 deletions

View File

@ -1794,6 +1794,33 @@
console.log('stopResponse'); console.log('stopResponse');
}; };
const submitMessage = async (parentId, prompt) => {
let userPrompt = prompt;
let userMessageId = uuidv4();
let userMessage = {
id: userMessageId,
parentId: parentId,
childrenIds: [],
role: 'user',
content: userPrompt,
models: selectedModels
};
if (parentId !== null) {
history.messages[parentId].childrenIds = [
...history.messages[parentId].childrenIds,
userMessageId
];
}
history.messages[userMessageId] = userMessage;
history.currentId = userMessageId;
await tick();
await sendPrompt(userPrompt, userMessageId);
};
const regenerateResponse = async (message) => { const regenerateResponse = async (message) => {
console.log('regenerateResponse'); console.log('regenerateResponse');
@ -2204,42 +2231,12 @@
{selectedModels} {selectedModels}
{sendPrompt} {sendPrompt}
{showMessage} {showMessage}
{submitMessage}
{continueResponse} {continueResponse}
{regenerateResponse} {regenerateResponse}
{mergeResponses} {mergeResponses}
{chatActionHandler} {chatActionHandler}
bottomPadding={files.length > 0} bottomPadding={files.length > 0}
on:submit={async (e) => {
if (e.detail) {
// New user message
let userPrompt = e.detail.prompt;
let userMessageId = uuidv4();
let userMessage = {
id: userMessageId,
parentId: e.detail.parentId,
childrenIds: [],
role: 'user',
content: userPrompt,
models: selectedModels
};
let messageParentId = e.detail.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);
}
}}
/> />
</div> </div>
</div> </div>

View File

@ -29,8 +29,10 @@
export let continueResponse: Function; export let continueResponse: Function;
export let regenerateResponse: Function; export let regenerateResponse: Function;
export let mergeResponses: Function; export let mergeResponses: Function;
export let chatActionHandler: Function; export let chatActionHandler: Function;
export let showMessage: Function = () => {}; export let showMessage: Function = () => {};
export let submitMessage: Function = () => {};
export let readOnly = false; export let readOnly = false;
@ -79,9 +81,9 @@
element.scrollTop = element.scrollHeight; element.scrollTop = element.scrollHeight;
}; };
const updateChatHistory = async () => { const updateChat = async () => {
await tick();
history = history; history = history;
await tick();
await updateChatById(localStorage.token, chatId, { await updateChatById(localStorage.token, chatId, {
history: history, history: history,
messages: messages messages: messages
@ -195,7 +197,7 @@
rating: rating rating: rating
}; };
await updateChatHistory(); await updateChat();
}; };
const editMessage = async (messageId, content, submit = true) => { const editMessage = async (messageId, content, submit = true) => {
@ -232,7 +234,7 @@
} else { } else {
// Edit user message // Edit user message
history.messages[messageId].content = content; history.messages[messageId].content = content;
await updateChatHistory(); await updateChat();
} }
} else { } else {
if (submit) { if (submit) {
@ -261,16 +263,25 @@
]; ];
} }
await updateChatHistory(); await updateChat();
} else { } else {
// Edit response message // Edit response message
history.messages[messageId].originalContent = history.messages[messageId].content; history.messages[messageId].originalContent = history.messages[messageId].content;
history.messages[messageId].content = content; history.messages[messageId].content = content;
await updateChatHistory(); await updateChat();
} }
} }
}; };
const actionMessage = async (actionId, event = null) => {
await chatActionHandler(chatId, actionId, message.model, message.id, event);
};
const saveMessage = async (messageId, message) => {
history.messages[messageId] = message;
await updateChat();
};
const deleteMessage = async (messageId) => { const deleteMessage = async (messageId) => {
const messageToDelete = history.messages[messageId]; const messageToDelete = history.messages[messageId];
const parentMessageId = messageToDelete.parentId; const parentMessageId = messageToDelete.parentId;
@ -306,7 +317,17 @@
showMessage({ id: parentMessageId }); showMessage({ id: parentMessageId });
// Update the chat // Update the chat
await updateChatHistory(); await updateChat();
};
const triggerScroll = () => {
if (autoScroll) {
const element = document.getElementById('messages-container');
autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
setTimeout(() => {
scrollToBottom();
}, 100);
}
}; };
</script> </script>
@ -372,37 +393,18 @@
{user} {user}
{showPreviousMessage} {showPreviousMessage}
{showNextMessage} {showNextMessage}
{updateChat}
{editMessage} {editMessage}
{deleteMessage} {deleteMessage}
{rateMessage} {rateMessage}
{actionMessage}
{saveMessage}
{submitMessage}
{regenerateResponse} {regenerateResponse}
{continueResponse} {continueResponse}
{mergeResponses} {mergeResponses}
{triggerScroll}
{readOnly} {readOnly}
on:submit={async (e) => {
dispatch('submit', e.detail);
}}
on:action={async (e) => {
if (typeof e.detail === 'string') {
await chatActionHandler(chatId, e.detail, message.model, message.id);
} else {
const { id, event } = e.detail;
await chatActionHandler(chatId, id, message.model, message.id, event);
}
}}
on:update={() => {
updateChatHistory();
}}
on:scroll={() => {
if (autoScroll) {
const element = document.getElementById('messages-container');
autoScroll =
element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
setTimeout(() => {
scrollToBottom();
}, 100);
}
}}
/> />
{/each} {/each}
</div> </div>

View File

@ -22,23 +22,21 @@
export let showPreviousMessage; export let showPreviousMessage;
export let showNextMessage; export let showNextMessage;
export let updateChat;
export let editMessage; export let editMessage;
export let saveMessage;
export let deleteMessage; export let deleteMessage;
export let rateMessage; export let rateMessage;
export let actionMessage;
export let submitMessage;
export let regenerateResponse; export let regenerateResponse;
export let continueResponse; export let continueResponse;
// MultiResponseMessages
export let mergeResponses; export let mergeResponses;
export let autoScroll = false; export let triggerScroll;
export let readOnly = false; export let readOnly = false;
onMount(() => {
// console.log('message', idx);
});
</script> </script>
<div <div
@ -61,7 +59,7 @@
{showPreviousMessage} {showPreviousMessage}
{showNextMessage} {showNextMessage}
{editMessage} {editMessage}
on:delete={() => deleteMessage(messageId)} {deleteMessage}
{readOnly} {readOnly}
/> />
{:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1} {:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1}
@ -73,30 +71,14 @@
siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []} siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
{showPreviousMessage} {showPreviousMessage}
{showNextMessage} {showNextMessage}
{updateChat}
{editMessage} {editMessage}
{saveMessage}
{rateMessage} {rateMessage}
{actionMessage}
{submitMessage}
{continueResponse} {continueResponse}
{regenerateResponse} {regenerateResponse}
on:submit={async (e) => {
dispatch('submit', e.detail);
}}
on:action={async (e) => {
dispatch('action', e.detail);
}}
on:update={async (e) => {
dispatch('update');
}}
on:save={async (e) => {
console.log('save', e);
const message = e.detail;
if (message) {
history.messages[message.id] = message;
dispatch('update');
} else {
dispatch('update');
}
}}
{readOnly} {readOnly}
/> />
{:else} {:else}
@ -105,35 +87,16 @@
{chatId} {chatId}
{messageId} {messageId}
isLastMessage={messageId === history?.currentId} isLastMessage={messageId === history?.currentId}
{rateMessage} {updateChat}
{editMessage} {editMessage}
{saveMessage}
{rateMessage}
{actionMessage}
{submitMessage}
{continueResponse} {continueResponse}
{regenerateResponse} {regenerateResponse}
{mergeResponses} {mergeResponses}
on:submit={async (e) => { {triggerScroll}
dispatch('submit', e.detail);
}}
on:action={async (e) => {
dispatch('action', e.detail);
}}
on:update={async (e) => {
dispatch('update');
}}
on:save={async (e) => {
console.log('save', e);
const message = e.detail;
if (message) {
history.messages[message.id] = message;
dispatch('update');
} else {
dispatch('update');
}
}}
on:change={async () => {
await tick();
dispatch('update');
dispatch('scroll');
}}
{readOnly} {readOnly}
/> />
{/if} {/if}

View File

@ -25,13 +25,19 @@
export let isLastMessage; export let isLastMessage;
export let readOnly = false; export let readOnly = false;
export let updateChat: Function;
export let editMessage: Function; export let editMessage: Function;
export let saveMessage: Function;
export let rateMessage: Function; export let rateMessage: Function;
export let actionMessage: Function;
export let submitMessage: Function;
export let continueResponse: Function; export let continueResponse: Function;
export let regenerateResponse: Function; export let regenerateResponse: Function;
export let mergeResponses: Function; export let mergeResponses: Function;
export let triggerScroll: Function;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let currentMessageId; let currentMessageId;
@ -46,7 +52,7 @@
} }
} }
const showPreviousMessage = (modelIdx) => { const showPreviousMessage = async (modelIdx) => {
groupedMessageIdsIdx[modelIdx] = Math.max(0, groupedMessageIdsIdx[modelIdx] - 1); groupedMessageIdsIdx[modelIdx] = Math.max(0, groupedMessageIdsIdx[modelIdx] - 1);
let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]]; let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]];
@ -60,10 +66,13 @@
} }
history.currentId = messageId; history.currentId = messageId;
dispatch('change');
await tick();
await updateChat();
triggerScroll();
}; };
const showNextMessage = (modelIdx) => { const showNextMessage = async (modelIdx) => {
groupedMessageIdsIdx[modelIdx] = Math.min( groupedMessageIdsIdx[modelIdx] = Math.min(
groupedMessageIds[modelIdx].messageIds.length - 1, groupedMessageIds[modelIdx].messageIds.length - 1,
groupedMessageIdsIdx[modelIdx] + 1 groupedMessageIdsIdx[modelIdx] + 1
@ -80,7 +89,10 @@
} }
history.currentId = messageId; history.currentId = messageId;
dispatch('change');
await tick();
await updateChat();
triggerScroll();
}; };
const initHandler = async () => { const initHandler = async () => {
@ -182,7 +194,7 @@
: `border-gray-50 dark:border-gray-850 border-dashed ${ : `border-gray-50 dark:border-gray-850 border-dashed ${
$mobile ? 'min-w-full' : 'min-w-80' $mobile ? 'min-w-full' : 'min-w-80'
}`} transition-all p-5 rounded-2xl" }`} transition-all p-5 rounded-2xl"
on:click={() => { on:click={async () => {
if (messageId != _messageId) { if (messageId != _messageId) {
let currentMessageId = _messageId; let currentMessageId = _messageId;
let messageChildrenIds = history.messages[currentMessageId].childrenIds; let messageChildrenIds = history.messages[currentMessageId].childrenIds;
@ -191,7 +203,10 @@
messageChildrenIds = history.messages[currentMessageId].childrenIds; messageChildrenIds = history.messages[currentMessageId].childrenIds;
} }
history.currentId = currentMessageId; history.currentId = currentMessageId;
dispatch('change');
await tick();
await updateChat();
triggerScroll();
} }
}} }}
> >
@ -205,8 +220,12 @@
siblings={groupedMessageIds[modelIdx].messageIds} siblings={groupedMessageIds[modelIdx].messageIds}
showPreviousMessage={() => showPreviousMessage(modelIdx)} showPreviousMessage={() => showPreviousMessage(modelIdx)}
showNextMessage={() => showNextMessage(modelIdx)} showNextMessage={() => showNextMessage(modelIdx)}
{rateMessage} {updateChat}
{editMessage} {editMessage}
{saveMessage}
{rateMessage}
{actionMessage}
{submitMessage}
{continueResponse} {continueResponse}
regenerateResponse={async (message) => { regenerateResponse={async (message) => {
regenerateResponse(message); regenerateResponse(message);
@ -214,18 +233,6 @@
groupedMessageIdsIdx[modelIdx] = groupedMessageIdsIdx[modelIdx] =
groupedMessageIds[modelIdx].messageIds.length - 1; groupedMessageIds[modelIdx].messageIds.length - 1;
}} }}
on:submit={async (e) => {
dispatch('submit', e.detail);
}}
on:action={async (e) => {
dispatch('action', e.detail);
}}
on:update={async (e) => {
dispatch('update', e.detail);
}}
on:save={async (e) => {
dispatch('save', e.detail);
}}
{readOnly} {readOnly}
/> />
{/if} {/if}

View File

@ -15,9 +15,6 @@
import { import {
copyToClipboard as _copyToClipboard, copyToClipboard as _copyToClipboard,
approximateToHumanReadable, approximateToHumanReadable,
extractParagraphsForAudio,
extractSentencesForAudio,
cleanText,
getMessageContentParts, getMessageContentParts,
sanitizeResponseContent, sanitizeResponseContent,
createMessagesList createMessagesList
@ -33,7 +30,6 @@
import Spinner from '$lib/components/common/Spinner.svelte'; import Spinner from '$lib/components/common/Spinner.svelte';
import WebSearchResults from './ResponseMessage/WebSearchResults.svelte'; import WebSearchResults from './ResponseMessage/WebSearchResults.svelte';
import Sparkles from '$lib/components/icons/Sparkles.svelte'; import Sparkles from '$lib/components/icons/Sparkles.svelte';
import Markdown from './Markdown.svelte';
import Error from './Error.svelte'; import Error from './Error.svelte';
import Citations from './Citations.svelte'; import Citations from './Citations.svelte';
import CodeExecutions from './CodeExecutions.svelte'; import CodeExecutions from './CodeExecutions.svelte';
@ -112,9 +108,13 @@
export let showPreviousMessage: Function; export let showPreviousMessage: Function;
export let showNextMessage: Function; export let showNextMessage: Function;
export let updateChat: Function;
export let editMessage: Function; export let editMessage: Function;
export let saveMessage: Function;
export let rateMessage: Function; export let rateMessage: Function;
export let actionMessage: Function;
export let submitMessage: Function;
export let continueResponse: Function; export let continueResponse: Function;
export let regenerateResponse: Function; export let regenerateResponse: Function;
@ -329,7 +329,10 @@
url: `${image.url}` url: `${image.url}`
})); }));
dispatch('save', { ...message, files: files }); saveMessage(message.id, {
...message,
files: files
});
} }
generatingImage = false; generatingImage = false;
@ -422,7 +425,7 @@
} }
console.log(updatedMessage); console.log(updatedMessage);
dispatch('save', updatedMessage); saveMessage(message.id, updatedMessage);
await tick(); await tick();
@ -443,7 +446,7 @@
updatedMessage.annotation.tags = tags; updatedMessage.annotation.tags = tags;
feedbackItem.data.tags = tags; feedbackItem.data.tags = tags;
dispatch('save', updatedMessage); saveMessage(message.id, updatedMessage);
await updateFeedbackById( await updateFeedbackById(
localStorage.token, localStorage.token,
updatedMessage.feedbackId, updatedMessage.feedbackId,
@ -631,22 +634,19 @@
message.id message.id
].content.replace(raw, raw.replace(oldContent, newContent)); ].content.replace(raw, raw.replace(oldContent, newContent));
dispatch('update'); updateChat();
}} }}
on:select={(e) => { on:select={(e) => {
const { type, content } = e.detail; const { type, content } = e.detail;
if (type === 'explain') { if (type === 'explain') {
dispatch('submit', { submitMessage(
parentId: message.id, message.id,
prompt: `Explain this section to me in more detail\n\n\`\`\`\n${content}\n\`\`\`` `Explain this section to me in more detail\n\n\`\`\`\n${content}\n\`\`\``
}); );
} else if (type === 'ask') { } else if (type === 'ask') {
const input = e.detail?.input ?? ''; const input = e.detail?.input ?? '';
dispatch('submit', { submitMessage(message.id, `\`\`\`\n${content}\n\`\`\`\n${input}`);
parentId: message.id,
prompt: `\`\`\`\n${content}\n\`\`\`\n${input}`
});
} }
}} }}
/> />
@ -1019,21 +1019,6 @@
disabled={feedbackLoading} disabled={feedbackLoading}
on:click={async () => { on:click={async () => {
await feedbackHandler(1); await feedbackHandler(1);
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
.forEach((action) => {
dispatch('action', {
id: action.id,
event: {
id: 'good-response',
data: {
messageId: message.id
}
}
});
});
window.setTimeout(() => { window.setTimeout(() => {
document document
.getElementById(`message-feedback-${message.id}`) .getElementById(`message-feedback-${message.id}`)
@ -1070,21 +1055,6 @@
disabled={feedbackLoading} disabled={feedbackLoading}
on:click={async () => { on:click={async () => {
await feedbackHandler(-1); await feedbackHandler(-1);
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
.forEach((action) => {
dispatch('action', {
id: action.id,
event: {
id: 'bad-response',
data: {
messageId: message.id
}
}
});
});
window.setTimeout(() => { window.setTimeout(() => {
document document
.getElementById(`message-feedback-${message.id}`) .getElementById(`message-feedback-${message.id}`)
@ -1120,20 +1090,6 @@
: '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" : '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={() => { on:click={() => {
continueResponse(); continueResponse();
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
.forEach((action) => {
dispatch('action', {
id: action.id,
event: {
id: 'continue-response',
data: {
messageId: message.id
}
}
});
});
}} }}
> >
<svg <svg
@ -1157,50 +1113,52 @@
</svg> </svg>
</button> </button>
</Tooltip> </Tooltip>
{/if}
<Tooltip content={$i18n.t('Regenerate')} placement="bottom"> <Tooltip content={$i18n.t('Regenerate')} placement="bottom">
<button <button
type="button" type="button"
class="{isLastMessage class="{isLastMessage
? 'visible' ? '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" : '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={() => { on:click={() => {
showRateComment = false; showRateComment = false;
regenerateResponse(message); regenerateResponse(message);
(model?.actions ?? []) (model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false) .filter((action) => action?.__webui__ ?? false)
.forEach((action) => { .forEach((action) => {
dispatch('action', { dispatch('action', {
id: action.id, id: action.id,
event: { event: {
id: 'regenerate-response', id: 'regenerate-response',
data: { data: {
messageId: message.id messageId: message.id
}
} }
}); }
}); });
}} });
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" stroke-linecap="round"
fill="none" stroke-linejoin="round"
viewBox="0 0 24 24" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
stroke-width="2.3" />
stroke="currentColor" </svg>
class="w-4 h-4" </button>
> </Tooltip>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
</button>
</Tooltip>
{#each (model?.actions ?? []).filter((action) => !(action?.__webui__ ?? false)) as action} {#if isLastMessage}
{#each model?.actions ?? [] as action}
<Tooltip content={action.name} placement="bottom"> <Tooltip content={action.name} placement="bottom">
<button <button
type="button" type="button"
@ -1208,7 +1166,7 @@
? 'visible' ? '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" : '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={() => { on:click={() => {
dispatch('action', action.id); actionMessage(action.id);
}} }}
> >
{#if action.icon_url} {#if action.icon_url}
@ -1242,22 +1200,6 @@
comment: e.detail.comment, comment: e.detail.comment,
reason: e.detail.reason reason: e.detail.reason
}); });
(model?.actions ?? [])
.filter((action) => action?.__webui__ ?? false)
.forEach((action) => {
dispatch('action', {
id: action.id,
event: {
id: 'rate-comment',
data: {
messageId: message.id,
comment: e.detail.comment,
reason: e.detail.reason
}
}
});
});
}} }}
/> />
{/if} {/if}

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { tick, createEventDispatcher, getContext, onMount } from 'svelte'; import { tick, getContext, onMount } from 'svelte';
import { models, settings } from '$lib/stores'; import { models, settings } from '$lib/stores';
import { user as _user } from '$lib/stores'; import { user as _user } from '$lib/stores';
@ -19,7 +19,6 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
export let user; export let user;
export let history; export let history;
@ -31,6 +30,7 @@
export let showNextMessage: Function; export let showNextMessage: Function;
export let editMessage: Function; export let editMessage: Function;
export let deleteMessage: Function;
export let isFirstMessage: boolean; export let isFirstMessage: boolean;
export let readOnly: boolean; export let readOnly: boolean;
@ -78,7 +78,7 @@
}; };
const deleteMessageHandler = async () => { const deleteMessageHandler = async () => {
dispatch('delete', message.id); deleteMessage(message.id);
}; };
onMount(() => { onMount(() => {