/** * Agent Monitoring Component * * Standalone component for monitoring agent performance * */ "use client" import { useState, useEffect } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Textarea } from "@/components/ui/textarea" import { ScrollArea } from "@/components/ui/scroll-area" import ThinkingProcess from "@/components/ThinkingProcess" import { Activity, AlertTriangle, BarChart3, Brain, CheckCircle, Clock, RefreshCw, Search, TrendingUp, Users, XCircle, Zap, Flag } from "lucide-react" import { cn } from "@/lib/utils" // Types (same as before) interface ThoughtStep { step: number action: string observation: string reasoning: string duration_ms?: number confidence?: number } interface ReasoningPath { query: string thoughts: ThoughtStep[] total_duration_ms: number final_conclusion: string } interface QueryLog { id: string timestamp: string query: string persona: "wanjiku" | "wakili" | "mwanahabari" intent: "news" | "law" | "hybrid" | "general" confidence: number response_time_ms: number evidence_count: number reasoning_steps: number human_review_required: boolean agent_path: string[] quality_issues: string[] reasoning_path?: ReasoningPath | string user_feedback?: "positive" | "negative" | null } interface AgentMetrics { total_queries: number avg_confidence: number avg_response_time_ms: number human_review_rate: number persona_distribution: { wanjiku: number wakili: number mwanahabari: number } intent_distribution: { news: number law: number hybrid: number general: number } confidence_buckets: { low: number medium: number high: number } } interface ReviewQueueItem extends QueryLog { review_reason: string priority: "low" | "medium" | "high" } export function AgentMonitoring() { const [metrics, setMetrics] = useState(null) const [queryLogs, setQueryLogs] = useState([]) const [reviewQueue, setReviewQueue] = useState([]) const [selectedLog, setSelectedLog] = useState(null) const [loading, setLoading] = useState(true) const [searchQuery, setSearchQuery] = useState("") const [filterPersona, setFilterPersona] = useState("all") const [filterConfidence, setFilterConfidence] = useState("all") // Fetch functions const fetchMetrics = async () => { try { const response = await fetch("/api/admin/agent-metrics?days=30") if (!response.ok) { console.error("Failed to fetch metrics:", response.statusText) setMetrics(null) return } const data = await response.json() setMetrics(data) } catch (error) { console.error("Error fetching metrics:", error) setMetrics(null) } } const fetchQueryLogs = async () => { setLoading(true) try { const response = await fetch("/api/admin/query-logs?limit=100&offset=0") if (!response.ok) { console.error("Failed to fetch query logs:", response.statusText) setQueryLogs([]) return } const data = await response.json() setQueryLogs(data.logs || []) } catch (error) { console.error("Error fetching query logs:", error) setQueryLogs([]) } finally { setLoading(false) } } const fetchReviewQueue = async () => { try { const response = await fetch("/api/admin/review-queue") if (!response.ok) { console.error("Failed to fetch review queue:", response.statusText) setReviewQueue([]) return } const data = await response.json() setReviewQueue(data.queue || []) } catch (error) { console.error("Error fetching review queue:", error) setReviewQueue([]) } } const handleReviewApprove = async (logId: string) => { try { const response = await fetch(`/api/admin/review-queue/${logId}/approve`, { method: "POST" }) if (!response.ok) { throw new Error(`Failed to approve: ${response.statusText}`) } setReviewQueue(reviewQueue.filter(item => item.id !== logId)) // Refresh metrics to reflect the approval fetchMetrics() } catch (error) { console.error("Error approving review:", error) alert("Failed to approve query. Please try again.") } } const handleReviewReject = async (logId: string, feedback: string) => { try { const response = await fetch(`/api/admin/review-queue/${logId}/reject`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ feedback }) }) if (!response.ok) { throw new Error(`Failed to reject: ${response.statusText}`) } setReviewQueue(reviewQueue.filter(item => item.id !== logId)) // Refresh metrics to reflect the rejection fetchMetrics() } catch (error) { console.error("Error rejecting review:", error) alert("Failed to reject query. Please try again.") } } const handleRetrain = async () => { try { await fetch("/api/admin/retrain", { method: "POST" }) alert("Retraining initiated. Check back in a few hours.") } catch (error) { console.error("Error initiating retrain:", error) } } // Fetch data on mount and set up polling for real-time updates useEffect(() => { fetchMetrics() fetchQueryLogs() fetchReviewQueue() // Auto-refresh every 30 seconds for real-time monitoring const interval = setInterval(() => { fetchMetrics() fetchQueryLogs() fetchReviewQueue() }, 30000) return () => clearInterval(interval) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // Filter logs const filteredLogs = queryLogs.filter(log => { if (searchQuery && !log.query.toLowerCase().includes(searchQuery.toLowerCase())) { return false } if (filterPersona !== "all" && log.persona !== filterPersona) { return false } if (filterConfidence === "low" && log.confidence >= 0.6) { return false } if (filterConfidence === "medium" && (log.confidence < 0.6 || log.confidence >= 0.8)) { return false } if (filterConfidence === "high" && log.confidence < 0.8) { return false } return true }) return (
{/* Header */}

Agent Monitoring

Monitor and manage AmaniQ agent performance

{/* Metrics Overview */} {metrics && } {/* Main Tabs */} Query Logs Review Queue ({reviewQueue.length}) Analytics Training
) } // Helper components with proper TypeScript types interface MetricsOverviewProps { metrics: AgentMetrics | null } function MetricsOverview({ metrics }: MetricsOverviewProps) { // Return placeholder if metrics not loaded if (!metrics) { return (
{[1, 2, 3, 4].map((i) => ( Loading...
--
))}
) } return (
Total Queries
{(metrics.total_queries ?? 0).toLocaleString()}

Last 30 days

Avg Confidence
{((metrics.avg_confidence ?? 0) * 100).toFixed(1)}%
= 0.8 ? "default" : "secondary"} className="text-xs"> {(metrics.avg_confidence ?? 0) >= 0.8 ? "High" : (metrics.avg_confidence ?? 0) >= 0.6 ? "Medium" : "Low"}
Avg Response Time
{(metrics.avg_response_time_ms ?? 0).toFixed(0)}ms

{(metrics.avg_response_time_ms ?? 0) < 1000 ? "Excellent" : (metrics.avg_response_time_ms ?? 0) < 2000 ? "Good" : "Slow"}

Human Review Rate
{((metrics.human_review_rate ?? 0) * 100).toFixed(1)}%

{(metrics.human_review_rate ?? 0) < 0.1 ? "Low" : (metrics.human_review_rate ?? 0) < 0.3 ? "Moderate" : "High"}

) } interface QueryLogsViewProps { logs: QueryLog[] selectedLog: QueryLog | null onSelectLog: (log: QueryLog | null) => void searchQuery: string onSearchChange: (query: string) => void filterPersona: string onFilterPersonaChange: (persona: string) => void filterConfidence: string onFilterConfidenceChange: (confidence: string) => void loading: boolean } function QueryLogsView({ logs, selectedLog, onSelectLog, searchQuery, onSearchChange, filterPersona, onFilterPersonaChange, filterConfidence, onFilterConfidenceChange, loading }: QueryLogsViewProps) { return (
Query History Recent agent queries
onSearchChange(e.target.value)} className="pl-8" />
{loading ? (
) : (
{logs.map((log: QueryLog) => (
onSelectLog(log)} className={cn( "p-3 rounded-lg border cursor-pointer hover:bg-accent", selectedLog?.id === log.id && "bg-accent border-primary" )} >
{log.persona} {log.intent}
{new Date(log.timestamp).toLocaleTimeString()}

{log.query}

{(log.confidence * 100).toFixed(0)}% {log.response_time_ms}ms
{log.human_review_required && ( )}
))}
)}
Query Details {selectedLog ? (
{/* Show ThinkingProcess if available */} {selectedLog.reasoning_path && ( )}

{selectedLog.query}

{selectedLog.persona}

= 0.8 ? "default" : selectedLog.confidence >= 0.6 ? "secondary" : "destructive" }> {(selectedLog.confidence * 100).toFixed(1)}%

{selectedLog.agent_path.map((node: string, idx: number) => ( {node} {idx < selectedLog.agent_path.length - 1 && " → "} ))}
{selectedLog.quality_issues?.length > 0 && (
    {selectedLog.quality_issues.map((issue: string, idx: number) => (
  • {issue}
  • ))}
)}
) : (
Select a query to view details
)}
) } interface ReviewQueueViewProps { queue: ReviewQueueItem[] onApprove: (logId: string) => void onReject: (logId: string, feedback: string) => void } function ReviewQueueView({ queue, onApprove, onReject }: ReviewQueueViewProps) { const [selectedItem, setSelectedItem] = useState(null) const [rejectFeedback, setRejectFeedback] = useState("") return (
Pending Review ({queue.length}) {queue.length === 0 ? (

All caught up!

) : (
{queue.map((item: ReviewQueueItem) => (
setSelectedItem(item)} className={cn( "p-3 rounded-lg border cursor-pointer hover:bg-accent", selectedItem?.id === item.id && "bg-accent border-primary" )} > {item.priority} priority

{item.query}

{item.review_reason}

))}
)}
Review Details {selectedItem ? (