Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

85 lines
2.8 KiB
Python

"""Hardware detection for local LLM model recommendation."""
from __future__ import annotations
import shutil
import subprocess
import sys
from dataclasses import dataclass
import platform
# Conservative fallback when RAM detection fails — leads recommend_model to pick the lightweight 3B model
_FALLBACK_RAM_GB = 8.0
@dataclass(frozen=True)
class HardwareProfile:
total_ram_gb: float
available_ram_gb: float
arch: str
is_apple_silicon: bool
has_nvidia_gpu: bool
def detect_hardware() -> HardwareProfile:
total = _get_total_ram_gb()
available = _get_available_ram_gb(total)
arch = platform.machine()
is_apple_silicon = sys.platform == "darwin" and arch == "arm64"
has_nvidia = shutil.which("nvidia-smi") is not None
return HardwareProfile(
total_ram_gb=total,
available_ram_gb=available,
arch=arch,
is_apple_silicon=is_apple_silicon,
has_nvidia_gpu=has_nvidia,
)
def _get_total_ram_gb() -> float:
try:
if sys.platform == "darwin":
out = subprocess.check_output(
["sysctl", "-n", "hw.memsize"], text=True, encoding="utf-8", errors="replace"
)
return int(out.strip()) / (1024**3)
elif sys.platform == "linux":
with open("/proc/meminfo") as f:
for line in f:
if line.startswith("MemTotal:"):
return int(line.split()[1]) / (1024**2)
except Exception:
return _FALLBACK_RAM_GB
return _FALLBACK_RAM_GB
def _get_available_ram_gb(total_ram_gb: float) -> float:
try:
if sys.platform == "darwin":
out = subprocess.check_output(
["sysctl", "-n", "hw.usermem"], text=True, encoding="utf-8", errors="replace"
)
return int(out.strip()) / (1024**3)
elif sys.platform == "linux":
with open("/proc/meminfo") as f:
for line in f:
if line.startswith("MemAvailable:"):
return int(line.split()[1]) / (1024**2)
except Exception:
return total_ram_gb * 0.5
return total_ram_gb * 0.5
def recommend_model(hw: HardwareProfile) -> tuple[str, str]:
"""Return (model_name, human_reason). Conservative — caps usable RAM at 50% of total."""
safe_ram = min(hw.available_ram_gb, hw.total_ram_gb * 0.5)
if hw.is_apple_silicon and hw.total_ram_gb >= 16 and safe_ram >= 6:
return (
"llama3.1:8b",
f"{hw.total_ram_gb:.0f}GB Apple Silicon ({safe_ram:.0f}GB free) — unified memory handles 8B well",
)
if hw.has_nvidia_gpu or safe_ram >= 12:
return "llama3.1:8b", f"{safe_ram:.0f}GB safely available — 8B model fits comfortably"
return "llama3.2", f"{safe_ram:.0f}GB safely available — lightweight 3B for smooth performance"