"use client"; import { useState, useRef, useEffect } from "react"; import type { Tender, CompanyProfile } from "../lib/types"; import { uploadDocument, getAPIBase } from "../lib/api"; type Message = { role: "user" | "assistant"; content: string; agent?: string; }; type Props = { tender: Tender; companyProfile: CompanyProfile; }; const agents = [ { id: "legal", name: "Dra. Legal", avatar: "โš–๏ธ", color: "text-amber-400" }, { id: "tech", name: "Ing. Tech", avatar: "๐Ÿ‘จโ€๐Ÿ’ป", color: "text-cyan" }, { id: "risk", name: "Sra. Estrategia", avatar: "๐Ÿ•ต๏ธโ€โ™€๏ธ", color: "text-purple-400" }, ]; const models = [ "Llama-3.3-70B (Groq)", "Llama-3.1-8B (Groq)", "Llama-3.2-11B-Vision (Groq)", "Gemini 2.5 Flash", "Qwen-2.5 (Featherless)", ]; export default function AgentChat({ tender, companyProfile }: Props) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); const [selectedAgent, setSelectedAgent] = useState(agents[0]); const [selectedModel, setSelectedModel] = useState(models[0]); const [isLoading, setIsLoading] = useState(false); const [isTyping, setIsTyping] = useState(false); const [isUploading, setIsUploading] = useState(false); const [isListening, setIsListening] = useState(false); const [contextText, setContextText] = useState(""); const [attachedFile, setAttachedFile] = useState(null); const scrollRef = useRef(null); const fileInputRef = useRef(null); const startSpeechRecognition = () => { const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition; if (!SpeechRecognition) { alert("Speech recognition not supported in this browser."); return; } const recognition = new SpeechRecognition(); recognition.lang = "es-CL"; recognition.interimResults = false; recognition.onstart = () => setIsListening(true); recognition.onend = () => setIsListening(false); recognition.onresult = (event: any) => { const transcript = event.results[0][0].transcript; setInput(transcript); // Optional: Auto-send after voice command // handleSend(transcript); }; recognition.start(); }; const suggestedQuestions = [ "Summarize the main requirements", "Identify legal risks for my company", "How does my experience fit here?", "Generate a technical summary", ]; const simulateTyping = (text: string, agentName: string) => { if (!text) return; // Don't simulate empty text setIsTyping(true); let currentText = ""; const words = text.split(" "); let i = 0; const interval = setInterval(() => { if (i < words.length) { currentText += (i === 0 ? "" : " ") + words[i]; setMessages(prev => { const last = prev[prev.length - 1]; if (last && last.role === 'assistant' && last.agent === agentName) { return [...prev.slice(0, -1), { ...last, content: currentText }]; } return [...prev, { role: 'assistant', content: currentText, agent: agentName }]; }); i++; } else { clearInterval(interval); setIsTyping(false); } }, 20); // Faster typing }; useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [messages]); const handleSend = async (overrideInput?: string) => { const messageToSend = overrideInput || input; if (!messageToSend.trim() || isLoading) return; let imageBase64 = ""; if (attachedFile && attachedFile.type.startsWith("image/")) { setIsUploading(true); try { imageBase64 = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); reader.onerror = reject; reader.readAsDataURL(attachedFile); }); } catch (err) { console.error("Error converting image:", err); } setIsUploading(false); } const userMsg: Message = { role: "user", content: messageToSend, agent: "User" }; setMessages(prev => [...prev, userMsg]); if (!overrideInput) setInput(""); setAttachedFile(null); setIsLoading(true); try { const finalMessage = imageBase64 ? `${messageToSend}\n\nIMAGE_DATA:${imageBase64}` : contextText ? `[DOC CONTEXT: ${contextText.slice(0, 3000)}]\n\nUSER QUESTION: ${messageToSend}` : messageToSend; const response = await fetch(`${getAPIBase()}/api/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ tender, company_profile: companyProfile, message: finalMessage, agent: selectedAgent.id, model: selectedModel, history: messages.map(({role, content, agent}) => ({role, content, agent_name: agent})), }), }); if (!response.ok) throw new Error("Failed to chat"); const data = await response.json(); simulateTyping(data.response, selectedAgent.name); } catch (error) { console.error(error); setMessages(prev => [...prev, { role: "assistant", content: "โš ๏ธ Error connecting to the agent. Please try again.", agent: selectedAgent.name }]); } finally { setIsLoading(false); } }; const handleFileUpload = async (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; if (file.type.startsWith("image/")) { setAttachedFile(file); setMessages(prev => [...prev, { role: "user", content: `๐Ÿ–ผ๏ธ Attached image: ${file.name}` }]); return; } setIsUploading(true); try { const result = await uploadDocument(file); setContextText(prev => prev + "\n" + result.text); setMessages(prev => [...prev, { role: "user", content: `๐Ÿ“Ž Attached document: ${file.name}` }]); simulateTyping(`He analizado el documento "${file.name}". ยฟQuรฉ te gustarรญa saber sobre su contenido?`, selectedAgent.name); } catch (error) { console.error(error); alert("Error uploading document."); } finally { setIsUploading(false); } } }; const handleSuggestedClick = (question: string) => { setInput(question); }; return (
{/* Chat Header */}
{selectedAgent.avatar} {(isLoading || isTyping || isUploading) && (
)}

{selectedAgent.name} {(isLoading || isTyping || isUploading) && }

Expert Consultant

{/* Messages Area */}
{messages.length === 0 && (
๐Ÿ’ฌ

Hi! I'm your {selectedAgent.name}. Ask me anything about this tender's requirements, risks, or strategy.

)} {messages.map((msg, i) => (
{msg.role === 'assistant' && (
{msg.agent}
)}

{msg.content}

))} {isLoading && !isTyping && (
)}
{/* Suggested Questions */} {messages.length < 3 && !isLoading && !isTyping && (
{suggestedQuestions.map((q, i) => ( ))}
)} {/* Input Area */}
setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSend()} placeholder={isUploading ? "Uploading..." : `Message...`} disabled={isUploading} className="flex-1 min-w-0 bg-black/40 border border-white/10 rounded-xl md:rounded-2xl px-3 md:px-5 py-2.5 md:py-3 text-white text-xs md:text-sm placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-purple-500/40 transition-all disabled:opacity-50" />
); }