diff --git a/examples/experimental/nodes/frontend/server.js b/examples/experimental/nodes/frontend/server.js index 93cb7e22..16b24763 100644 --- a/examples/experimental/nodes/frontend/server.js +++ b/examples/experimental/nodes/frontend/server.js @@ -40,12 +40,24 @@ io.on('connection', (socket) => { console.log('A user connected'); socket.on('terminal_command', async (command) => { - console.log(`Received command: ${command}`); - // try { - // await redisClient.publish('TerminalChannel', command); // Publish to a Redis channel - // } catch (err) { - // console.error('Error publishing to Redis:', err); - // } + // console.log(`Received command: ${command}`); + // Create the message envelope that matches the Python Message[AgentAction] structure + const messageEnvelope = { + data: { + agent_name: "Terminal", + action_type: "run", + argument: command, + path: "", + data_type: "agent_action" // This needs to be inside the data object + } + }; + + try { + // Publish the JSON-serialized message to the Agent:Runtime channel + await redisClient.publish('Agent:Runtime', JSON.stringify(messageEnvelope)); + } catch (err) { + console.error('Error publishing to Redis:', err); + } }); socket.on('disconnect', () => { diff --git a/examples/experimental/nodes/frontend/src/App.tsx b/examples/experimental/nodes/frontend/src/App.tsx index 73a3202f..e9325149 100644 --- a/examples/experimental/nodes/frontend/src/App.tsx +++ b/examples/experimental/nodes/frontend/src/App.tsx @@ -45,15 +45,27 @@ const App: React.FC = () => { useEffect(() => { const handleNewMessage = (data: any) => { - const messageData = JSON.parse(data.message); - if (messageData.data.data_type === "agent_action") { - handleAgentAction(messageData); - } else if (messageData.data.data_type === "text" && messageData.data.text.includes("CmdOutputObservation")) { - const parts = messageData.data.text.split("**CmdOutputObservation (source=None, exit code=0)**"); - if (parts.length > 1) { - const outputText = parts[1].trim(); - setTerminalMessages((prev) => [...prev, outputText]); + try { + const messageData = JSON.parse(data.message); + + // Check if it's an agent action + if (messageData.data?.data_type === "agent_action") { + handleAgentAction(messageData); + } + // Check if it's a command output + else if (messageData.data?.data_type === "text" && + messageData.data.text.includes("CmdOutputObservation")) { + const parts = messageData.data.text.split("**CmdOutputObservation (source=None, exit code=0)**"); + if (parts.length > 1) { + const outputText = parts[1].trim(); + setTerminalMessages(prev => [...prev, outputText]); + } } + // Log the message for debugging + console.log('Parsed message:', messageData); + + } catch (error) { + console.error('Error parsing message:', error); } }; @@ -67,38 +79,49 @@ const App: React.FC = () => { const actionType = messageData.data.action_type; const agentName = messageData.data.agent_name; - if (actionType === "speak") { - const newMessage = { - text: `${agentName}: ${messageData.data.argument}`, - type: 'message' as const - }; - setMessages(prev => [...prev, newMessage]); - } else if (actionType === "write") { - const filePath = messageData.data.path; - const fileContent = messageData.data.argument; - setFiles(prevFiles => ({ ...prevFiles, [filePath]: fileContent })); - setActiveTab('editor'); - const statusMessage = { - text: `${agentName} is writing code...`, - type: 'status' as const - }; - setMessages(prev => [...prev, statusMessage]); - } else if (actionType === "run") { - setTerminalMessages((prev) => [...prev, `$ ${messageData.data.argument}`]); - const statusMessage = { - text: `${agentName} is executing a command...`, - type: 'status' as const - }; - setMessages(prev => [...prev, statusMessage]); - } else if (actionType === "browse") { - const url = messageData.data.argument; - setBrowserUrl(url); - setActiveTab('browser'); - const statusMessage = { - text: `${agentName} is browsing ${url}`, - type: 'status' as const - }; - setMessages(prev => [...prev, statusMessage]); + // Always log the action for debugging + console.log('Processing agent action:', actionType, 'from', agentName); + + switch (actionType) { + case "speak": + const newMessage = { + text: `${agentName}: ${messageData.data.argument}`, + type: 'message' as const + }; + setMessages(prev => [...prev, newMessage]); + break; + + case "write": + const filePath = messageData.data.path; + const fileContent = messageData.data.argument; + setFiles(prev => ({ ...prev, [filePath]: fileContent })); + setActiveTab('editor'); + setMessages(prev => [...prev, { + text: `${agentName} is writing code...`, + type: 'status' as const + }]); + break; + + case "run": + setTerminalMessages(prev => [...prev, `$ ${messageData.data.argument}`]); + setMessages(prev => [...prev, { + text: `${agentName} is executing a command...`, + type: 'status' as const + }]); + break; + + case "browse": + const url = messageData.data.argument; + setBrowserUrl(url); + setActiveTab('browser'); + setMessages(prev => [...prev, { + text: `${agentName} is browsing ${url}`, + type: 'status' as const + }]); + break; + + default: + console.log('Unknown action type:', actionType); } }; @@ -106,6 +129,13 @@ const App: React.FC = () => { setActivePanel(option); }; + socket.on('chat_message', (message: string) => { + setMessages(prev => [...prev, { + text: message, + type: 'message' as const + }]); + }); + return (
@@ -152,7 +182,16 @@ const App: React.FC = () => {
- + { + setMessages(prev => [...prev, { + text: `User: ${text}`, + type: 'message' as const + }]); + }} + />
); diff --git a/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.css b/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.css index 3c34aba9..18cd25b6 100644 --- a/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.css +++ b/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.css @@ -46,6 +46,7 @@ font-size: 0.875rem; color: #808080; margin-bottom: 4px; + padding-left: 10px; } .message-bubble { @@ -109,6 +110,7 @@ text-align: right; width: 100%; color: #808080; + padding-right: 10px; } .message[data-sender="Jane"] .message-content-wrapper { diff --git a/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.tsx b/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.tsx index 145c8796..397768de 100644 --- a/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.tsx +++ b/examples/experimental/nodes/frontend/src/components/ChatInterface/ChatInterface.tsx @@ -28,12 +28,18 @@ interface ChatInterfaceProps { type: 'message' | 'status'; }>; socket: Socket; + onSendMessage: (text: string) => void; } -export const ChatInterface: React.FC = ({ messages: initialMessages , socket}) => { - const [messages, setMessages] = useState(initialMessages); +export const ChatInterface: React.FC = ({ + messages, + socket, + onSendMessage +}) => { const [input, setInput] = useState(''); const messagesEndRef = React.useRef(null); + + console.log("ChatInterface received messages:", messages); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); @@ -43,14 +49,10 @@ export const ChatInterface: React.FC = ({ messages: initialM scrollToBottom(); }, [messages]); - const handleSend = () => { if (input.trim()) { - setMessages([...messages, { - text: `User: ${input}`, // Prefix with "User:" - type: 'message' - }]); - socket.emit('chat_message', `User: ${input}`); // Emit the command to the server + socket.emit('chat_message', input.trim()); + onSendMessage(input.trim()); setInput(''); } }; @@ -64,6 +66,13 @@ export const ChatInterface: React.FC = ({ messages: initialM }; } const colonIndex = message.text.indexOf(':'); + if (colonIndex === -1) { + return { + agentName: 'System', + text: message.text, + type: 'message' + }; + } const agentName = message.text.slice(0, colonIndex); const text = message.text.slice(colonIndex + 1).trim(); return { diff --git a/examples/experimental/nodes/frontend/src/components/Terminal/Terminal.tsx b/examples/experimental/nodes/frontend/src/components/Terminal/Terminal.tsx index 46e5efc9..ac452974 100644 --- a/examples/experimental/nodes/frontend/src/components/Terminal/Terminal.tsx +++ b/examples/experimental/nodes/frontend/src/components/Terminal/Terminal.tsx @@ -18,9 +18,6 @@ const processTerminalLine = (line: string): JSX.Element => { if (line.startsWith('Requirement already satisfied:')) { return
{stripAnsiCodes(line)}
; } - if (line.startsWith('$')) { - return
{stripAnsiCodes(line)}
; - } if (line.includes('notice')) { return
{stripAnsiCodes(line)}
; } @@ -40,28 +37,29 @@ export const Terminal: React.FC = ({ externalMessages, socket }) useEffect(() => { if (externalMessages.length > 0) { - setHistory((prevHistory) => [...prevHistory, ...externalMessages]); + // Append the latest message to history + setHistory((prevHistory) => [ + ...prevHistory, + externalMessages[externalMessages.length - 1], + ]); } }, [externalMessages]); const handleCommand = (command: string) => { - setHistory([...history, `$ ${command}`, '']); setInput(''); - console.log('Command: ' + command); socket.emit('terminal_command', command); // Emit the command to the server + // Optionally, you can add the command to history if desired + // setHistory((prevHistory) => [...prevHistory, `$ ${command}`]); }; return (
- + Terminal
-
+
{history.map((line, index) => (
{processTerminalLine(line)} @@ -82,4 +80,4 @@ export const Terminal: React.FC = ({ externalMessages, socket })
); -}; +}; \ No newline at end of file