import React, { useState, useEffect, useCallback } from 'react'; import { useAuth } from '../context/AuthContext'; import { BarChart2, TrendingUp, AlertTriangle, CheckCircle, RefreshCw, Zap, FileText, GitCommit, MessageSquare } from 'lucide-react'; const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api'; interface EvalDashboard { total_evaluations: number; avg_overall_score: number; avg_faithfulness: number; avg_relevancy: number; hallucination_rate: number; trend_data: TrendPoint[]; } interface TrendPoint { timestamp: string; overall_score: number; faithfulness: number; answer_relevancy: number; hallucination_detected: boolean; document_id?: string; } interface EvalForm { question: string; answer: string; contexts: string; } // Helper: build markdown from report result function buildReportMarkdown(result: any, topic: string): string { const lines: string[] = []; lines.push(`# ${result.topic || topic}`); lines.push(''); if (result.confidence !== undefined) { lines.push(`**Confidence:** ${(result.confidence * 100).toFixed(1)}%`); } if (result.tool_calls_made !== undefined) { lines.push(`**Tool calls:** ${result.tool_calls_made}`); } lines.push(`**Generated:** ${new Date().toLocaleString()}`); lines.push(''); if (result.executive_summary) { lines.push('## Executive Summary'); lines.push(''); lines.push(result.executive_summary); lines.push(''); } if (result.sections && typeof result.sections === 'object' && !Array.isArray(result.sections)) { Object.entries(result.sections).forEach(([title, content], i) => { lines.push(`## ${i + 1}. ${title}`); lines.push(''); lines.push(String(content)); lines.push(''); }); } else if (Array.isArray(result.sections)) { result.sections.forEach((s: any, i: number) => { lines.push(`## ${i + 1}. ${s.title || ''}`); lines.push(''); lines.push(s.content || ''); lines.push(''); }); } else if (!result.sections) { lines.push(result.report || result.content || result.markdown || JSON.stringify(result, null, 2)); lines.push(''); } if (result.key_entities && result.key_entities.length > 0) { lines.push('## Key Entities'); lines.push(''); lines.push(result.key_entities.join(', ')); } return lines.join('\n'); } const InsightsView: React.FC = () => { const { token } = useAuth(); const [dashboard, setDashboard] = useState(null); const [loading, setLoading] = useState(false); const [evalForm, setEvalForm] = useState({ question: '', answer: '', contexts: '' }); const [evalResult, setEvalResult] = useState(null); const [evalLoading, setEvalLoading] = useState(false); const [communities, setCommunities] = useState([]); const [communityLoading, setCommunityLoading] = useState(false); const [activeTab, setActiveTab] = useState<'metrics' | 'evaluate' | 'communities' | 'export' | 'report' | 'graph-update' | 'entity-chat'>('metrics'); // Report Agent state const [reportTopic, setReportTopic] = useState(''); const [reportDepth, setReportDepth] = useState(3); const [reportResult, setReportResult] = useState(null); const [reportLoading, setReportLoading] = useState(false); const [reportTimestamp, setReportTimestamp] = useState(null); const [copyDone, setCopyDone] = useState(false); // Graph Update state const [updateText, setUpdateText] = useState(''); const [updateResult, setUpdateResult] = useState(null); const [updateLoading, setUpdateLoading] = useState(false); // Entity Chat state const [entities, setEntities] = useState([]); const [selectedEntity, setSelectedEntity] = useState(''); const [entityContext, setEntityContext] = useState(''); const [chatMsg, setChatMsg] = useState(''); const [chatHistory, setChatHistory] = useState<{role: string; content: string}[]>([]); const [chatLoading, setChatLoading] = useState(false); const fetchDashboard = useCallback(async () => { setLoading(true); try { const res = await fetch(`${API_BASE}/eval/dashboard?limit=200`, { headers: { Authorization: `Bearer ${token}` } }); if (res.ok) setDashboard(await res.json()); } catch (e) { console.error(e); } setLoading(false); }, [token]); const fetchCommunities = useCallback(async () => { setCommunityLoading(true); try { const res = await fetch(`${API_BASE}/graph/communities?limit=30`, { headers: { Authorization: `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); setCommunities(data.communities || []); } } catch (e) { console.error(e); } setCommunityLoading(false); }, [token]); const fetchEntities = useCallback(async () => { try { const res = await fetch(`${API_BASE}/admin/graph/nodes?query=&limit=200`, { headers: { Authorization: `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); setEntities((data.nodes || []).slice(0, 100)); } } catch {} }, [token]); useEffect(() => { fetchDashboard(); fetchCommunities(); fetchEntities(); }, [fetchDashboard, fetchCommunities, fetchEntities]); const runEval = async () => { if (!evalForm.question || !evalForm.answer || !evalForm.contexts) return; setEvalLoading(true); setEvalResult(null); try { const res = await fetch(`${API_BASE}/eval/score`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ question: evalForm.question, answer: evalForm.answer, contexts: evalForm.contexts.split('\n---\n').map(c => c.trim()).filter(Boolean) }) }); if (res.ok) { setEvalResult(await res.json()); fetchDashboard(); // Refresh metrics } } catch (e) { console.error(e); } setEvalLoading(false); }; const runReport = async () => { if (!reportTopic.trim()) return; setReportLoading(true); setReportResult(null); try { const res = await fetch(`${API_BASE}/report`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ topic: reportTopic, depth: reportDepth }) }); if (res.ok) { setReportResult(await res.json()); setReportTimestamp(new Date()); } } catch (e) { console.error(e); } setReportLoading(false); }; const runGraphUpdate = async () => { if (!updateText.trim()) return; setUpdateLoading(true); setUpdateResult(null); try { const res = await fetch(`${API_BASE}/graph/update`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ text: updateText }) }); if (res.ok) setUpdateResult(await res.json()); } catch (e) { console.error(e); } setUpdateLoading(false); }; const sendEntityChat = async () => { if (!selectedEntity || !chatMsg.trim()) return; const userMsg = { role: 'user', content: chatMsg }; setChatHistory(h => [...h, userMsg]); setChatMsg(''); setChatLoading(true); try { const res = await fetch(`${API_BASE}/entities/${encodeURIComponent(selectedEntity)}/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify({ message: userMsg.content }) }); if (res.ok) { const data = await res.json(); setChatHistory(h => [...h, { role: 'assistant', content: data.response || data.answer || JSON.stringify(data) }]); } } catch (e) { console.error(e); } setChatLoading(false); }; const assignCommunities = async () => { setCommunityLoading(true); try { const res = await fetch(`${API_BASE}/graph/communities/assign`, { method: 'POST', headers: { Authorization: `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); alert(`βœ“ ${data.message}`); fetchCommunities(); } } catch (e) { console.error(e); } setCommunityLoading(false); }; const exportGraph = async (fmt: string) => { const res = await fetch(`${API_BASE}/graph/export?format=${fmt}`, { headers: { Authorization: `Bearer ${token}` } }); if (!res.ok) return; const blob = await res.blob(); const ext = fmt === 'json' ? 'json' : fmt === 'cypher' ? 'cypher' : 'graphml'; const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `graph_export.${ext}`; a.click(); URL.revokeObjectURL(url); }; const handleCopyReport = () => { if (!reportResult) return; const text = buildReportMarkdown(reportResult, reportTopic); navigator.clipboard.writeText(text).then(() => { setCopyDone(true); setTimeout(() => setCopyDone(false), 2000); }); }; const handleDownloadReport = () => { if (!reportResult) return; const text = buildReportMarkdown(reportResult, reportTopic); const blob = new Blob([text], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `report_${reportTopic.slice(0, 30).replace(/\s+/g, '_')}.md`; a.click(); URL.revokeObjectURL(url); }; const score2color = (s: number) => { if (s >= 0.8) return '#16a34a'; if (s >= 0.6) return '#d97706'; return '#dc2626'; }; const score2label = (s: number) => s >= 0.8 ? 'HIGH' : s >= 0.6 ? 'MEDIUM' : 'LOW'; const ScoreBar: React.FC<{ label: string; value: number }> = ({ label, value }) => (
{label} {(value * 100).toFixed(1)}%
); const confidenceBadgeColor = (c: number) => c >= 0.8 ? '#16a34a' : c >= 0.6 ? '#d97706' : '#dc2626'; return (

INSIGHTS HQ

QUALITY METRICS // EVAL DASHBOARD // GRAPH INTELLIGENCE

{/* Tab Nav */}
{([ { id: 'metrics', label: 'METRICS' }, { id: 'evaluate', label: 'EVALUATE' }, { id: 'communities', label: 'COMMUNITIES' }, { id: 'export', label: 'EXPORT' }, { id: 'report', label: '⚑ REPORT' }, { id: 'graph-update', label: '↑ LIVE UPDATE' }, { id: 'entity-chat', label: 'πŸ’¬ ENTITY CHAT' }, ] as const).map(t => ( ))}
{/* ── METRICS TAB ── */} {activeTab === 'metrics' && (
{loading ? (
LOADING METRICS...
) : !dashboard || dashboard.total_evaluations === 0 ? (

NO EVALUATION DATA YET

Use the EVALUATE tab to score Q&A pairs and build your quality history.

) : ( <> {/* KPI Cards */}
{[ { label: 'TOTAL EVALS', value: dashboard.total_evaluations, raw: true, icon: }, { label: 'AVG QUALITY', value: dashboard.avg_overall_score, icon: }, { label: 'FAITHFULNESS', value: dashboard.avg_faithfulness, icon: }, { label: 'HALLUCINATION RATE', value: dashboard.hallucination_rate, invert: true, icon: }, ].map(card => (
0.3 : false) ? '#fff5f5' : '#fff' }}>
{card.label} {card.icon}
{card.raw ? card.value : `${((card.value as number) * 100).toFixed(1)}%`}
))}
{/* Score Breakdown */}

METRIC BREAKDOWN

{/* Trend Table */} {dashboard.trend_data.length > 0 && (

EVALUATION HISTORY (LATEST {Math.min(dashboard.trend_data.length, 20)})

{['TIMESTAMP', 'QUALITY', 'FAITHFULNESS', 'RELEVANCY', 'HALLUCINATION'].map(h => ( ))} {dashboard.trend_data.slice(0, 20).map((p, i) => ( ))}
{h}
{p.timestamp} {(p.overall_score * 100).toFixed(1)}% {(p.faithfulness * 100).toFixed(1)}% {(p.answer_relevancy * 100).toFixed(1)}% {p.hallucination_detected ? 'YES' : 'NO'}
)} )}
)} {/* ── EVALUATE TAB ── */} {activeTab === 'evaluate' && (

SCORE A Q&A PAIR

Paste a question, its generated answer, and the retrieved context chunks (separate chunks with "---").