Files
LocalAGI/webui/react-ui/src/hooks/useChat.js
Richard Palethorpe 45078e1fa7 fix(ui): Re-add Chat
2025-04-01 14:36:33 +01:00

145 lines
4.1 KiB
JavaScript

import { useState, useCallback, useEffect, useRef } from 'react';
import { chatApi } from '../utils/api';
import { useSSE } from './useSSE';
/**
* Custom hook for chat functionality
* @param {string} agentName - Name of the agent to chat with
* @returns {Object} - Chat state and functions
*/
export function useChat(agentName) {
const [messages, setMessages] = useState([]);
const [sending, setSending] = useState(false);
const [error, setError] = useState(null);
const processedMessageIds = useRef(new Set());
// Use SSE hook to receive real-time messages
const { messages: sseMessages, statusUpdates, errorMessages, isConnected } = useSSE(agentName);
// Process SSE messages into chat messages
useEffect(() => {
if (!sseMessages || sseMessages.length === 0) return;
// Process the latest SSE message
const latestMessage = sseMessages[sseMessages.length - 1];
// Skip if we've already processed this message
if (processedMessageIds.current.has(latestMessage.id)) {
return;
}
// Handle JSON messages
if (latestMessage.type === 'json_message') {
try {
// The message should already be a parsed JSON object
const messageData = latestMessage.content;
// Skip if we've already processed this message ID
if (processedMessageIds.current.has(messageData.id)) {
return;
}
// Add to processed set to avoid duplicates
processedMessageIds.current.add(messageData.id);
// Add the message to our state
setMessages(prev => [...prev, {
id: messageData.id,
sender: messageData.sender,
content: messageData.content,
timestamp: messageData.timestamp,
}]);
} catch (err) {
console.error('Error processing JSON message:', err);
}
}
}, [sseMessages]);
// Process status updates
useEffect(() => {
if (!statusUpdates || statusUpdates.length === 0) return;
const latestStatus = statusUpdates[statusUpdates.length - 1];
// Handle status updates
if (latestStatus.type === 'status') {
try {
// The status should be a parsed JSON object
const statusData = latestStatus.content;
if (statusData.status === 'processing') {
setSending(true);
} else if (statusData.status === 'completed') {
setSending(false);
}
} catch (err) {
console.error('Error processing status update:', err);
}
}
}, [statusUpdates]);
// Process error messages
useEffect(() => {
if (!errorMessages || errorMessages.length === 0) return;
const latestError = errorMessages[errorMessages.length - 1];
try {
// The error should be a parsed JSON object
const errorData = latestError.content;
if (errorData.error) {
setError(errorData.error);
}
} catch (err) {
console.error('Error processing error message:', err);
}
}, [errorMessages]);
// Send a message to the agent
const sendMessage = useCallback(async (content) => {
if (!agentName || !content) return false;
setSending(true);
setError(null);
// Add user message to the local state immediately for better UX
const messageId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const userMessage = {
id: messageId,
sender: 'user',
content,
timestamp: new Date().toISOString(),
};
setMessages(prev => [...prev, userMessage]);
try {
// Use the JSON-based API endpoint
await chatApi.sendMessage(agentName, content);
return true;
} catch (err) {
setError(err.message || 'Failed to send message');
console.error('Error sending message:', err);
setSending(false);
return false;
}
}, [agentName]);
// Clear chat history
const clearChat = useCallback(() => {
setMessages([]);
processedMessageIds.current.clear();
}, []);
return {
messages,
sending,
error,
isConnected,
sendMessage,
clearChat,
};
}