Files
kwcode/kaiwu/core/sysinfo.py
YonghaoZhao722 d9b51b4b29 feat: add macOS platform support with Apple Silicon GPU detection
- Add macOS GPU detection using sysctl hw.model
- Display "Apple Silicon GPU" for Mac users
- Preserve NVIDIA GPU detection for Windows/Linux
- Add macOS installation guide in README
- Document Apple Silicon unified memory architecture

Fixes platform compatibility issue where Mac users see no GPU info.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 19:21:52 +08:00

120 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
硬件信息采集模块。
- psutil 获取 RAM/CPU跨平台
- nvidia-smi 获取 GPU VRAMgraceful fallback
- VRAMWatcher 后台线程每10秒刷新 VRAM
"""
import platform
import subprocess
import threading
from dataclasses import dataclass
import psutil
@dataclass
class SysInfo:
gpu_name: str = "N/A"
vram_used_gb: float = 0.0
vram_total_gb: float = 0.0
ram_used_gb: float = 0.0
ram_total_gb: float = 0.0
cpu_name: str = "N/A"
def get_sysinfo() -> SysInfo:
"""采集一次完整硬件信息(启动时调用)。"""
info = SysInfo()
# RAMpsutil跨平台
vm = psutil.virtual_memory()
info.ram_total_gb = vm.total / 1024**3
info.ram_used_gb = vm.used / 1024**3
# CPU
try:
info.cpu_name = platform.processor() or "Unknown CPU"
if len(info.cpu_name) > 20:
info.cpu_name = info.cpu_name[:20] + ""
except Exception:
pass
# GPU VRAM平台特定检测
if platform.system() == "Darwin":
# macOS: Apple Silicon 或 AMD GPU
try:
out = subprocess.check_output(
["sysctl", "hw.model"],
timeout=2,
stderr=subprocess.DEVNULL,
).decode(encoding="utf-8").strip()
if "Mac" in out:
info.gpu_name = "Apple Silicon GPU"
# macOS 使用统一内存架构VRAM 信息不适用
except Exception:
pass
else:
# Windows/Linux: NVIDIA GPUnvidia-smi
try:
out = subprocess.check_output(
[
"nvidia-smi",
"--query-gpu=name,memory.used,memory.total",
"--format=csv,noheader,nounits",
],
timeout=2,
stderr=subprocess.DEVNULL,
).decode(encoding="utf-8").strip().split("\n")[0]
parts = [p.strip() for p in out.split(",")]
if len(parts) == 3:
info.gpu_name = parts[0][:20]
info.vram_used_gb = int(parts[1]) / 1024
info.vram_total_gb = int(parts[2]) / 1024
except Exception:
pass # 非NVIDIA或未安装驱动显示 N/A
return info
class VRAMWatcher:
"""
后台守护线程每10秒刷新一次 VRAM 使用量到 status_bar.vram_used。
daemon=True 随主进程退出自动销毁。
"""
INTERVAL = 10 # 秒
def __init__(self, status_bar):
self._status = status_bar
self._stop = threading.Event()
self._thread = threading.Thread(
target=self._run, daemon=True, name="vram-watcher"
)
def start(self):
self._thread.start()
def stop(self):
self._stop.set()
def _run(self):
while not self._stop.wait(timeout=self.INTERVAL):
try:
out = subprocess.check_output(
[
"nvidia-smi",
"--query-gpu=memory.used",
"--format=csv,noheader,nounits",
],
timeout=2,
stderr=subprocess.DEVNULL,
).decode(encoding="utf-8").strip()
# 只取第一行第一个数字防止多GPU或格式变化
first_line = out.split("\n")[0].strip()
val = int("".join(c for c in first_line if c.isdigit()) or "0")
if val > 0:
self._status.vram_used = val / 1024
except Exception:
pass # nvidia-smi 不可用时静默跳过