diff --git a/frontend/src/components/thread/tool-views/expand-message-tool/ExpandMessageToolView.tsx b/frontend/src/components/thread/tool-views/expand-message-tool/ExpandMessageToolView.tsx new file mode 100644 index 00000000..3743c67b --- /dev/null +++ b/frontend/src/components/thread/tool-views/expand-message-tool/ExpandMessageToolView.tsx @@ -0,0 +1,175 @@ +import React from 'react'; +import { + Expand, + CheckCircle, + AlertTriangle, + Loader2, + Clock, + MessageSquareText, + Copy, + Check, +} from 'lucide-react'; +import { ToolViewProps } from '../types'; +import { formatTimestamp, getToolTitle } from '../utils'; +import { extractExpandMessageData } from './_utils'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Button } from '@/components/ui/button'; +import { toast } from 'sonner'; +import Markdown from 'react-markdown'; + +export function ExpandMessageToolView({ + name = 'expand_message', + assistantContent, + toolContent, + assistantTimestamp, + toolTimestamp, + isSuccess = true, + isStreaming = false, +}: ToolViewProps) { + const { + messageId, + message, + status, + actualIsSuccess, + actualToolTimestamp, + actualAssistantTimestamp + } = extractExpandMessageData( + assistantContent, + toolContent, + isSuccess, + toolTimestamp, + assistantTimestamp + ); + + const [isCopying, setIsCopying] = React.useState(false); + const toolTitle = getToolTitle(name) || 'Message Expansion'; + + const copyToClipboard = React.useCallback(async (text: string) => { + try { + await navigator.clipboard.writeText(text); + return true; + } catch (err) { + console.error('Failed to copy text: ', err); + return false; + } + }, []); + + const handleCopyMessage = React.useCallback(async () => { + if (!message) return; + + setIsCopying(true); + const success = await copyToClipboard(message); + if (success) { + toast.success('Message copied to clipboard'); + } else { + toast.error('Failed to copy message'); + } + setTimeout(() => setIsCopying(false), 500); + }, [message, copyToClipboard]); + + return ( + + +
+
+
+ +
+
+ + {toolTitle} + +
+
+ + {!isStreaming && ( + + {actualIsSuccess ? ( + + ) : ( + + )} + {actualIsSuccess ? 'Expanded' : 'Failed'} + + )} + + {isStreaming && ( + + + Expanding + + )} +
+
+ + + +
+ {/* Message ID */} + {messageId && ( +
+ + ID: {messageId} + +
+ )} + + {/* Expanded Message Content - Simple display */} + {message ? ( +
+
+ {message} +
+
+ ) : !isStreaming ? ( +
+
+ +
+

+ {actualIsSuccess ? 'No Message Content' : 'Expansion Failed'} +

+

+ {actualIsSuccess + ? 'The expanded message does not contain any displayable content.' + : 'Unable to expand the requested message. It may not exist or you may not have access to it.'} +

+
+ ) : null} +
+
+
+ +
+
+ + + Message Retrieval + +
+ +
+ + {actualToolTimestamp + ? formatTimestamp(actualToolTimestamp) + : actualAssistantTimestamp + ? formatTimestamp(actualAssistantTimestamp) + : ''} +
+
+
+ ); +} + diff --git a/frontend/src/components/thread/tool-views/expand-message-tool/_utils.ts b/frontend/src/components/thread/tool-views/expand-message-tool/_utils.ts new file mode 100644 index 00000000..7762979d --- /dev/null +++ b/frontend/src/components/thread/tool-views/expand-message-tool/_utils.ts @@ -0,0 +1,161 @@ +import { extractToolData } from '../utils'; + +export interface ExpandMessageData { + messageId?: string; + message?: string; + status?: string; + actualIsSuccess: boolean; + actualToolTimestamp?: string; + actualAssistantTimestamp?: string; +} + +const parseContent = (content: any): any => { + if (typeof content === 'string') { + try { + return JSON.parse(content); + } catch (e) { + return content; + } + } + return content; +}; + +const extractFromNewFormat = (content: any): { + messageId?: string; + message?: string; + status?: string; + success?: boolean; + timestamp?: string; +} => { + const parsedContent = parseContent(content); + + if (!parsedContent || typeof parsedContent !== 'object') { + return {}; + } + + // Handle new format with tool_execution + if ('tool_execution' in parsedContent && typeof parsedContent.tool_execution === 'object') { + const toolExecution = parsedContent.tool_execution; + const args = toolExecution.arguments || {}; + + let parsedOutput = toolExecution.result?.output; + if (typeof parsedOutput === 'string') { + try { + parsedOutput = JSON.parse(parsedOutput); + } catch (e) { + // Keep as string + } + } + + const extractedData: any = { + messageId: args.message_id, + success: toolExecution.result?.success, + timestamp: toolExecution.execution_details?.timestamp + }; + + // Extract message and status from output + if (parsedOutput && typeof parsedOutput === 'object') { + extractedData.status = parsedOutput.status; + extractedData.message = parsedOutput.message || parsedOutput.content; + } else if (typeof parsedOutput === 'string') { + extractedData.message = parsedOutput; + } + + return extractedData; + } + + // Handle content wrapper + if ('role' in parsedContent && 'content' in parsedContent) { + return extractFromNewFormat(parsedContent.content); + } + + return {}; +}; + +const extractFromLegacyFormat = (content: any): { + messageId?: string; + message?: string; + status?: string; +} => { + const toolData = extractToolData(content); + + if (toolData.arguments || toolData.toolResult) { + const result: any = { + messageId: toolData.arguments?.message_id + }; + + const output = toolData.toolResult?.toolOutput; + if (output) { + if (typeof output === 'object' && output !== null) { + result.status = (output as any).status; + result.message = (output as any).message || (output as any).content; + } else if (typeof output === 'string') { + try { + const parsed = JSON.parse(output); + result.status = parsed.status; + result.message = parsed.message || parsed.content; + } catch { + result.message = output; + } + } + } + + return result; + } + + return {}; +}; + +export function extractExpandMessageData( + assistantContent: any, + toolContent: any, + isSuccess: boolean, + toolTimestamp?: string, + assistantTimestamp?: string +): ExpandMessageData { + let messageId: string | undefined; + let message: string | undefined; + let status: string | undefined; + let actualIsSuccess = isSuccess; + let actualToolTimestamp = toolTimestamp; + const actualAssistantTimestamp = assistantTimestamp; + + // Try new format first + const assistantNewFormat = extractFromNewFormat(assistantContent); + const toolNewFormat = extractFromNewFormat(toolContent); + + // Extract from assistant content (parameters) + if (assistantNewFormat.messageId) { + messageId = assistantNewFormat.messageId; + } + + // Extract from tool result (output) + if (toolNewFormat.message || toolNewFormat.status) { + message = toolNewFormat.message; + status = toolNewFormat.status; + if (toolNewFormat.success !== undefined) { + actualIsSuccess = toolNewFormat.success; + } + if (toolNewFormat.timestamp) { + actualToolTimestamp = toolNewFormat.timestamp; + } + } else { + // Try legacy format + const assistantLegacy = extractFromLegacyFormat(assistantContent); + const toolLegacy = extractFromLegacyFormat(toolContent); + + messageId = messageId || assistantLegacy.messageId || toolLegacy.messageId; + message = toolLegacy.message; + status = toolLegacy.status; + } + + return { + messageId, + message, + status, + actualIsSuccess, + actualToolTimestamp, + actualAssistantTimestamp + }; +} + diff --git a/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx b/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx index 8b27e89a..3d03892d 100644 --- a/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx +++ b/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx @@ -58,6 +58,7 @@ import ListAgentWorkflowsToolView from '../list-agent-workflows/list-agent-workf import { createPresentationViewerToolContent, parsePresentationSlidePath } from '../utils/presentation-utils'; import { extractToolData } from '../utils'; import { KbToolView } from '../KbToolView'; +import { ExpandMessageToolView } from '../expand-message-tool/ExpandMessageToolView'; export type ToolViewComponent = React.ComponentType; @@ -122,6 +123,8 @@ const defaultRegistry: ToolViewRegistryType = { 'ask': AskToolView, 'complete': CompleteToolView, 'wait': WaitToolView, + 'expand_message': ExpandMessageToolView, + 'expand-message': ExpandMessageToolView, 'deploy': DeployToolView,