mirror of
https://github.com/unslothai/unsloth
synced 2026-04-21 13:37:39 +00:00
fix: install.sh Mac Intel compatibility + Studio no-torch support (#4624)
* fix: install.sh Mac Intel compatibility + Studio no-torch support (#4621) On Intel Macs (x86_64), PyTorch has no wheels for torch >= 2.3, so the installer crashes. Even when torch is absent, Studio crashes on startup because two files have bare top-level torch imports. Studio's GGUF inference (llama.cpp) does not need PyTorch. Training and HF-inference already isolate torch to subprocesses. Only 2 files in the server startup chain had top-level torch imports preventing startup. Changes: - install.sh: detect architecture, default to Python 3.12 on Intel Mac, skip torch install, add Python 3.13.8 guard for arm64, pass UNSLOTH_NO_TORCH env var to setup.sh - data_collators.py: remove unused `import torch` (no torch.* refs) - chat_templates.py: lazy-import IterableDataset into function bodies - install_python_stack.py: add IS_MACOS/NO_TORCH constants, skip torch-dependent packages, skip overrides.txt, skip triton on macOS No existing working flow changes. Linux/WSL and macOS arm64 behavior is identical. * tests: add test suite for Mac Intel compat + no-torch mode Shell tests (test_mac_intel_compat.sh): - version_ge edge cases (9 tests) - Architecture detection for Darwin x86_64/arm64, Linux x86_64/aarch64 - get_torch_index_url returns cpu on simulated Darwin - UNSLOTH_NO_TORCH propagation to both setup.sh branches Python unit tests (test_no_torch_filtering.py): - _filter_requirements with NO_TORCH_SKIP_PACKAGES - NO_TORCH env var parsing (true/1/TRUE/false/0/unset) - IS_MACOS constant check - Overrides skip and triton macOS skip guards Python import tests (test_studio_import_no_torch.py): - data_collators.py loads in isolated no-torch venv - chat_templates.py has no top-level torch imports - Negative control confirms import torch fails without torch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests: add E2E sandbox tests for Mac Intel no-torch mode Replace static/synthetic test stubs with real sandbox tests: - Shell: E2E uv venv creation at Python 3.12, mock uv shim to verify torch install is skipped when MAC_INTEL=true, dynamic env propagation test for UNSLOTH_NO_TORCH in both local and non-local install paths - Python filtering: test real extras.txt and extras-no-deps.txt with NO_TORCH_SKIP_PACKAGES, subprocess mock of install_python_stack() for 5 platform configs (NO_TORCH+macOS, Windows+NO_TORCH, normal Linux, Windows-only, macOS-only), VCS URL and env marker edge cases - Python imports: parametrized Python 3.12+3.13 venv fixture, dataclass instantiation for all 3 collator classes, chat_templates.py exec with stubs, negative controls proving import torch and torchao install fail in no-torch venvs 91 total tests, all passing. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: address reviewer findings for Intel Mac no-torch mode P1 fixes: - Auto-infer NO_TORCH in install_python_stack.py via platform.machine() so `unsloth studio update` preserves GGUF-only mode without needing the UNSLOTH_NO_TORCH env var (6/10 reviewers) - Add openai-whisper and transformers-cfg to NO_TORCH_SKIP_PACKAGES since both have unconditional torch dependencies (4/10 reviewers) - Skip unsloth-zoo on Intel Mac --local installs (depends on torch) in both migrated and fresh install paths (1/10) - Recreate stale 3.13 venvs as 3.12 on Intel Mac re-runs (1/10) - Detect Apple Silicon under Rosetta via sysctl hw.optional.arm64 and warn user to use native arm64 terminal (1/10) P2 fixes: - Wire new test files into tests/run_all.sh (4/10 reviewers) - Add update-path tests (skip_base=False) for Intel Mac - Add _infer_no_torch tests for platform auto-detection P3 fixes: - Fix macOS progress bar total (triton step skipped but was counted) - Fix temp file leak when Windows + NO_TORCH filters stack All tests pass: 30 shell, 66 Python (96 total). * feat: add --python override flag to install.sh Lets users force a specific Python version, e.g. ./install.sh --python 3.12. Addresses M2 Mac users whose systems resolve to a problematic 3.13.x patch. When --python is set, the Intel Mac stale-venv guard and 3.13.8 auto-downgrade are skipped so the user's choice is respected. * tests: add comprehensive E2E sandbox tests for no-torch mode Add test_e2e_no_torch_sandbox.py with 7 test groups (43 tests total) covering the full no-torch import chain, edge cases, and install logic: - Group 1: BEFORE vs AFTER import chain comparison (proves the bug existed and the fix works by synthetically prepending top-level torch imports) - Group 2: Dataclass instantiation without torch - Group 3: Edge cases with broken/fake torch modules on sys.path - Group 4: Hardware detection fallback to CPU without torch - Group 5: install.sh flag parsing, version resolution, arch detection - Group 6: install_python_stack.py NO_TORCH filtering - Group 7: Live server startup without torch (marked @server, skipped when studio venv is unavailable) All 43 tests pass on both Python 3.12 and 3.13 isolated venvs. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: add --no-torch flag to install.sh/ps1, fix lazy import bug in dataset formatting - Fix chat_templates.py: narrow torch IterableDataset import into inner try/except ImportError so dataset.map() works without torch installed - Fix format_conversion.py: same lazy import fix for convert_chatml_to_alpaca and convert_alpaca_to_chatml - Add --no-torch flag to install.sh with unified SKIP_TORCH variable (driven by --no-torch flag OR MAC_INTEL auto-detection) - Add --no-torch flag to install.ps1 with $SkipTorch variable - Print CPU hint when no GPU detected and --no-torch not set - Replace MAC_INTEL guards with SKIP_TORCH in torch install sections - Update shell tests (40 pass) and Python tests (90 pass) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: address reviewer findings for --no-torch installer paths - Fix migrated-env branch in install.sh and install.ps1: check SKIP_TORCH first, then branch on STUDIO_LOCAL_INSTALL. Previously SKIP_TORCH+non-local fell into else and installed unsloth-zoo (which depends on torch), defeating --no-torch mode. - Fix $env:UNSLOTH_NO_TORCH leak in install.ps1: always set to "true" or "false" instead of only setting on the true branch. Prevents stale no-torch state from leaking across runs in the same PS session. - Fix install_python_stack.py update path: add NO_TORCH guard around base.txt install so unsloth studio update does not reinstall unsloth-zoo (which depends on torch) in no-torch mode. * fix: install unsloth + unsloth-zoo with --no-deps in no-torch mode Instead of skipping unsloth-zoo entirely (which breaks unsloth's dependency on it), install both packages with --no-deps so they are present but torch is not pulled in transitively. Applied consistently across all no-torch paths: migrated-env, fresh-local, fresh-non-local in install.sh, install.ps1, and install_python_stack.py. * chore: temporarily remove test files (will be added in a follow-up) * refactor: deduplicate SKIP_TORCH conditional branches in installers Collapse if/else blocks that differ only by --no-deps into a single branch with a conditional flag variable. Applied to migrated-env and fresh-local paths in install.sh, install.ps1, and install_python_stack.py. * fix: apply --no-deps to fresh non-local --no-torch install path The non-local else branch was missing $_no_deps_arg/$noDepsArg, so uv pip install unsloth would resolve torch from PyPI metadata (the published unsloth package still declares torch as a hard dep). Now --no-deps is applied consistently to all SKIP_TORCH code paths. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
d57a4d993d
commit
e9ac785346
6 changed files with 259 additions and 40 deletions
40
install.ps1
40
install.ps1
|
|
@ -1,6 +1,7 @@
|
|||
# Unsloth Studio Installer for Windows PowerShell
|
||||
# Usage: irm https://raw.githubusercontent.com/unslothai/unsloth/main/install.ps1 | iex
|
||||
# Local: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; .\install.ps1 --local
|
||||
# NoTorch: .\install.ps1 --no-torch (skip PyTorch, GGUF-only mode)
|
||||
# Test: .\install.ps1 --package roland-sloth
|
||||
|
||||
function Install-UnslothStudio {
|
||||
|
|
@ -10,11 +11,13 @@ function Install-UnslothStudio {
|
|||
$StudioLocalInstall = $false
|
||||
$PackageName = "unsloth"
|
||||
$RepoRoot = ""
|
||||
$SkipTorch = $false
|
||||
$argList = $args
|
||||
for ($i = 0; $i -lt $argList.Count; $i++) {
|
||||
switch ($argList[$i]) {
|
||||
"--local" { $StudioLocalInstall = $true }
|
||||
"--package" {
|
||||
"--local" { $StudioLocalInstall = $true }
|
||||
"--no-torch" { $SkipTorch = $true }
|
||||
"--package" {
|
||||
$i++
|
||||
if ($i -ge $argList.Count) {
|
||||
Write-Host "[ERROR] --package requires an argument." -ForegroundColor Red
|
||||
|
|
@ -585,6 +588,16 @@ shell.Run cmd, 0, False
|
|||
}
|
||||
$TorchIndexUrl = Get-TorchIndexUrl
|
||||
|
||||
# ── Print CPU-only hint when no GPU detected ──
|
||||
if (-not $SkipTorch -and $TorchIndexUrl -like "*/cpu") {
|
||||
Write-Host ""
|
||||
Write-Host " NOTE: No NVIDIA GPU detected." -ForegroundColor Yellow
|
||||
Write-Host " Installing CPU-only PyTorch. If you only need GGUF chat/inference,"
|
||||
Write-Host " re-run with --no-torch for a faster, lighter install:"
|
||||
Write-Host " .\install.ps1 --no-torch"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# ── Install PyTorch first, then unsloth separately ──
|
||||
#
|
||||
# Why two steps?
|
||||
|
|
@ -607,26 +620,32 @@ shell.Run cmd, 0, False
|
|||
# Migrated env: force-reinstall unsloth+unsloth-zoo to ensure clean state
|
||||
# in the new venv location, while preserving existing torch/CUDA
|
||||
Write-Host "==> Upgrading unsloth in migrated environment..."
|
||||
uv pip install --python $VenvPython --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.3.14" unsloth-zoo
|
||||
$noDepsArg = if ($SkipTorch) { "--no-deps" } else { $null }
|
||||
uv pip install --python $VenvPython $noDepsArg --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.3.14" unsloth-zoo
|
||||
if ($StudioLocalInstall) {
|
||||
Write-Host "==> Overlaying local repo (editable)..."
|
||||
uv pip install --python $VenvPython -e $RepoRoot --no-deps
|
||||
}
|
||||
} elseif ($TorchIndexUrl) {
|
||||
Write-Host "==> Installing PyTorch ($TorchIndexUrl)..."
|
||||
uv pip install --python $VenvPython "torch>=2.4,<2.11.0" torchvision torchaudio --index-url $TorchIndexUrl
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[ERROR] Failed to install PyTorch (exit code $LASTEXITCODE)" -ForegroundColor Red
|
||||
return
|
||||
if ($SkipTorch) {
|
||||
Write-Host "==> Skipping PyTorch (--no-torch flag set)."
|
||||
} else {
|
||||
Write-Host "==> Installing PyTorch ($TorchIndexUrl)..."
|
||||
uv pip install --python $VenvPython "torch>=2.4,<2.11.0" torchvision torchaudio --index-url $TorchIndexUrl
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[ERROR] Failed to install PyTorch (exit code $LASTEXITCODE)" -ForegroundColor Red
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "==> Installing unsloth (this may take a few minutes)..."
|
||||
$noDepsArg = if ($SkipTorch) { "--no-deps" } else { $null }
|
||||
if ($StudioLocalInstall) {
|
||||
uv pip install --python $VenvPython --upgrade-package unsloth "unsloth>=2026.3.14" unsloth-zoo
|
||||
uv pip install --python $VenvPython $noDepsArg --upgrade-package unsloth "unsloth>=2026.3.14" unsloth-zoo
|
||||
Write-Host "==> Overlaying local repo (editable)..."
|
||||
uv pip install --python $VenvPython -e $RepoRoot --no-deps
|
||||
} else {
|
||||
uv pip install --python $VenvPython --upgrade-package unsloth "$PackageName"
|
||||
uv pip install --python $VenvPython $noDepsArg --upgrade-package unsloth "$PackageName"
|
||||
}
|
||||
} else {
|
||||
# Fallback: GPU detection failed to produce a URL -- let uv resolve torch
|
||||
|
|
@ -659,6 +678,7 @@ shell.Run cmd, 0, False
|
|||
# Tell setup.ps1 to skip base package installation (install.ps1 already did it)
|
||||
$env:SKIP_STUDIO_BASE = "1"
|
||||
$env:STUDIO_PACKAGE_NAME = $PackageName
|
||||
$env:UNSLOTH_NO_TORCH = if ($SkipTorch) { "true" } else { "false" }
|
||||
if ($StudioLocalInstall) {
|
||||
$env:STUDIO_LOCAL_INSTALL = "1"
|
||||
$env:STUDIO_LOCAL_REPO = $RepoRoot
|
||||
|
|
|
|||
132
install.sh
132
install.sh
|
|
@ -3,22 +3,34 @@
|
|||
# Usage (curl): curl -fsSL https://unsloth.ai/install.sh | sh
|
||||
# Usage (wget): wget -qO- https://unsloth.ai/install.sh | sh
|
||||
# Usage (local): ./install.sh --local (install from local repo instead of PyPI)
|
||||
# Usage (no-torch): ./install.sh --no-torch (skip PyTorch, GGUF-only mode)
|
||||
# Usage (test): ./install.sh --package roland-sloth (install a different package name)
|
||||
# Usage (py): ./install.sh --python 3.12 (override auto-detected Python version)
|
||||
set -e
|
||||
|
||||
# ── Parse flags ──
|
||||
STUDIO_LOCAL_INSTALL=false
|
||||
PACKAGE_NAME="unsloth"
|
||||
_USER_PYTHON=""
|
||||
_NO_TORCH_FLAG=false
|
||||
_next_is_package=false
|
||||
_next_is_python=false
|
||||
for arg in "$@"; do
|
||||
if [ "$_next_is_package" = true ]; then
|
||||
PACKAGE_NAME="$arg"
|
||||
_next_is_package=false
|
||||
continue
|
||||
fi
|
||||
if [ "$_next_is_python" = true ]; then
|
||||
_USER_PYTHON="$arg"
|
||||
_next_is_python=false
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
--local) STUDIO_LOCAL_INSTALL=true ;;
|
||||
--package) _next_is_package=true ;;
|
||||
--python) _next_is_python=true ;;
|
||||
--no-torch) _NO_TORCH_FLAG=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
|
@ -26,8 +38,12 @@ if [ "$_next_is_package" = true ]; then
|
|||
echo "❌ ERROR: --package requires an argument." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$_next_is_python" = true ]; then
|
||||
echo "❌ ERROR: --python requires a version argument (e.g. --python 3.12)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PYTHON_VERSION="3.13"
|
||||
PYTHON_VERSION="" # resolved after platform detection
|
||||
STUDIO_HOME="$HOME/.unsloth/studio"
|
||||
VENV_DIR="$STUDIO_HOME/unsloth_studio"
|
||||
|
||||
|
|
@ -563,6 +579,47 @@ elif grep -qi microsoft /proc/version 2>/dev/null; then
|
|||
fi
|
||||
echo "==> Platform: $OS"
|
||||
|
||||
# ── Architecture detection & Python version ──
|
||||
_ARCH=$(uname -m)
|
||||
MAC_INTEL=false
|
||||
if [ "$OS" = "macos" ] && [ "$_ARCH" = "x86_64" ]; then
|
||||
# Guard against Apple Silicon running under Rosetta (reports x86_64).
|
||||
# sysctl hw.optional.arm64 returns "1" on Apple Silicon even in Rosetta.
|
||||
if [ "$(sysctl -in hw.optional.arm64 2>/dev/null || echo 0)" = "1" ]; then
|
||||
echo ""
|
||||
echo " WARNING: Apple Silicon detected, but this shell is running under Rosetta (x86_64)."
|
||||
echo " Re-run install.sh from a native arm64 terminal for full PyTorch support."
|
||||
echo " Continuing in GGUF-only mode for now."
|
||||
echo ""
|
||||
fi
|
||||
MAC_INTEL=true
|
||||
fi
|
||||
|
||||
if [ -n "$_USER_PYTHON" ]; then
|
||||
PYTHON_VERSION="$_USER_PYTHON"
|
||||
echo " Using user-specified Python $PYTHON_VERSION (--python override)"
|
||||
elif [ "$MAC_INTEL" = true ]; then
|
||||
PYTHON_VERSION="3.12"
|
||||
else
|
||||
PYTHON_VERSION="3.13"
|
||||
fi
|
||||
|
||||
if [ "$MAC_INTEL" = true ]; then
|
||||
echo ""
|
||||
echo " NOTE: Intel Mac (x86_64) detected."
|
||||
echo " PyTorch is unavailable for this platform (dropped Jan 2024)."
|
||||
echo " Studio will install in GGUF-only mode."
|
||||
echo " Chat, inference via GGUF, and data recipes will work."
|
||||
echo " Training requires Apple Silicon or Linux with GPU."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Unified SKIP_TORCH: --no-torch flag OR Intel Mac auto-detection ──
|
||||
SKIP_TORCH=false
|
||||
if [ "$_NO_TORCH_FLAG" = true ] || [ "$MAC_INTEL" = true ]; then
|
||||
SKIP_TORCH=true
|
||||
fi
|
||||
|
||||
# ── Check system dependencies ──
|
||||
# cmake and git are needed by unsloth studio setup to build the GGUF inference
|
||||
# engine (llama.cpp). build-essential and libcurl-dev are also needed on Linux.
|
||||
|
|
@ -714,11 +771,38 @@ torch.testing.assert_close(torch.unique(E), torch.tensor((20,), device=E.device,
|
|||
fi
|
||||
fi
|
||||
|
||||
# If an Intel Mac has a stale 3.13 venv from a previous failed install, recreate
|
||||
# (skip when the user explicitly chose a version via --python)
|
||||
if [ "$SKIP_TORCH" = true ] && [ "$MAC_INTEL" = true ] && [ -z "$_USER_PYTHON" ] && [ -x "$VENV_DIR/bin/python" ]; then
|
||||
_PY_MM=$("$VENV_DIR/bin/python" -c \
|
||||
"import sys; print('{}.{}'.format(*sys.version_info[:2]))" 2>/dev/null || echo "")
|
||||
if [ "$_PY_MM" != "3.12" ]; then
|
||||
echo " Recreating Intel Mac environment with Python 3.12 (was $_PY_MM)..."
|
||||
rm -rf "$VENV_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$VENV_DIR/bin/python" ]; then
|
||||
echo "==> Creating Python ${PYTHON_VERSION} virtual environment (${VENV_DIR})..."
|
||||
uv venv "$VENV_DIR" --python "$PYTHON_VERSION"
|
||||
else
|
||||
echo "==> Using migrated environment at ${VENV_DIR}"
|
||||
fi
|
||||
|
||||
# Guard against Python 3.13.8 torch import bug on Apple Silicon
|
||||
# (skip when the user explicitly chose a version via --python)
|
||||
if [ -z "$_USER_PYTHON" ] && [ "$OS" = "macos" ] && [ "$_ARCH" = "arm64" ]; then
|
||||
_PY_VER=$("$VENV_DIR/bin/python" -c \
|
||||
"import sys; print('{}.{}.{}'.format(*sys.version_info[:3]))" 2>/dev/null || echo "")
|
||||
if [ "$_PY_VER" = "3.13.8" ]; then
|
||||
echo " WARNING: Python 3.13.8 has a known torch import bug."
|
||||
echo " Recreating venv with Python 3.12..."
|
||||
rm -rf "$VENV_DIR"
|
||||
PYTHON_VERSION="3.12"
|
||||
uv venv "$VENV_DIR" --python "$PYTHON_VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -x "$VENV_DIR/bin/python" ]; then
|
||||
echo "==> Using environment at ${VENV_DIR}"
|
||||
fi
|
||||
|
||||
# ── Resolve repo root (for --local installs) ──
|
||||
|
|
@ -759,13 +843,31 @@ get_torch_index_url() {
|
|||
}
|
||||
TORCH_INDEX_URL=$(get_torch_index_url)
|
||||
|
||||
# ── Print CPU-only hint when no GPU detected ──
|
||||
case "$TORCH_INDEX_URL" in
|
||||
*/cpu)
|
||||
if [ "$SKIP_TORCH" = false ] && [ "$OS" != "macos" ]; then
|
||||
echo ""
|
||||
echo " NOTE: No NVIDIA GPU detected (nvidia-smi not found)."
|
||||
echo " Installing CPU-only PyTorch. If you only need GGUF chat/inference,"
|
||||
echo " re-run with --no-torch for a faster, lighter install:"
|
||||
echo " curl -fsSL https://unsloth.ai/install.sh | sh -s -- --no-torch"
|
||||
echo ""
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# ── Install unsloth directly into the venv (no activation needed) ──
|
||||
_VENV_PY="$VENV_DIR/bin/python"
|
||||
if [ "$_MIGRATED" = true ]; then
|
||||
# Migrated env: force-reinstall unsloth+unsloth-zoo to ensure clean state
|
||||
# in the new venv location, while preserving existing torch/CUDA
|
||||
echo "==> Upgrading unsloth in migrated environment..."
|
||||
uv pip install --python "$_VENV_PY" \
|
||||
_no_deps_arg=""
|
||||
if [ "$SKIP_TORCH" = true ]; then
|
||||
_no_deps_arg="--no-deps"
|
||||
fi
|
||||
uv pip install --python "$_VENV_PY" $_no_deps_arg \
|
||||
--reinstall-package unsloth --reinstall-package unsloth-zoo \
|
||||
"unsloth>=2026.3.14" unsloth-zoo
|
||||
if [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
||||
|
|
@ -773,19 +875,27 @@ if [ "$_MIGRATED" = true ]; then
|
|||
uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps
|
||||
fi
|
||||
elif [ -n "$TORCH_INDEX_URL" ]; then
|
||||
# Fresh: Step 1 - install torch from explicit index
|
||||
echo "==> Installing PyTorch ($TORCH_INDEX_URL)..."
|
||||
uv pip install --python "$_VENV_PY" "torch>=2.4,<2.11.0" torchvision torchaudio \
|
||||
--index-url "$TORCH_INDEX_URL"
|
||||
# Fresh: Step 1 - install torch from explicit index (skip when --no-torch or Intel Mac)
|
||||
if [ "$SKIP_TORCH" = true ]; then
|
||||
echo "==> Skipping PyTorch (--no-torch or Intel Mac x86_64)."
|
||||
else
|
||||
echo "==> Installing PyTorch ($TORCH_INDEX_URL)..."
|
||||
uv pip install --python "$_VENV_PY" "torch>=2.4,<2.11.0" torchvision torchaudio \
|
||||
--index-url "$TORCH_INDEX_URL"
|
||||
fi
|
||||
# Fresh: Step 2 - install unsloth, preserving pre-installed torch
|
||||
echo "==> Installing unsloth (this may take a few minutes)..."
|
||||
_no_deps_arg=""
|
||||
if [ "$SKIP_TORCH" = true ]; then
|
||||
_no_deps_arg="--no-deps"
|
||||
fi
|
||||
if [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
||||
uv pip install --python "$_VENV_PY" \
|
||||
uv pip install --python "$_VENV_PY" $_no_deps_arg \
|
||||
--upgrade-package unsloth "unsloth>=2026.3.14" unsloth-zoo
|
||||
echo "==> Overlaying local repo (editable)..."
|
||||
uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps
|
||||
else
|
||||
uv pip install --python "$_VENV_PY" \
|
||||
uv pip install --python "$_VENV_PY" $_no_deps_arg \
|
||||
--upgrade-package unsloth "$PACKAGE_NAME"
|
||||
fi
|
||||
else
|
||||
|
|
@ -837,10 +947,12 @@ if [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
|||
STUDIO_PACKAGE_NAME="$PACKAGE_NAME" \
|
||||
STUDIO_LOCAL_INSTALL=1 \
|
||||
STUDIO_LOCAL_REPO="$_REPO_ROOT" \
|
||||
UNSLOTH_NO_TORCH="$SKIP_TORCH" \
|
||||
bash "$SETUP_SH" </dev/null
|
||||
else
|
||||
SKIP_STUDIO_BASE=1 \
|
||||
STUDIO_PACKAGE_NAME="$PACKAGE_NAME" \
|
||||
UNSLOTH_NO_TORCH="$SKIP_TORCH" \
|
||||
bash "$SETUP_SH" </dev/null
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ This module contains functions for applying chat templates to datasets
|
|||
and generating dataset info summaries.
|
||||
"""
|
||||
|
||||
from torch.utils.data import IterableDataset
|
||||
|
||||
from .format_detection import detect_dataset_format, detect_multimodal_dataset, detect_custom_format_heuristic
|
||||
from .model_mappings import MODEL_TO_TEMPLATE_MAPPER
|
||||
from loggers import get_logger
|
||||
|
|
@ -290,7 +288,13 @@ def apply_chat_template_to_dataset(
|
|||
'batch_size': batch_size,
|
||||
}
|
||||
|
||||
if not isinstance(dataset, IterableDataset):
|
||||
try:
|
||||
from torch.utils.data import IterableDataset
|
||||
_is_torch_iterable = isinstance(dataset, IterableDataset)
|
||||
except ImportError:
|
||||
_is_torch_iterable = False
|
||||
|
||||
if not _is_torch_iterable:
|
||||
from utils.hardware import dataset_map_num_proc
|
||||
if num_proc is None or type(num_proc) is not int:
|
||||
num_proc = dataset_map_num_proc()
|
||||
|
|
@ -351,12 +355,18 @@ def apply_chat_template_to_dataset(
|
|||
return {"text": texts}
|
||||
|
||||
try:
|
||||
try:
|
||||
from torch.utils.data import IterableDataset
|
||||
_is_torch_iterable = isinstance(dataset, IterableDataset)
|
||||
except ImportError:
|
||||
_is_torch_iterable = False
|
||||
|
||||
dataset_map_kwargs = {
|
||||
'batched': True,
|
||||
'batch_size': batch_size,
|
||||
}
|
||||
|
||||
if not isinstance(dataset, IterableDataset):
|
||||
if not _is_torch_iterable:
|
||||
from utils.hardware import dataset_map_num_proc
|
||||
if num_proc is None or type(num_proc) is not int:
|
||||
num_proc = dataset_map_num_proc()
|
||||
|
|
@ -367,7 +377,7 @@ def apply_chat_template_to_dataset(
|
|||
|
||||
# Monitor tqdm progress from dataset.map() and relay to callback
|
||||
_tqdm_monitor_stop = None
|
||||
if progress_callback and not isinstance(dataset, IterableDataset):
|
||||
if progress_callback and not _is_torch_iterable:
|
||||
import threading
|
||||
from tqdm.auto import tqdm as _tqdm_cls
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ This module contains custom data collators for training,
|
|||
particularly for VLM/OCR processing.
|
||||
"""
|
||||
|
||||
import torch
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, List, Optional, Union
|
||||
from loggers import get_logger
|
||||
|
|
|
|||
|
|
@ -149,7 +149,12 @@ def convert_chatml_to_alpaca(dataset, batch_size = 1000, num_proc = None):
|
|||
- "messages" or "conversations" column
|
||||
- "role"/"content" (standard) or "from"/"value" (ShareGPT)
|
||||
"""
|
||||
from torch.utils.data import IterableDataset
|
||||
try:
|
||||
from torch.utils.data import IterableDataset
|
||||
|
||||
_is_torch_iterable = isinstance(dataset, IterableDataset)
|
||||
except ImportError:
|
||||
_is_torch_iterable = False
|
||||
|
||||
def _convert(examples):
|
||||
# Auto-detect which column name is used
|
||||
|
|
@ -196,7 +201,7 @@ def convert_chatml_to_alpaca(dataset, batch_size = 1000, num_proc = None):
|
|||
"batch_size": batch_size,
|
||||
}
|
||||
|
||||
if not isinstance(dataset, IterableDataset):
|
||||
if not _is_torch_iterable:
|
||||
from utils.hardware import dataset_map_num_proc
|
||||
|
||||
if num_proc is None or type(num_proc) is not int:
|
||||
|
|
@ -216,7 +221,12 @@ def convert_alpaca_to_chatml(dataset, batch_size = 1000, num_proc = None):
|
|||
|
||||
Output format: Uses 'conversations' column with standard 'role'/'content' structure.
|
||||
"""
|
||||
from torch.utils.data import IterableDataset
|
||||
try:
|
||||
from torch.utils.data import IterableDataset
|
||||
|
||||
_is_torch_iterable = isinstance(dataset, IterableDataset)
|
||||
except ImportError:
|
||||
_is_torch_iterable = False
|
||||
|
||||
def _convert(examples):
|
||||
conversations = []
|
||||
|
|
@ -246,7 +256,7 @@ def convert_alpaca_to_chatml(dataset, batch_size = 1000, num_proc = None):
|
|||
"batch_size": batch_size,
|
||||
}
|
||||
|
||||
if not isinstance(dataset, IterableDataset):
|
||||
if not _is_torch_iterable:
|
||||
from utils.hardware import dataset_map_num_proc
|
||||
|
||||
if num_proc is None or type(num_proc) is not int:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ PATH to point at the venv.
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -21,6 +22,25 @@ import urllib.request
|
|||
from pathlib import Path
|
||||
|
||||
IS_WINDOWS = sys.platform == "win32"
|
||||
IS_MACOS = sys.platform == "darwin"
|
||||
IS_MAC_INTEL = IS_MACOS and platform.machine() == "x86_64"
|
||||
|
||||
|
||||
def _infer_no_torch() -> bool:
|
||||
"""Determine whether to run in no-torch (GGUF-only) mode.
|
||||
|
||||
Checks UNSLOTH_NO_TORCH env var first. When unset, falls back to
|
||||
platform detection so that Intel Macs automatically use GGUF-only
|
||||
mode even when invoked from ``unsloth studio update`` (which does
|
||||
not inject the env var).
|
||||
"""
|
||||
env = os.environ.get("UNSLOTH_NO_TORCH")
|
||||
if env is not None:
|
||||
return env.strip().lower() in ("1", "true")
|
||||
return IS_MAC_INTEL
|
||||
|
||||
|
||||
NO_TORCH = _infer_no_torch()
|
||||
|
||||
# -- Verbosity control ----------------------------------------------------------
|
||||
# By default the installer shows a minimal progress bar (one line, in-place).
|
||||
|
|
@ -161,6 +181,19 @@ def run(
|
|||
# Packages to skip on Windows (require special build steps)
|
||||
WINDOWS_SKIP_PACKAGES = {"open_spiel", "triton_kernels"}
|
||||
|
||||
# Packages to skip when torch is unavailable (Intel Mac GGUF-only mode).
|
||||
# These packages either *are* torch extensions or have unconditional
|
||||
# ``Requires-Dist: torch`` in their published metadata, so installing
|
||||
# them would pull torch back into the environment.
|
||||
NO_TORCH_SKIP_PACKAGES = {
|
||||
"torch-stoi",
|
||||
"timm",
|
||||
"torchcodec",
|
||||
"torch-c-dlpack-ext",
|
||||
"openai-whisper",
|
||||
"transformers-cfg",
|
||||
}
|
||||
|
||||
# -- uv bootstrap ------------------------------------------------------
|
||||
|
||||
USE_UV = False # Set by _bootstrap_uv() at the start of install_python_stack()
|
||||
|
|
@ -273,8 +306,13 @@ def pip_install(
|
|||
constraint_args = ["-c", str(CONSTRAINTS)]
|
||||
|
||||
actual_req = req
|
||||
temp_reqs: list[Path] = []
|
||||
if req is not None and IS_WINDOWS and WINDOWS_SKIP_PACKAGES:
|
||||
actual_req = _filter_requirements(req, WINDOWS_SKIP_PACKAGES)
|
||||
temp_reqs.append(actual_req)
|
||||
if actual_req is not None and NO_TORCH and NO_TORCH_SKIP_PACKAGES:
|
||||
actual_req = _filter_requirements(actual_req, NO_TORCH_SKIP_PACKAGES)
|
||||
temp_reqs.append(actual_req)
|
||||
req_args: list[str] = []
|
||||
if actual_req is not None:
|
||||
req_args = ["-r", str(actual_req)]
|
||||
|
|
@ -298,8 +336,8 @@ def pip_install(
|
|||
pip_cmd = _build_pip_cmd(args) + constraint_args + req_args
|
||||
run(f"{label} (pip)" if USE_UV else label, pip_cmd)
|
||||
finally:
|
||||
if actual_req is not None and actual_req != req:
|
||||
actual_req.unlink(missing_ok = True)
|
||||
for temp_req in temp_reqs:
|
||||
temp_req.unlink(missing_ok = True)
|
||||
|
||||
|
||||
def download_file(url: str, dest: Path) -> None:
|
||||
|
|
@ -352,6 +390,8 @@ def install_python_stack() -> int:
|
|||
# When --local is used, overlay a local repo checkout after updating deps
|
||||
local_repo = os.environ.get("STUDIO_LOCAL_REPO", "")
|
||||
base_total = 10 if IS_WINDOWS else 11
|
||||
if IS_MACOS:
|
||||
base_total -= 1 # triton step is skipped on macOS
|
||||
_TOTAL = (base_total - 1) if skip_base else base_total
|
||||
|
||||
# 1. Try to use uv for faster installs (must happen before pip upgrade
|
||||
|
|
@ -399,6 +439,28 @@ def install_python_stack() -> int:
|
|||
# 3. Core packages: unsloth-zoo + unsloth (or custom package name)
|
||||
if skip_base:
|
||||
print(_green(f"✅ {package_name} already installed — skipping base packages"))
|
||||
elif NO_TORCH:
|
||||
# No-torch mode: install unsloth + unsloth-zoo without torch deps
|
||||
_progress("base packages (no torch)")
|
||||
pip_install(
|
||||
"Updating base packages (no-torch mode)",
|
||||
"--no-cache-dir",
|
||||
"--no-deps",
|
||||
"--upgrade-package",
|
||||
"unsloth",
|
||||
"--upgrade-package",
|
||||
"unsloth-zoo",
|
||||
req = REQ_ROOT / "base.txt",
|
||||
)
|
||||
if local_repo:
|
||||
pip_install(
|
||||
"Overlaying local repo (editable)",
|
||||
"--no-cache-dir",
|
||||
"--no-deps",
|
||||
"-e",
|
||||
local_repo,
|
||||
constrain = False,
|
||||
)
|
||||
elif local_repo:
|
||||
# Local dev install: update deps from base.txt, then overlay the
|
||||
# local checkout as an editable install (--no-deps so torch is
|
||||
|
|
@ -462,16 +524,22 @@ def install_python_stack() -> int:
|
|||
)
|
||||
|
||||
# 4. Overrides (torchao, transformers) -- force-reinstall
|
||||
_progress("dependency overrides")
|
||||
pip_install(
|
||||
"Installing dependency overrides",
|
||||
"--force-reinstall",
|
||||
"--no-cache-dir",
|
||||
req = REQ_ROOT / "overrides.txt",
|
||||
)
|
||||
# Skip entirely when torch is unavailable (e.g. Intel Mac GGUF-only mode)
|
||||
# because overrides.txt contains torchao which requires torch.
|
||||
if NO_TORCH:
|
||||
_progress("dependency overrides (skipped, no torch)")
|
||||
else:
|
||||
_progress("dependency overrides")
|
||||
pip_install(
|
||||
"Installing dependency overrides",
|
||||
"--force-reinstall",
|
||||
"--no-cache-dir",
|
||||
req = REQ_ROOT / "overrides.txt",
|
||||
)
|
||||
|
||||
# 5. Triton kernels (no-deps, from source)
|
||||
if not IS_WINDOWS:
|
||||
# Skip on Windows (no support) and macOS (no support).
|
||||
if not IS_WINDOWS and not IS_MACOS:
|
||||
_progress("triton kernels")
|
||||
pip_install(
|
||||
"Installing triton kernels",
|
||||
|
|
|
|||
Loading…
Reference in a new issue