diff --git a/install.ps1 b/install.ps1 index b967eaf627..424c3d5857 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1285,12 +1285,8 @@ shell.Run cmd, 0, False } # ── AMD ROCm: prefer Python 3.12 (Windows wheels are cp312-only) ── - # Python detection runs before GPU detection, so if an AMD GPU is found and the - # selected Python is not 3.12, try to locate a 3.12 install now. This lets - # users who have both 3.13 and 3.12 installed get ROCm support automatically - # without having to uninstall 3.13. 3.13 remains the default for NVIDIA. - # Fires on $ROCmGpuLabel (WMI-only, no HIP SDK) as well as $HasROCm so that - # users are switched to 3.12 upfront rather than after a second install pass. + # If a non-3.12 Python was selected and an AMD GPU is present, try to find 3.12. + # Fires on $ROCmGpuLabel (WMI/no-HIP-SDK) as well as $HasROCm. if (($HasROCm -or $ROCmGpuLabel) -and $DetectedPython -and ($DetectedPython.Version -split '\.')[0..1] -join '.' -ne "3.12") { $py312 = $null # 1. Try py launcher (official CPython installs) diff --git a/install.sh b/install.sh index f1462088d9..287327b5e5 100755 --- a/install.sh +++ b/install.sh @@ -1477,11 +1477,8 @@ _find_no_torch_runtime() { } # ── AMD ROCm GPU detection helper ── -# Returns 0 (true) if an actual AMD GPU is present, 1 (false) otherwise. -# Checks rocminfo for gfx[1-9][0-9]+ (excludes gfx000 CPU agent), -# amd-smi list for GPU data rows, and falls back to sysfs KFD topology -# which is env-var-independent (works even when HIP_VISIBLE_DEVICES or -# ROCR_VISIBLE_DEVICES hides devices from rocminfo/amd-smi). +# Returns 0 if an AMD GPU is present. Checks rocminfo, amd-smi, then sysfs +# KFD topology (env-var-independent fallback for when HIP/ROCR_VISIBLE_DEVICES hides devices). _has_amd_rocm_gpu() { if command -v rocminfo >/dev/null 2>&1 && \ rocminfo 2>/dev/null | awk '/Name:[[:space:]]*gfx[1-9][0-9]/{found=1} END{exit !found}'; then @@ -1573,10 +1570,7 @@ get_torch_index_url() { case "$_rocm_tag" in rocm[1-5].*) echo "$_base/cpu"; return ;; esac - # Enumerate explicit supported ROCm wheel tags. A host on ROCm - # 6.5+ (no published PyTorch wheels) is clipped to rocm6.4. - # PyTorch publishes: rocm5.7, 6.0, 6.1, 6.2, 6.3, 6.4, 7.0, 7.1, - # 7.2 (5.7 is below our minimum; rocm7.2 ships torch 2.11.0). + # Supported tags; 6.5+ clips to rocm6.4, 7.3+ caps to rocm7.2. case "$_rocm_tag" in rocm6.0|rocm6.0.*|rocm6.1|rocm6.1.*|rocm6.2|rocm6.2.*|rocm6.3|rocm6.3.*|rocm6.4|rocm6.4.*|rocm7.0|rocm7.0.*|rocm7.1|rocm7.1.*|rocm7.2|rocm7.2.*) echo "$_base/$_rocm_tag" ;; diff --git a/studio/backend/core/training/worker.py b/studio/backend/core/training/worker.py index ba60aa802c..9089eeb480 100644 --- a/studio/backend/core/training/worker.py +++ b/studio/backend/core/training/worker.py @@ -1085,12 +1085,9 @@ def run_training_process( ) # ── 1d. Ensure torch.distributed is importable before ML libs load ── - # The Windows ROCm wheel ships without torch._C._distributed_c10d. - # Two failure modes: (a) ImportError on `import torch.distributed`, or - # (b) the import succeeds (lazy load) but the first call by trl/transformers - # crashes. Pre-stubbing before the import covers both. - # Guard with `not in sys.modules` so we never overwrite a real CUDA/NVIDIA - # implementation that was already loaded. + # Windows ROCm wheel lacks torch._C._distributed_c10d. Pre-stub it to handle + # both ImportError and lazy-load crashes from trl/transformers. The + # `not in sys.modules` guard preserves a real NVIDIA implementation. import types as _types _td_stubs = { diff --git a/studio/backend/tests/test_log_filter_no_truncation.py b/studio/backend/tests/test_log_filter_no_truncation.py index d78643f5b9..d9a6e2bc4a 100644 --- a/studio/backend/tests/test_log_filter_no_truncation.py +++ b/studio/backend/tests/test_log_filter_no_truncation.py @@ -2,27 +2,11 @@ # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 """ -Regression tests for studio.backend.loggers.handlers.filter_sensitive_data. +Regression tests for loggers.handlers.filter_sensitive_data. -Context: filter_sensitive_data was originally written with a base64-detection -heuristic that truncated any string >100 chars containing ',' or '/' down to -20 chars + '...'. The block was dormant until PR #5246 wired the processor -into the structlog chain to redact native-path leases. Once active, the -heuristic ate normal log lines emitted by llama_cpp_backend (GGUF size -summary, mmproj selection, the full llama-server command line) and any -exception traceback that happened to contain a file path. - -These tests pin two properties: - -1. Long, comma- or slash-bearing log messages flow through filter_sensitive_data - unchanged. The exact strings exercised match the call sites at - studio/backend/core/inference/llama_cpp.py:2117, :2283, and :2312 that - were truncated in the original bug report. - -2. PR #5246's native-path lease redaction still fires for both the inline - ``native_path_lease=...`` regex form and the ``nativePathLease`` dict-key - form. This guards against future regressions that strip redaction along - with the truncation block. +Pins two properties: (1) long strings with commas/slashes pass through +unchanged (the base64-truncation heuristic from PR #5246 was too aggressive), +and (2) native-path lease redaction still fires for both inline and dict-key forms. """ from loggers.handlers import filter_sensitive_data diff --git a/studio/backend/utils/hardware/hardware.py b/studio/backend/utils/hardware/hardware.py index 3593d9d677..94787d4d95 100644 --- a/studio/backend/utils/hardware/hardware.py +++ b/studio/backend/utils/hardware/hardware.py @@ -1632,23 +1632,15 @@ def apply_gpu_ids(gpu_ids) -> None: # parent process already set a ROCm visibility variable -- that # way a downstream ROCm process inherits the narrowed mask even # before Studio's hardware detection has classified the host. - # As a final fallback, probe torch.version.hip directly so spawned - # training workers on AMD hosts where the user never set HIP_VISIBLE_DEVICES - # still get the correct ROCm visibility mask (mirrors the llama_cpp.py - # approach for llama-server subprocess GPU pinning). + # Final fallback: probe torch.version.hip so AMD workers without + # HIP_VISIBLE_DEVICES still get the correct ROCm visibility mask. _inherits_rocm_visibility = ( "HIP_VISIBLE_DEVICES" in os.environ or "ROCR_VISIBLE_DEVICES" in os.environ ) _is_rocm = IS_ROCM or _inherits_rocm_visibility if not _is_rocm: - # Use ``is not None`` here to match the detect_hardware() check at - # module top -- torch ships HIP version as a non-empty string on - # ROCm builds and None on CUDA builds, so the two forms agree on - # every shipping torch wheel; the ``is not None`` form is the one - # the rest of the codebase reads for "this torch was built with - # HIP". Keep the broad ``except`` as a safety net (we never want - # apply_gpu_ids to crash a worker over a probe failure) but log at - # debug level so the skip is observable when needed. + # torch.version.hip is a non-empty string on ROCm, None on CUDA. + # Broad except: a probe failure must never crash a training worker. try: import torch as _torch diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py index 0eb1a81d40..8af5fe3829 100644 --- a/studio/install_python_stack.py +++ b/studio/install_python_stack.py @@ -51,9 +51,7 @@ _ROCM_TORCH_INDEX: dict[tuple[int, int], str] = { (6, 0): "rocm6.0", } -# Per-tag torch/torchvision/torchaudio version specs for pip. -# rocm7.2 ships torch 2.11.0 which is a major version bump; older tags top out -# at 2.10.x. These specs prevent uv from picking an incompatible minor. +# Per-tag pip specs; rocm7.2 ships torch 2.11.0 (older tags cap at 2.10.x). _ROCM_TORCH_PKG_SPECS: dict[str, tuple[str, str, str]] = { "rocm7.2": ( "torch>=2.11.0,<2.12.0", @@ -71,8 +69,7 @@ _PYTORCH_WHL_BASE = ( os.environ.get("UNSLOTH_PYTORCH_MIRROR") or "https://download.pytorch.org/whl" ).rstrip("/") -# AMD Windows ROCm wheels — repo.radeon.com (cp312 only; AMD does not publish -# Windows ROCm wheels for other Python versions) +# AMD Windows ROCm wheels — repo.radeon.com (cp312 only) _ROCM_WINDOWS_WHEEL_BASE = ( os.environ.get("UNSLOTH_ROCM_WINDOWS_MIRROR") or "https://repo.radeon.com/rocm/windows" @@ -316,9 +313,7 @@ def _detect_amd_gfx_codes() -> list[str]: return list(dict.fromkeys(f"gfx{c}" for c in codes)) # deduplicate, preserve order -# Set to True by _ensure_rocm_torch() when AMD Windows wheels are installed -# successfully. Used by the post-install warning block to skip the "must be -# installed manually" note without spawning a subprocess. +# Set by _ensure_rocm_torch() on success; suppresses the post-install AMD warning. _rocm_windows_torch_installed: bool = False @@ -403,9 +398,7 @@ def _ensure_rocm_torch() -> None: _rocm_windows_torch_installed = True return - # ── Linux x86_64 path ────────────────────────────────────────────────────── - # PyTorch only publishes ROCm wheels for linux_x86_64; skip aarch64 / arm64 - # to avoid a missing-wheel error on `unsloth studio update`. + # ── Linux x86_64 only: PyTorch ROCm wheels are not published for aarch64 ── if platform.machine().lower() not in {"x86_64", "amd64"}: return # NVIDIA takes precedence on mixed hosts -- but only if an actual GPU is usable diff --git a/studio/setup.ps1 b/studio/setup.ps1 index fdffaa30b8..476f2f974b 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -661,9 +661,7 @@ if (-not $HasNvidiaSmi) { } } } -# ── AMD ROCm detection (Windows) ──────────────────────────────────────────── -# Mirror setup.sh: probe hipinfo then amd-smi for an actual GPU, not just -# tool presence. amdhip64.dll alone is NOT treated as GPU evidence. +# ── AMD ROCm detection (Windows): probe hipinfo/amd-smi for actual GPU ── $HasROCm = $false $ROCmGpuLabel = $null if (-not $HasNvidiaSmi) { @@ -1863,9 +1861,7 @@ if ($HasROCm -and $CuTag -eq "cpu") { if ($pyMajMin -eq "3.12" -and $ROCmVersion) { if ($ROCmVersion -match '^7\.2') { $rb = "$amdWheelBase/rocm-rel-7.2.1" - # rocm tarball (14 KB) provides the 'rocm_sdk' Python namespace that - # torch/_rocm_init.py imports at startup. - $ROCmTarballUrl = "$rb/rocm-7.2.1.tar.gz" + $ROCmTarballUrl = "$rb/rocm-7.2.1.tar.gz" # rocm_sdk namespace $ROCmTorchWheelUrls = @( "$rb/rocm_sdk_core-7.2.1-py3-none-win_amd64.whl", "$rb/rocm_sdk_devel-7.2.1-py3-none-win_amd64.whl", @@ -1876,9 +1872,7 @@ if ($HasROCm -and $CuTag -eq "cpu") { ) } elseif ($ROCmVersion -match '^7\.1') { $rb = "$amdWheelBase/rocm-rel-7.1.1" - # rocm tarball (14 KB) provides the 'rocm_sdk' Python namespace that - # torch/_rocm_init.py imports at startup. - $ROCmTarballUrl = "$rb/rocm-0.1.dev0.tar.gz" + $ROCmTarballUrl = "$rb/rocm-0.1.dev0.tar.gz" # rocm_sdk namespace $ROCmTorchWheelUrls = @( "$rb/rocm_sdk_core-0.1.dev0-py3-none-win_amd64.whl", "$rb/rocm_sdk_libraries_custom-0.1.dev0-py3-none-win_amd64.whl", @@ -1911,8 +1905,7 @@ if ($ROCmTorchWheelUrls) { Write-Host $output -ForegroundColor Yellow $ROCmTorchWheelUrls = $null } else { - # Signal to install_python_stack.py that AMD wheels are already installed - # so it skips the subprocess probe and suppresses the manual-install warning. + # Tell install_python_stack.py to skip probe + suppress manual-install warning. $env:UNSLOTH_ROCM_TORCH_INSTALLED = "1" } }