Multi-Agent AI Assistant
Weather • Documents • Meetings • SQL
import React, { useState, useRef, useEffect } from 'react';
import axios from 'axios';
import ReactMarkdown from 'react-markdown';
import {
Send,
Paperclip,
Trash2,
Bot,
User,
AlertCircle,
FileText,
Loader2,
Cloud,
Calendar,
Database,
File,
X,
Plus,
MessageSquare,
History,
Menu,
HardDrive // Added icon for storage
} from 'lucide-react';
import './App.css';
import StorageManager from './components/StorageManager'; // Import StorageManager
function App() {
// Use environment variable for backend URL, default to http://localhost:7860
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL || 'http://localhost:7860';
const [sessions, setSessions] = useState(() => {
const saved = localStorage.getItem('chat_sessions');
return saved ? JSON.parse(saved) : [];
});
const [currentSessionId, setCurrentSessionId] = useState(Date.now());
const [messages, setMessages] = useState([
{
role: 'assistant',
content: 'Hello! I\'m your **Multi-Agent AI assistant**. I can help with:\n\n- 🌤️ **Weather information**\n- 📄 **Document analysis** (upload PDF/TXT/MD)\n- 📅 **Meeting scheduling** with weather checks\n- 💾 **Database queries** about meetings\n\nHow can I help you today?'
}
]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [uploadedFile, setUploadedFile] = useState(null);
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [isPersistentUpload, setIsPersistentUpload] = useState(false); // State for persistent upload toggle
const [showStorageManager, setShowStorageManager] = useState(false); // State to toggle Storage Manager view
const messagesEndRef = useRef(null);
const fileInputRef = useRef(null);
const textInputRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
// Save sessions to localStorage
useEffect(() => {
localStorage.setItem('chat_sessions', JSON.stringify(sessions));
}, [sessions]);
// Update current session in sessions list
useEffect(() => {
if (messages.length <= 1 && !uploadedFile) return;
setSessions(prev => {
const existingIdx = prev.findIndex(s => s.id === currentSessionId);
const title = messages.find(m => m.role === 'user')?.content.substring(0, 30) || 'New Chat';
const sessionData = {
id: currentSessionId,
title: title.length >= 30 ? title + '...' : title,
messages,
uploadedFile,
timestamp: new Date().toISOString()
};
if (existingIdx >= 0) {
const newSessions = [...prev];
newSessions[existingIdx] = sessionData;
return newSessions;
} else {
return [sessionData, ...prev];
}
});
}, [messages, uploadedFile, currentSessionId]);
const createNewChat = () => {
setCurrentSessionId(Date.now());
setMessages([
{
role: 'assistant',
content: 'Hello! I\'m your **Multi-Agent AI assistant**. I can help with:\n\n- 🌤️ **Weather information**\n- 📄 **Document analysis** (upload PDF/TXT/MD)\n- 📅 **Meeting scheduling** with weather checks\n- 💾 **Database queries** about meetings\n\nHow can I help you today?'
}
]);
setUploadedFile(null);
if (textInputRef.current) textInputRef.current.focus();
};
const loadSession = (session) => {
setCurrentSessionId(session.id);
setMessages(session.messages);
setUploadedFile(session.uploadedFile);
};
const deleteSession = (e, id) => {
e.stopPropagation();
setSessions(prev => prev.filter(s => s.id !== id));
if (currentSessionId === id) {
createNewChat();
}
};
const handleFileUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
if (isPersistentUpload) {
formData.append('persistent', 'true');
}
try {
setIsLoading(true);
const response = await axios.post(`${BACKEND_URL}/upload`, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
setUploadedFile({
name: file.name,
path: response.data.file_path,
size: response.data.file_size,
isPersistent: isPersistentUpload
});
setMessages(prev => [...prev, {
role: 'system',
content: `📎 **File uploaded:** ${file.name} (${response.data.file_size}) ${isPersistentUpload ? '(Persistent)' : ''}\n\nYou can now ask questions about this document!`
}]);
} catch (error) {
setMessages(prev => [...prev, {
role: 'error',
content: `❌ File upload failed: ${error.response?.data?.detail || error.message}`
}]);
} finally {
setIsLoading(false);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
}
};
const removeFile = (e) => {
e.stopPropagation();
setUploadedFile(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
setMessages(prev => [...prev, {
role: 'system',
content: '📎 File removed from context.'
}]);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!input.trim() || isLoading) return;
const userMessage = input.trim();
setInput('');
setMessages(prev => [...prev, { role: 'user', content: userMessage }]);
setIsLoading(true);
try {
const response = await axios.post(`${BACKEND_URL}/chat`, {
query: userMessage,
file_path: uploadedFile?.path || null
});
setMessages(prev => [...prev, {
role: 'assistant',
content: response.data.response
}]);
} catch (error) {
setMessages(prev => [...prev, {
role: 'error',
content: `❌ Error: ${error.response?.data?.detail || error.message}`
}]);
} finally {
setIsLoading(false);
setTimeout(() => textInputRef.current?.focus(), 100);
}
};
const clearChat = () => {
setMessages([{
role: 'assistant',
content: 'Chat cleared! How can I help you?'
}]);
setUploadedFile(null);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
if (textInputRef.current) {
textInputRef.current.focus();
}
};
const handleExampleClick = (text) => {
setInput(text);
if (textInputRef.current) {
textInputRef.current.focus();
}
};
const exampleQueries = [
{ icon:
Weather • Documents • Meetings • SQL