diff --git a/install.ps1 b/install.ps1 index 8ae84f97bb..c1714b104a 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1808,7 +1808,13 @@ shell.Run cmd, 0, False # pass only space-free args, avoiding Start-Process mis-splitting `bash -lc `. $_pathLine = 'export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/wsl/lib:$PATH"' + "`n" $_jobsLine = if ($env:UNSLOTH_LLAMA_BUILD_JOBS) { "export UNSLOTH_LLAMA_BUILD_JOBS=$($env:UNSLOTH_LLAMA_BUILD_JOBS)`n" } else { "" } - $_runner = "#!/usr/bin/env bash`n" + $_pathLine + $_jobsLine + "exec bash /root/.unsloth/provision_llama_cuda.sh > /root/.unsloth/llama_cuda_build.log 2>&1`n" + # Bridge llama.cpp pins into WSL: the provisioner honors UNSLOTH_LLAMA_TAG / + # UNSLOTH_LLAMA_PR, but Windows env vars don't cross into WSL on their own -- + # without these exports a user's pin would be silently ignored by the + # deferred background build. sh-single-quoted (tags/PRs are simple tokens). + $_tagLine = if ($env:UNSLOTH_LLAMA_TAG) { "export UNSLOTH_LLAMA_TAG='$($env:UNSLOTH_LLAMA_TAG)'`n" } else { "" } + $_prLine = if ($env:UNSLOTH_LLAMA_PR) { "export UNSLOTH_LLAMA_PR='$($env:UNSLOTH_LLAMA_PR)'`n" } else { "" } + $_runner = "#!/usr/bin/env bash`n" + $_pathLine + $_jobsLine + $_tagLine + $_prLine + "exec bash /root/.unsloth/provision_llama_cuda.sh > /root/.unsloth/llama_cuda_build.log 2>&1`n" $_runnerB64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($_runner)) $_fetchCmd = 'mkdir -p /root/.unsloth; if curl -fsSL "' + $_llamaUrl + '" -o /root/.unsloth/provision_llama_cuda.sh && [ -s /root/.unsloth/provision_llama_cuda.sh ]; then chmod +x /root/.unsloth/provision_llama_cuda.sh; echo ' + $_runnerB64 + ' | base64 -d > /root/.unsloth/run_llama_build.sh; chmod +x /root/.unsloth/run_llama_build.sh; echo PROV_FETCHED; else echo PROV_NOSCRIPT; fi' $_fetchOut = & wsl.exe -d $distro --cd /root -u root -- bash -lc $_fetchCmd 2>$null diff --git a/scripts/uninstall.ps1 b/scripts/uninstall.ps1 index 2cdf01e5d8..8a5ccaf185 100644 --- a/scripts/uninstall.ps1 +++ b/scripts/uninstall.ps1 @@ -390,13 +390,17 @@ function Uninstall-UnslothStudio { # /root/.local/bin/unsloth symlink (its target under /root/.unsloth is gone but the link still # resolves on PATH). Scope STRICTLY to /root: the WoA fallback installs there (wsl -u root), so # touching /home/*/.unsloth would erase an unrelated WSL user's own Unsloth/cache that this - # installer never created. pkill patterns use the [x]-regex self-exclusion trick: '[u]nsloth_studio' - # keeps the shell's own argv from matching while real processes still match. Same for '[l]lama-server'. + # installer never created. # The port-8888 kill is gated on an Unsloth install actually existing in the # distro (checked BEFORE the rm deletes the marker): a probed distro with an - # unrelated listener on 8888 (Jupyter etc.) must not lose it. The pkills are - # already Unsloth-specific, so they stay unconditional. - $_clean = '_had=0; if [ -d /root/.unsloth ] || [ -L /root/.local/bin/unsloth ]; then _had=1; fi; rm -rf /root/.unsloth /root/llama-cuda /root/provision_llama_cuda.sh /root/llama_cuda_build.log 2>/dev/null; rm -f /root/.local/bin/unsloth 2>/dev/null; if [ $_had -eq 1 ]; then fuser -k 8888/tcp 2>/dev/null; fi; pkill -9 -f ''[u]nsloth_studio'' 2>/dev/null; pkill -9 -f ''[l]lama-server'' 2>/dev/null; true' + # unrelated listener on 8888 (Jupyter etc.) must not lose it. The process kill is + # scoped to argv referencing /root/.unsloth/ -- the fallback's install dir, which + # its Studio server, llama-server, and build runner all reference -- instead of + # bare name patterns that would also kill a user's own unrelated llama-server or + # a /home Studio in that distro. The backslash in '/root/\.unslot[h]/' keeps the + # pattern from matching this command's own argv (whose literal text contains the + # escaped form, not the resolved path) -- same idea as the [x]-bracket trick. + $_clean = '_had=0; if [ -d /root/.unsloth ] || [ -L /root/.local/bin/unsloth ]; then _had=1; fi; rm -rf /root/.unsloth /root/llama-cuda /root/provision_llama_cuda.sh /root/llama_cuda_build.log 2>/dev/null; rm -f /root/.local/bin/unsloth 2>/dev/null; if [ $_had -eq 1 ]; then fuser -k 8888/tcp 2>/dev/null; fi; pkill -9 -f ''/root/\.unslot[h]/'' 2>/dev/null; true' $_cands = @('', 'Ubuntu', 'Ubuntu-24.04', 'Ubuntu-22.04', 'Debian') if ($_recordedDistro) { $_cands = @($_recordedDistro) + $_cands } if ($env:UNSLOTH_WSL_DISTRO) { $_cands = @($env:UNSLOTH_WSL_DISTRO) + $_cands } diff --git a/unsloth/kernels/flex_attention.py b/unsloth/kernels/flex_attention.py index e1e0dc399b..512e6361ff 100644 --- a/unsloth/kernels/flex_attention.py +++ b/unsloth/kernels/flex_attention.py @@ -27,8 +27,12 @@ torch_compile_options = { def _flex_is_dgx_spark(): - # Inlined copy of _utils.is_dgx_spark() to avoid a circular import. - # Spark = aarch64 + NVIDIA CUDA + a Spark device-name token. + # Inlined CUDA-free copy of _utils._is_dgx_spark_no_cuda_init() (kept local to + # avoid a circular import). Spark = aarch64 + a Spark device name via nvidia-smi. + # Must NOT touch torch.cuda: this runs at module import, and vision.py imports + # ..kernels before ._utils -- a device-name query here would initialize the CUDA + # allocator before patch_dgx_spark_memory_config() can set PYTORCH_CUDA_ALLOC_CONF + # on the very Spark hosts this check targets. _force = os.environ.get("UNSLOTH_FORCE_DGX_SPARK") if _force == "1": return True @@ -39,11 +43,13 @@ def _flex_is_dgx_spark(): if platform.machine().lower() not in ("aarch64", "arm64"): return False - if not (hasattr(torch, "cuda") and torch.cuda.is_available()): - return False - names = " ".join( - str(torch.cuda.get_device_name(i)).upper() for i in range(torch.cuda.device_count()) + import subprocess + + out = subprocess.run( + ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"], + capture_output = True, text = True, timeout = 5, ) + names = (out.stdout or "").upper() return any(t in names for t in ("GB10", "JMJWOA", "N1X", "DGX SPARK", "GB110")) except Exception: return False diff --git a/unsloth/models/_utils.py b/unsloth/models/_utils.py index c13e46bfce..e990b32737 100644 --- a/unsloth/models/_utils.py +++ b/unsloth/models/_utils.py @@ -1177,8 +1177,10 @@ def patch_dgx_spark_dataloader_defaults(): return _orig_post_init = Base.__post_init__ - def __post_init__(self): - _orig_post_init(self) + # Forward *args/**kwargs so a future TrainingArguments (or a subclass) that + # adds InitVar parameters to __post_init__ keeps working through the wrapper. + def __post_init__(self, *args, **kwargs): + _orig_post_init(self, *args, **kwargs) if getattr(self, "dataloader_pin_memory", None) is True: self.dataloader_pin_memory = False