Дашборд: карточка загрузки GPU (util/Вт/°C из results/gpustat.json)

Веб в Docker без nvidia-smi: хост-сервис gausse-gpustat пишет JSON рядом с
базой, /api/overview отдаёт поле gpu (протухшее >30с — честно null).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-07-08 04:01:48 +05:00
parent 6fba55f2d6
commit ac459d26a4
2 changed files with 24 additions and 0 deletions

View File

@@ -61,6 +61,7 @@ PAGE_HTML = r"""<!doctype html>
<div class="card"><div class="k">Нереализуемо</div><div class="v" id="c-infeas">—</div></div>
<div class="card"><div class="k">Доля реализуемых</div><div class="v" id="c-pct">—</div></div>
<div class="card"><div class="k">Лучший КПД / скорость</div><div class="v" id="c-best" style="font-size:20px">—</div></div>
<div class="card"><div class="k">GPU (GTX 1070)</div><div class="v" id="c-gpu" style="font-size:20px">—</div></div>
</div>
<div class="grid2">
@@ -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='';

View File

@@ -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),
}