File size: 3,344 Bytes
3f2dde4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from __future__ import annotations

from dataclasses import asdict, dataclass
from pathlib import Path
import os
import platform
import shutil


@dataclass(slots=True)
class HardwareSpecs:
    hostname: str
    platform: str
    cpu_model: str
    physical_cores: int
    logical_cores: int
    memory_total_gb: float
    memory_available_gb: float
    disk_total_gb: float
    disk_free_gb: float
    python_version: str
    cuda_available: bool
    cuda_device: str

    def to_dict(self) -> dict[str, object]:
        return asdict(self)


def collect_hardware_specs() -> HardwareSpecs:
    try:
        import psutil
    except Exception:  # pragma: no cover - psutil is expected to be present in the venv
        psutil = None

    hostname = platform.node() or os.environ.get("COMPUTERNAME", "unknown")
    platform_name = f"{platform.system()} {platform.release()}"
    cpu_model = platform.processor() or platform.machine() or "unknown"

    physical_cores = psutil.cpu_count(logical=False) if psutil else 0
    logical_cores = psutil.cpu_count(logical=True) if psutil else os.cpu_count() or 0

    memory_total_gb = 0.0
    memory_available_gb = 0.0
    disk_total_gb = 0.0
    disk_free_gb = 0.0
    if psutil:
        memory = psutil.virtual_memory()
        memory_total_gb = memory.total / (1024**3)
        memory_available_gb = memory.available / (1024**3)
        disk = psutil.disk_usage(str(Path.home().anchor or Path.cwd().anchor or Path.cwd()))
        disk_total_gb = disk.total / (1024**3)
        disk_free_gb = disk.free / (1024**3)

    cuda_available = False
    cuda_device = "cpu"
    try:
        import torch

        cuda_available = bool(torch.cuda.is_available())
        if cuda_available:
            cuda_device = torch.cuda.get_device_name(0)
    except Exception:
        pass

    return HardwareSpecs(
        hostname=hostname,
        platform=platform_name,
        cpu_model=cpu_model,
        physical_cores=int(physical_cores or 0),
        logical_cores=int(logical_cores or 0),
        memory_total_gb=memory_total_gb,
        memory_available_gb=memory_available_gb,
        disk_total_gb=disk_total_gb,
        disk_free_gb=disk_free_gb,
        python_version=platform.python_version(),
        cuda_available=cuda_available,
        cuda_device=cuda_device,
    )


def hardware_table_rows(specs: HardwareSpecs) -> list[dict[str, str]]:
    return [
        {"Metric": "Hostname", "Value": specs.hostname},
        {"Metric": "Platform", "Value": specs.platform},
        {"Metric": "CPU", "Value": specs.cpu_model},
        {"Metric": "Physical Cores", "Value": str(specs.physical_cores)},
        {"Metric": "Logical Cores", "Value": str(specs.logical_cores)},
        {"Metric": "Memory Total (GB)", "Value": f"{specs.memory_total_gb:.2f}"},
        {"Metric": "Memory Available (GB)", "Value": f"{specs.memory_available_gb:.2f}"},
        {"Metric": "Disk Total (GB)", "Value": f"{specs.disk_total_gb:.2f}"},
        {"Metric": "Disk Free (GB)", "Value": f"{specs.disk_free_gb:.2f}"},
        {"Metric": "Python", "Value": specs.python_version},
        {"Metric": "CUDA", "Value": "yes" if specs.cuda_available else "no"},
        {"Metric": "CUDA Device", "Value": specs.cuda_device},
    ]