Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 46 additions & 34 deletions components/conversation/activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ export const severities = {
},
};

export function getTimeDifference(timestamp1, timestamp2) {
export function getTimeDifference(timestamp1: string | Date, timestamp2: string | Date) {
// Convert timestamps to Date objects
const date1 = new Date(timestamp1);
const date2 = new Date(timestamp2);

// Calculate the difference in milliseconds
const diffInMs = Math.abs(date1 - date2);
const diffInMs = Math.abs(date1.getTime() - date2.getTime());

// Convert milliseconds to seconds
const diffInSeconds = Math.floor(diffInMs / 1000);
Expand All @@ -142,7 +142,7 @@ export type ActivityProps = {
message: string;
alternateBackground?: string;
timestamp: string;
nextTimestamp: string;
nextTimestamp?: string;
children?: any[];
};

Expand Down Expand Up @@ -254,38 +254,50 @@ export function Activity({
</AccordionTrigger>
<AccordionContent className='pl-4 border-b-0 bg-muted/30 border-l-2 border-l-primary/20 ml-2'>
{children?.map((child, index) => {
const messageType = child.message.split(' ')[0];
const messageBody = child.message.substring(child.message.indexOf(' '));
return (
<Activity
key={child.timestamp + '-' + messageBody}
activityType={
messageType.startsWith('[SUBACTIVITY]') && !messageType.split('[')[3]
? 'success'
: (messageType
.split('[')
[messageType.startsWith('[SUBACTIVITY]') ? 3 : 2].split(']')[0]
.toLowerCase() as
| 'error'
| 'info'
| 'success'
| 'warn'
| 'thought'
| 'reflection'
| 'execution'
| 'diagram')
const messageType = child.message.split(' ')[0];
const messageBody = child.message.substring(child.message.indexOf(' '));

// Calculate proper nextTimestamp for child activities
const getChildNextTimestamp = () => {
// If there's a next child, use its timestamp
if (index < children.length - 1) {
return children[index + 1].timestamp;
}
message={messageBody}
nextTimestamp={index === children.length - 1 ? nextTimestamp : children[index + 1].timestamp}
timestamp={child.timestamp}
alternateBackground={alternateBackground}
children={child.children}
/>
);
})}
</AccordionContent>
</AccordionItem>
</Accordion>
// If this is the last child, use the parent's nextTimestamp
// which should now be correctly calculated based on when the activity group ends
return nextTimestamp;
};

return (
<Activity
key={child.timestamp + '-' + messageBody}
activityType={
messageType.startsWith('[SUBACTIVITY]') && !messageType.split('[')[3]
? 'success'
: (messageType
.split('[')
[messageType.startsWith('[SUBACTIVITY]') ? 3 : 2].split(']')[0]
.toLowerCase() as
| 'error'
| 'info'
| 'success'
| 'warn'
| 'thought'
| 'reflection'
| 'execution'
| 'diagram')
}
message={messageBody}
nextTimestamp={getChildNextTimestamp()}
timestamp={child.timestamp}
alternateBackground={alternateBackground}
children={child.children}
/>
);
})}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
);
}
54 changes: 42 additions & 12 deletions components/conversation/conversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,15 @@ import { getCookie } from 'cookies-next';
import axios from 'axios';
import { useRouter } from 'next/navigation';
import { Paperclip, Pencil, Plus, Check, Download, Trash2 } from 'lucide-react';

import { useConversationWebSocket } from '@/hooks/useConversationWebSocketStable';
import { InteractiveConfigContext, Overrides } from '@/components/interactive/InteractiveConfigContext';
import { useCompany } from '@/components/interactive/useUser';
import { useConversations } from '@/components/interactive/useConversation';
import { toast } from '@/components/layout/toast';

interface ConversationMessage {
id: string;
role: string;
message: string;
timestamp: string;
children: ConversationMessage[];
}

import { Activity as ChatActivity } from '@/components/conversation/activity';
import Message from '@/components/conversation/Message/Message';
import { SidebarContent } from '@/components/layout/SidebarContentManager';
import { ChatBar } from '@/components/conversation/input/chat-input';

import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
Expand All @@ -40,6 +29,13 @@ import {
useSidebar,
} from '@/components/ui/sidebar';

interface ConversationMessage {
id: string;
role: string;
message: string;
timestamp: string;
children: ConversationMessage[];
}
export type UIProps = {
showSelectorsCSV?: string;
enableFileUpload?: boolean;
Expand Down Expand Up @@ -398,7 +394,41 @@ export function ChatLog({
| 'execution'
| 'diagram')
}
nextTimestamp={conversation[index + 1]?.timestamp}
nextTimestamp={(() => {
// If this activity has children, use the last child's timestamp as the activity end time
if (chatItem.children && chatItem.children.length > 0) {
const lastChild = chatItem.children[chatItem.children.length - 1];
return lastChild.timestamp;
}

// For activities without children, look for the next activity or message
// that has a timestamp very close to this activity (likely part of the same response)
for (let i = index + 1; i < conversation.length; i++) {
const nextItem = conversation[i];
const nextMessageType = nextItem.message.split(' ')[0];

// If it's an assistant message that comes shortly after the activity,
// this is likely when the activity completed
if (
nextItem.role === 'assistant' &&
!validTypes.some((x) => nextMessageType.includes(x)) &&
!nextItem.message.startsWith('[SUBACTIVITY]')
) {
const timeDiff = new Date(nextItem.timestamp).getTime() - new Date(chatItem.timestamp).getTime();
// If the next message is within 30 seconds, it's likely the completion of this activity
if (timeDiff <= 30000) {
return nextItem.timestamp;
}
}

// If it's a different activity group (different base timestamp), use its timestamp
if (validTypes.some((x) => nextMessageType.includes(x)) && nextItem.timestamp !== chatItem.timestamp) {
return nextItem.timestamp;
}
}
// No next timestamp found, this activity is still running
return undefined;
})()}
message={messageBody}
timestamp={chatItem.timestamp}
alternateBackground={alternateBackground}
Expand Down
Loading