feat: add disk telemetry and clean navigation

This commit is contained in:
Hermes Agent
2026-08-26 00:43:48 +10:00
parent 57c8b841f2
commit a79f033c54
6 changed files with 58 additions and 8 deletions
+25 -3
View File
@@ -167,7 +167,7 @@
function RuntimePanel(props) {
var runtime = props.runtime || {}, total = Number(runtime.memory_total_bytes || 0), used = Number(runtime.memory_used_bytes || 0), pct = total ? Math.min(100, used * 100 / total) : 0;
var gpu = runtime.gpu || {}, cpu = runtime.cpu || {}, models = runtime.model_memory || [], loading = runtime.model_loading || [], cores = cpu.cores || [], gpus = gpu.gpus || [];
var gpu = runtime.gpu || {}, cpu = runtime.cpu || {}, disk = runtime.disk || {}, models = runtime.model_memory || [], loading = runtime.model_loading || [], cores = cpu.cores || [], gpus = gpu.gpus || [];
var ollamaBytes = Number(runtime.ollama_model_bytes || 0), ollamaTargetBytes = Number(runtime.ollama_target_model_bytes || ollamaBytes), ollamaPct = total ? Math.min(100, ollamaTargetBytes * 100 / total) : 0;
function percent(value) { return value == null ? "n/a" : Number(value).toFixed(1) + "%"; }
function meter(value) { return value == null ? 0 : Math.max(0, Math.min(100, Number(value))); }
@@ -176,7 +176,7 @@
h("div", { className: "ollama-runtime-grid" },
h("div", { className: "ollama-runtime-stat" }, h("small", null, "System RAM used"), h("strong", null, fmtBytes(used), " / ", fmtBytes(total)), h("div", { className: "ollama-meter" }, h("span", { style: { width: pct + "%" } })), h("small", null, fmtBytes(runtime.memory_available_bytes || 0), " available")),
h("div", { className: "ollama-runtime-stat ollama-cpu-stat" }, h("small", null, "CPU usage"), h("strong", null, percent(cpu.usage_percent)), h("div", { className: "ollama-meter" }, h("span", { style: { width: meter(cpu.usage_percent) + "%" } })), h("small", null, cpu.count ? cpu.count + " logical CPUs · load " + (cpu.load_average || []).map(function (value) { return Number(value).toFixed(2); }).join(" / ") : "Unavailable")),
h("div", { className: "ollama-runtime-stat" }, h("small", null, "Swap used"), h("strong", null, fmtBytes(runtime.swap_used_bytes || 0), " / ", fmtBytes(runtime.swap_total_bytes || 0)), h("small", null, "Host-wide live statistic")),
h("div", { className: "ollama-runtime-stat ollama-disk-stat" }, h("small", null, "Disk usage"), h("strong", null, percent(disk.used_percent)), h("div", { className: "ollama-meter" }, h("span", { style: { width: meter(disk.used_percent) + "%" } })), h("small", null, disk.available ? fmtBytes(disk.used_bytes) + " used · " + fmtBytes(disk.free_bytes) + " free" : "Unavailable")),
h("div", { className: "ollama-runtime-stat ollama-gpu-stat" }, h("small", null, "GPU usage"), h("strong", null, percent(gpu.utilization_percent), " · ", gpu.count || 0, " GPU", (gpu.count || 0) === 1 ? "" : "s"), h("div", { className: "ollama-meter" }, h("span", { style: { width: meter(gpu.utilization_percent) + "%" } })), h("small", null, gpu.telemetry_available && gpus.length ? gpus.map(function (item) { return item.name + " · " + fmtBytes(item.used_bytes) + " / " + fmtBytes(item.total_bytes); }).join("; ") : "Unavailable")),
h("div", { className: "ollama-runtime-stat ollama-weight-stat" }, h("small", null, "Ollama model weights"), h("strong", null, fmtBytes(ollamaBytes), " resident"), h("div", { className: "ollama-meter" }, h("span", { style: { width: ollamaPct + "%" } })), h("small", null, loading.length ? "Loading target: " + fmtBytes(ollamaTargetBytes) : "Mapped weight bytes; Linux may report them as file cache"))
),
@@ -456,10 +456,32 @@
var needle = query.toLowerCase().trim(); if (needle) models = models.filter(function (model) { return (model.name + " " + model.family + " " + (model.strengths || []).join(" ") + " " + (model.capabilities || []).join(" ")).toLowerCase().indexOf(needle) >= 0; });
var catalogCapabilities = data && data.catalog_filter_options ? data.catalog_filter_options.capabilities || [] : [];
var jobs = data && data.jobs ? data.jobs.filter(function (job) { return job.state === "running"; }) : [];
var disk = data && data.disk ? data.disk : {};
var navTabs = h("nav", { className: "ollama-tabs", "aria-label": "Ollama views" },
h(Button, { className: tab === "chat" ? "selected" : "", onClick: function () { setTab("chat"); } }, "Ollama Chat"),
h(Button, { className: tab === "installed" ? "selected" : "", onClick: function () { setTab("installed"); } }, "Installed (" + ((data && data.models) || []).length + ")"),
h(Button, { className: tab === "popular" ? "selected" : "", onClick: function () { setTab("popular"); } }, "Top 20 popular (" + ((data && data.popular) || []).length + ")"),
h(Button, { className: tab === "catalog" ? "selected" : "", onClick: function () { setTab("catalog"); } }, "Available downloads (" + ((data && data.catalog) || []).length + ")")
);
var catalogControls = tab === "catalog" && h("div", { className: "ollama-catalog-controls" },
h("label", null, "Type", h("select", { className: "ollama-catalog-select", value: catalogType, onChange: function (event) { setCatalogType(event.target.value); } },
h("option", { value: "all" }, "All types"), h("option", { value: "moe" }, "MoE only"), h("option", { value: "dense" }, "Dense only")
)),
h("label", null, "Ability", h("select", { className: "ollama-catalog-select", value: catalogCapability, onChange: function (event) { setCatalogCapability(event.target.value); } },
h("option", { value: "all" }, "All abilities"), catalogCapabilities.map(function (capability) { return h("option", { key: capability, value: capability }, capability); })
)),
h("label", null, "Organize", h("select", { className: "ollama-catalog-select", value: catalogSort, onChange: function (event) { setCatalogSort(event.target.value); } },
h("option", { value: "popularity" }, "Popularity"), h("option", { value: "newest" }, "Newest"), h("option", { value: "size_asc" }, "Size: smallest first"), h("option", { value: "size_desc" }, "Size: largest first"), h("option", { value: "name" }, "Name")
))
);
var browseToolbar = tab !== "chat" && h("div", { className: "ollama-browse-row" },
h("input", { className: "ollama-search", value: query, placeholder: "Search models, capabilities, or strengths…", onChange: function (event) { setQuery(event.target.value); } }),
catalogControls
);
return h("main", { className: "ollama-page" }, h("header", { className: "ollama-hero" }, h("div", null, h("div", { className: "ollama-eyebrow" }, "LOCAL MODEL OPERATIONS"), h("h1", null, "Ollama Models"), h("p", null, "Inspect, chat with, download, update, and remove models from the local Ollama runtime.")), h("div", { className: "ollama-health" }, h(Badge, { tone: data && data.ollama && data.ollama.available ? "live" : "danger" }, data && data.ollama && data.ollama.available ? "Ollama online" : "Ollama unavailable"), data && data.ollama && h("span", null, "v" + (data.ollama.version || "unknown")), h(Button, { disabled: busy === "catalog", onClick: refreshCatalog }, busy === "catalog" ? "Refreshing…" : "Refresh catalog")), h(ConnectionPanel, { data: data, reload: load })),
targetDialog && h("div", { className: "ollama-target-modal" }, h("div", { className: "ollama-target-card" }, h("h3", null, "Where should " + targetDialog.name + " be downloaded?"), h("p", null, "Both local and remote Ollama instances are online. Choose the destination for this model."), targetDialog.targets.map(function (item) { return h(Button, { key: item.kind, onClick: function () { var chosen = targetDialog; setTargetDialog(null); action(chosen.kind, chosen.name, item.kind); } }, (item.kind || "local").toUpperCase(), " · ", item.url, " · v", item.version, " · ", item.models, " models"); }), h(Button, { className: "secondary", onClick: function () { setTargetDialog(null); } }, "Cancel"))),
notice && h("div", { className: "ollama-notice " + (notice.error ? "error" : notice.warning ? "warning" : "ok") }, notice.error || notice.warning || notice.ok),
h("section", { className: "ollama-toolbar" }, h("div", { className: "ollama-tabs" }, h(Button, { className: tab === "chat" ? "selected" : "", onClick: function () { setTab("chat"); } }, "Ollama Chat"), h(Button, { className: tab === "installed" ? "selected" : "", onClick: function () { setTab("installed"); } }, "Installed (" + ((data && data.models) || []).length + ")"), h(Button, { className: tab === "popular" ? "selected" : "", onClick: function () { setTab("popular"); } }, "Top 20 popular (" + ((data && data.popular) || []).length + ")"), h(Button, { className: tab === "catalog" ? "selected" : "", onClick: function () { setTab("catalog"); } }, "Available downloads (" + ((data && data.catalog) || []).length + ")")), tab !== "chat" && h("input", { className: "ollama-search", value: query, placeholder: "Search models, capabilities, or strengths…", onChange: function (event) { setQuery(event.target.value); } }), tab === "catalog" && h("div", { className: "ollama-catalog-controls" }, h("label", null, "Type", h("select", { className: "ollama-catalog-select", value: catalogType, onChange: function (event) { setCatalogType(event.target.value); } }, h("option", { value: "all" }, "All types"), h("option", { value: "moe" }, "MoE only"), h("option", { value: "dense" }, "Dense only"))), h("label", null, "Ability", h("select", { className: "ollama-catalog-select", value: catalogCapability, onChange: function (event) { setCatalogCapability(event.target.value); } }, h("option", { value: "all" }, "All abilities"), catalogCapabilities.map(function (capability) { return h("option", { key: capability, value: capability }, capability); }))), h("label", null, "Organize", h("select", { className: "ollama-catalog-select", value: catalogSort, onChange: function (event) { setCatalogSort(event.target.value); } }, h("option", { value: "popularity" }, "Popularity"), h("option", { value: "newest" }, "Newest"), h("option", { value: "size_asc" }, "Size: smallest first"), h("option", { value: "size_desc" }, "Size: largest first"), h("option", { value: "name" }, "Name"))))),
h("section", { className: "ollama-toolbar" }, h("div", { className: "ollama-nav-row" }, navTabs, h("div", { className: "ollama-toolbar-disk" }, h("span", null, "Disk"), h("strong", null, disk.used_percent == null ? "n/a" : Number(disk.used_percent).toFixed(1) + "%"), h("small", null, disk.available ? fmtBytes(disk.free_bytes) + " free" : "Unavailable"))), browseToolbar),
tab !== "chat" && h("div", { className: "ollama-info-strip" }, h("span", null, data && data.models ? data.models.filter(function (m) { return m.loaded; }).length + " currently loaded" : "Loading runtime state…"), h("span", null, "Catalog checked " + (data && data.catalog_updated_at ? fmtDate(data.catalog_updated_at) : "not yet")), h("span", null, "Next daily check " + (data && data.next_catalog_refresh ? fmtDate(data.next_catalog_refresh) : "01:00 Melbourne time") + " (1:00 AM Melbourne time)")),
tab === "chat" && h(ChatPanel, { models: data && data.models ? data.models : [], refresh: load }),
tab === "popular" && h("p", { className: "ollama-popular-note" }, "Popular is limited to models with known size and RAM estimates at or below the detected system RAM (" + (data && data.popular_filter && data.popular_filter.max_expected_ram_gib ? data.popular_filter.max_expected_ram_gib + " GiB" : "detecting…") + "). Oversized families are represented by a smaller fitting variant when available."), jobs.length > 0 && h("section", { className: "ollama-jobs" }, jobs.map(function (job) { return h("div", { key: job.id }, h("strong", null, job.action + " · " + job.name + " · " + (job.target || "local") + (job.endpoint ? " · " + job.endpoint : "")), h("span", null, job.percent == null ? job.status : job.percent + "%")); })),