SantoshKumar1310's picture
Upload folder using huggingface_hub
49e53ae verified
'use client'
import { useState, useRef, useEffect } from 'react'
import { Send, Bot, User, Loader2, Sparkles } from 'lucide-react'
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'
interface Message {
role: 'user' | 'assistant'
content: string
timestamp: Date
}
interface ChatBotProps {
transactions: any[]
metrics: any
}
const suggestedQuestions = [
"Why is my precision so low?",
"How can I improve recall performance?",
"Explain how the quantum ensemble works",
"What's the optimal fraud threshold?",
"Compare VQC, QAOA, and QNN performance"
]
export default function ChatBot({ transactions, metrics }: ChatBotProps) {
const [messages, setMessages] = useState<Message[]>([])
const [input, setInput] = useState('')
const [isLoading, setIsLoading] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}
useEffect(() => {
scrollToBottom()
}, [messages])
const sendMessage = async (messageText?: string) => {
const text = messageText || input
if (!text.trim() || isLoading) return
const userMessage: Message = {
role: 'user',
content: text,
timestamp: new Date()
}
setMessages(prev => [...prev, userMessage])
setInput('')
setIsLoading(true)
try {
const response = await fetch(`${API_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: text,
history: transactions
})
})
const data = await response.json()
const assistantMessage: Message = {
role: 'assistant',
content: data.response || 'Sorry, I could not generate a response.',
timestamp: new Date()
}
setMessages(prev => [...prev, assistantMessage])
} catch (error) {
const errorMessage: Message = {
role: 'assistant',
content: '⚠️ Failed to connect to the AI service. Please check if the backend is running.',
timestamp: new Date()
}
setMessages(prev => [...prev, errorMessage])
} finally {
setIsLoading(false)
}
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
return (
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
{/* Main chat area */}
<div className="lg:col-span-3 glass rounded-xl flex flex-col h-[600px]">
{/* Header */}
<div className="p-4 border-b border-dark-700">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Bot className="w-5 h-5 text-primary-500" />
AI Fraud Intelligence Assistant
</h3>
<p className="text-sm text-dark-400 mt-1">
Ask questions about fraud patterns, model optimization, or performance metrics
</p>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 && (
<div className="flex flex-col items-center justify-center h-full text-center">
<Sparkles className="w-12 h-12 text-primary-500 mb-4" />
<h4 className="text-lg font-semibold mb-2">Welcome to AI Assistant</h4>
<p className="text-dark-400 mb-6 max-w-md">
I can help you understand fraud patterns, optimize your model,
and explain the quantum-classical hybrid architecture.
</p>
{/* Suggested questions */}
<div className="flex flex-wrap gap-2 justify-center max-w-lg">
{suggestedQuestions.slice(0, 3).map((q, i) => (
<button
key={i}
onClick={() => sendMessage(q)}
className="text-sm bg-dark-700 hover:bg-dark-600 border border-dark-600 rounded-lg px-3 py-2 transition-colors"
>
{q}
</button>
))}
</div>
</div>
)}
{messages.map((message, index) => (
<div
key={index}
className={`flex gap-3 ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
{message.role === 'assistant' && (
<div className="w-8 h-8 rounded-full bg-primary-500/20 flex items-center justify-center flex-shrink-0">
<Bot className="w-4 h-4 text-primary-500" />
</div>
)}
<div
className={`
max-w-[80%] rounded-2xl px-4 py-3
${message.role === 'user'
? 'bg-primary-600 text-white rounded-br-sm'
: 'bg-dark-700 text-white rounded-bl-sm'
}
`}
>
<p className="whitespace-pre-wrap text-sm leading-relaxed">
{message.content}
</p>
<p className="text-xs opacity-50 mt-1">
{message.timestamp.toLocaleTimeString()}
</p>
</div>
{message.role === 'user' && (
<div className="w-8 h-8 rounded-full bg-blue-500/20 flex items-center justify-center flex-shrink-0">
<User className="w-4 h-4 text-blue-400" />
</div>
)}
</div>
))}
{isLoading && (
<div className="flex gap-3">
<div className="w-8 h-8 rounded-full bg-primary-500/20 flex items-center justify-center flex-shrink-0">
<Bot className="w-4 h-4 text-primary-500" />
</div>
<div className="bg-dark-700 rounded-2xl rounded-bl-sm px-4 py-3">
<div className="flex items-center gap-2">
<Loader2 className="w-4 h-4 animate-spin text-primary-500" />
<span className="text-sm text-dark-300">Analyzing...</span>
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
{/* Input */}
<div className="p-4 border-t border-dark-700">
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Ask about fraud patterns, model optimization..."
className="flex-1 bg-dark-700 border border-dark-600 rounded-lg px-4 py-3 text-sm focus:border-primary-500 focus:outline-none"
disabled={isLoading}
/>
<button
onClick={() => sendMessage()}
disabled={!input.trim() || isLoading}
className="btn-primary px-4 disabled:opacity-50"
>
<Send className="w-4 h-4" />
</button>
</div>
</div>
</div>
{/* Sidebar */}
<div className="space-y-4">
{/* Current metrics */}
<div className="glass rounded-xl p-4">
<h4 className="text-sm font-semibold text-dark-300 mb-3">📊 Current Metrics</h4>
{metrics ? (
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-dark-400">Accuracy</span>
<span className="font-mono">{(metrics.accuracy * 100).toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span className="text-dark-400">Precision</span>
<span className="font-mono">{(metrics.precision * 100).toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span className="text-dark-400">Recall</span>
<span className="font-mono">{(metrics.recall * 100).toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span className="text-dark-400">F1 Score</span>
<span className="font-mono">{metrics.f1.toFixed(3)}</span>
</div>
</div>
) : (
<p className="text-dark-500 text-sm">No data yet</p>
)}
</div>
{/* Suggested questions */}
<div className="glass rounded-xl p-4">
<h4 className="text-sm font-semibold text-dark-300 mb-3">💡 Try asking</h4>
<div className="space-y-2">
{suggestedQuestions.map((q, i) => (
<button
key={i}
onClick={() => sendMessage(q)}
disabled={isLoading}
className="w-full text-left text-xs bg-dark-700 hover:bg-dark-600 rounded-lg px-3 py-2 transition-colors disabled:opacity-50"
>
{q}
</button>
))}
</div>
</div>
{/* Model info */}
<div className="glass rounded-xl p-4">
<h4 className="text-sm font-semibold text-dark-300 mb-3">🧠 Model Architecture</h4>
<div className="space-y-2 text-xs">
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-blue-500 rounded-full" />
<span className="text-dark-400">Classical (80%)</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-purple-500 rounded-full" />
<span className="text-dark-400">VQC (8%)</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-pink-500 rounded-full" />
<span className="text-dark-400">QAOA (6%)</span>
</div>
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-cyan-500 rounded-full" />
<span className="text-dark-400">QNN (6%)</span>
</div>
</div>
</div>
</div>
</div>
)
}