Last active
June 8, 2026 07:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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