mirror of
https://github.com/open-webui/open-webui
synced 2025-03-04 19:38:54 +00:00
refac: deprecate messages for history
This commit is contained in:
parent
291b6dd744
commit
fd5e8b4fcf
@ -5,6 +5,8 @@
|
||||
import { PaneGroup, Pane, PaneResizer } from 'paneforge';
|
||||
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
@ -67,11 +69,9 @@
|
||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||
import ChatControls from './ChatControls.svelte';
|
||||
import EventConfirmDialog from '../common/ConfirmDialog.svelte';
|
||||
import EllipsisVertical from '../icons/EllipsisVertical.svelte';
|
||||
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
export let chatIdProp = '';
|
||||
|
||||
let loaded = false;
|
||||
const eventTarget = new EventTarget();
|
||||
let controlPane;
|
||||
@ -89,9 +89,10 @@
|
||||
let eventConfirmationInputValue = '';
|
||||
let eventCallback = null;
|
||||
|
||||
let chatIdUnsubscriber: Unsubscriber | undefined;
|
||||
|
||||
let selectedModels = [''];
|
||||
let atSelectedModel: Model | undefined;
|
||||
|
||||
let selectedModelIds = [];
|
||||
$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
|
||||
|
||||
@ -102,35 +103,17 @@
|
||||
let tags = [];
|
||||
|
||||
let title = '';
|
||||
let prompt = '';
|
||||
|
||||
let chatFiles = [];
|
||||
let files = [];
|
||||
let messages = [];
|
||||
let history = {
|
||||
messages: {},
|
||||
currentId: null
|
||||
};
|
||||
|
||||
// Chat Input
|
||||
let prompt = '';
|
||||
let chatFiles = [];
|
||||
let files = [];
|
||||
let params = {};
|
||||
|
||||
let chatIdUnsubscriber: Unsubscriber | undefined;
|
||||
|
||||
$: if (history.currentId !== null) {
|
||||
let _messages = [];
|
||||
let currentMessage = history.messages[history.currentId];
|
||||
while (currentMessage) {
|
||||
_messages.unshift({ ...currentMessage });
|
||||
currentMessage =
|
||||
currentMessage.parentId !== null ? history.messages[currentMessage.parentId] : null;
|
||||
}
|
||||
|
||||
// This is most likely causing the performance issue
|
||||
messages = _messages;
|
||||
} else {
|
||||
messages = [];
|
||||
}
|
||||
|
||||
$: if (chatIdProp) {
|
||||
(async () => {
|
||||
console.log(chatIdProp);
|
||||
@ -227,8 +210,6 @@
|
||||
} else {
|
||||
console.log('Unknown message type', data);
|
||||
}
|
||||
|
||||
messages = messages;
|
||||
}
|
||||
};
|
||||
|
||||
@ -310,6 +291,9 @@
|
||||
showOverview.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
const chatInput = document.getElementById('chat-textarea');
|
||||
chatInput?.focus();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@ -331,7 +315,6 @@
|
||||
autoScroll = true;
|
||||
|
||||
title = '';
|
||||
messages = [];
|
||||
history = {
|
||||
messages: {},
|
||||
currentId: null
|
||||
@ -428,8 +411,8 @@
|
||||
autoScroll = true;
|
||||
await tick();
|
||||
|
||||
if (messages.length > 0) {
|
||||
history.messages[messages.at(-1).id].done = true;
|
||||
if (history.currentId) {
|
||||
history.messages[history.currentId].done = true;
|
||||
}
|
||||
await tick();
|
||||
|
||||
@ -448,8 +431,12 @@
|
||||
};
|
||||
|
||||
const createMessagesList = (responseMessageId) => {
|
||||
if (responseMessageId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const message = history.messages[responseMessageId];
|
||||
if (message.parentId) {
|
||||
if (message?.parentId) {
|
||||
return [...createMessagesList(message.parentId), message];
|
||||
} else {
|
||||
return [message];
|
||||
@ -510,6 +497,8 @@
|
||||
};
|
||||
|
||||
const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
|
||||
const messages = createMessagesList(responseMessageId);
|
||||
|
||||
const res = await chatAction(localStorage.token, actionId, {
|
||||
model: modelId,
|
||||
messages: messages.map((m) => ({
|
||||
@ -575,6 +564,7 @@
|
||||
const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
|
||||
let _responses = [];
|
||||
console.log('submitPrompt', $chatId);
|
||||
const messages = createMessagesList(history.currentId);
|
||||
|
||||
selectedModels = selectedModels.map((modelId) =>
|
||||
$models.map((m) => m.id).includes(modelId) ? modelId : ''
|
||||
@ -668,8 +658,34 @@
|
||||
parentId: string,
|
||||
{ modelId = null, modelIdx = null, newChat = false } = {}
|
||||
) => {
|
||||
let _responses: string[] = [];
|
||||
// Create new chat if newChat is true and first user message
|
||||
if (
|
||||
newChat &&
|
||||
history.messages[history.currentId].parentId === null &&
|
||||
history.messages[history.currentId].role === 'user'
|
||||
) {
|
||||
if (!$temporaryChatEnabled) {
|
||||
chat = await createNewChat(localStorage.token, {
|
||||
id: $chatId,
|
||||
title: $i18n.t('New Chat'),
|
||||
models: selectedModels,
|
||||
system: $settings.system ?? undefined,
|
||||
params: params,
|
||||
history: history,
|
||||
tags: [],
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
await chatId.set(chat.id);
|
||||
} else {
|
||||
await chatId.set('local');
|
||||
}
|
||||
await tick();
|
||||
}
|
||||
|
||||
let _responses: string[] = [];
|
||||
// If modelId is provided, use it, else use selected model
|
||||
let selectedModelIds = modelId
|
||||
? [modelId]
|
||||
@ -714,38 +730,14 @@
|
||||
}
|
||||
await tick();
|
||||
|
||||
// Create new chat if only one message in messages
|
||||
if (newChat && messages.length == 2) {
|
||||
if (!$temporaryChatEnabled) {
|
||||
chat = await createNewChat(localStorage.token, {
|
||||
id: $chatId,
|
||||
title: $i18n.t('New Chat'),
|
||||
models: selectedModels,
|
||||
system: $settings.system ?? undefined,
|
||||
params: params,
|
||||
messages: messages,
|
||||
history: history,
|
||||
tags: [],
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
await chatId.set(chat.id);
|
||||
} else {
|
||||
await chatId.set('local');
|
||||
}
|
||||
await tick();
|
||||
}
|
||||
|
||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||
|
||||
await Promise.all(
|
||||
selectedModelIds.map(async (modelId, _modelIdx) => {
|
||||
console.log('modelId', modelId);
|
||||
const model = $models.filter((m) => m.id === modelId).at(0);
|
||||
|
||||
if (model) {
|
||||
const messages = createMessagesList(parentId);
|
||||
// If there are image files, check if model is vision capable
|
||||
const hasImages = messages.some((message) =>
|
||||
message.files?.some((file) => file.type === 'image')
|
||||
@ -844,7 +836,7 @@
|
||||
}`
|
||||
}
|
||||
: undefined,
|
||||
...messages
|
||||
...createMessagesList(responseMessageId)
|
||||
]
|
||||
.filter((message) => message?.content?.trim())
|
||||
.map((message) => {
|
||||
@ -895,7 +887,7 @@
|
||||
}
|
||||
];
|
||||
files.push(...model.info.meta.knowledge);
|
||||
messages = messages; // Trigger Svelte update
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
files.push(
|
||||
...(userMessage?.files ?? []).filter((item) =>
|
||||
@ -969,7 +961,7 @@
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag || _chatId !== $chatId) {
|
||||
responseMessage.done = true;
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
if (stopResponseFlag) {
|
||||
controller.abort('User: Stop Response');
|
||||
@ -1036,7 +1028,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
} else {
|
||||
responseMessage.done = true;
|
||||
@ -1059,7 +1051,8 @@
|
||||
eval_count: data.eval_count,
|
||||
eval_duration: data.eval_duration
|
||||
};
|
||||
messages = messages;
|
||||
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
if ($settings.notificationEnabled && !document.hasFocus()) {
|
||||
const notification = new Notification(`${model.id}`, {
|
||||
@ -1128,7 +1121,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
await saveChatHandler(_chatId);
|
||||
|
||||
@ -1161,7 +1154,8 @@
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (messages.length == 2 && messages.at(1).content !== '' && selectedModels[0] === model.id) {
|
||||
const messages = createMessagesList(responseMessageId);
|
||||
if (messages.length == 2 && messages.at(-1).content !== '' && selectedModels[0] === model.id) {
|
||||
window.history.replaceState(history.state, '', `/c/${_chatId}`);
|
||||
const _title = await generateChatTitle(userPrompt);
|
||||
await setChatTitle(_chatId, _title);
|
||||
@ -1189,7 +1183,7 @@
|
||||
}
|
||||
];
|
||||
files.push(...model.info.meta.knowledge);
|
||||
messages = messages; // Trigger Svelte update
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
files.push(
|
||||
...(userMessage?.files ?? []).filter((item) =>
|
||||
@ -1240,7 +1234,7 @@
|
||||
}`
|
||||
}
|
||||
: undefined,
|
||||
...messages
|
||||
...createMessagesList(responseMessageId)
|
||||
]
|
||||
.filter((message) => message?.content?.trim())
|
||||
.map((message, idx, arr) => ({
|
||||
@ -1318,7 +1312,7 @@
|
||||
}
|
||||
if (done || stopResponseFlag || _chatId !== $chatId) {
|
||||
responseMessage.done = true;
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
if (stopResponseFlag) {
|
||||
controller.abort('User: Stop Response');
|
||||
@ -1373,7 +1367,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
|
||||
if (autoScroll) {
|
||||
@ -1414,7 +1408,7 @@
|
||||
|
||||
await saveChatHandler(_chatId);
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
stopResponseFlag = false;
|
||||
await tick();
|
||||
@ -1445,9 +1439,9 @@
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
const messages = createMessagesList(responseMessageId);
|
||||
if (messages.length == 2 && selectedModels[0] === model.id) {
|
||||
window.history.replaceState(history.state, '', `/c/${_chatId}`);
|
||||
|
||||
const _title = await generateChatTitle(userPrompt);
|
||||
await setChatTitle(_chatId, _title);
|
||||
}
|
||||
@ -1497,7 +1491,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
};
|
||||
|
||||
const stopResponse = () => {
|
||||
@ -1508,7 +1502,7 @@
|
||||
const regenerateResponse = async (message) => {
|
||||
console.log('regenerateResponse');
|
||||
|
||||
if (messages.length != 0) {
|
||||
if (history.currentId) {
|
||||
let userMessage = history.messages[message.parentId];
|
||||
let userPrompt = userMessage.content;
|
||||
|
||||
@ -1530,7 +1524,7 @@
|
||||
console.log('continueGeneration');
|
||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||
|
||||
if (messages.length != 0 && messages.at(-1).done == true) {
|
||||
if (history.currentId && history.messages[history.currentId].done == true) {
|
||||
const responseMessage = history.messages[history.currentId];
|
||||
responseMessage.done = false;
|
||||
await tick();
|
||||
@ -1600,7 +1594,7 @@
|
||||
description: $i18n.t('Generating search query')
|
||||
}
|
||||
];
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
const prompt = userMessage.content;
|
||||
let searchQuery = await generateSearchQuery(
|
||||
@ -1620,7 +1614,7 @@
|
||||
action: 'web_search',
|
||||
description: $i18n.t('No search query generated')
|
||||
});
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1629,7 +1623,7 @@
|
||||
action: 'web_search',
|
||||
description: $i18n.t(`Searching "{{searchQuery}}"`, { searchQuery })
|
||||
});
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
|
||||
const results = await runWebSearch(localStorage.token, searchQuery).catch((error) => {
|
||||
console.log(error);
|
||||
@ -1657,8 +1651,7 @@
|
||||
type: 'web_search_results',
|
||||
urls: results.filenames
|
||||
});
|
||||
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
} else {
|
||||
responseMessage.statusHistory.push({
|
||||
done: true,
|
||||
@ -1666,7 +1659,7 @@
|
||||
action: 'web_search',
|
||||
description: 'No search results found'
|
||||
});
|
||||
messages = messages;
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1680,9 +1673,8 @@
|
||||
if ($chatId == _chatId) {
|
||||
if (!$temporaryChatEnabled) {
|
||||
chat = await updateChatById(localStorage.token, _chatId, {
|
||||
messages: messages,
|
||||
history: history,
|
||||
models: selectedModels,
|
||||
history: history,
|
||||
params: params,
|
||||
files: chatFiles
|
||||
});
|
||||
@ -1700,7 +1692,7 @@
|
||||
content: ''
|
||||
};
|
||||
message.merged = mergedResponse;
|
||||
messages = messages;
|
||||
history.messages[messageId] = message;
|
||||
|
||||
try {
|
||||
const [res, controller] = await generateMoACompletion(
|
||||
@ -1722,7 +1714,7 @@
|
||||
continue;
|
||||
} else {
|
||||
mergedResponse.content += value;
|
||||
messages = messages;
|
||||
history.messages[messageId] = message;
|
||||
}
|
||||
|
||||
if (autoScroll) {
|
||||
@ -1788,11 +1780,11 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Navbar {chat} {title} bind:selectedModels shareEnabled={messages.length > 0} {initNewChat} />
|
||||
<Navbar {chat} {title} bind:selectedModels shareEnabled={!!history.currentId} {initNewChat} />
|
||||
|
||||
<PaneGroup direction="horizontal" class="w-full h-full">
|
||||
<Pane defaultSize={50} class="h-full flex w-full relative">
|
||||
{#if $banners.length > 0 && messages.length === 0 && !$chatId && selectedModels.length <= 1}
|
||||
{#if $banners.length > 0 && !history.currentId && !$chatId && selectedModels.length <= 1}
|
||||
<div class="absolute top-3 left-0 right-0 w-full z-20">
|
||||
<div class=" flex flex-col gap-1 w-full">
|
||||
{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
|
||||
@ -1834,7 +1826,6 @@
|
||||
bind:history
|
||||
bind:autoScroll
|
||||
bind:prompt
|
||||
{messages}
|
||||
{selectedModels}
|
||||
{processing}
|
||||
{sendPrompt}
|
||||
@ -1850,13 +1841,13 @@
|
||||
|
||||
<div class="">
|
||||
<MessageInput
|
||||
{history}
|
||||
bind:files
|
||||
bind:prompt
|
||||
bind:autoScroll
|
||||
bind:selectedToolIds
|
||||
bind:webSearchEnabled
|
||||
bind:atSelectedModel
|
||||
{messages}
|
||||
{selectedModels}
|
||||
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
|
||||
const model = $models.find((m) => m.id === e);
|
||||
|
@ -61,15 +61,14 @@
|
||||
let user = null;
|
||||
let chatInputPlaceholder = '';
|
||||
|
||||
export let files = [];
|
||||
export let history;
|
||||
|
||||
export let prompt = '';
|
||||
export let files = [];
|
||||
export let availableToolIds = [];
|
||||
export let selectedToolIds = [];
|
||||
export let webSearchEnabled = false;
|
||||
|
||||
export let prompt = '';
|
||||
export let messages = [];
|
||||
|
||||
let visionCapableModels = [];
|
||||
$: visionCapableModels = [...(atSelectedModel ? [atSelectedModel] : selectedModels)].filter(
|
||||
(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true
|
||||
@ -272,7 +271,7 @@
|
||||
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||
<div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
|
||||
<div class="relative">
|
||||
{#if autoScroll === false && messages.length > 0}
|
||||
{#if autoScroll === false && !history?.currentId}
|
||||
<div
|
||||
class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none"
|
||||
>
|
||||
@ -692,7 +691,7 @@
|
||||
/>
|
||||
|
||||
<div class="self-end mb-2 flex space-x-1 mr-1">
|
||||
{#if messages.length == 0 || messages.at(-1).done == true}
|
||||
{#if !history?.currentId || history.messages[history.currentId].done == true}
|
||||
<Tooltip content={$i18n.t('Record voice')}>
|
||||
<button
|
||||
id="voice-input-button"
|
||||
@ -744,7 +743,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-end w-10">
|
||||
{#if messages.length == 0 || messages.at(-1).done == true}
|
||||
{#if !history.currentId || history.messages[history.currentId].done == true}
|
||||
{#if prompt === ''}
|
||||
<div class=" flex items-center mb-1">
|
||||
<Tooltip content={$i18n.t('Call')}>
|
||||
|
@ -7,10 +7,8 @@
|
||||
import { getChatList, updateChatById } from '$lib/apis/chats';
|
||||
import { copyToClipboard, findWordIndices } from '$lib/utils';
|
||||
|
||||
import UserMessage from './Messages/UserMessage.svelte';
|
||||
import ResponseMessage from './Messages/ResponseMessage.svelte';
|
||||
import Placeholder from './Messages/Placeholder.svelte';
|
||||
import MultiResponseMessages from './Messages/MultiResponseMessages.svelte';
|
||||
import Message from './Messages/Message.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -45,11 +43,14 @@
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
const copyToClipboardWithToast = async (text) => {
|
||||
const res = await copyToClipboard(text);
|
||||
if (res) {
|
||||
toast.success($i18n.t('Copying to clipboard was successful!'));
|
||||
}
|
||||
const updateChatMessages = async () => {
|
||||
await tick();
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
history: history
|
||||
});
|
||||
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
};
|
||||
|
||||
const confirmEditMessage = async (messageId, content, submit = true) => {
|
||||
@ -85,23 +86,11 @@
|
||||
history.messages[messageId].content = content;
|
||||
await tick();
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const updateChatMessages = async () => {
|
||||
await tick();
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
|
||||
currentChatPage.set(1);
|
||||
await chats.set(await getChatList(localStorage.token, $currentChatPage));
|
||||
};
|
||||
|
||||
const confirmEditResponseMessage = async (messageId, content) => {
|
||||
history.messages[messageId].originalContent = history.messages[messageId].content;
|
||||
history.messages[messageId].content = content;
|
||||
@ -243,7 +232,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
const deleteMessageHandler = async (messageId) => {
|
||||
const deleteMessage = async (messageId) => {
|
||||
const messageToDelete = history.messages[messageId];
|
||||
const parentMessageId = messageToDelete.parentId;
|
||||
const childMessageIds = messageToDelete.childrenIds ?? [];
|
||||
@ -279,14 +268,13 @@
|
||||
|
||||
// Update the chat
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="h-full flex">
|
||||
{#if messages.length == 0}
|
||||
{#if Object.keys(history?.messages ?? {}).length == 0}
|
||||
<Placeholder
|
||||
modelIds={selectedModels}
|
||||
submitPrompt={async (p) => {
|
||||
@ -327,116 +315,9 @@
|
||||
{:else}
|
||||
<div class="w-full pt-2">
|
||||
{#key chatId}
|
||||
{#each messages as message, messageIdx (message.id)}
|
||||
<div class=" w-full {messageIdx === messages.length - 1 ? ' pb-12' : ''}">
|
||||
<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 message.role === 'user'}
|
||||
<UserMessage
|
||||
on:delete={() => deleteMessageHandler(message.id)}
|
||||
{user}
|
||||
{readOnly}
|
||||
{message}
|
||||
isFirstMessage={messageIdx === 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}
|
||||
/>
|
||||
{:else if (history.messages[message.parentId]?.models?.length ?? 1) === 1}
|
||||
{#key message.id}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
||||
isLastMessage={messageIdx + 1 === messages.length}
|
||||
{readOnly}
|
||||
{updateChatMessages}
|
||||
{confirmEditResponseMessage}
|
||||
{saveNewResponseMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{rateMessage}
|
||||
copyToClipboard={copyToClipboardWithToast}
|
||||
{continueGeneration}
|
||||
{regenerateResponse}
|
||||
on:action={async (e) => {
|
||||
console.log('action', 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:save={async (e) => {
|
||||
console.log('save', e);
|
||||
|
||||
const message = e.detail;
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
{#key message.parentId}
|
||||
<MultiResponseMessages
|
||||
bind:history
|
||||
isLastMessage={messageIdx + 1 === messages.length}
|
||||
{messages}
|
||||
{readOnly}
|
||||
{chatId}
|
||||
parentMessage={history.messages[message.parentId]}
|
||||
{messageIdx}
|
||||
{updateChatMessages}
|
||||
{saveNewResponseMessage}
|
||||
{confirmEditResponseMessage}
|
||||
{rateMessage}
|
||||
copyToClipboard={copyToClipboardWithToast}
|
||||
{continueGeneration}
|
||||
{mergeResponses}
|
||||
{regenerateResponse}
|
||||
on:action={async (e) => {
|
||||
console.log('action', 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:change={async () => {
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
|
||||
if (autoScroll) {
|
||||
const element = document.getElementById('messages-container');
|
||||
autoScroll =
|
||||
element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{JSON.stringify(history)}
|
||||
<!-- <Message {chatId} {history} messageId={history.currentId} {user} /> -->
|
||||
<div class="pb-12" />
|
||||
{#if bottomPadding}
|
||||
<div class=" pb-6" />
|
||||
{/if}
|
||||
|
156
src/lib/components/chat/Messages/Message.svelte
Normal file
156
src/lib/components/chat/Messages/Message.svelte
Normal file
@ -0,0 +1,156 @@
|
||||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { tick, getContext, onMount } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import { settings } from '$lib/stores';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
import MultiResponseMessages from './MultiResponseMessages.svelte';
|
||||
import ResponseMessage from './ResponseMessage.svelte';
|
||||
import UserMessage from './UserMessage.svelte';
|
||||
|
||||
export let chatId;
|
||||
|
||||
export let history;
|
||||
export let messageId;
|
||||
|
||||
export let user;
|
||||
|
||||
export let scrollToBottom;
|
||||
export let chatActionHandler;
|
||||
|
||||
export let confirmEditMessage;
|
||||
export let confirmEditResponseMessage;
|
||||
|
||||
export let showPreviousMessage;
|
||||
export let showNextMessage;
|
||||
|
||||
export let rateMessage;
|
||||
export let deleteMessage;
|
||||
|
||||
export let saveNewResponseMessage;
|
||||
|
||||
export let regenerateResponse;
|
||||
export let continueGeneration;
|
||||
|
||||
// MultiResponseMessages
|
||||
export let mergeResponses;
|
||||
|
||||
export let readOnly = false;
|
||||
|
||||
const copyToClipboardWithToast = async (text) => {
|
||||
const res = await copyToClipboard(text);
|
||||
if (res) {
|
||||
toast.success($i18n.t('Copying to clipboard was successful!'));
|
||||
}
|
||||
};
|
||||
</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]}
|
||||
<UserMessage
|
||||
{user}
|
||||
{history}
|
||||
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}
|
||||
on:delete={() => deleteMessage(message.id)}
|
||||
{readOnly}
|
||||
/>
|
||||
{:else if (history.messages[message.parentId]?.models?.length ?? 1) === 1}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
||||
isLastMessage={messageIdx + 1 === messages.length}
|
||||
{readOnly}
|
||||
{updateChatMessages}
|
||||
{confirmEditResponseMessage}
|
||||
{saveNewResponseMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{rateMessage}
|
||||
copyToClipboard={copyToClipboardWithToast}
|
||||
{continueGeneration}
|
||||
{regenerateResponse}
|
||||
on:action={async (e) => {
|
||||
console.log('action', 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:save={async (e) => {
|
||||
console.log('save', e);
|
||||
|
||||
const message = e.detail;
|
||||
if (message) {
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
history: history
|
||||
});
|
||||
} else {
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
history: history
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<MultiResponseMessages
|
||||
bind:history
|
||||
{chatId}
|
||||
messageIds={[]}
|
||||
parentMessage={history.messages[message.parentId]}
|
||||
isLastMessage={messageIdx + 1 === messages.length}
|
||||
{readOnly}
|
||||
{updateChatMessages}
|
||||
{saveNewResponseMessage}
|
||||
{confirmEditResponseMessage}
|
||||
{rateMessage}
|
||||
copyToClipboard={copyToClipboardWithToast}
|
||||
{continueGeneration}
|
||||
{mergeResponses}
|
||||
{regenerateResponse}
|
||||
on:action={async (e) => {
|
||||
console.log('action', 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:change={async () => {
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
history: history
|
||||
});
|
||||
|
||||
if (autoScroll) {
|
||||
const element = document.getElementById('messages-container');
|
||||
autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -22,7 +22,6 @@
|
||||
export let chatId;
|
||||
|
||||
export let history;
|
||||
export let messages = [];
|
||||
export let messageIdx;
|
||||
|
||||
export let parentMessage;
|
||||
@ -46,7 +45,9 @@
|
||||
let groupedMessages = {};
|
||||
let groupedMessagesIdx = {};
|
||||
|
||||
$: if (parentMessage) {
|
||||
$: if (history.messages) {
|
||||
console.log('history.messages', history.messages);
|
||||
|
||||
initHandler();
|
||||
}
|
||||
|
||||
@ -87,6 +88,7 @@
|
||||
};
|
||||
|
||||
const initHandler = async () => {
|
||||
console.log('multiresponse:initHandler');
|
||||
await tick();
|
||||
currentMessageId = messages[messageIdx].id;
|
||||
|
||||
@ -146,78 +148,76 @@
|
||||
class="flex snap-x snap-mandatory overflow-x-auto scrollbar-hidden"
|
||||
id="responses-container-{chatId}-{parentMessage.id}"
|
||||
>
|
||||
{#key currentMessageId}
|
||||
{#each Object.keys(groupedMessages) as modelIdx}
|
||||
{#if groupedMessagesIdx[modelIdx] !== undefined && groupedMessages[modelIdx].messages.length > 0}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{@const message = groupedMessages[modelIdx].messages[groupedMessagesIdx[modelIdx]]}
|
||||
{#each Object.keys(groupedMessages) as modelIdx}
|
||||
{#if groupedMessagesIdx[modelIdx] !== undefined && groupedMessages[modelIdx].messages.length > 0}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
{@const message = groupedMessages[modelIdx].messages[groupedMessagesIdx[modelIdx]]}
|
||||
|
||||
<div
|
||||
class=" snap-center w-full max-w-full m-1 border {history.messages[currentMessageId]
|
||||
?.modelIdx == modelIdx
|
||||
? `border-gray-100 dark:border-gray-800 border-[1.5px] ${
|
||||
$mobile ? 'min-w-full' : 'min-w-[32rem]'
|
||||
}`
|
||||
: `border-gray-50 dark:border-gray-850 border-dashed ${
|
||||
$mobile ? 'min-w-full' : 'min-w-80'
|
||||
}`} transition-all p-5 rounded-2xl"
|
||||
on:click={() => {
|
||||
if (currentMessageId != message.id) {
|
||||
currentMessageId = message.id;
|
||||
let messageId = message.id;
|
||||
console.log(messageId);
|
||||
//
|
||||
let messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
while (messageChildrenIds.length !== 0) {
|
||||
messageId = messageChildrenIds.at(-1);
|
||||
messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
}
|
||||
history.currentId = messageId;
|
||||
dispatch('change');
|
||||
<div
|
||||
class=" snap-center w-full max-w-full m-1 border {history.messages[currentMessageId]
|
||||
?.modelIdx == modelIdx
|
||||
? `border-gray-100 dark:border-gray-800 border-[1.5px] ${
|
||||
$mobile ? 'min-w-full' : 'min-w-[32rem]'
|
||||
}`
|
||||
: `border-gray-50 dark:border-gray-850 border-dashed ${
|
||||
$mobile ? 'min-w-full' : 'min-w-80'
|
||||
}`} transition-all p-5 rounded-2xl"
|
||||
on:click={() => {
|
||||
if (currentMessageId != message.id) {
|
||||
currentMessageId = message.id;
|
||||
let messageId = message.id;
|
||||
console.log(messageId);
|
||||
//
|
||||
let messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
while (messageChildrenIds.length !== 0) {
|
||||
messageId = messageChildrenIds.at(-1);
|
||||
messageChildrenIds = history.messages[messageId].childrenIds;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#key history.currentId}
|
||||
{#if message}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
siblings={groupedMessages[modelIdx].messages.map((m) => m.id)}
|
||||
isLastMessage={true}
|
||||
{updateChatMessages}
|
||||
{saveNewResponseMessage}
|
||||
{confirmEditResponseMessage}
|
||||
showPreviousMessage={() => showPreviousMessage(modelIdx)}
|
||||
showNextMessage={() => showNextMessage(modelIdx)}
|
||||
{readOnly}
|
||||
{rateMessage}
|
||||
{copyToClipboard}
|
||||
{continueGeneration}
|
||||
regenerateResponse={async (message) => {
|
||||
regenerateResponse(message);
|
||||
await tick();
|
||||
groupedMessagesIdx[modelIdx] = groupedMessages[modelIdx].messages.length - 1;
|
||||
}}
|
||||
on:action={async (e) => {
|
||||
dispatch('action', e.detail);
|
||||
}}
|
||||
on:save={async (e) => {
|
||||
console.log('save', e);
|
||||
history.currentId = messageId;
|
||||
dispatch('change');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#key history.currentId}
|
||||
{#if message}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
siblings={groupedMessages[modelIdx].messages.map((m) => m.id)}
|
||||
isLastMessage={true}
|
||||
{updateChatMessages}
|
||||
{saveNewResponseMessage}
|
||||
{confirmEditResponseMessage}
|
||||
showPreviousMessage={() => showPreviousMessage(modelIdx)}
|
||||
showNextMessage={() => showNextMessage(modelIdx)}
|
||||
{readOnly}
|
||||
{rateMessage}
|
||||
{copyToClipboard}
|
||||
{continueGeneration}
|
||||
regenerateResponse={async (message) => {
|
||||
regenerateResponse(message);
|
||||
await tick();
|
||||
groupedMessagesIdx[modelIdx] = groupedMessages[modelIdx].messages.length - 1;
|
||||
}}
|
||||
on:action={async (e) => {
|
||||
dispatch('action', e.detail);
|
||||
}}
|
||||
on:save={async (e) => {
|
||||
console.log('save', e);
|
||||
|
||||
const message = e.detail;
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/key}
|
||||
const message = e.detail;
|
||||
history.messages[message.id] = message;
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if !readOnly && isLastMessage}
|
||||
|
Loading…
Reference in New Issue
Block a user