This commit is contained in:
Timothy Jaeryang Baek
2026-05-09 03:40:23 +09:00
parent 4d766a3edf
commit cdfcbc4af6
5 changed files with 76 additions and 10 deletions
+11
View File
@@ -2263,6 +2263,9 @@ async def process_chat_payload(request, form_data, user, metadata, model):
form_data = apply_params_to_form_data(form_data, model)
log.debug(f'form_data: {form_data}')
# Guided regeneration: extract before it reaches the LLM provider
regeneration_prompt = form_data.pop('regeneration_prompt', None)
# Load messages from DB when available — DB preserves structured 'output' items
# which the frontend strips, causing tool calls to be merged into content.
chat_id = metadata.get('chat_id')
@@ -2298,6 +2301,9 @@ async def process_chat_payload(request, form_data, user, metadata, model):
# Strip files field — it's been incorporated into content
message.pop('files', None)
if regeneration_prompt:
form_data['messages'].append({'role': 'user', 'content': regeneration_prompt})
# Process messages with OR-aligned output items for clean LLM messages
form_data['messages'] = process_messages_with_output(form_data.get('messages', []))
@@ -4876,6 +4882,11 @@ async def streaming_chat_response_handler(response, ctx):
log.debug(f'Code interpreter output: {ci_output}')
# Handle error responses from event_caller
# (e.g. session disconnected, timeout)
if isinstance(ci_output, dict) and ci_output.get('error'):
ci_output = {'stderr': ci_output['error']}
if isinstance(ci_output, dict):
stdout = ci_output.get('stdout', '')
+21 -10
View File
@@ -121,8 +121,10 @@
let controlPaneComponent: ChatControls | undefined;
let messageInput: MessageInput | undefined;
let messagesRef: Messages | undefined;
let autoScroll = true;
let isNearTop = true;
let processing = '';
let messagesContainerElement: HTMLDivElement;
@@ -1413,6 +1415,10 @@
}
};
const scrollToTop = async () => {
await messagesRef?.scrollToTop();
};
let scrollRAF = null;
let contentsRAF = null;
const scheduleScrollToBottom = () => {
@@ -1969,11 +1975,13 @@
{
messages = null,
modelId = null,
modelIdx = null
modelIdx = null,
regenerationPrompt = null
}: {
messages?: any[] | null;
modelId?: string | null;
modelIdx?: number | null;
regenerationPrompt?: string | null;
} = {}
) => {
if (autoScroll) {
@@ -2085,7 +2093,8 @@
_history,
primaryResponseMessageId,
_chatId,
selectedModelIds.length > 1 ? messageIdsMap : undefined
selectedModelIds.length > 1 ? messageIdsMap : undefined,
regenerationPrompt
);
if (chatEventEmitter) clearInterval(chatEventEmitter);
@@ -2150,7 +2159,8 @@
_history,
responseMessageId,
_chatId,
messageIdsMap?: Record<string, string>
messageIdsMap?: Record<string, string>,
regenerationPrompt?: string | null
) => {
const responseMessage = _history.messages[responseMessageId];
const userMessage = _history.messages[responseMessage.parentId];
@@ -2206,6 +2216,8 @@
? { role: 'system', content: `${params?.system ?? $settings?.system ?? ''}` }
: undefined
].filter(Boolean);
if ($temporaryChatEnabled) {
messages = [
...messages,
@@ -2349,6 +2361,7 @@
...(messageIdsMap ? { message_ids: messageIdsMap } : {}),
parent_id: userMessage?.parentId ?? null,
user_message: userMessage,
...(regenerationPrompt ? { regeneration_prompt: regenerationPrompt } : {}),
background_tasks: {
...(!$temporaryChatEnabled && !_chatId && (userMessage?.parentId ?? null) === null
@@ -2580,13 +2593,8 @@
await sendMessage(history, userMessage.id, {
...(suggestionPrompt
? {
messages: [
...createMessagesList(history, message.id),
{
role: 'user',
content: suggestionPrompt
}
]
messages: createMessagesList(history, message.id),
regenerationPrompt: suggestionPrompt
}
: {}),
...((userMessage?.models ?? [...selectedModels]).length > 1
@@ -2916,6 +2924,7 @@
bind:selectedModels
shareEnabled={!!history.currentId}
{initNewChat}
scrollToTop={!isNearTop ? scrollToTop : null}
{archiveChatHandler}
{deleteChatHandler}
{moveChatHandler}
@@ -2968,10 +2977,12 @@
autoScroll =
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
messagesContainerElement.clientHeight + 5;
isNearTop = messagesContainerElement.scrollTop <= 100;
}}
>
<div class=" h-full w-full flex flex-col">
<Messages
bind:this={messagesRef}
chatId={$chatId}
bind:history
bind:autoScroll
+12
View File
@@ -143,6 +143,18 @@
element.scrollTop = element.scrollHeight;
};
export const scrollToTop = async () => {
messagesCount = null;
buildMessages();
await tick();
if (messages.length > 0) {
const firstMessageEl = document.getElementById(`message-${messages[0].id}`);
if (firstMessageEl) {
firstMessageEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
};
const updateChat = async () => {
if (!$temporaryChatEnabled) {
history = history;
+2
View File
@@ -45,6 +45,7 @@
export let initNewChat: Function;
export let shareEnabled: boolean = false;
export let scrollTop = 0;
export let scrollToTop: (() => void) | null = null;
export let chat;
export let history;
@@ -194,6 +195,7 @@
<Menu
{chat}
{shareEnabled}
{scrollToTop}
shareHandler={() => {
showShareChatModal = !showShareChatModal;
}}
@@ -50,6 +50,7 @@
export let chat;
export let onClose: Function = () => {};
export let scrollToTop: (() => void) | null = null;
let showFullMessages = false;
@@ -310,6 +311,35 @@
</svg>
<div class="flex items-center">{$i18n.t('Settings')}</div>
</DropdownMenu.Item> -->
<!-- Settings commented out block above -->
{#if scrollToTop}
<button
draggable="false"
class="flex gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl select-none w-full"
on:click={() => {
scrollToTop();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18"
/>
</svg>
<div class="flex items-center">{$i18n.t('Scroll to Top')}</div>
</button>
<hr class="border-gray-50/30 dark:border-gray-800/30 my-1" />
{/if}
{#if ($artifactContents ?? []).length > 0}
<button