diff --git a/ansible/roles/cezen-backend/files/main.py b/ansible/roles/cezen-backend/files/main.py
index c768c1a..5a8b9c4 100644
--- a/ansible/roles/cezen-backend/files/main.py
+++ b/ansible/roles/cezen-backend/files/main.py
@@ -1621,14 +1621,40 @@ async def system_info(user: dict = Depends(current_user)):
except Exception:
v = "unknown"
+ try:
+ os_name = subprocess.check_output(["lsb_release", "-ds"], text=True, timeout=3).strip().strip('"')
+ except Exception:
+ os_name = subprocess.getoutput("uname -sr") or "unknown"
+
+ ip_address = ""
+ try:
+ for _name, addrs in psutil.net_if_addrs().items():
+ for addr in addrs:
+ if addr.address.count(".") == 3 and not addr.address.startswith("127."):
+ ip_address = addr.address
+ raise StopIteration
+ except StopIteration:
+ pass
+ except Exception:
+ ip_address = ""
+
+ metrics = collect_metrics()
+ gpu_name = subprocess.getoutput(
+ "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1"
+ ) or "N/A"
+
return {
"hostname": hostname,
+ "ip_address": ip_address,
+ "os": os_name,
"uptime": uptime,
"python_version": subprocess.getoutput("python3 --version"),
"ollama_version": v,
- "gpu_name": subprocess.getoutput(
- "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1"
- ) or "N/A",
+ "gpu_name": gpu_name,
+ "gpu_vram_gb": metrics.get("gpu_mem_total_gb"),
+ "ram_total_gb": metrics.get("ram_total_gb"),
+ "disk_total_gb": metrics.get("disk_total_gb"),
+ "cpu_cores": metrics.get("cpu_cores"),
}
@app.get("/api/system/feasibility")
diff --git a/cezen-portal/index.html b/cezen-portal/index.html
index f81c5ed..cabe9a8 100644
--- a/cezen-portal/index.html
+++ b/cezen-portal/index.html
@@ -5,7 +5,6 @@
@@ -163,27 +162,27 @@
Server Specifications
Hostname
- ai-server
+ Detecting…
IP Address
- 192.168.1.100
+ Detecting…
GPU
- NVIDIA RTX Pro 6000 (96 GB VRAM)
+ Detecting…
System RAM
- 128 GB DDR5
+ Detecting…
Storage
- 4 TB NVMe SSD
+ Detecting…
OS
- Ubuntu 22.04 LTS
+ Detecting…
Tier
@@ -743,14 +742,11 @@ const set = (id, val) => { const el = document.getElementById(id); if (el) el.te
// Realistic mock metrics for demo / offline use
// Slight random drift so numbers feel live
function mockMetrics() {
- const base = { cpu: 23, gpu: 67, gtemp: 72, ram: 41, disk: 28 };
- const drift = v => Math.max(5, Math.min(95, v + Math.round((Math.random() - 0.5) * 6)));
return {
- cpu_pct: drift(base.cpu), gpu_pct: drift(base.gpu),
- gpu_temp: drift(base.gtemp), ram_pct: drift(base.ram),
- ram_used_gb: 52, ram_total_gb: 128,
- disk_pct: base.disk, disk_used_gb: 1.1, disk_total_gb: 4.0,
- uptime: '14 days, 6 hrs'
+ cpu_pct: null, gpu_pct: null, gpu_temp: null, ram_pct: null,
+ ram_used_gb: null, ram_total_gb: null,
+ disk_pct: null, disk_used_gb: null, disk_total_gb: null,
+ uptime: 'Unavailable'
};
}
@@ -774,30 +770,49 @@ function applyMetrics(d) {
async function fetchMetrics() {
try {
- const d = await fetch('/api/metrics', { credentials: 'include' }).then(r => r.json());
+ const d = await fetch('/api/metrics', { credentials: 'include' }).then(r => {
+ if (!r.ok) throw new Error('metrics unavailable');
+ return r.json();
+ });
applyMetrics(d);
const notice = document.getElementById('metrics-notice');
if (notice) notice.style.display = 'none';
} catch {
- // Backend offline — show realistic mock so dashboard looks live
applyMetrics(mockMetrics());
const notice = document.getElementById('metrics-notice');
- if (notice) notice.style.display = 'none'; // hide warning in demo mode
+ if (notice) notice.style.display = 'flex';
}
}
async function fetchSystemInfo() {
try {
const [info, metrics] = await Promise.all([
- fetch('/api/system/info', { credentials: 'include' }).then(r => r.json()),
- fetch('/api/metrics', { credentials: 'include' }).then(r => r.json()),
+ fetch('/api/system/info', { credentials: 'include' }).then(r => {
+ if (!r.ok) throw new Error('system info unavailable');
+ return r.json();
+ }),
+ fetch('/api/metrics', { credentials: 'include' }).then(r => {
+ if (!r.ok) throw new Error('metrics unavailable');
+ return r.json();
+ }),
]);
set('spec-hostname', info.hostname || '—');
- if (info.gpu_name && info.gpu_name !== 'N/A') set('spec-gpu', info.gpu_name);
- if (metrics.ram_total_gb) set('spec-ram', metrics.ram_total_gb + ' GB DDR5');
- if (metrics.disk_total_gb) set('spec-storage', metrics.disk_total_gb + ' TB NVMe SSD');
+ set('spec-ip', info.ip_address || '—');
+ set('spec-os', info.os || '—');
+ const gpuName = info.gpu_name && info.gpu_name !== 'N/A' ? info.gpu_name : null;
+ const gpuVram = info.gpu_vram_gb || metrics.gpu_mem_total_gb;
+ set('spec-gpu', gpuName ? gpuName + (gpuVram ? ' (' + gpuVram + ' GB VRAM)' : '') : 'None detected');
+ set('stat-vram', gpuVram ? gpuVram + ' GB' : '—');
+ set('spec-ram', (info.ram_total_gb || metrics.ram_total_gb) ? (info.ram_total_gb || metrics.ram_total_gb) + ' GB' : '—');
+ set('spec-storage', (info.disk_total_gb || metrics.disk_total_gb) ? (info.disk_total_gb || metrics.disk_total_gb) + ' GB' : '—');
} catch {
- // API offline — keep the crisp hardcoded defaults (already accurate for Entry tier)
+ set('spec-hostname', 'Unavailable');
+ set('spec-ip', 'Unavailable');
+ set('spec-gpu', 'Unavailable');
+ set('spec-ram', 'Unavailable');
+ set('spec-storage', 'Unavailable');
+ set('spec-os', 'Unavailable');
+ set('stat-vram', '—');
}
}
@@ -807,7 +822,47 @@ setInterval(fetchMetrics, 30000);
-
+