From 655910457f473630c8df23b1d18897f63769195c Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 15 Mar 2026 08:46:09 -0700 Subject: [PATCH] Auto-update CUDA binary on app update: check version on startup, download if stale --- .../ServerSettings/GpuAcceleration.tsx | 9 +++- backend/cuda_download.py | 50 +++++++++++++++++++ backend/main.py | 4 ++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/app/src/components/ServerSettings/GpuAcceleration.tsx b/app/src/components/ServerSettings/GpuAcceleration.tsx index bafcf738..b9cb70b3 100644 --- a/app/src/components/ServerSettings/GpuAcceleration.tsx +++ b/app/src/components/ServerSettings/GpuAcceleration.tsx @@ -246,13 +246,18 @@ export function GpuAcceleration() { {/* CUDA download section - only show when no GPU is active (native or CUDA) */} {!hasNativeGpu && !isCurrentlyCuda && ( <> - {/* Download progress */} + {/* Download progress (manual download or auto-update) */} {cudaDownloading && downloadProgress && (
- {downloadProgress.filename || 'Downloading CUDA backend...'} + + {downloadProgress.filename || + (cudaAvailable + ? 'Updating CUDA backend...' + : 'Downloading CUDA backend...')} +
{downloadProgress.total > 0 && ( diff --git a/backend/cuda_download.py b/backend/cuda_download.py index 1b4a4e60..bafc324d 100644 --- a/backend/cuda_download.py +++ b/backend/cuda_download.py @@ -199,6 +199,56 @@ async def download_cuda_binary(version: Optional[str] = None): raise +def get_cuda_binary_version() -> Optional[str]: + """Get the version of the installed CUDA binary, or None if not installed.""" + import subprocess + cuda_path = get_cuda_binary_path() + if not cuda_path: + return None + try: + result = subprocess.run( + [str(cuda_path), "--version"], + capture_output=True, text=True, timeout=30, + ) + # Output format: "voicebox-server 0.2.0" + for line in result.stdout.strip().splitlines(): + if "voicebox-server" in line: + return line.split()[-1] + except Exception as e: + logger.warning(f"Could not get CUDA binary version: {e}") + return None + + +async def check_and_update_cuda_binary(): + """Check if the CUDA binary is outdated and auto-download if so. + + Called on server startup. If a CUDA binary exists but its version + doesn't match the current app version, triggers a background download + of the updated CUDA binary. The download progress is visible to the + frontend via the existing SSE progress endpoint. + """ + cuda_path = get_cuda_binary_path() + if not cuda_path: + return # No CUDA binary installed, nothing to update + + cuda_version = get_cuda_binary_version() + current_version = __version__ + + if cuda_version == current_version: + logger.info(f"CUDA binary is up to date (v{current_version})") + return + + logger.info( + f"CUDA binary version mismatch: binary=v{cuda_version}, app=v{current_version}. " + f"Auto-downloading updated CUDA backend..." + ) + + try: + await download_cuda_binary() + except Exception as e: + logger.error(f"Auto-update of CUDA binary failed: {e}") + + async def delete_cuda_binary() -> bool: """Delete the downloaded CUDA binary. Returns True if deleted.""" path = get_cuda_binary_path() diff --git a/backend/main.py b/backend/main.py index dfb942be..9d238884 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3104,6 +3104,10 @@ async def startup_event(): print(f"Backend: {backend_type.upper()}") print(f"GPU available: {_get_gpu_status()}") + # Auto-update CUDA binary if installed but outdated + from .cuda_download import check_and_update_cuda_binary + _create_background_task(check_and_update_cuda_binary()) + # Initialize progress manager with main event loop for thread-safe operations try: progress_manager = get_progress_manager()