"""Hardware collection. No hostname, no username, no paths that doxx the box.""" from __future__ import annotations import json import platform import shutil import subprocess from pathlib import Path from schema.models import HardwareReadout, Stack def _run(cmd: list[str]) -> str | None: try: r = subprocess.run(cmd, capture_output=True, text=True, timeout=8) except (OSError, subprocess.TimeoutExpired): return None if r.returncode != 0: return None return r.stdout.strip() or None def _mem_mib() -> int | None: try: data = Path("/proc/meminfo").read_text() except OSError: return None for line in data.splitlines(): if line.startswith("MemTotal:"): kb = int(line.split()[1]) return kb // 1024 return None def _cpu() -> str | None: try: for line in Path("/proc/cpuinfo").read_text().splitlines(): if line.lower().startswith("model name"): return line.split(":", 1)[1].strip() except OSError: return None return platform.processor() or None def _os_name() -> str | None: # Distro / kernel family only. No hostname, no user. try: for line in Path("/etc/os-release").read_text().splitlines(): if line.startswith("PRETTY_NAME="): return line.split("=", 1)[1].strip().strip('"') except OSError: pass sysname = platform.system() return sysname or None def _nvlink_up() -> bool: """Family only. Do not parse PCI addresses or MACs.""" raw = _run(["nvidia-smi", "nvlink", "--status"]) if not raw: return False s = raw.lower() return "gb/s" in s or "active" in s def _cuda_interconnect(n: int) -> str | None: if n < 2: return None return "nvlink" if _nvlink_up() else "pcie" def collect_linux_nvidia() -> HardwareReadout | None: if not shutil.which("nvidia-smi"): return None raw = _run( [ "nvidia-smi", "--query-gpu=name,memory.total,driver_version", "--format=csv,noheader,nounits", ] ) if not raw: return None lines = [ln.strip() for ln in raw.splitlines() if ln.strip()] if not lines: return None parsed = [] for line in lines: parts = [p.strip() for p in line.split(",")] if len(parts) < 3: continue name, mem, driver = parts[0], parts[1], parts[2] try: mem_mib = int(float(mem)) except ValueError: mem_mib = None parsed.append((name, mem_mib, driver)) if not parsed: return None names = [p[0] for p in parsed] n = len(parsed) if n > 1 and len(set(names)) > 1: name = "mixed: " + "+".join(sorted(set(names))) elif n > 1: name = f"{n}× {names[0]}" else: name = names[0] mems = [p[1] for p in parsed if p[1] is not None] mem_mib = sum(mems) if mems else None driver = parsed[0][2] return HardwareReadout( stack=Stack.cuda, gpu_or_soc=name, vram_or_unified_mib=mem_mib, driver=driver, cpu=_cpu(), system_ram_mib=_mem_mib(), os_name=_os_name(), interconnect=_cuda_interconnect(n), ) def collect() -> HardwareReadout: nvidia = collect_linux_nvidia() if nvidia: return nvidia # Apple / other: honest unknown rather than inventing a Studio. return HardwareReadout( stack=Stack.unknown, gpu_or_soc=platform.machine() or "unknown", cpu=_cpu(), system_ram_mib=_mem_mib(), os_name=_os_name(), notes="No NVIDIA readout. On macOS run this CLI there for MLX/unified stats.", ) def to_pretty(readout: HardwareReadout) -> str: return json.dumps(readout.model_dump(mode="json"), indent=2)