diff --git a/frontend/src/components/thread/content/ThreadContent.tsx b/frontend/src/components/thread/content/ThreadContent.tsx index 47a81c16..b75de217 100644 --- a/frontend/src/components/thread/content/ThreadContent.tsx +++ b/frontend/src/components/thread/content/ThreadContent.tsx @@ -421,13 +421,76 @@ export const ThreadContent: React.FC = ({ groupedMessages.push(currentGroup); } + // Debug logging to help identify grouping issues + console.log('=== MESSAGE GROUPING DEBUG ==='); + console.log('Input messages:', displayMessages.map((m, i) => ({ index: i, type: m.type, id: m.message_id }))); + console.log('Grouped messages:', groupedMessages.map(g => ({ + type: g.type, + key: g.key, + messageCount: g.messages.length, + messageTypes: g.messages.map(m => m.type), + messageIds: g.messages.map(m => m.message_id) + }))); + + // Show detailed breakdown of assistant groups + groupedMessages.forEach((group, i) => { + if (group.type === 'assistant_group') { + console.log(`Group ${i} (${group.key}):`, group.messages.map(m => ({ type: m.type, id: m.message_id }))); + } + }); + + // Merge consecutive assistant groups + const mergedGroups: MessageGroup[] = []; + let currentMergedGroup: MessageGroup | null = null; + + groupedMessages.forEach((group, index) => { + if (group.type === 'assistant_group') { + if (currentMergedGroup && currentMergedGroup.type === 'assistant_group') { + // Merge with the current group + currentMergedGroup.messages.push(...group.messages); + console.log(`Merged group ${index} into existing assistant group`); + } else { + // Finalize previous group if it exists + if (currentMergedGroup) { + mergedGroups.push(currentMergedGroup); + } + // Start new merged group + currentMergedGroup = { ...group }; + } + } else { + // Finalize current merged group if it exists + if (currentMergedGroup) { + mergedGroups.push(currentMergedGroup); + currentMergedGroup = null; + } + // Add non-assistant group as-is + mergedGroups.push(group); + } + }); + + // Finalize any remaining merged group + if (currentMergedGroup) { + mergedGroups.push(currentMergedGroup); + } + + console.log('After merging consecutive assistant groups:', mergedGroups.map(g => ({ + type: g.type, + key: g.key, + messageCount: g.messages.length, + messageTypes: g.messages.map(m => m.type) + }))); + console.log('==============================='); + + // Use merged groups instead of original grouped messages + const finalGroupedMessages = mergedGroups; + // Handle streaming content - only add to existing group or create new one if needed if (streamingTextContent) { - const lastGroup = groupedMessages.at(-1); + const lastGroup = finalGroupedMessages.at(-1); if (!lastGroup || lastGroup.type === 'user') { // Create new assistant group for streaming content assistantGroupCounter++; - groupedMessages.push({ + finalGroupedMessages.push({ type: 'assistant_group', messages: [{ content: streamingTextContent, @@ -443,21 +506,25 @@ export const ThreadContent: React.FC = ({ key: `assistant-group-${assistantGroupCounter}-streaming` }); } else if (lastGroup.type === 'assistant_group') { - lastGroup.messages.push({ - content: streamingTextContent, - type: 'assistant', - message_id: 'streamingTextContent', - metadata: 'streamingTextContent', - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - is_llm_message: true, - thread_id: 'streamingTextContent', - sequence: Infinity, - }); + // Only add streaming content if it's not already represented in the last message + const lastMessage = lastGroup.messages[lastGroup.messages.length - 1]; + if (lastMessage.message_id !== 'streamingTextContent') { + lastGroup.messages.push({ + content: streamingTextContent, + type: 'assistant', + message_id: 'streamingTextContent', + metadata: 'streamingTextContent', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + is_llm_message: true, + thread_id: 'streamingTextContent', + sequence: Infinity, + }); + } } } - return groupedMessages.map((group, groupIndex) => { + return finalGroupedMessages.map((group, groupIndex) => { if (group.type === 'user') { const message = group.messages[0]; const messageContent = (() => { @@ -565,12 +632,12 @@ export const ThreadContent: React.FC = ({ const renderedToolResultIds = new Set(); const elements: React.ReactNode[] = []; + let assistantMessageCount = 0; // Move this outside the loop group.messages.forEach((message, msgIndex) => { if (message.type === 'assistant') { const parsedContent = safeJsonParse(message.content, {}); const msgKey = message.message_id || `submsg-assistant-${msgIndex}`; - let assistantMessageCount = 0; if (!parsedContent.content) return; @@ -591,13 +658,15 @@ export const ThreadContent: React.FC = ({ ); + + assistantMessageCount++; // Increment after adding the element } }); return elements; })()} - {groupIndex === groupedMessages.length - 1 && !readOnly && (streamHookStatus === 'streaming' || streamHookStatus === 'connecting') && ( + {groupIndex === finalGroupedMessages.length - 1 && !readOnly && (streamHookStatus === 'streaming' || streamHookStatus === 'connecting') && (
{(() => { // In debug mode, show raw streaming content @@ -701,7 +770,7 @@ export const ThreadContent: React.FC = ({ )} {/* For playback mode, show streaming text and tool calls */} - {readOnly && groupIndex === groupedMessages.length - 1 && isStreamingText && ( + {readOnly && groupIndex === finalGroupedMessages.length - 1 && isStreamingText && (
{(() => { let detectedTag: string | null = null;