fix(desktop): NVIDIA build silently falling back to CPU (#247) (#267)

* fix(desktop): NVIDIA build silently falling back to CPU (#247)

Three independent defects each land the NVIDIA build on CPU with no visible
error and no recovery path:

1. The cpu-only marker was trusted in the shared per-user data dir, not just
   the app root. The CPU build wrote/migrated that marker there, so anyone who
   ever ran the CPU build got the NVIDIA build permanently pinned to CPU --
   GPU detection never even ran. is_cpu_only_package now checks the app root
   only; a stale data-dir marker is auto-deleted and logged.

2. A CPU result from a transient failure (no GPU detected, CUDA verify
   failed) was persisted the same as a real CPU-only package, and the setup
   gate treated any truthy torchDevice as "done" -- one bad first run pinned
   CPU forever. Device selection now persists a reason (torchDeviceReason),
   and the setup gate only treats cuda/mps or a genuine cpu-only package as
   settled; a failure-born CPU or a legacy install with no reason re-probes
   the GPU on the next launch. Existing affected installs self-heal on
   relaunch, no user action needed.

3. nvidia-smi discovery only checked System32 and PATH; some DCH driver
   installs place it only under DriverStore\FileRepository\nv*\. Added that
   scan (newest package wins) and raised the first probe's timeout to 30s for
   Optimus laptops waking a sleeping dGPU. Every detection decision is now
   logged to setup.log.

Also drops the Windows CPU-only portable package's data\cpu-only staging
(scripts/windows/make-portable.ps1), which was the source of the poisoned
marker.

5 new Rust unit tests cover marker precedence, the self-heal + log line, CPU
builds not churning their own marker, and the DriverStore newest-wins scan.

* feat(settings): compute device selector for the self-hosted server

Companion to the desktop #247 fix, for the server/Docker/Unraid path: device
selection was a frozen constant (DEMUCS_DEVICE, computed once at import), so
the only override was the STEMDECK_DEMUCS_DEVICE env var plus a restart --
invisible to Docker/Unraid users without container access.

- app/core/settings.py: demucs_device setting (auto | cuda | mps | cpu,
  default auto = hardware probe). Forcing cuda/mps verifies availability
  BEFORE persisting and rejects with a clear error otherwise -- never persist
  a device that would silently fall back later (the #247 lesson applied
  here). STEMDECK_DEMUCS_DEVICE seeds the default so existing env-based
  deployments keep their forced device.
- app/core/config.py: _detect_device -> detect_torch_device (pure hardware
  probe; env handling moved to the settings seed); DEMUCS_DEVICE constant
  removed.
- app/pipeline/separate.py: reads the device fresh per job -- a Settings
  change applies to the next separation, no restart.
- app/main.py: /api/settings gains demucs_device (choice) and
  demucs_device_resolved (what jobs will run on); POST validates via the
  setter (422 with the reason). Startup log and /api/health read live.
- static/js/catalog.js: "Compute device" select in Settings -> Advanced,
  showing the resolved device; a rejected force surfaces the server's reason
  via showError and reverts the select. Also aligns the port-input fallback
  with the 8000 default from the earlier port unification.
- .docs/improvements/self-hosted-compute-device-setting.md: design doc.

5 new tests: auto-resolution, env seeding, verify-before-persist rejection,
unknown-choice rejection, and the API round trip incl. 422 paths.

* feat(settings): gray out compute devices this machine can't use

The Compute device dropdown now disables options that aren't available or
detected (Auto and CPU are always selectable; CUDA/MPS depend on the
hardware + torch build), labeling them "— not available" so it's clear why.

- config.py: available_torch_devices() returns the usable devices best-first;
  detect_torch_device() is now its first element (no duplicated torch probe).
- settings.py: set_demucs_device verifies against membership in
  available_torch_devices() rather than only the top pick.
- /api/settings: new demucs_devices_available list for the UI.
- catalog.js: disable + relabel unavailable <option>s on load and after each
  change.

* fix(ui): settings scrollbar no longer overlaps right-aligned controls

The Advanced settings pane scrolls, and its scrollbar drew directly over the
right-aligned Port / Compute device controls. Reserve a scrollbar gutter
(padding-right + equal negative margin so it sits in the card's existing 12px
padding), keeping content aligned with the fixed header/footer. Surfaced once
the new Compute device row made the pane tall enough to scroll.
This commit is contained in:
Tha.Les
2026-07-15 20:47:19 +01:00
committed by GitHub
parent abc09e4894
commit c19d67eb79
10 changed files with 497 additions and 51 deletions
+3 -1
View File
@@ -169,8 +169,10 @@ foreach ($Dir in @("cache", "downloads", "ffmpeg", "jobs", "logs", "models")) {
New-Item -ItemType Directory -Force (Join-Path $Stage "data\$Dir") | Out-Null
}
if ($CpuOnly) {
# Root marker only: the app trusts cpu-only solely in the app root (#247).
# A data\cpu-only copy used to leak into the shared per-user data dir and
# silently forced later NVIDIA installs onto CPU.
New-Item -ItemType File -Force (Join-Path $Stage "cpu-only") | Out-Null
New-Item -ItemType File -Force (Join-Path $Stage "data\cpu-only") | Out-Null
}
Copy-Tree (Join-Path $Root "app") (Join-Path $BackendDir "app")