diff --git a/src/gausse/web/page.py b/src/gausse/web/page.py
index 266d5c0..afcd68f 100644
--- a/src/gausse/web/page.py
+++ b/src/gausse/web/page.py
@@ -61,6 +61,7 @@ PAGE_HTML = r"""
+
@@ -131,6 +132,10 @@ async function refresh(){
let best = (o.top && o.top.length) ? o.top[0].efficiency : null;
let bestV = (o.top_by_velocity && o.top_by_velocity.length) ? o.top_by_velocity[0].exit_velocity_mps : null;
document.getElementById('c-best').textContent = fmtPct(best) + (bestV!=null ? ' / '+fmt(bestV,0)+' м/с' : '');
+ const g = o.gpu;
+ document.getElementById('c-gpu').textContent = g
+ ? `${g.util_pct}% · ${fmt(g.power_w,0)}Вт · ${g.temp_c}°C`
+ : 'нет данных';
renderTop(o);
const hist=document.getElementById('hist'); hist.innerHTML='';
diff --git a/src/gausse/web/stats.py b/src/gausse/web/stats.py
index fb427f0..7da95e2 100644
--- a/src/gausse/web/stats.py
+++ b/src/gausse/web/stats.py
@@ -1,6 +1,7 @@
"""Агрегаты по таблице `runs` для веб-морды — считаются запросами к SQLite."""
import json
+import time
from pathlib import Path
from gausse.storage.database import fetch_run_by_id, fetch_runs, open_connection
@@ -13,6 +14,23 @@ def _tail_log(log_path: Path, n_lines: int = 40) -> list[str]:
return lines[-n_lines:]
+def _gpu_stat(db_path: Path) -> dict | None:
+ """Загрузка GPU из gpustat.json рядом с базой (пишет gausse-gpustat на хосте).
+
+ Веб живёт в Docker без доступа к nvidia-smi, поэтому хост-сервис раз в
+ несколько секунд кладёт JSON в каталог результатов. Протухшие данные
+ (>30с — писатель умер/GPU нет) честно не показываем.
+ """
+ p = db_path.parent / "gpustat.json"
+ try:
+ stat = json.loads(p.read_text(encoding="utf-8"))
+ if time.time() - float(stat.get("ts", 0)) > 30:
+ return None
+ return stat
+ except (OSError, ValueError):
+ return None
+
+
def overview(db_path: Path, log_path: Path | None = None, top_n: int = 15) -> dict:
"""Сводка для дашборда: счётчики, гистограмма КПД, топ конфигураций, хвост лога."""
conn = open_connection(db_path)
@@ -71,6 +89,7 @@ def overview(db_path: Path, log_path: Path | None = None, top_n: int = 15) -> di
"top": top,
"top_by_velocity": top_by_velocity,
"log_tail": _tail_log(log_path, 40) if log_path else [],
+ "gpu": _gpu_stat(db_path),
}