import { useState, useEffect, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Activity, Key, RefreshCw, Zap, AlertTriangle, CheckCircle, XCircle, Clock, Plus, Cpu, TrendingUp, Gauge } from 'lucide-react' const API_BASE = '' const GLYPHS = { active: '◉', expired: '⟁', rate_limited: '⧖', invalid: '✕', untested: '◌', unknown: '◌', loading: '◍' } const COLORS = { active: 'text-emerald-400', expired: 'text-red-400', rate_limited: 'text-amber-400', invalid: 'text-red-500', untested: 'text-zinc-500', unknown: 'text-zinc-500', loading: 'text-blue-400' } const PULSE = { active: true, expired: false, rate_limited: true, invalid: false, untested: false, unknown: false, loading: true } const GLOW = { active: 'shadow-[0_0_12px_rgba(52,211,153,0.3)]', expired: '', rate_limited: 'shadow-[0_0_8px_rgba(251,191,36,0.2)]', invalid: '', untested: '', unknown: '', loading: 'shadow-[0_0_8px_rgba(96,165,250,0.2)]' } export default function ApiKeyPanel() { const [keys, setKeys] = useState([]) const [chain, setChain] = useState([]) const [health, setHealth] = useState({ providers: [], summary: {} }) const [usage, setUsage] = useState({}) const [catalog, setCatalog] = useState({ services: [] }) const [models, setModels] = useState({ models: [] }) const [loading, setLoading] = useState(false) const [showAdd, setShowAdd] = useState(false) const [newKey, setNewKey] = useState({ provider: '', key: '' }) const [chatMsg, setChatMsg] = useState('') const [chatResp, setChatResp] = useState(null) const [chatLoading, setChatLoading] = useState(false) const [lastUpdate, setLastUpdate] = useState(0) const fetchAll = useCallback(async () => { setLoading(true) try { const [k, c, h, u, cat, m] = await Promise.all([ fetch(`${API_BASE}/llm/keys`).then(r => r.json()).catch(() => ({ keys: [] })), fetch(`${API_BASE}/llm/chain`).then(r => r.json()).catch(() => ({ chain: [] })), fetch(`${API_BASE}/llm/health`).then(r => r.json()).catch(() => ({ providers: [], summary: {} })), fetch(`${API_BASE}/llm/usage`).then(r => r.json()).catch(() => ({})), fetch(`${API_BASE}/llm/catalog`).then(r => r.json()).catch(() => ({ services: [] })), fetch(`${API_BASE}/llm/models`).then(r => r.json()).catch(() => ({ models: [] })), ]) setKeys(k.keys || []); setChain(c.chain || []); setHealth(h); setUsage(u); setCatalog(cat); setModels(m) setLastUpdate(Date.now()) } finally { setLoading(false) } }, []) useEffect(() => { fetchAll(); const i = setInterval(fetchAll, 30000); return () => clearInterval(i) }, [fetchAll]) const rotate = async () => { setLoading(true) try { await fetch(`${API_BASE}/llm/rotate`, { method: 'POST' }) } finally { setTimeout(fetchAll, 2000) } } const addKey = async () => { await fetch(`${API_BASE}/llm/keys/add`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newKey) }) setNewKey({ provider: '', key: '' }); setShowAdd(false); await fetchAll() } const sendChat = async () => { setChatLoading(true) try { const r = await fetch(`${API_BASE}/llm/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: chatMsg }) }) setChatResp(await r.json()) } finally { setChatLoading(false) } } const activeCount = health.summary?.active || 0 const totalCount = health.summary?.total || 0 const catCount = catalog.services?.length || 0 const modelCount = models.models?.length || 0 const deadKeys = keys.filter(k => k.status !== 'active' && k.status !== 'untested') const providerGroups = {} keys.forEach(k => { if (!providerGroups[k.provider]) providerGroups[k.provider] = { keys: [], bestLatency: Infinity, models: new Set(), lastValidated: '' } providerGroups[k.provider].keys.push(k) if (k.status === 'active' && k.latency_ms < providerGroups[k.provider].bestLatency) providerGroups[k.provider].bestLatency = k.latency_ms if (k.model) providerGroups[k.provider].models.add(k.model) if (k.timestamp) providerGroups[k.provider].lastValidated = k.timestamp }) return (