diff --git a/example/src/components/ConversationManager.tsx b/example/src/components/ConversationManager.tsx index 4ce8531..7155c07 100644 --- a/example/src/components/ConversationManager.tsx +++ b/example/src/components/ConversationManager.tsx @@ -39,6 +39,44 @@ export const ConversationManager: React.FC = () => { } }, [settings.agentServerUrl, settings.agentServerApiKey]); + // Cleanup WebSocket connections on unmount + useEffect(() => { + return () => { + if (selectedConversation?.remoteConversation) { + selectedConversation.remoteConversation.stopWebSocketClient().catch(err => { + console.warn('Failed to stop WebSocket client on unmount:', err); + }); + } + }; + }, [selectedConversation?.remoteConversation]); + + // Periodic status refresh for running conversations + useEffect(() => { + if (!selectedConversation?.remoteConversation) return; + + const refreshStatus = async () => { + try { + const status = await selectedConversation.remoteConversation!.state.getAgentStatus(); + setConversations(prev => prev.map(conv => + conv.id === selectedConversation.id + ? { ...conv, agentStatus: status } + : conv + )); + } catch (err) { + console.warn('Failed to refresh agent status:', err); + } + }; + + // Refresh every 2 seconds if the agent is running + const interval = setInterval(() => { + if (selectedConversation.agentStatus === 'running') { + refreshStatus(); + } + }, 2000); + + return () => clearInterval(interval); + }, [selectedConversation?.id, selectedConversation?.agentStatus]); + const loadConversations = async (conversationManager?: SDKConversationManager) => { const mgr = conversationManager || manager; if (!mgr) return; @@ -84,13 +122,46 @@ export const ConversationManager: React.FC = () => { } }; - const conversation = await manager.createConversation(agent, { - initialMessage: 'Hello! I\'m ready to help you with your tasks.', - maxIterations: 50, - }); + const conversation = await RemoteConversation.create( + manager.host, + agent, + { + apiKey: manager.apiKey, + initialMessage: 'Hello! I\'m ready to help you with your tasks.', + maxIterations: 50, + callback: (event: Event) => { + console.log('Received WebSocket event for new conversation:', event); + + // Update the conversation's events in real-time + setConversations(prev => prev.map(conv => { + if (conv.id === conversation.id && conv.events) { + const updatedEvents = [...conv.events, event]; + return { ...conv, events: updatedEvents }; + } + return conv; + })); + }, + } + ); console.log('Created conversation:', conversation); + // Start WebSocket client for real-time updates + try { + await conversation.startWebSocketClient(); + console.log('WebSocket client started for new conversation'); + } catch (wsErr) { + console.warn('Failed to start WebSocket client for new conversation:', wsErr); + } + + // Run the initial message to start the conversation + try { + await conversation.run(); + console.log('Started conversation with initial message'); + } catch (runErr) { + console.warn('Failed to run initial message:', runErr); + } + // Reload conversations to show the new one await loadConversations(); } catch (err) { @@ -127,14 +198,72 @@ export const ConversationManager: React.FC = () => { const selectConversation = async (conversationId: string) => { if (!manager) return; + // Clean up previous conversation's WebSocket if any + if (selectedConversation?.remoteConversation) { + try { + await selectedConversation.remoteConversation.stopWebSocketClient(); + console.log('Stopped WebSocket client for previous conversation'); + } catch (err) { + console.warn('Failed to stop previous WebSocket client:', err); + } + } + console.log('Selecting conversation:', conversationId); setSelectedConversationId(conversationId); // Load conversation details try { - const remoteConversation = await manager.loadConversation(conversationId); + // Create a callback to handle real-time events + const eventCallback = (event: Event) => { + console.log('Received WebSocket event:', event); + + // Update the conversation's events in real-time + setConversations(prev => prev.map(conv => { + if (conv.id === conversationId && conv.events) { + // Add the new event to the existing events + const updatedEvents = [...conv.events, event]; + return { ...conv, events: updatedEvents }; + } + return conv; + })); + + // If it's a status change event, update the agent status + if (event.type === 'agent_status_change' || event.type === 'agent_state_changed') { + // Refresh agent status after a short delay to ensure the server has updated + setTimeout(() => { + if (remoteConversation) { + remoteConversation.state.getAgentStatus().then(status => { + setConversations(prev => prev.map(conv => + conv.id === conversationId + ? { ...conv, agentStatus: status } + : conv + )); + }).catch(err => console.warn('Failed to update agent status:', err)); + } + }, 100); + } + }; + + // Load conversation with callback + const remoteConversation = await RemoteConversation.load( + manager.host, + conversationId, + { + apiKey: manager.apiKey, + callback: eventCallback, + } + ); console.log('Loaded remote conversation:', remoteConversation); + // Start WebSocket client for real-time updates + try { + await remoteConversation.startWebSocketClient(); + console.log('WebSocket client started for conversation:', conversationId); + } catch (wsErr) { + console.warn('Failed to start WebSocket client:', wsErr); + // Don't fail the whole operation if WebSocket fails + } + // Get events const events = await remoteConversation.state.events.getEvents(); console.log('Loaded events:', events); @@ -160,14 +289,19 @@ export const ConversationManager: React.FC = () => { if (!selectedConversation?.remoteConversation || !messageInput.trim()) return; try { + // Send the message await selectedConversation.remoteConversation.sendMessage(messageInput); setMessageInput(''); - // Reload conversation details to show new events - await selectConversation(selectedConversation.id); + // Start the agent to process the message (non-blocking) + await selectedConversation.remoteConversation.run(); + console.log('Agent started to process the message'); + + // The WebSocket will receive events as the agent works + // No need to reload immediately - events will come via WebSocket } catch (err) { - console.error('Failed to send message:', err); - setError(err instanceof Error ? err.message : 'Failed to send message'); + console.error('Failed to send message or start agent:', err); + setError(err instanceof Error ? err.message : 'Failed to send message or start agent'); } }; @@ -185,10 +319,23 @@ export const ConversationManager: React.FC = () => { case 'running': return 'text-green-500'; case 'stopped': return 'text-red-500'; case 'paused': return 'text-orange-500'; + case 'finished': return 'text-blue-500'; + case 'error': return 'text-red-600'; default: return 'text-gray-500'; } }; + const getStatusIcon = (status?: string) => { + switch (status) { + case 'running': return 'đ'; + case 'stopped': return 'âšī¸'; + case 'paused': return 'â¸ī¸'; + case 'finished': return 'â '; + case 'error': return 'â'; + default: return 'â'; + } + }; + return (