Commit graph

7 commits

Author SHA1 Message Date
pre-commit-ci[bot]
f116f78b1d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-05-24 15:24:38 +00:00
danielhanchen
0d574d8161 Address reviewer-2 findings on PR #5748
Round-2 of the 12-persona reviewer.py pass found 17 issues. Address the
P1s + the regression-class P2s in this commit; the remaining nits are
left for a follow-up cleanup pass.

1. unsloth/_gpu_init.py: the `NVIDIA_VISIBLE_DEVICES in os.environ` check
   triggered for every NVIDIA-runtime container including `--gpus all`
   (NVIDIA_VISIBLE_DEVICES=all is the default). Gate strictly on a
   non-special device list. Also drop the precondition that the env var
   was absent: if the user already pinned TORCHINDUCTOR_COMPILE_THREADS=1
   we should still plant the UNSLOTH_FORCE_SINGLE_COMPILE_WORKER sentinel
   so the zoo-side patch knows to preserve the forcing.

2. unsloth/_gpu_init.py: after the post-`import unsloth_zoo` reassertion,
   monkey-patch `unsloth_zoo.temporary_patches.common.determine_compile_threads`
   to return 1, so any later `torch.compile` call that rebuilds the
   options dict still sees the single-worker forcing even if a downstream
   patch_torch_compile pops the env var again.

3. docker/Dockerfile: torchaudio==2.11.0 mismatched the torch==2.10.0
   release pairing; pin to 2.10.0 so the ABI is correct and the audio
   stack matches torch/cu128.

4. docker/Dockerfile: drop `12.1+PTX` from TORCH_CUDA_ARCH_LIST. The
   cu128 toolkit compiler does not know about compute_121; the trailing
   PTX entry forced nvcc to emit a `sm_121` gencode that breaks any
   in-container source builds.

5. docker/smoke_test.py: the device-capability floor said `cap[0] < 8`,
   rejecting Turing (sm_75) while the Dockerfile + entrypoint advertise
   sm_75 as supported. Lower the smoke floor to sm_75 and print a hint
   that bf16 is not available on Turing.

6. docker/run.sh: `-it` is unconditional; CI / non-TTY invocations died
   with "the input device is not a TTY". Probe `[ -t 0 ] && [ -t 1 ]`
   first. Also remove `set -x` which echoed the forwarded HF_TOKEN /
   WANDB_API_KEY / UNSLOTH_LICENSE values to stdout.

7. docker/test_locally.sh: `-e HF_TOKEN="${HF_TOKEN:-}"` either pasted
   the secret verbatim into the process arg list or shadowed any
   in-container value with an empty string. Forward conditionally.

8. .github/workflows/docker-publish.yml: gate `latest` on default branch
   AND on `unsloth_ref` not being overridden via workflow_dispatch.
   Otherwise a maintainer testing a feature SHA from main could overwrite
   `:latest` with non-main source.

9. docker/Dockerfile.studio: add an `UNSLOTH_STUDIO_REF` build-arg so
   the Studio companion image is pinned to a known unsloth ref instead
   of cloning `main` whenever it builds.
2026-05-24 15:24:20 +00:00
pre-commit-ci[bot]
6e45c278ff [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-05-24 14:07:30 +00:00
danielhanchen
29a6bde4d1 Address reviewer findings on PR #5748: 4 release-path bugs
Round-trip with the reviewer.py 12-persona pass surfaced four real
issues. Fix all four in this PR so the new Docker release path is
self-consistent.

1. docker/smoke_test.py used `import xformers` unconditionally, which
   guarantees a failure on arm64 (built with `[huggingface]` extras to
   skip xformers since it has no aarch64 cu128 wheel). Wrap the import
   in try/except so the same smoke script validates both arches.

2. unsloth/_gpu_init.py forced `TORCHINDUCTOR_COMPILE_THREADS=1` before
   `import unsloth_zoo`, but `patch_torch_compile` in unsloth_zoo main
   pops that env var in non-debug mode. After unsloth_zoo init the
   guard was effectively undone, so cgroup-pinned `docker --gpus
   '"device=N"'` containers still spawned the Inductor subprocess pool
   that cannot enumerate the GPU. Set `torch._inductor.config.
   compile_threads = 1` directly post-import-torch and re-populate the
   env var so `determine_compile_threads()` in the zoo options dict
   also returns 1, regardless of whether the zoo-side fix from PR #694
   has shipped yet.

3. docker-publish.yml UNSLOTH_REF build-arg defaulted to `'main'` for
   tag pushes and scheduled runs, so a `v1.2.3` release image would
   contain whatever `main` happened to be at build time, not v1.2.3.
   Pick the tag's `github.ref_name` for tag events and `github.sha`
   for branch/schedule events.

4. The smoke-test job pulled `:latest` regardless of which tag the
   merge job had just published, so tag/schedule/sha publishes were
   never actually validated. Re-run docker/metadata-action with the
   same config the merge job used, then smoke-test the first tag from
   its output.

All four changes are gated and backwards-compatible.
2026-05-24 14:05:36 +00:00
Daniel Han
00cbc82513 smoke_test.py: import unsloth before unsloth_zoo / transformers / trl / peft
unsloth_zoo/__init__.py guards against being imported standalone:

  if "UNSLOTH_IS_PRESENT" not in os.environ:
      raise ImportError("Please install Unsloth via `pip install unsloth`!")

The env var is set by unsloth/__init__.py at import time, so importing
unsloth must happen first. The old check_imports() imported xformers,
bnb, transformers, trl, peft, then unsloth_zoo -- which fired the guard
because unsloth had not been imported yet.

Reorder check_imports() to import unsloth (and unsloth_zoo) first, then
the rest. check_unsloth_import() becomes a thin re-import to keep the
"FastLanguageModel reachable" banner in the output.

Same fix the unsloth README has been recommending for years: "import
unsloth at the top of your file, before transformers/trl/peft."
2026-05-24 08:11:37 +00:00
pre-commit-ci[bot]
a75aef063c [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-05-24 06:54:37 +00:00
Daniel Han
c6d92160f6 Add Docker build for Blackwell that runs on any NVIDIA GPU host
Adds a multi-stage Dockerfile producing an image that works on Ampere through
Blackwell (sm_80 through sm_120: A100, RTX 30/40, H100, B100/B200, RTX 50-series,
RTX 6000 Pro Blackwell). The build itself requires no GPU at all and runs on a
free GitHub-hosted ubuntu-latest runner.

How the GPU-less build works:

1. cu128 PyTorch wheels are fat binaries. torch._C._cuda_getArchFlags() returns
   'sm_70 sm_75 sm_80 sm_86 sm_90 sm_100 sm_120' regardless of which GPU
   compiled the image, because the wheels are cross-compiled upstream by the
   PyTorch team.

2. All deps resolve in a single uv pip install pass with explicit pins
   (torch==2.10.0, --extra-index-url cu128, no --torch-backend=auto, no
   install.sh). This prevents the silent cu cascade where bitsandbytes'
   transitive cuda-toolkit==13 dep upgrades torch to 2.12+cu130 in a later
   resolver pass, leaving xformers and other cu128 wheels stranded.

3. Build-time verification uses package metadata (importlib.metadata.version)
   and the raw torch._C._cuda_getArchFlags() accessor. We deliberately avoid
   import unsloth at build time because unsloth.__init__ calls
   torch.cuda.get_device_properties(0), which requires an actual CUDA device
   and is not bypassable. Import-time correctness is exercised at deploy time
   by smoke_test.py with --gpus all.

4. UNSLOTH_COMPILE_DISABLE=1 and CUDA_VISIBLE_DEVICES="" during the build stage
   prevent any code path from JIT-compiling kernels for the build host's
   compute capability and baking the resulting cache into the image. The
   deploy GPU produces its own cache on first use.

Other notes:

- --index-strategy unsafe-best-match is needed because the PyTorch wheel index
  serves an old requests==2.28.1 that conflicts with datasets>=2.32.2, which
  the default first-index-wins strategy rejects.
- Extra is cu128-ampere-torch2100 (ampere precedes the torch version in the
  pyproject ordering).
- No flash-attn in the base image. FA3 is hard-refused on Blackwell upstream
  and unsloth gracefully falls back to xformers + SDPA. Users on Ampere /
  Ada / Hopper who want FA2 can pip install flash-attn on top.
- Two stages: nvidia/cuda:12.8.1-cudnn-devel-ubuntu24.04 for the build,
  -cudnn-runtime for the deploy image. No nvcc in the published image.
- A lockfile is emitted at /opt/unsloth-venv/requirements.lock.txt inside
  the image and can be extracted with docker/freeze.sh for byte-identical
  rebuilds even after PyPI moves on.

CI workflow .github/workflows/docker-publish.yml:

- Builds on ubuntu-latest on every push to main, every tag, weekly via cron,
  and manually via workflow_dispatch. Pushes to docker.io/unsloth/unsloth
  with cache via type=gha.
- Optional smoke-test job runs on a self-hosted GPU runner if vars.HAS_GPU_RUNNER
  is set; skipped otherwise. End-to-end verification on sm_120 hardware is a
  nice-to-have, not a publish blocker.

Validation:

- Install path validated on a B200 host with CUDA_VISIBLE_DEVICES="" set
  (simulating the GPU-less CI runner): torch 2.10.0+cu128 holds, xformers
  0.0.34, bitsandbytes 0.49.2, triton 3.6.0, transformers 5.5.0, trl 0.24.0,
  peft 0.19.1, accelerate 1.13.0. Arch flags include sm_100 and sm_120.
- Runtime path validated end-to-end on B200: smoke_test.py imports unsloth,
  loads Llama-3.2-1B-Instruct-bnb-4bit in 4-bit, completes 5 LoRA steps with
  loss decreasing 4.11 -> 3.75. xformers fallback active as designed.

Files:

- docker/Dockerfile             multi-stage cu128 build
- docker/build.sh               local build wrapper
- docker/freeze.sh              extract lockfile from a built image
- docker/smoke_test.py          runtime verification, run with --gpus all
- docker/.dockerignore
- .github/workflows/docker-publish.yml
2026-05-24 06:52:58 +00:00