diff --git a/apps/sim/app/api/copilot/chat/route.ts b/apps/sim/app/api/copilot/chat/route.ts index e42fb317a4..6b5bfad14d 100644 --- a/apps/sim/app/api/copilot/chat/route.ts +++ b/apps/sim/app/api/copilot/chat/route.ts @@ -244,7 +244,7 @@ export async function POST(req: NextRequest) { stream: stream, streamToolCalls: true, mode: mode, - ...(createNewChat && session?.user?.name && { userName: session.user.name }), + ...(session?.user?.name && { userName: session.user.name }), }), }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 4bf460d61f..f70e8ec1ad 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -90,7 +90,12 @@ export function Panel() { // Load chats for the current workflow - let the store handle caching if (activeWorkflowId) { await loadChats(forceRefresh) - validateCurrentChat() + + // Only validate current chat if we're not actively streaming + // This prevents clearing the current conversation during a stream + if (!isSendingMessage) { + validateCurrentChat() + } // Mark this workflow as loaded for the legacy ref lastLoadedWorkflowRef.current = activeWorkflowId @@ -106,6 +111,7 @@ export function Panel() { loadChats, validateCurrentChat, isLoadingChats, + isSendingMessage, ] ) @@ -121,9 +127,10 @@ export function Panel() { // Open dropdown immediately for better UX setIsHistoryDropdownOpen(open) - // If opening, ensure data is loaded but don't force refresh unless needed - if (open && activeWorkflowId) { - // Only load if we don't have fresh chats for this workflow + // If opening and there's an active stream, don't do any data loading at all + // Just show what's already loaded to avoid any interference + if (open && activeWorkflowId && !isSendingMessage) { + // Only load if we don't have fresh chats for this workflow AND we're not streaming if (!areChatsFresh(activeWorkflowId)) { // Don't await - let it load in background while dropdown is already open ensureCopilotDataLoaded(false).catch((error) => { @@ -131,8 +138,13 @@ export function Panel() { }) } } + + // If streaming, just log that we're showing cached data + if (open && isSendingMessage) { + console.log('Chat history opened during stream - showing cached data only') + } }, - [ensureCopilotDataLoaded, activeWorkflowId, areChatsFresh] + [ensureCopilotDataLoaded, activeWorkflowId, areChatsFresh, isSendingMessage] ) // Group chats by day @@ -412,7 +424,11 @@ export function Panel() {
{ - selectChat(chat) + // Only call selectChat if it's a different chat + // This prevents aborting streams when clicking the currently active chat + if (currentChat?.id !== chat.id) { + selectChat(chat) + } setIsHistoryDropdownOpen(false) }} className={`group mx-1 flex h-8 cursor-pointer items-center rounded-lg px-2 py-1.5 text-left transition-colors ${ diff --git a/apps/sim/stores/copilot/store.ts b/apps/sim/stores/copilot/store.ts index a85c048577..d49749b129 100644 --- a/apps/sim/stores/copilot/store.ts +++ b/apps/sim/stores/copilot/store.ts @@ -1597,13 +1597,30 @@ export const useCopilotStore = create()( (chat: CopilotChat) => chat.id === currentChat.id ) if (updatedCurrentChat) { - set({ - currentChat: updatedCurrentChat, - messages: ensureToolCallDisplayNames(updatedCurrentChat.messages || []), - }) - logger.info( - `Preserved current chat selection: ${updatedCurrentChat.title || 'Untitled'} (${updatedCurrentChat.messages?.length || 0} messages)` - ) + const { isSendingMessage } = get() + + // If we're currently streaming, preserve the current messages state + // to avoid overwriting streaming content with stale database state + if (isSendingMessage) { + set({ + currentChat: { + ...updatedCurrentChat, + messages: get().messages, // Preserve current streaming messages + }, + }) + logger.info( + `Preserved current chat and streaming messages during active stream: ${updatedCurrentChat.title || 'Untitled'}` + ) + } else { + // Safe to update messages when not streaming + set({ + currentChat: updatedCurrentChat, + messages: ensureToolCallDisplayNames(updatedCurrentChat.messages || []), + }) + logger.info( + `Updated current chat with latest database state: ${updatedCurrentChat.title || 'Untitled'} (${updatedCurrentChat.messages?.length || 0} messages)` + ) + } // Load checkpoints for the preserved chat try { @@ -1614,22 +1631,31 @@ export const useCopilotStore = create()( } } else { // Only auto-select most recent chat if no current chat or current chat is stale - const mostRecentChat = data.chats[0] - set({ - currentChat: mostRecentChat, - messages: ensureToolCallDisplayNames(mostRecentChat.messages || []), - }) - logger.info( - `Auto-selected most recent chat for workflow ${workflowId}: ${mostRecentChat.title || 'Untitled'}` - ) + // But don't auto-select during streaming to avoid disrupting the conversation + const { isSendingMessage } = get() - // Load checkpoints for the auto-selected chat - try { - await get().loadMessageCheckpoints(mostRecentChat.id) - } catch (checkpointError) { - logger.error( - 'Failed to load checkpoints for auto-selected chat:', - checkpointError + if (!isSendingMessage) { + const mostRecentChat = data.chats[0] + set({ + currentChat: mostRecentChat, + messages: ensureToolCallDisplayNames(mostRecentChat.messages || []), + }) + logger.info( + `Auto-selected most recent chat for workflow ${workflowId}: ${mostRecentChat.title || 'Untitled'}` + ) + + // Load checkpoints for the auto-selected chat + try { + await get().loadMessageCheckpoints(mostRecentChat.id) + } catch (checkpointError) { + logger.error( + 'Failed to load checkpoints for auto-selected chat:', + checkpointError + ) + } + } else { + logger.info( + `Skipped auto-selecting chat during active stream for workflow ${workflowId}` ) } }