From bda69752c0d5f87c2ab763230ada4d5157a7d5ad Mon Sep 17 00:00:00 2001 From: rathi Date: Sat, 7 Dec 2024 18:20:22 -0500 Subject: [PATCH] working chat interface with n8n --- .env | 7 +++ src/lib/chat-service.ts | 122 ++++++++++++++++++++++++++++++++++-- src/pages/dashboard/ask.tsx | 68 +++++++++++--------- 3 files changed, 160 insertions(+), 37 deletions(-) diff --git a/.env b/.env index 27a0361..87e0f0b 100644 --- a/.env +++ b/.env @@ -1,2 +1,9 @@ VITE_SUPABASE_URL=https://nvatjthzedykhikmttot.supabase.co VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im52YXRqdGh6ZWR5a2hpa210dG90Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzM1OTYxOTMsImV4cCI6MjA0OTE3MjE5M30.u4euR8U-XxxvOdLFmWJD2yrd4E_MPMt_X1yqRrDTF2I + +# N8N Configuration +VITE_N8N_WEBHOOK_URL=https://harivansh.app.n8n.cloud/webhook/chat-webhook +VITE_N8N_UPLOAD_WEBHOOK_URL=https://harivansh.app.n8n.cloud/webhook/upload-webhook + +# Google Drive Configuration +VITE_GOOGLE_DRIVE_FOLDER_ID=your_drive_folder_id diff --git a/src/lib/chat-service.ts b/src/lib/chat-service.ts index b4be14e..c224b47 100644 --- a/src/lib/chat-service.ts +++ b/src/lib/chat-service.ts @@ -13,6 +13,64 @@ const setInStorage = (key: string, data: T[]) => { localStorage.setItem(key, JSON.stringify(data)); }; +// N8N webhook configuration +const N8N_WEBHOOK_URL = import.meta.env.VITE_N8N_WEBHOOK_URL; + +// Define the expected response type from n8n RAG workflow +interface N8NResponse { + success: boolean; + response: { + content: string; + role: 'assistant'; + metadata?: { + sources?: string[]; + error?: string; + }; + }; +} + +// Function to send message to n8n webhook and handle response +const sendToN8N = async (sessionId: string, message: string): Promise => { + try { + console.log('Sending message to n8n:', { sessionId, message }); + + const response = await fetch(N8N_WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + sessionId, + action: 'sendMessage', + chatInput: message + }) + }); + + console.log('Raw n8n response:', response); + + if (!response.ok) { + throw new Error(`N8N webhook error: ${response.statusText}`); + } + + const responseData = await response.json(); + console.log('Parsed n8n response:', responseData); + + return responseData; + } catch (error) { + console.error('Error sending message to n8n:', error); + return { + success: false, + response: { + content: 'Sorry, I encountered an error processing your request.', + role: 'assistant', + metadata: { + error: error instanceof Error ? error.message : 'Unknown error' + } + } + }; + } +}; + export const chatService = { async createChatInstance(userId: string, title: string): Promise { try { @@ -91,9 +149,10 @@ export const chatService = { content: string, role: 'user' | 'assistant', metadata?: ChatMessage['metadata'] - ): Promise { + ): Promise { try { - const message: ChatMessage = { + // Create and save the user message + const userMessage: ChatMessage = { id: generateId(), chat_id: chatId, content, @@ -102,10 +161,57 @@ export const chatService = { metadata, }; - const messages = getFromStorage('chat_messages'); - messages.push(message); + let messages = getFromStorage('chat_messages'); + messages.push(userMessage); setInStorage('chat_messages', messages); + // If it's a user message, send to n8n and create assistant message + if (role === 'user') { + try { + const n8nResponse = await sendToN8N(chatId, content); + console.log('Creating assistant message with n8n response:', n8nResponse); + + if (n8nResponse.success && n8nResponse.response) { + // Create and save the assistant's response + const assistantMessage: ChatMessage = { + id: generateId(), + chat_id: chatId, + content: n8nResponse.response.content, + role: 'assistant', + created_at: new Date().toISOString(), + metadata: { + ...n8nResponse.response.metadata, + make_response_id: userMessage.id // Link to the user's message + } + }; + + messages = getFromStorage('chat_messages'); + messages.push(assistantMessage); + setInStorage('chat_messages', messages); + console.log('Assistant message saved:', assistantMessage); + } else { + throw new Error('Invalid response format from n8n'); + } + } catch (n8nError) { + console.error('Failed to get response from n8n:', n8nError); + // Add an error message if n8n fails + const errorMessage: ChatMessage = { + id: generateId(), + chat_id: chatId, + content: 'Sorry, I encountered an error processing your request.', + role: 'assistant', + created_at: new Date().toISOString(), + metadata: { + error: 'Failed to process message', + make_response_id: userMessage.id + } + }; + messages = getFromStorage('chat_messages'); + messages.push(errorMessage); + setInStorage('chat_messages', messages); + } + } + // Update last_message_at in chat instance const chats = getFromStorage('chat_instances'); const chatIndex = chats.findIndex(chat => chat.id === chatId); @@ -114,10 +220,14 @@ export const chatService = { setInStorage('chat_instances', chats); } - return message; + // Return all messages for this chat + const updatedMessages = messages.filter(msg => msg.chat_id === chatId) + .sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()); + console.log('Returning updated messages:', updatedMessages); + return updatedMessages; } catch (error) { console.error('Error adding message:', error); - return null; + return []; } }, diff --git a/src/pages/dashboard/ask.tsx b/src/pages/dashboard/ask.tsx index 8d0aee8..d288069 100644 --- a/src/pages/dashboard/ask.tsx +++ b/src/pages/dashboard/ask.tsx @@ -18,6 +18,7 @@ export default function AskQuestion() { const [chat, setChat] = useState(null); const [isNewChat, setIsNewChat] = useState(false); const [hasExistingChats, setHasExistingChats] = useState(null); + const [isTyping, setIsTyping] = useState(false); // Check for existing chats and redirect to most recent if on base path useEffect(() => { @@ -121,30 +122,20 @@ export default function AskQuestion() { currentChatId = chatId; } - // Add user message - const userMessage = await chatService.addMessage(currentChatId, question.trim(), 'user'); - if (userMessage) { - setMessages(prev => [...prev, userMessage]); - } + // Show typing indicator before sending message + setIsTyping(true); + // Add user message and get all updated messages including the AI response + const updatedMessages = await chatService.addMessage(currentChatId, question.trim(), 'user'); + setMessages(updatedMessages); setQuestion(''); - // TODO: Integrate with Make.com for AI response - // For now, using a placeholder response - const aiMessage = await chatService.addMessage( - currentChatId, - 'This is a placeholder response. AI integration coming soon!', - 'assistant', - { make_response_id: 'placeholder' } - ); - if (aiMessage) { - setMessages(prev => [...prev, aiMessage]); - } } catch (err) { setError('Failed to send message'); console.error('Failed to process message:', err); } finally { setLoading(false); + setIsTyping(false); } }; @@ -242,26 +233,41 @@ export default function AskQuestion() { ) : ( - messages.map((message) => ( -
+ <> + {messages.map((message) => (
- {message.content} +
+ {message.content} +
-
- )) + ))} + {/* Typing indicator */} + {isTyping && ( +
+
+
+ + + +
+ AI is thinking... +
+
+ )} + )}