Skip to content

Instantly share code, notes, and snippets.

@kipavy
Last active June 8, 2026 07:48
Show Gist options
  • Select an option

  • Save kipavy/2c5acabbff81a410b340464a667a12c4 to your computer and use it in GitHub Desktop.

Select an option

Save kipavy/2c5acabbff81a410b340464a667a12c4 to your computer and use it in GitHub Desktop.
NEWER VERSION AVAILABLE AT:https://gitlab.com/yoanncure/gpu_installer ! GPU setup helper for Python ML projects. Detects CUDA version and installs matching GPU wheels for PyTorch and llama-cpp-python, with post-install verification.
#!/usr/bin/env python3
"""
NEWER VERSION AVAILABLE AT:https://gitlab.com/yoanncure/gpu_installer
=============================================================================
ML Package Installation Assistant
An intelligent installer that automatically detects your system, GPU, and CUDA
configuration to install the optimal ML package setup for your hardware.
mix from https://gist.github.com/kipavy/2c5acabbff81a410b340464a667a12c4/93a56a0cc6bcca4868bcb7724e0f7f3331b32f02 + https://pypi.org/project/torch-installer-coff33ninja/
"""
import os
import shutil
import subprocess
import sys
import re
import platform
import urllib.request
import argparse
import datetime
__version__ = "3.0.0"
# =============================================================================
# Logging & Utilities
# =============================================================================
def setup_logging(enable_logging):
if enable_logging:
logfile = f"ml_install_log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
print(f"๐Ÿ’พ Logging install to {logfile}")
class TeeOutput:
def __init__(self, file):
self.terminal = sys.stdout
self.log = file
def write(self, message):
self.terminal.write(message)
self.log.write(message)
self.log.flush()
def flush(self):
self.terminal.flush()
self.log.flush()
log_file = open(logfile, "w", encoding="utf-8")
sys.stdout = TeeOutput(log_file)
return log_file
return None
def run_cmd(cmd, capture=False):
try:
if capture:
return subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL).decode().strip()
else:
subprocess.check_call(cmd, shell=True)
return True
except subprocess.CalledProcessError:
return None
# =============================================================================
# System & Hardware Detectors
# =============================================================================
def get_system_info():
info = {
"system": platform.system(),
"machine": platform.machine(),
"architecture": platform.architecture()[0],
"processor": platform.processor(),
"python_version": platform.python_version(),
}
return info
def detect_gpu_info():
gpu_info = {"model": None, "cuda_version": None, "memory": None}
if shutil.which("nvidia-smi"):
output = run_cmd("nvidia-smi", capture=True)
if output:
gpu_match = re.search(r"\|\s+\d+\s+(GeForce|Quadro|Tesla|RTX|GTX|GT)\s+([A-Za-z0-9\s]+?)\s+(?:WDDM|TCC)", output)
if not gpu_match:
gpu_match = re.search(r"(GeForce|Quadro|Tesla|RTX|GTX|GT)\s+([A-Za-z0-9\s]+)", output.split("\n")[7] if len(output.split("\n")) > 7 else "")
if gpu_match:
gpu_info["model"] = f"{gpu_match.group(1)} {gpu_match.group(2).strip()}"
mem_match = re.search(r"(\d+)MiB\s*/\s*(\d+)MiB", output)
if mem_match:
gpu_info["memory"] = f"{mem_match.group(2)}MB"
return gpu_info
def detect_cuda_version(system_info):
if system_info["system"] == "Darwin":
return "mps"
version = None
if shutil.which("nvcc"):
out = run_cmd("nvcc --version", capture=True)
if out:
match = re.search(r"release (\d+\.\d+)", out)
if match: version = match.group(1)
if not version and shutil.which("nvidia-smi"):
out = run_cmd("nvidia-smi", capture=True)
if out:
match = re.search(r"CUDA Version:\s+(\d+\.\d+)", out)
if match: version = match.group(1)
if not version and os.environ.get("CUDA_PATH"):
match = re.search(r"v(\d+\.\d+)", os.environ.get("CUDA_PATH", ""))
if match: version = match.group(1)
return version.replace(".", "") if version else None
# =============================================================================
# Advanced Compatibility Logic
# =============================================================================
def get_pytorch_versions_for_cuda(cuda_version):
"""Fallback matrix for older CUDA versions no longer supported in latest PyTorch"""
cuda_pytorch_compatibility = {
"111": [],
"113": [],
"116": [("2.1.2", "0.16.2", "2.1.2")],
"117": [("2.2.2", "0.17.2", "2.2.2"), ("2.1.2", "0.16.2", "2.1.2")],
"118": [("2.5.1", "0.20.1", "2.5.1"), ("2.4.1", "0.19.1", "2.4.1")],
"121": [("2.5.1", "0.20.1", "2.5.1"), ("2.4.1", "0.19.1", "2.4.1")],
}
return cuda_pytorch_compatibility.get(cuda_version, [])
def find_best_pytorch_version(detected_cuda):
if not detected_cuda: return None, None, None
compat = get_pytorch_versions_for_cuda(detected_cuda)
if compat:
torch_ver, vision_ver, audio_ver = compat[0]
return torch_ver, vision_ver, audio_ver
return None, None, None
def get_supported_cuda_versions():
fallback = ["130", "128", "126", "124", "121", "118"]
try:
url = "https://download.pytorch.org/whl/"
with urllib.request.urlopen(url, timeout=5) as resp:
content = resp.read().decode("utf-8")
matches = re.findall(r"cu(\d{3})", content)
if matches:
return sorted(list(set(matches)), reverse=True)[:6]
except Exception:
pass
return fallback
def find_best_cuda_match(detected_version, supported_versions):
if not detected_version or detected_version == "mps":
return None
det_int = int(detected_version)
sup_ints = sorted([int(v) for v in supported_versions], reverse=True)
for v in sup_ints:
if v == det_int: return str(v)
for v in sup_ints:
if v <= det_int: return str(v)
return str(sup_ints[-1]) if sup_ints else None
# =============================================================================
# Install Execution Engine
# =============================================================================
def run_install(install_args, dry_run=False):
uv_path = shutil.which("uv")
if uv_path:
cmd = [uv_path, "pip", "install", "--python", sys.executable] + install_args
print("โšก Using `uv` for blazing fast installation...")
else:
cmd = [sys.executable, "-m", "pip", "install"] + install_args
print("๐Ÿ `uv` not found, falling back to standard `pip`...")
if dry_run:
print(f"๐Ÿ’ก Dry run: {' '.join(cmd)}")
return True
try:
subprocess.check_call(cmd)
return True
except subprocess.CalledProcessError as e:
print(f"โŒ Installation failed: {e}")
return False
# =============================================================================
# Package Specific Installers
# =============================================================================
def install_pytorch(cuda_version, is_cpu=False, force_reinstall=False, dry_run=False):
print("\n--- ๐Ÿ“ฆ Installing PyTorch Ecosystem ---")
args = ["--upgrade"]
if force_reinstall:
args.extend(["--force-reinstall", "--no-deps"])
if is_cpu or not cuda_version:
print("โš™๏ธ Target: CPU-only")
args.extend(["torch", "torchvision", "torchaudio"])
elif cuda_version == "mps":
print("๐ŸŽ Target: Apple Silicon (MPS / Metal)")
args.extend(["torch", "torchvision", "torchaudio"])
else:
supported = get_supported_cuda_versions()
match = find_best_cuda_match(cuda_version, supported)
# If the detected CUDA is too old for current wheels, fallback to specific older PyTorch versions
if match and int(match) < 118:
torch_ver, vision_ver, audio_ver = find_best_pytorch_version(match)
if torch_ver:
print(f"๐Ÿ”„ Older CUDA detected (cu{match}). Falling back to compatible PyTorch {torch_ver}...")
args.extend([
f"torch=={torch_ver}+cu{match}",
f"torchvision=={vision_ver}+cu{match}",
f"torchaudio=={audio_ver}",
f"--extra-index-url=https://download.pytorch.org/whl/cu{match}"
])
else:
print(f"โŒ No compatible PyTorch version found for CUDA {match}. Falling to CPU.")
args.extend(["torch", "torchvision", "torchaudio"])
else:
print(f"๐ŸŽฏ Target: Latest PyTorch matching cu{match}")
args.extend([
"torch", "torchvision", "torchaudio",
f"--extra-index-url=https://download.pytorch.org/whl/cu{match}"
])
run_install(args, dry_run)
def install_cupy(cuda_version, is_cpu=False, force_reinstall=False, dry_run=False):
print("\n--- ๐ŸงŠ Installing CuPy ---")
if is_cpu or not cuda_version or cuda_version == "mps":
print("โš ๏ธ CuPy requires NVIDIA/AMD GPUs. Skipping.")
return
major = cuda_version[:2]
pkg = f"cupy-cuda{major}x"
args = [pkg, "--upgrade"]
if force_reinstall: args.append("--force-reinstall")
run_install(args, dry_run)
def install_llamacpp(cuda_version, is_cpu=False, force_reinstall=False, dry_run=False):
print("\n--- ๐Ÿฆ™ Installing Llama-CPP-Python ---")
args = ["llama-cpp-python", "--upgrade"]
if force_reinstall: args.append("--force-reinstall")
if is_cpu or not cuda_version:
print("โš™๏ธ Target: CPU-only build")
elif cuda_version == "mps":
print("๐ŸŽ Target: Apple Silicon (Native Metal via Pip)")
else:
llamacpp_milestones = ["130", "125", "124", "123", "122", "121"]
match = find_best_cuda_match(cuda_version, llamacpp_milestones)
index_url = f"https://abetlen.github.io/llama-cpp-python/whl/cu{match}"
print(f"๐ŸŽฏ Target: GPU Offload Wheel (cu{match})")
args.extend(["--extra-index-url", index_url])
run_install(args, dry_run)
# =============================================================================
# Verifiers & Info displays
# =============================================================================
def verify_pytorch():
try:
import torch
print("\n๐Ÿ“Š Installed PyTorch Ecosystem:")
print(f" ๐Ÿ”ฅ PyTorch: {torch.__version__}")
try:
import torchvision
print(f" ๐Ÿ‘๏ธ TorchVision: {torchvision.__version__}")
except ImportError:
print(" ๐Ÿ‘๏ธ TorchVision: Not installed")
try:
import torchaudio
print(f" ๐Ÿ”Š TorchAudio: {torchaudio.__version__}")
except ImportError:
print(" ๐Ÿ”Š TorchAudio: Not installed")
print(f" ๐ŸŽฏ CUDA Support: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f" ๐ŸŽฎ GPU Count: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):
print(f" ๐ŸŽฎ GPU {i}: {torch.cuda.get_device_name(i)}")
# Tensor Test
x = torch.rand(2, 2).cuda()
res = x * 2
print(" ๐Ÿงช CUDA Tensor test passed.")
print(f" Sample result: {res[0, :2].cpu().numpy()}")
return True
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
print(" ๐ŸŽ MPS Support: Available")
x = torch.rand(2, 2).to("mps")
res = x * 2
print(" ๐Ÿงช MPS Tensor test passed.")
return True
return True
except ImportError:
print("โŒ PyTorch: Not installed or broken.")
return False
def verify_cupy():
try:
import cupy
count = cupy.cuda.runtime.getDeviceCount()
print(f"\nโœ… CuPy ({cupy.__version__}): Installed | CUDA Devices: {count}")
return count > 0
except ImportError:
return False
except Exception as e:
print(f"โŒ CuPy verification failed: {e}")
return False
def verify_llamacpp():
try:
from llama_cpp import llama_cpp as lib
support = bool(lib.llama_supports_gpu_offload())
if support:
print("\nโœ… Llama-CPP: GPU offload successfully activated (CUDA/Metal).")
else:
print("\nโš™๏ธ Llama-CPP: Installed (CPU-only build).")
return True
except ImportError:
return False
# =============================================================================
# GPU Upgrade & Auto-Install Guidance
# =============================================================================
def get_gpu_upgrade_guidance(gpu_info, detected_cuda, auto_install=False):
if not gpu_info["model"]:
return ["๐Ÿ’ก GPU ACCELERATION UPGRADE GUIDE:", " 1. Ensure NVIDIA drivers are installed."]
gpu_model = gpu_info["model"]
guidance = [f"๐Ÿ’ก GPU ACCELERATION UPGRADE GUIDE ({gpu_model}):"]
# Kepler Check
if any(k in gpu_model.upper() for k in ["GT 610", "GT 620", "GT 630", "GT 710", "GT 720", "GT 730"]):
guidance.extend([
f" โš ๏ธ Your {gpu_model} is a Kepler-generation GPU.",
" ๐Ÿ’ก Recommended: CUDA 11.3 for maximum compatibility.",
" ๐Ÿ”ง Note: CUDA 11.8+ may cause driver conflicts (exit code 46)."
])
elif any(m in gpu_model.upper() for m in ["RTX", "GTX 16", "GTX 20", "GTX 30", "GTX 40"]):
guidance.extend([
f" ๐Ÿš€ Your {gpu_model} supports modern CUDA versions.",
" โœจ Recommended: CUDA 12.1 or 12.4 for best performance."
])
else:
guidance.extend([f" ๐Ÿ’ก Recommended: CUDA 11.8 or 12.1 for compatibility."])
return guidance
def install_cuda_windows(target_version, gpu_info, dry_run=False):
if platform.system() != "Windows":
return False, "CUDA auto-install only supported on Windows"
has_winget = shutil.which("winget") is not None
has_choco = shutil.which("choco") is not None
gpu_model = gpu_info.get("model", "Unknown GPU")
print(f"๐Ÿ”ง Attempting to install CUDA {target_version or 'Recommended'} for {gpu_model}")
if not target_version:
if any(k in gpu_model.upper() for k in ["GT 610", "GT 620", "GT 630", "GT 710", "GT 720", "GT 730"]):
target_version = "11.3"
else:
target_version = "12.1"
if has_winget:
print(f"๐Ÿ“ฆ Trying winget: CUDA {target_version}...")
cmd = f"winget install NVIDIA.CUDA --version {target_version}"
if dry_run: return True, f"Would run: {cmd}"
if run_cmd(cmd): return True, f"Successfully installed CUDA {target_version} via winget"
if has_choco:
print(f"๐Ÿซ Trying chocolatey: CUDA {target_version}...")
cmd = f"choco install cuda --version={target_version} -y"
if dry_run: return True, f"Would run: {cmd}"
if run_cmd(cmd): return True, f"Successfully installed CUDA {target_version} via chocolatey"
return False, "Failed to auto-install CUDA. Please install manually."
# =============================================================================
# CLI Main
# =============================================================================
PACKAGE_REGISTRY = {
"torch": (install_pytorch, verify_pytorch),
"cupy": (install_cupy, verify_cupy),
"llamacpp": (install_llamacpp, verify_llamacpp)
}
def main():
parser = argparse.ArgumentParser(description="๐Ÿš€ Ultimate ML Setup Assistant")
parser.add_argument("--packages", type=str, default="torch,cupy,llamacpp", help="Comma-separated packages")
parser.add_argument("--force-cuda", metavar="VER", help="Force specific CUDA version (e.g. 121)")
parser.add_argument("--cpu-only", action="store_true", help="Force CPU-only installation")
parser.add_argument("--force-reinstall", action="store_true", help="Force reinstall packages")
parser.add_argument("--dry-run", action="store_true", help="Print commands without executing")
# Restored CLI arguments
parser.add_argument("--list-cuda", action="store_true", help="List supported CUDA versions and exit")
parser.add_argument("--show-matching", action="store_true", help="Show CUDA matching logic and exit")
parser.add_argument("--gpu-info", action="store_true", help="Show GPU info and Upgrade Guidance")
parser.add_argument("--auto-install-cuda", action="store_true", help="Auto-install CUDA (Windows only)")
parser.add_argument("--cuda-version", metavar="VER", help="Specific version for auto-install (e.g. 11.8)")
parser.add_argument("--show-versions", action="store_true", help="Show currently installed ecosystem")
parser.add_argument("--log", action="store_true", help="Log output to file")
args = parser.parse_args()
log_file = setup_logging(args.log)
print(f"โœจ ML Package Installation Assistant v{__version__} โœจ")
print("=" * 55)
sys_info = get_system_info()
detected_cuda = detect_cuda_version(sys_info)
# Restored: --list-cuda
if args.list_cuda:
print("๐Ÿ“‹ Supported PyTorch CUDA versions:")
for v in get_supported_cuda_versions(): print(f" โ€ข cu{v}")
return
# Restored: --show-matching
if args.show_matching:
print(f"๐Ÿ” Detected CUDA: {detected_cuda}")
supported = get_supported_cuda_versions()
match = find_best_cuda_match(detected_cuda, supported)
if match: print(f"โœ… Best Wheel Match: cu{match}")
else: print("โŒ No direct match found.")
return
# Restored: --gpu-info
gpu_info = detect_gpu_info()
if args.gpu_info:
print(f"๐Ÿ–ฅ๏ธ System: {sys_info['system']} {sys_info['architecture']}")
print(f"๐ŸŽฎ GPU Detected: {gpu_info['model'] or 'None'} ({gpu_info.get('memory') or 'N/A'})")
print(f"โš™๏ธ CUDA Detected: {detected_cuda}")
for line in get_gpu_upgrade_guidance(gpu_info, detected_cuda):
print(line)
return
# Restored: --auto-install-cuda
if args.auto_install_cuda:
success, msg = install_cuda_windows(args.cuda_version, gpu_info, dry_run=args.dry_run)
print(f"{'โœ…' if success else 'โŒ'} {msg}")
return
# Restored: --show-versions
if args.show_versions:
verify_pytorch()
verify_cupy()
verify_llamacpp()
return
# Main Install Path
if gpu_info["model"]:
print(f"๐ŸŽฎ GPU Detected: {gpu_info['model']} ({gpu_info.get('memory', 'Unknown VRAM')})")
for line in get_gpu_upgrade_guidance(gpu_info, detected_cuda):
if "Recommended" in line or "โš ๏ธ" in line: print(line)
target_cuda = None
if not args.cpu_only:
target_cuda = args.force_cuda.replace("cu", "").replace(".", "") if args.force_cuda else detected_cuda
packages = [p.strip().lower() for p in args.packages.split(",") if p.strip().lower() in PACKAGE_REGISTRY]
for pkg in packages:
installer, verifier = PACKAGE_REGISTRY[pkg]
installer(target_cuda, args.cpu_only, args.force_reinstall, args.dry_run)
if not args.dry_run:
print("\n" + "=" * 55)
print("๐Ÿ”ฌ Post-Installation Verification Report")
print("=" * 55)
for pkg in packages:
_, verifier = PACKAGE_REGISTRY[pkg]
verifier()
if log_file: log_file.close()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment