// Provider table + drawer const { useState: useStateP } = React; const StatusPill = ({ status }) => { const map = { healthy: { cls: 'pill-green', label: 'healthy' }, degraded: { cls: 'pill-amber', label: 'degraded' }, down: { cls: 'pill-red', label: 'down' }, unconfigured: { cls: 'pill-neutral', label: 'unconfigured' }, disabled: { cls: 'pill-neutral', label: 'disabled' }, }; const m = map[status] || map.unconfigured; return ( {m.label} ); }; const ProviderLogo = ({ p, size = 22 }) => (
{p.logo}
); const ProviderTable = ({ onSelect, selected, filter, providers: propProviders, search = '' }) => { const providers = propProviders || window.LLMGateData.providers; const t = window.t; const [sort, setSort] = useStateP({ key: 'req24h', dir: 'desc' }); let list = providers.slice(); if (filter && filter !== 'all') { if (filter === 'external') list = list.filter(p => p.kind === 'external'); if (filter === 'self-hosted') list = list.filter(p => p.kind === 'self-hosted'); if (filter === 'issues') list = list.filter(p => p.status !== 'healthy' || p.key === 'missing'); } if (search && search.trim()) { const q = search.trim().toLowerCase(); list = list.filter(p => p.name.toLowerCase().includes(q) || p.id.toLowerCase().includes(q) || (p.modelList || []).some(m => m.toLowerCase().includes(q)) ); } list.sort((a, b) => { const av = a[sort.key], bv = b[sort.key]; if (typeof av === 'number') return sort.dir === 'asc' ? av - bv : bv - av; return sort.dir === 'asc' ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av)); }); const headCell = (label, key, align) => ( setSort({ key, dir: sort.key === key && sort.dir === 'desc' ? 'asc' : 'desc' })} style={{textAlign: align || 'left', cursor:'pointer', userSelect:'none'}}> {label} {sort.key === key && {sort.dir === 'desc' ? '↓' : '↑'}} ); return (
{headCell(t('tbl.provider'), 'name')} {headCell(t('tbl.models'), 'models', 'right')} {headCell(t('tbl.rpm'), 'rpm', 'right')} {headCell(t('tbl.tpm'), 'tpm', 'right')} {headCell(t('tbl.p50'), 'p50', 'right')} {headCell(t('tbl.p99'), 'p99', 'right')} {headCell(t('tbl.err'), 'err', 'right')} {headCell(t('tbl.cost24h'), 'cost24h', 'right')} {list.length === 0 && ( )} {list.map(p => ( onSelect(p)}> ))}
{t('tbl.kind')} {t('tbl.status')}{t('tbl.traffic')}
{t('tbl.noResults')}
{p.name} {p.key === 'missing' ? {t('tbl.missingKey')} : p.keyMasked === '—' ? 'local' : p.keyMasked}
{p.kind} {p.models} {p.rpm || '—'} {p.tpm === '0' ? '—' : p.tpm} {p.p50 ? p.p50 + 'ms' : '—'} 3000 ? 'var(--amber)' : undefined}}>{p.p99 ? p.p99 + 'ms' : '—'} = 1 ? 'var(--red)' : p.err > 0.2 ? 'var(--amber)' : 'var(--text-2)'}}> {p.rpm ? p.err.toFixed(2) : '—'} {p.cost24h > 0 ? '$' + p.cost24h.toFixed(2) : p.kind === 'self-hosted' ? 'free' : '$0'}
); }; const Drawer = ({ provider, onClose, onToggleEnabled }) => { const [tab, setTab] = useStateP('overview'); if (!provider) return null; const p = provider; return ( <>

{p.name}

{p.kind} · {p.region} · added {p.added}
{['overview', 'models', 'logs', 'settings'].map(t => ( ))}
{tab === 'overview' && ( <>
Enabled onToggleEnabled(p.id)}>
{[ ['Requests 24h', p.req24h.toLocaleString()], ['Cost 24h', p.cost24h > 0 ? '$' + p.cost24h.toFixed(2) : 'free'], ['p50 latency', p.p50 ? p.p50 + 'ms' : '—'], ['p99 latency', p.p99 ? p.p99 + 'ms' : '—'], ['Error rate', p.rpm ? p.err.toFixed(2) + '%' : '—'], ['Models', p.models], ].map(([k, v]) => (
{k}
{v}
))}

Connection

Endpoint
{p.kind === 'self-hosted' ? 'http://ollama.svc.cluster/v1' : 'https://api.' + p.name + '.com/v1'}
Auth
{p.keyMasked}
Region
{p.region}
Timeout
30s
Retries
3 · exponential

Traffic · last 60 min

)} {tab === 'models' && (
{p.modelList.length} model{p.modelList.length !== 1 && 's'} registered
{p.modelList.length === 0 && ( )} {p.modelList.map((m, i) => ( ))}
ModelContextReq 24hEnabled
No models registered. Add a key first to sync the catalog.
{m} {[200000, 128000, 64000, 32000, 8192][i % 5].toLocaleString()} {Math.floor(p.req24h / Math.max(1, p.modelList.length) * (0.5 + Math.random())).toLocaleString()}
)} {tab === 'logs' && (
{window.LLMGateData.activity.filter(a => a.prov === p.id).slice(0, 8).map((a, i) => (
{a.t} {a.status}
))} {window.LLMGateData.activity.filter(a => a.prov === p.id).length === 0 && (
No recent activity.
)}
)} {tab === 'settings' && ( <>
)}
); }; Object.assign(window, { ProviderTable, Drawer, StatusPill, ProviderLogo });