/** * LLM Monitor - Data Sync Worker * Aggiorna i dati in background e notifica il main thread */ const API_BASE = "/api/v1"; const REFRESH_INTERVAL = 30000; // 30 secondi let activeServerId = null; let activeHost = null; let nextSyncTimeout = null; // Formattare bytes function formatBytes(bytes) { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i]; } // Recuperare health async function fetchHealth() { if (!activeHost) { return null; } try { const response = await fetch(buildApiUrl(`${API_BASE}/health`)); if (response.ok) { const data = await response.json(); return { status: data.status, ollama_status: data.ollama_status, timestamp: new Date().toISOString(), serverId: activeServerId }; } } catch (error) { console.error("Health check error:", error); } return null; } // Recuperare modelli async function fetchModels() { if (!activeHost) { return null; } try { const response = await fetch(buildApiUrl(`${API_BASE}/models`)); if (!response.ok) throw new Error("Failed to load models"); const data = await response.json(); const models = data.models || []; return { models, total: models.length, totalSize: formatBytes(models.reduce((sum, m) => sum + m.size, 0)), timestamp: new Date().toISOString(), serverId: activeServerId }; } catch (error) { console.error("Error loading models:", error); return null; } } // Recuperare dettagli show per un modello async function fetchModelShow(modelName) { try { const response = await fetch(buildApiUrl(`${API_BASE}/models/${encodeURIComponent(modelName)}/show`)); if (!response.ok) { return null; } return await response.json(); } catch (error) { console.error(`Error loading show data for model ${modelName}:`, error); return null; } } // Recuperare dettagli show per tutti i modelli async function fetchAllModelsShow(models) { const showByModel = {}; const results = await Promise.allSettled( models.map(async (model) => { const showData = await fetchModelShow(model.name); return { name: model.name, showData }; }) ); results.forEach((result) => { if (result.status === "fulfilled" && result.value.showData) { showByModel[result.value.name] = result.value.showData; } }); return showByModel; } async function fetchRunningModels() { if (!activeHost) { return null; } try { const response = await fetch(buildApiUrl(`${API_BASE}/models/running`)); if (!response.ok) { throw new Error("Failed to load running models"); } const data = await response.json(); return { models: data.models || [], total: data.total || (data.models || []).length, timestamp: new Date().toISOString(), serverId: activeServerId }; } catch (error) { console.error("Error loading running models:", error); return null; } } // Sincronizzare i dati async function syncData() { if (!activeHost) { self.postMessage({ type: "DATA_UPDATED", health: null, modelsData: null, serverId: activeServerId }); return; } const health = await fetchHealth(); const modelsData = await fetchModels(); const runningData = await fetchRunningModels(); if (modelsData && modelsData.models.length > 0) { modelsData.showByModel = await fetchAllModelsShow(modelsData.models); } else if (modelsData) { modelsData.showByModel = {}; } // Notificare il main thread // (il main thread gestisce localStorage) self.postMessage({ type: "DATA_UPDATED", health, modelsData, runningData, serverId: activeServerId }); } function buildApiUrl(path) { const url = new URL(path, self.location.origin); url.searchParams.set("host", activeHost); return `${url.pathname}${url.search}`; } function clearNextSync() { if (nextSyncTimeout) { clearTimeout(nextSyncTimeout); nextSyncTimeout = null; } } function scheduleNextSync(lastSyncTimestamp = 0) { clearNextSync(); const ageMs = lastSyncTimestamp ? Math.max(0, Date.now() - lastSyncTimestamp) : REFRESH_INTERVAL; const delayMs = Math.max(0, REFRESH_INTERVAL - ageMs); nextSyncTimeout = setTimeout(async () => { await syncData(); scheduleNextSync(Date.now()); }, delayMs); } // Gestire messaggi dal main thread self.onmessage = (event) => { if (event.data.type === "SET_SERVER") { activeServerId = event.data.serverId || null; activeHost = event.data.host || null; const lastSyncTimestamp = Number(event.data.lastSyncTimestamp || 0); if (event.data.syncImmediately) { syncData().finally(() => scheduleNextSync(Date.now())); return; } scheduleNextSync(lastSyncTimestamp); } if (event.data.type === "SYNC_NOW") { syncData().finally(() => scheduleNextSync(Date.now())); } };