let evtSource = null; let currentInstalledModels = []; // Initialize SSE Stream function initSSE() { if (evtSource) { evtSource.close(); } evtSource = new EventSource('/api/stream'); evtSource.onopen = () => { document.getElementById('sse-badge').textContent = 'SSE LIVE'; document.getElementById('sse-badge').className = 'text-emerald-400 font-bold'; }; evtSource.onmessage = (event) => { try { const data = JSON.parse(event.data); updateDashboard(data); } catch (err) { console.error('Error parsing SSE event:', err); } }; evtSource.onerror = (err) => { console.warn('SSE disconnected, retrying...', err); document.getElementById('sse-badge').textContent = 'RECONNECTING...'; document.getElementById('sse-badge').className = 'text-rose-400 font-bold'; }; } function updateDashboard(data) { if (!data) return; // 1. GPU VRAM Stats const gpu = data.gpu || {}; if (gpu.available) { document.getElementById('gpu-chip-name').textContent = gpu.device_name || 'NVIDIA GPU'; document.getElementById('vram-total-used').textContent = gpu.vram_used_gb || '0.0'; document.getElementById('vram-used-pct').textContent = `${gpu.vram_used_pct || 0}% USED`; const bd = gpu.breakdown || {}; const totalBytes = gpu.vram_total_bytes || (16 * 1024**3); const ollamaPct = ((bd.ollama_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const comfyPct = ((bd.comfyui_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const systemPct = ((bd.system_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const freePct = Math.max(0, 100 - (ollamaPct + comfyPct + systemPct)); document.getElementById('bar-ollama').style.width = `${ollamaPct}%`; document.getElementById('bar-comfy').style.width = `${comfyPct}%`; document.getElementById('bar-system').style.width = `${systemPct}%`; document.getElementById('bar-free').style.width = `${freePct}%`; document.getElementById('tooltip-ollama').textContent = `${bd.ollama_gb || 0} GB`; document.getElementById('tooltip-comfy').textContent = `${bd.comfyui_gb || 0} GB`; document.getElementById('tooltip-system').textContent = `${bd.system_gb || 0} GB`; document.getElementById('legend-ollama').textContent = `${bd.ollama_gb || 0} GB`; document.getElementById('legend-comfy').textContent = `${bd.comfyui_gb || 0} GB`; document.getElementById('legend-system').textContent = `${bd.system_gb || 0} GB`; document.getElementById('legend-free').textContent = `${bd.free_gb || 0} GB`; // Hardware sensors document.getElementById('gpu-util-val').textContent = `${gpu.gpu_util_pct || 0}%`; document.getElementById('gpu-temp-val').textContent = `${gpu.temperature_c || 0}°C`; document.getElementById('gpu-power-val').textContent = `${gpu.power_w || 0} W`; document.getElementById('gpu-fan-val').textContent = `${gpu.fan_pct || 0}%`; // Processes table const tbody = document.getElementById('gpu-proc-table'); if (bd.processes && bd.processes.length > 0) { tbody.innerHTML = bd.processes.map(p => { let tagClass = 'text-slate-400'; let badge = ''; if (p.is_ollama) { tagClass = 'text-purple-400 font-bold'; badge = 'OLLAMA'; } else if (p.is_comfy) { tagClass = 'text-cyan-400 font-bold'; badge = 'COMFY'; } return `