diff --git a/src/components/chat/AgentMessage.tsx b/src/components/chat/AgentMessage.tsx index 84b8266..676d214 100644 --- a/src/components/chat/AgentMessage.tsx +++ b/src/components/chat/AgentMessage.tsx @@ -5,6 +5,7 @@ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { ChatMessage } from '@/types'; import { Copy, Check } from 'lucide-react'; +import ExecutionTrace from './ExecutionTrace'; interface AgentMessageProps { message: ChatMessage; @@ -241,6 +242,12 @@ export default function AgentMessage({ message, onCopy }: AgentMessageProps) { {renderContent()} + + {/* Execution Trace */} + {message.metadata?.executionTrace && ( + + )} + >(new Set()); + const [isExpanded, setIsExpanded] = useState(false); + + const toggleStep = (stepId: string) => { + const newExpanded = new Set(expandedSteps); + if (newExpanded.has(stepId)) { + newExpanded.delete(stepId); + } else { + newExpanded.add(stepId); + } + setExpandedSteps(newExpanded); + }; + + const getStepIcon = (type: ExecutionStep['type']) => { + switch (type) { + case 'thought': + return ; + case 'action': + return ; + case 'tool_call': + return ; + case 'result': + return ; + case 'error': + return ; + default: + return ; + } + }; + + const getStepColor = (type: ExecutionStep['type']) => { + switch (type) { + case 'thought': + return 'border-purple-500/30 bg-purple-500/10'; + case 'action': + return 'border-blue-500/30 bg-blue-500/10'; + case 'tool_call': + return 'border-orange-500/30 bg-orange-500/10'; + case 'result': + return 'border-green-500/30 bg-green-500/10'; + case 'error': + return 'border-red-500/30 bg-red-500/10'; + default: + return 'border-gray-500/30 bg-gray-500/10'; + } + }; + + const formatDuration = (ms: number) => { + if (ms < 1000) return `${ms}ms`; + return `${(ms / 1000).toFixed(2)}s`; + }; + + const renderStep = (step: ExecutionStep, depth: number = 0) => { + const isStepExpanded = expandedSteps.has(step.id); + const hasSubsteps = step.substeps && step.substeps.length > 0; + + return ( + 0 ? 'ml-6' : ''}`}> + hasSubsteps && toggleStep(step.id)} + > + + + {hasSubsteps && ( + + {isStepExpanded ? : } + + )} + {getStepIcon(step.type)} + + + {step.name} + + + {formatDuration(step.duration)} + + + {step.description} + + + + + {/* Step Details */} + {step.details && ( + + + {typeof step.details === 'string' + ? step.details + : JSON.stringify(step.details, null, 2) + } + + + )} + + + {/* Substeps */} + {hasSubsteps && isStepExpanded && ( + + {step.substeps!.map(substep => renderStep(substep, depth + 1))} + + )} + + ); + }; + + if (!trace || !trace.steps || trace.steps.length === 0) { + return null; + } + + return ( + + {/* Header */} + setIsExpanded(!isExpanded)} + > + + + + Execution Trace + ({trace.steps.length} steps) + + + + + {formatDuration(trace.totalTime)} + + + + + + + {/* Content */} + {isExpanded && ( + + {/* Summary */} + + + + Start Time: + + {new Date(trace.startTime).toLocaleTimeString()} + + + + End Time: + + {new Date(trace.endTime).toLocaleTimeString()} + + + + Total Duration: + {formatDuration(trace.totalTime)} + + + Steps: + {trace.steps.length} + + + + + {/* Steps */} + + {trace.steps.map(step => renderStep(step))} + + + )} + + ); +} diff --git a/src/components/examples/ExecutionTraceDemo.tsx b/src/components/examples/ExecutionTraceDemo.tsx new file mode 100644 index 0000000..54c5d15 --- /dev/null +++ b/src/components/examples/ExecutionTraceDemo.tsx @@ -0,0 +1,130 @@ +'use client'; + +import React from 'react'; +import { ExecutionTrace } from '@/types'; +import ExecutionTraceComponent from '@/components/chat/ExecutionTrace'; + +// Sample execution trace data for demonstration +const sampleExecutionTrace: ExecutionTrace = { + steps: [ + { + id: '1', + name: 'Understanding User Query', + type: 'thought', + timestamp: '2024-03-27T13:15:00.000Z', + duration: 150, + description: 'Analyzing the user query about vault strategies and identifying key requirements', + details: { + query: 'What are the best vault strategies for high yield?', + intent: 'User wants investment recommendations', + entities: ['vault strategies', 'high yield', 'investment'] + } + }, + { + id: '2', + name: 'Fetching Vault Data', + type: 'action', + timestamp: '2024-03-27T13:15:00.150Z', + duration: 300, + description: 'Retrieving current vault information and performance data', + substeps: [ + { + id: '2.1', + name: 'Query Vault Registry', + type: 'tool_call', + timestamp: '2024-03-27T13:15:00.200Z', + duration: 120, + description: 'Calling vault registry API to get available vaults', + details: { + tool: 'vault_registry', + parameters: { include_performance: true }, + result: { vaults_found: 15 } + } + }, + { + id: '2.2', + name: 'Filter High-Performance Vaults', + type: 'action', + timestamp: '2024-03-27T13:15:00.320Z', + duration: 80, + description: 'Filtering vaults based on APY and risk metrics', + details: { + criteria: { min_apy: 8, max_risk: 'medium' }, + filtered_count: 7 + } + } + ] + }, + { + id: '3', + name: 'Analysis and Ranking', + type: 'thought', + timestamp: '2024-03-27T13:15:00.450Z', + duration: 200, + description: 'Analyzing vault performance metrics and ranking by risk-adjusted returns', + details: { + metrics: ['apy', 'tvl', 'volatility', 'risk_score'], + ranking_method: 'sharpe_ratio_weighted' + } + }, + { + id: '4', + name: 'Generate Recommendations', + type: 'result', + timestamp: '2024-03-27T13:15:01.150Z', + duration: 100, + description: 'Creating personalized recommendations based on user preferences', + details: { + recommendations: [ + { + vault_name: 'Stable Yield Plus', + apy: 12.5, + risk_level: 'low', + confidence: 0.95 + }, + { + vault_name: 'Growth Strategy Alpha', + apy: 18.2, + risk_level: 'medium', + confidence: 0.88 + } + ] + } + } + ], + totalTime: 750, + startTime: '2024-03-27T13:15:00.000Z', + endTime: '2024-03-27T13:15:01.150Z' +}; + +export function ExecutionTraceDemo() { + return ( + + + Execution Trace Demo + + This demonstrates how execution traces will appear when the backend provides agent thought-processes. + + + + + Sample Agent Response: + + Based on your query about high-yield vault strategies, I've analyzed the current market and identified two excellent options: + + + 🏦 Stable Yield Plus - 12.5% APY, Low Risk + + 📈 Growth Strategy Alpha - 18.2% APY, Medium Risk + + + + + + + + Note: This is sample data. In production, execution traces will be automatically populated from the backend API response. + + + ); +} diff --git a/src/store/slices/chatSlice.ts b/src/store/slices/chatSlice.ts index da8c923..58c111c 100644 --- a/src/store/slices/chatSlice.ts +++ b/src/store/slices/chatSlice.ts @@ -103,41 +103,23 @@ export const sendMessage = createAsyncThunk( timestamp: new Date().toISOString(), }; - // Mock agent response - const mockResponse = { - result: { - success: true, - data: `Mock agent response to: "${query}". This is a simulated response since backend is disconnected.`, - error: null, - metadata: { - type: 'info', - action: 'mock', - amount: null, - asset: null, - requiresConfirmation: false, - } - } - }; + // Call the API service to get actual response + const response = await apiService.queryAgent({ userId, query }); - // Save agent response locally (no server call needed) + // Create agent message with execution trace if available const agentMessage: ChatMessage = { id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, type: 'agent', - content: mockResponse.result.data, + content: response.result.data, timestamp: new Date().toISOString(), metadata: { - success: mockResponse.result.success, - error: mockResponse.result.error, - transactionHash: null, - type: mockResponse.result.metadata?.type, - action: mockResponse.result.metadata?.action, - amount: mockResponse.result.metadata?.amount, - asset: mockResponse.result.metadata?.asset, - requiresConfirmation: mockResponse.result.metadata?.requiresConfirmation, + success: response.result.success, + error: response.result.error, + executionTrace: response.result.executionTrace, } }; - return { response: mockResponse, conversation, userMessage, agentMessage }; + return { response, conversation, userMessage, agentMessage }; } catch (error: any) { return rejectWithValue(error.response?.data?.message || 'Failed to send message'); } diff --git a/src/types/index.ts b/src/types/index.ts index 97c1b20..ce17520 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -162,9 +162,29 @@ export interface AgentQueryResponse { success: boolean; data: string; error?: string; + executionTrace?: ExecutionTrace; }; } +// Execution Trace Types +export interface ExecutionTrace { + steps: ExecutionStep[]; + totalTime: number; + startTime: string; + endTime: string; +} + +export interface ExecutionStep { + id: string; + name: string; + type: 'thought' | 'action' | 'tool_call' | 'result' | 'error'; + timestamp: string; + duration: number; + description: string; + details?: any; + substeps?: ExecutionStep[]; +} + // Chat and Message Types export interface ChatMessage { id: string; @@ -182,6 +202,7 @@ export interface ChatMessage { action?: string; asset?: string; requiresConfirmation?: boolean; + executionTrace?: ExecutionTrace; }; }
{step.description}
+ {typeof step.details === 'string' + ? step.details + : JSON.stringify(step.details, null, 2) + } +
+ This demonstrates how execution traces will appear when the backend provides agent thought-processes. +
Note: This is sample data. In production, execution traces will be automatically populated from the backend API response.