diff --git a/packages/ai/python/gpu.py b/packages/ai/python/gpu.py index b151eefa..7c2e931d 100644 --- a/packages/ai/python/gpu.py +++ b/packages/ai/python/gpu.py @@ -1,7 +1,7 @@ """Runtime GPU/CUDA detection utility.""" -import ctypes import functools import os +import subprocess import sys @@ -28,31 +28,25 @@ def gpu_available(): except ImportError as e: print(f"[gpu] torch not importable: {e}", file=sys.stderr, flush=True) - # Fallback: check if onnxruntime's CUDA provider can actually load. - # get_available_providers() only reports *compiled-in* backends, not whether - # the required libraries (cuDNN, etc.) are present at runtime. We verify - # by creating a minimal CUDA session — this transitively checks that cuDNN - # and all required libraries are present. + # Fallback: check if onnxruntime-gpu is installed and CUDA EP is available, + # then verify an actual NVIDIA GPU is present via nvidia-smi. try: import onnxruntime as _ort providers = _ort.get_available_providers() if "CUDAExecutionProvider" not in providers: return False - # Smoke-test: create a session with CUDA to verify libraries load - import numpy as _np - _ort.InferenceSession( - _np.zeros(0, dtype=_np.uint8).tobytes(), - providers=["CUDAExecutionProvider"], + # CUDA EP is compiled in — verify hardware is actually present. + # nvidia-smi is the most reliable cross-platform check. + result = subprocess.run( + ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"], + capture_output=True, text=True, timeout=5, ) - # If we get here without error, CUDA provider is functional - # (the empty model will fail, but the provider DLLs loaded) - return True - except Exception as e: - # CUDA provider may report as available but fail to load (missing cuDNN, etc.) - # Any error here means CUDA isn't usable — fall back to CPU - err_str = str(e).lower() - if "cuda" in err_str or "cudnn" in err_str or "provider" in err_str: - print(f"[gpu] ONNX CUDA provider not functional: {e}", file=sys.stderr, flush=True) + if result.returncode == 0 and result.stdout.strip(): + print(f"[gpu] CUDA available via ONNX Runtime + nvidia-smi: {result.stdout.strip()}", + file=sys.stderr, flush=True) + return True + return False + except (ImportError, FileNotFoundError, subprocess.TimeoutExpired): return False diff --git a/packages/ai/python/install_feature.py b/packages/ai/python/install_feature.py index b385ecb6..479a7b9b 100644 --- a/packages/ai/python/install_feature.py +++ b/packages/ai/python/install_feature.py @@ -90,8 +90,11 @@ def cpu_fallback_packages(packages: list[str]) -> list[str]: name = pkg.split("==")[0].split(">=")[0].split("[")[0].strip() if name in replacements: - version = pkg[len(name):] # e.g. "==1.20.1" - result.append(replacements[name] + version) + # Extract only the version spec, drop any inline flags + # (e.g. "--extra-index-url https://...cu126/" is GPU-specific) + tokens = pkg.split() + version_token = tokens[0][len(name):] # e.g. ">=3.2.1" + result.append(replacements[name] + version_token) else: result.append(pkg) return result diff --git a/packages/ai/python/restore.py b/packages/ai/python/restore.py index e8aa0e47..2c9c75b7 100644 --- a/packages/ai/python/restore.py +++ b/packages/ai/python/restore.py @@ -331,7 +331,7 @@ def enhance_faces(img_bgr, fidelity=0.7): result = img_bgr.copy() faces_enhanced = 0 - for face_box in face_boxes: + for i, face_box in enumerate(face_boxes): x = face_box["x"] y = face_box["y"] w = face_box["w"]