Compare commits

...
Sign in to create a new pull request.

22 commits

Author SHA1 Message Date
Roland Tannous
b362c60719 fix: derive HF_HUB_CACHE from HF_HOME when set
Previously HF_HUB_CACHE always defaulted to ~/.cache/huggingface/hub
even when HF_HOME was explicitly set (e.g. in Docker). This caused
models to download to the wrong location instead of the configured
HF_HOME path.
2026-04-13 09:24:57 +04:00
Roland Tannous
c26d909265 update 2026-04-10 19:05:20 +00:00
pre-commit-ci[bot]
940223ea6b [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-10 19:05:20 +00:00
Roland Tannous
44d0d69cb2 clean venv_t5 dirs before re-install in setup.sh, clarify version alias comment 2026-04-10 19:05:20 +00:00
Roland Tannous
f9923b398c narrow Nemotron trust_remote_code to nemotron_h/nemotron-3-nano, add to export worker 2026-04-10 19:05:20 +00:00
Roland Tannous
c512c36cf0 extract shared activate_transformers_for_subprocess into transformers_version.py 2026-04-10 19:05:16 +00:00
Roland Tannous
5375c2628a reorder tier checks: all substring matches before config.json fetches 2026-04-10 19:04:44 +00:00
pre-commit-ci[bot]
da68764ef1 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-10 19:04:44 +00:00
Roland Tannous
c77294bdc9 add unsloth/nvidia namespace guard to Nemotron trust_remote_code auto-enable 2026-04-10 19:04:44 +00:00
Roland Tannous
4f01b4569d Revert "use config.json model_type for tier detection, add unsloth/nvidia namespace guard"
This reverts commit fc49ae2453.
2026-04-10 19:04:44 +00:00
Roland Tannous
370b41c4fc Revert "[pre-commit.ci] auto fixes from pre-commit.com hooks"
This reverts commit fb43d468e2.
2026-04-10 19:04:44 +00:00
pre-commit-ci[bot]
a2aba7a2be [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-10 19:04:44 +00:00
Roland Tannous
567df493ab use config.json model_type for tier detection, add unsloth/nvidia namespace guard 2026-04-10 19:04:42 +00:00
pre-commit-ci[bot]
db5a044c91 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-10 19:04:09 +00:00
Roland Tannous
833b38375a restrict trust_remote_code auto-enable to Nemotron models only 2026-04-10 19:04:06 +00:00
Roland Tannous
f3ff84fc5c revert FORCE_FLOAT32 dtype change 2026-04-10 19:02:19 +00:00
Roland Tannous
245f70d9ad fix bfloat16 crash on T4 for FORCE_FLOAT32 models and disable trust_remote_code auto-enable for native t5 models 2026-04-10 19:02:16 +00:00
Roland Tannous
d0f15deab7 split venv_t5 into venv_t5_530 and venv_t5_550 for tiered transformers 5.x support 2026-04-10 18:57:17 +00:00
Roland Tannous
47939e63fd Skip llama.cpp install in Docker mode 2026-04-10 18:47:36 +00:00
pre-commit-ci[bot]
b9b829bf59 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-10 18:47:36 +00:00
Roland Tannous
14f8daeaf3 fix: patch PEFT for Gemma4ClippableLinear in loader checkpoint path
The same Gemma4ClippableLinear monkey-patch that exists in vision.py
for training is needed in loader.py for loading existing checkpoints
(used by export and inference).

Gemma4ClippableLinear wraps nn.Linear but does not subclass it, so
PEFT's LoRA injection fails with "Target module not supported".
The patch redirects PEFT to target the inner .linear child instead.

Applied only to the vision model PeftModel.from_pretrained path.
Temporary fix until PEFT adds native support (peft#3129).
2026-04-10 18:47:36 +00:00
Roland Tannous
04bca978b4 add Docker support: skip venv, install only missing deps 2026-04-10 18:47:32 +00:00
7 changed files with 382 additions and 364 deletions

View file

@ -86,7 +86,6 @@ def _probe_causal_conv1d_env() -> dict[str, str] | None:
"'python_tag': f'cp{sys.version_info.major}{sys.version_info.minor}', "
"'torch_mm': torch_mm, "
"'cuda_major': str(int(str(torch.version.cuda).split('.', 1)[0])) if torch.version.cuda else '', "
"'hip_version': str(torch.version.hip) if getattr(torch.version, 'hip', None) else '', "
"'cxx11abi': str(torch._C._GLIBCXX_USE_CXX11_ABI).upper()"
"}))"
),
@ -238,111 +237,28 @@ def _install_package_wheel_first(
else:
logger.info("No published %s wheel found: %s", display_name, wheel_url)
is_hip = env and env.get("hip_version")
if is_hip and not shutil.which("hipcc"):
logger.error(
"%s requires hipcc for source compilation on ROCm. "
"Install the ROCm HIP SDK: https://rocm.docs.amd.com",
display_name,
)
_send_status(
event_queue,
f"{display_name}: hipcc not found (ROCm HIP SDK required)",
)
return
if is_hip:
_send_status(
event_queue,
f"Compiling {display_name} from source for ROCm "
"(this may take several minutes)...",
)
else:
_send_status(event_queue, f"Installing {display_name} from PyPI...")
# Prefer uv for faster dependency resolution when available
if shutil.which("uv"):
pypi_cmd = [
"uv",
"pip",
"install",
"--python",
sys.executable,
"--no-build-isolation",
"--no-deps",
]
# Avoid stale cache artifacts from partial HIP source builds
if is_hip:
pypi_cmd.append("--no-cache")
pypi_cmd.append(f"{pypi_name}=={pypi_version}")
else:
pypi_cmd = [
sys.executable,
"-m",
"pip",
"install",
"--no-build-isolation",
"--no-deps",
"--no-cache-dir",
f"{pypi_name}=={pypi_version}",
]
# Source compilation on ROCm can take 10-30 minutes; use a generous
# timeout. Non-HIP installs preserve the pre-existing "no timeout"
# behaviour so unrelated slow installs (e.g. causal-conv1d source
# build on Linux aarch64 or unsupported torch/CUDA combinations)
# are not aborted at 5 minutes by this PR.
_run_kwargs: dict[str, Any] = {
"stdout": _sp.PIPE,
"stderr": _sp.STDOUT,
"text": True,
}
if is_hip:
_run_kwargs["timeout"] = 1800
try:
result = _sp.run(pypi_cmd, **_run_kwargs)
except _sp.TimeoutExpired:
logger.error(
"%s installation timed out after %ds",
display_name,
_run_kwargs.get("timeout"),
)
_send_status(
event_queue,
f"{display_name} installation timed out after "
f"{_run_kwargs.get('timeout')}s",
)
return
_send_status(event_queue, f"Installing {display_name} from PyPI...")
pypi_cmd = [
sys.executable,
"-m",
"pip",
"install",
"--no-build-isolation",
"--no-deps",
"--no-cache-dir",
f"{pypi_name}=={pypi_version}",
]
result = _sp.run(
pypi_cmd,
stdout = _sp.PIPE,
stderr = _sp.STDOUT,
text = True,
)
if result.returncode != 0:
if is_hip:
# Surface a clear error for ROCm source build failures
error_lines = (result.stdout or "").strip().splitlines()
snippet = "\n".join(error_lines[-5:]) if error_lines else "(no output)"
logger.error(
"Failed to compile %s for ROCm:\n%s",
display_name,
result.stdout,
)
_send_status(
event_queue,
f"Failed to compile {display_name} for ROCm. "
"Check that hipcc and ROCm development headers are installed.\n"
f"{snippet}",
)
else:
logger.error(
"Failed to install %s from PyPI:\n%s",
display_name,
result.stdout,
)
logger.error("Failed to install %s from PyPI:\n%s", display_name, result.stdout)
return
if is_hip:
logger.info("Compiled and installed %s from source for ROCm", display_name)
else:
logger.info("Installed %s from PyPI", display_name)
logger.info("Installed %s from PyPI", display_name)
def _ensure_causal_conv1d_fast_path(event_queue: Any, model_name: str) -> None:

View file

@ -148,10 +148,11 @@ def _setup_cache_env() -> None:
os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")
).expanduser()
hf_default = xdg_cache / "huggingface"
hf_home = Path(os.environ.get("HF_HOME", str(hf_default)))
defaults: dict[str, str] = {
"HF_HOME": str(hf_default),
"HF_HUB_CACHE": str(hf_default / "hub"),
"HF_XET_CACHE": str(hf_default / "xet"),
"HF_HUB_CACHE": str(hf_home / "hub"),
"HF_XET_CACHE": str(hf_home / "xet"),
"UV_CACHE_DIR": str(root / "uv"),
"VLLM_CACHE_ROOT": str(root / "vllm"),
}

View file

@ -768,216 +768,153 @@ def install_python_stack() -> int:
base_total += 2
_TOTAL = (base_total - 1) if skip_base else base_total
# 1. Try to use uv for faster installs (must happen before pip upgrade
# because uv venvs don't include pip by default)
USE_UV = _bootstrap_uv()
# # 1. Try to use uv for faster installs (must happen before pip upgrade
# # because uv venvs don't include pip by default)
# USE_UV = _bootstrap_uv()
# 2. Ensure pip is available (uv venvs created by install.sh don't include pip)
_progress("pip bootstrap")
if USE_UV:
run(
"Bootstrapping pip via uv",
[
"uv",
"pip",
"install",
"--python",
sys.executable,
"pip",
],
)
else:
# pip may not exist yet (uv-created venvs omit it). Try ensurepip
# first, then upgrade. Only fall back to a direct upgrade when pip
# is already present.
_has_pip = (
subprocess.run(
[sys.executable, "-m", "pip", "--version"],
stdout = subprocess.DEVNULL,
stderr = subprocess.DEVNULL,
).returncode
== 0
)
# # 2. Ensure pip is available (uv venvs created by install.sh don't include pip)
# _progress("pip bootstrap")
# if USE_UV:
# run(
# "Bootstrapping pip via uv",
# [
# "uv",
# "pip",
# "install",
# "--python",
# sys.executable,
# "pip",
# ],
# )
# else:
# # pip may not exist yet (uv-created venvs omit it). Try ensurepip
# # first, then upgrade. Only fall back to a direct upgrade when pip
# # is already present.
# _has_pip = (
# subprocess.run(
# [sys.executable, "-m", "pip", "--version"],
# stdout = subprocess.DEVNULL,
# stderr = subprocess.DEVNULL,
# ).returncode
# == 0
# )
#
# if not _has_pip:
# run(
# "Bootstrapping pip via ensurepip",
# [sys.executable, "-m", "ensurepip", "--upgrade"],
# )
# else:
# run(
# "Upgrading pip",
# [sys.executable, "-m", "pip", "install", "--upgrade", "pip"],
# )
if not _has_pip:
run(
"Bootstrapping pip via ensurepip",
[sys.executable, "-m", "ensurepip", "--upgrade"],
)
else:
run(
"Upgrading pip",
[sys.executable, "-m", "pip", "install", "--upgrade", "pip"],
)
# # 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 update path: install unsloth + unsloth-zoo with --no-deps
# # (current PyPI metadata still declares torch as a hard dep), then
# # runtime deps with --no-deps (avoids transitive torch).
# _progress("base packages (no torch)")
# pip_install(
# f"Updating {package_name} + unsloth-zoo (no-torch mode)",
# "--no-cache-dir",
# "--no-deps",
# "--upgrade-package",
# package_name,
# "--upgrade-package",
# "unsloth-zoo",
# package_name,
# "unsloth-zoo",
# )
# pip_install(
# "Installing no-torch runtime deps",
# "--no-cache-dir",
# "--no-deps",
# req = REQ_ROOT / "no-torch-runtime.txt",
# )
# if local_repo:
# pip_install(
# "Overlaying local repo (editable)",
# "--no-cache-dir",
# "--no-deps",
# "-e",
# local_repo,
# constrain = False,
# )
# elif local_repo:
# _progress("base packages")
# pip_install(
# "Updating base packages",
# "--no-cache-dir",
# "--upgrade-package",
# "unsloth",
# "--upgrade-package",
# "unsloth-zoo",
# req = REQ_ROOT / "base.txt",
# )
# pip_install(
# "Overlaying local repo (editable)",
# "--no-cache-dir",
# "--no-deps",
# "-e",
# local_repo,
# constrain = False,
# )
# elif package_name != "unsloth":
# _progress("base packages")
# pip_install(
# f"Installing {package_name}",
# "--no-cache-dir",
# package_name,
# )
# else:
# _progress("base packages")
# pip_install(
# "Updating base packages",
# "--no-cache-dir",
# "--upgrade-package",
# "unsloth",
# "--upgrade-package",
# "unsloth-zoo",
# req = REQ_ROOT / "base.txt",
# )
# 3. Core packages: unsloth-zoo + unsloth (or custom package name)
if skip_base:
pass
elif NO_TORCH:
# No-torch update path: install unsloth + unsloth-zoo with --no-deps
# (current PyPI metadata still declares torch as a hard dep), then
# runtime deps with --no-deps (avoids transitive torch).
_progress("base packages (no torch)")
pip_install(
f"Updating {package_name} + unsloth-zoo (no-torch mode)",
"--no-cache-dir",
"--no-deps",
"--upgrade-package",
package_name,
"--upgrade-package",
"unsloth-zoo",
package_name,
"unsloth-zoo",
)
pip_install(
"Installing no-torch runtime deps",
"--no-cache-dir",
"--no-deps",
req = REQ_ROOT / "no-torch-runtime.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
# never re-resolved).
_progress("base packages")
pip_install(
"Updating base packages",
"--no-cache-dir",
"--upgrade-package",
"unsloth",
"--upgrade-package",
"unsloth-zoo",
req = REQ_ROOT / "base.txt",
)
pip_install(
"Overlaying local repo (editable)",
"--no-cache-dir",
"--no-deps",
"-e",
local_repo,
constrain = False,
)
elif package_name != "unsloth":
# Custom package name (e.g. roland-sloth for testing) — install directly
_progress("base packages")
pip_install(
f"Installing {package_name}",
"--no-cache-dir",
package_name,
)
else:
# Update path: upgrade only unsloth + unsloth-zoo while preserving
# existing torch/CUDA installations. Torch is pre-installed by
# install.sh / setup.ps1; --upgrade-package targets only base pkgs.
_progress("base packages")
pip_install(
"Updating base packages",
"--no-cache-dir",
"--upgrade-package",
"unsloth",
"--upgrade-package",
"unsloth-zoo",
req = REQ_ROOT / "base.txt",
)
# pip_install(
# "Installing additional unsloth dependencies",
# "--no-cache-dir",
# req = REQ_ROOT / "extras.txt",
# )
# 2b. AMD ROCm: reinstall torch with HIP wheels if the host has ROCm but the
# venv received CPU-only torch (common when pip resolves torch from PyPI).
# Must come immediately after base packages so torch is present for inspection.
if not IS_WINDOWS and not IS_MACOS and not NO_TORCH:
_progress("ROCm torch check")
_ensure_rocm_torch()
# pip_install(
# "Installing extras (no-deps)",
# "--no-deps",
# "--no-cache-dir",
# req = REQ_ROOT / "extras-no-deps.txt",
# )
# Windows + AMD GPU: PyTorch does not publish ROCm wheels for Windows.
# Detect and warn so users know manual steps are needed for GPU training.
if IS_WINDOWS and not NO_TORCH and not _has_usable_nvidia_gpu():
# Validate actual AMD GPU presence (not just tool existence)
import re as _re_win
# # 4. Overrides (torchao, transformers) -- force-reinstall
# _progress("dependency overrides")
# pip_install(
# "Installing dependency overrides",
# "--force-reinstall",
# "--no-cache-dir",
# req = REQ_ROOT / "overrides.txt",
# )
def _win_amd_smi_has_gpu(stdout: str) -> bool:
return bool(_re_win.search(r"(?im)^gpu\s*[:\[]\s*\d", stdout))
_win_amd_gpu = False
for _wcmd, _check_fn in (
(["hipinfo"], lambda out: "gcnarchname" in out.lower()),
(["amd-smi", "list"], _win_amd_smi_has_gpu),
):
_wexe = shutil.which(_wcmd[0])
if not _wexe:
continue
try:
_wr = subprocess.run(
[_wexe, *_wcmd[1:]],
stdout = subprocess.PIPE,
stderr = subprocess.DEVNULL,
text = True,
timeout = 10,
)
except Exception:
continue
if _wr.returncode == 0 and _check_fn(_wr.stdout):
_win_amd_gpu = True
break
if _win_amd_gpu:
_safe_print(
_dim(" Note:"),
"AMD GPU detected on Windows. ROCm-enabled PyTorch must be",
)
_safe_print(
" " * 8,
"installed manually. See: https://docs.unsloth.ai/get-started/install-and-update/amd",
)
# 3. Extra dependencies
_progress("unsloth extras")
pip_install(
"Installing additional unsloth dependencies",
"--no-cache-dir",
req = REQ_ROOT / "extras.txt",
)
# 3b. Extra dependencies (no-deps) -- audio model support etc.
_progress("extra codecs")
pip_install(
"Installing extras (no-deps)",
"--no-deps",
"--no-cache-dir",
req = REQ_ROOT / "extras-no-deps.txt",
)
# 4. Overrides (torchao, transformers) -- force-reinstall
# 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)
# 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",
"--no-deps",
"--no-cache-dir",
req = REQ_ROOT / "triton-kernels.txt",
constrain = False,
)
# # 5. Triton kernels (no-deps, from source)
# # 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",
# "--no-deps",
# "--no-cache-dir",
# req = REQ_ROOT / "triton-kernels.txt",
# constrain = False,
# )
# # 6. Patch: override llama_cpp.py with fix from unsloth-zoo feature/llama-cpp-windows-support branch
# patch_package_file(

View file

@ -1579,6 +1579,89 @@ if ($stackExit -ne 0) {
exit 1
}
# ── Pre-install transformers 5.x into .venv_t5_530/ and .venv_t5_550/ ──
# Models like GLM-4.7-Flash, Qwen3 MoE need transformers>=5.3.0.
# Gemma 4 models need transformers>=5.5.0.
# Pre-install into separate directories to avoid runtime pip overhead.
# The training subprocess prepends the appropriate dir to sys.path.
Write-Host ""
# Clean up legacy single .venv_t5 directory
$VenvT5Legacy = Join-Path $env:USERPROFILE ".unsloth\studio\.venv_t5"
if (Test-Path $VenvT5Legacy) { Remove-Item -Recurse -Force $VenvT5Legacy }
$prevEAP_t5 = $ErrorActionPreference
$ErrorActionPreference = "Continue"
# --- .venv_t5_530 (transformers 5.3.0) ---
substep "pre-installing transformers 5.3.0 for newer model support..."
$VenvT5_530Dir = Join-Path $env:USERPROFILE ".unsloth\studio\.venv_t5_530"
if (Test-Path $VenvT5_530Dir) { Remove-Item -Recurse -Force $VenvT5_530Dir }
New-Item -ItemType Directory -Path $VenvT5_530Dir -Force | Out-Null
foreach ($pkg in @("transformers==5.3.0", "huggingface_hub==1.8.0", "hf_xet==1.4.2")) {
if ($script:UnslothVerbose) {
Fast-Install --target $VenvT5_530Dir --no-deps $pkg
$t5PkgExit = $LASTEXITCODE
$output = ""
} else {
$output = Fast-Install --target $VenvT5_530Dir --no-deps $pkg | Out-String
$t5PkgExit = $LASTEXITCODE
}
if ($t5PkgExit -ne 0) {
Write-Host "[FAIL] Could not install $pkg into .venv_t5_530/" -ForegroundColor Red
Write-Host $output -ForegroundColor Red
$ErrorActionPreference = $prevEAP_t5
exit 1
}
}
if ($script:UnslothVerbose) {
Fast-Install --target $VenvT5_530Dir tiktoken
$tiktokenInstallExit = $LASTEXITCODE
$output = ""
} else {
$output = Fast-Install --target $VenvT5_530Dir tiktoken | Out-String
$tiktokenInstallExit = $LASTEXITCODE
}
if ($tiktokenInstallExit -ne 0) {
substep "Could not install tiktoken into .venv_t5_530/ -- Qwen tokenizers may fail" "Yellow"
}
step "transformers" "5.3.0 pre-installed"
# --- .venv_t5_550 (transformers 5.5.0) ---
substep "pre-installing transformers 5.5.0 for Gemma 4 support..."
$VenvT5_550Dir = Join-Path $env:USERPROFILE ".unsloth\studio\.venv_t5_550"
if (Test-Path $VenvT5_550Dir) { Remove-Item -Recurse -Force $VenvT5_550Dir }
New-Item -ItemType Directory -Path $VenvT5_550Dir -Force | Out-Null
foreach ($pkg in @("transformers==5.5.0", "huggingface_hub==1.8.0", "hf_xet==1.4.2")) {
if ($script:UnslothVerbose) {
Fast-Install --target $VenvT5_550Dir --no-deps $pkg
$t5PkgExit = $LASTEXITCODE
$output = ""
} else {
$output = Fast-Install --target $VenvT5_550Dir --no-deps $pkg | Out-String
$t5PkgExit = $LASTEXITCODE
}
if ($t5PkgExit -ne 0) {
Write-Host "[FAIL] Could not install $pkg into .venv_t5_550/" -ForegroundColor Red
Write-Host $output -ForegroundColor Red
$ErrorActionPreference = $prevEAP_t5
exit 1
}
}
if ($script:UnslothVerbose) {
Fast-Install --target $VenvT5_550Dir tiktoken
$tiktokenInstallExit = $LASTEXITCODE
$output = ""
} else {
$output = Fast-Install --target $VenvT5_550Dir tiktoken | Out-String
$tiktokenInstallExit = $LASTEXITCODE
}
if ($tiktokenInstallExit -ne 0) {
substep "Could not install tiktoken into .venv_t5_550/ -- Qwen tokenizers may fail" "Yellow"
}
$ErrorActionPreference = $prevEAP_t5
step "transformers" "5.5.0 pre-installed"
} else {
step "python" "dependencies up to date"
# Restore ErrorActionPreference (was lowered for pip/python section)

View file

@ -392,7 +392,7 @@ if [ -d "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator" ] && command -v npm
fi
# ── Python venv + deps ──
STUDIO_HOME="$HOME/.unsloth/studio"
STUDIO_HOME="${UNSLOTH_STUDIO_HOME:-$HOME/.unsloth/studio}"
VENV_DIR="$STUDIO_HOME/unsloth_studio"
VENV_T5_530_DIR="$STUDIO_HOME/.venv_t5_530"
VENV_T5_550_DIR="$STUDIO_HOME/.venv_t5_550"
@ -405,7 +405,12 @@ VENV_T5_550_DIR="$STUDIO_HOME/.venv_t5_550"
# Note: do NOT delete $STUDIO_HOME/.venv here — install.sh handles migration
_COLAB_NO_VENV=false
if [ ! -x "$VENV_DIR/bin/python" ]; then
_DOCKER_NO_VENV=false
if [ -n "$UNSLOTH_DOCKER" ]; then
# Docker: packages already in /opt/conda — skip venv entirely.
# Only pre-install .venv_t5 for transformers 5.x switching (handled below).
_DOCKER_NO_VENV=true
elif [ ! -x "$VENV_DIR/bin/python" ]; then
if [ "$IS_COLAB" = true ]; then
# On Colab there is no Studio venv -- install backend deps into system Python.
# Strip all version constraints so pip keeps Colab's pre-installed
@ -470,6 +475,52 @@ if [ "$_COLAB_NO_VENV" = true ]; then
substep "continuing to llama.cpp install for GGUF inference support"
fi
# In Docker, packages are pre-installed in /opt/conda — only install missing
# studio/data-designer deps and pre-install .venv_t5 for transformers 5.x.
if [ "$_DOCKER_NO_VENV" = true ]; then
echo " Docker detected — skipping venv activation."
# Install branch's unsloth/unsloth_cli/studio into /opt/conda
# (overwrites PyPI version with Docker-aware code)
echo " Installing local unsloth from branch..."
pip install --force-reinstall --no-deps "$REPO_ROOT"
# Install only missing deps (studio, data-designer, plugin, metadata patch).
# Heavy packages (torch, unsloth, vllm, etc.) are already in /opt/conda.
# install_python_stack.py has steps 1-5 commented out for this branch.
python "$SCRIPT_DIR/install_python_stack.py"
# Pre-install transformers 5.x into .venv_t5_530/ and .venv_t5_550/
echo ""
echo " Pre-installing transformers 5.x for newer model support..."
# Clean up legacy single .venv_t5 directory
[ -d "$STUDIO_HOME/.venv_t5" ] && rm -rf "$STUDIO_HOME/.venv_t5"
[ -d "$VENV_T5_530_DIR" ] && rm -rf "$VENV_T5_530_DIR"
mkdir -p "$VENV_T5_530_DIR"
pip install --target "$VENV_T5_530_DIR" --no-deps "transformers==5.3.0" 2>/dev/null
pip install --target "$VENV_T5_530_DIR" --no-deps "huggingface_hub==1.8.0" 2>/dev/null
pip install --target "$VENV_T5_530_DIR" --no-deps "hf_xet==1.4.2" 2>/dev/null
pip install --target "$VENV_T5_530_DIR" "tiktoken" 2>/dev/null
[ -d "$VENV_T5_550_DIR" ] && rm -rf "$VENV_T5_550_DIR"
mkdir -p "$VENV_T5_550_DIR"
pip install --target "$VENV_T5_550_DIR" --no-deps "transformers==5.5.0" 2>/dev/null
pip install --target "$VENV_T5_550_DIR" --no-deps "huggingface_hub==1.8.0" 2>/dev/null
pip install --target "$VENV_T5_550_DIR" --no-deps "hf_xet==1.4.2" 2>/dev/null
pip install --target "$VENV_T5_550_DIR" "tiktoken" 2>/dev/null
echo "✅ Transformers 5.3.0 pre-installed to $VENV_T5_530_DIR/"
echo "✅ Transformers 5.5.0 pre-installed to $VENV_T5_550_DIR/"
echo ""
echo "╔══════════════════════════════════════╗"
echo "║ Docker Studio Setup Complete! ║"
echo "╚══════════════════════════════════════╝"
exit 0
fi
# ── Check if Python deps need updating ──
# Compare installed package version against PyPI latest.
# Skip all Python dependency work if versions match (fast update path).
@ -504,6 +555,31 @@ fi
if [ "$_SKIP_PYTHON_DEPS" = false ]; then
install_python_stack
# ── 6b. Pre-install transformers 5.x into .venv_t5_530/ and .venv_t5_550/ ──
# Models like GLM-4.7-Flash, Qwen3 MoE need transformers>=5.3.0.
# Gemma 4 models need transformers>=5.5.0.
# Pre-install into separate directories to avoid runtime pip overhead.
# The training subprocess prepends the appropriate dir to sys.path.
# Clean up legacy single .venv_t5 directory
[ -d "$STUDIO_HOME/.venv_t5" ] && rm -rf "$STUDIO_HOME/.venv_t5"
[ -d "$VENV_T5_530_DIR" ] && rm -rf "$VENV_T5_530_DIR"
mkdir -p "$VENV_T5_530_DIR"
run_quiet "install transformers 5.3.0" fast_install --target "$VENV_T5_530_DIR" --no-deps "transformers==5.3.0"
run_quiet "install huggingface_hub for t5_530" fast_install --target "$VENV_T5_530_DIR" --no-deps "huggingface_hub==1.8.0"
run_quiet "install hf_xet for t5_530" fast_install --target "$VENV_T5_530_DIR" --no-deps "hf_xet==1.4.2"
run_quiet "install tiktoken for t5_530" fast_install --target "$VENV_T5_530_DIR" "tiktoken"
step "transformers" "5.3.0 pre-installed"
[ -d "$VENV_T5_550_DIR" ] && rm -rf "$VENV_T5_550_DIR"
mkdir -p "$VENV_T5_550_DIR"
run_quiet "install transformers 5.5.0" fast_install --target "$VENV_T5_550_DIR" --no-deps "transformers==5.5.0"
run_quiet "install huggingface_hub for t5_550" fast_install --target "$VENV_T5_550_DIR" --no-deps "huggingface_hub==1.8.0"
run_quiet "install hf_xet for t5_550" fast_install --target "$VENV_T5_550_DIR" --no-deps "hf_xet==1.4.2"
run_quiet "install tiktoken for t5_550" fast_install --target "$VENV_T5_550_DIR" "tiktoken"
step "transformers" "5.5.0 pre-installed"
else
step "python" "dependencies up to date"
verbose_substep "python deps check: installed=$_PKG_NAME@${INSTALLED_VER:-unknown} latest=${LATEST_VER:-unknown}"
@ -548,6 +624,9 @@ fi
fi
# ── 7. Prefer prebuilt llama.cpp bundles before any source build path ──
if [ "$_DOCKER_NO_VENV" = true ]; then
step "llama.cpp" "skipped (Docker)"
else # begin non-Docker llama.cpp block
UNSLOTH_HOME="$HOME/.unsloth"
mkdir -p "$UNSLOTH_HOME"
LLAMA_CPP_DIR="$UNSLOTH_HOME/llama.cpp"
@ -1031,6 +1110,7 @@ else
fi
}
fi # end _SKIP_GGUF_BUILD check
fi # end non-Docker llama.cpp block
# ── Footer ──
if [ "$_LLAMA_ONLY" = "1" ]; then

View file

@ -1545,7 +1545,6 @@ class FastModel(FastBaseModel):
if _clippable_linear_cls is not None:
from peft.tuners.lora.model import LoraModel as _LoraModel
_original_car = _LoraModel._create_and_replace
def _patched_car(

View file

@ -84,56 +84,58 @@ def studio_default(
if ctx.invoked_subcommand is not None:
return
# Always use the studio venv if it exists and we're not already in it
studio_venv_dir = STUDIO_HOME / "unsloth_studio"
in_studio_venv = sys.prefix.startswith(str(studio_venv_dir))
# In Docker, packages live in /opt/conda — skip venv re-exec entirely.
if not os.environ.get("UNSLOTH_DOCKER"):
# Always use the studio venv if it exists and we're not already in it
studio_venv_dir = STUDIO_HOME / "unsloth_studio"
in_studio_venv = sys.prefix.startswith(str(studio_venv_dir))
if not in_studio_venv:
studio_python = _studio_venv_python()
run_py = _find_run_py()
if studio_python and run_py:
if not silent:
typer.echo("Launching Unsloth Studio... Please wait...")
args = [
str(studio_python),
str(run_py),
"--host",
host,
"--port",
str(port),
]
if frontend:
args.extend(["--frontend", str(frontend)])
if silent:
args.append("--silent")
# On Windows, os.execvp() spawns a child but the parent lingers,
# so Ctrl+C only kills the parent leaving the child orphaned.
# Use subprocess.run() on Windows so the parent waits for the child.
if sys.platform == "win32":
import subprocess as _sp
if not in_studio_venv:
studio_python = _studio_venv_python()
run_py = _find_run_py()
if studio_python and run_py:
if not silent:
typer.echo("Launching Unsloth Studio... Please wait...")
args = [
str(studio_python),
str(run_py),
"--host",
host,
"--port",
str(port),
]
if frontend:
args.extend(["--frontend", str(frontend)])
if silent:
args.append("--silent")
# On Windows, os.execvp() spawns a child but the parent lingers,
# so Ctrl+C only kills the parent leaving the child orphaned.
# Use subprocess.run() on Windows so the parent waits for the child.
if sys.platform == "win32":
import subprocess as _sp
proc = _sp.Popen(args)
try:
rc = proc.wait()
except KeyboardInterrupt:
# Child has its own signal handler — let it finish
rc = proc.wait()
if rc != 0:
typer.echo(
f"\nError: Studio server exited unexpectedly (code {rc}).",
err = True,
)
typer.echo(
"Check the error above. If a package is missing, "
"re-run: unsloth studio setup",
err = True,
)
raise typer.Exit(rc)
proc = _sp.Popen(args)
try:
rc = proc.wait()
except KeyboardInterrupt:
# Child has its own signal handler — let it finish
rc = proc.wait()
if rc != 0:
typer.echo(
f"\nError: Studio server exited unexpectedly (code {rc}).",
err = True,
)
typer.echo(
"Check the error above. If a package is missing, "
"re-run: unsloth studio setup",
err = True,
)
raise typer.Exit(rc)
else:
os.execvp(str(studio_python), args)
else:
os.execvp(str(studio_python), args)
else:
typer.echo("Studio not set up. Run install.sh first.")
raise typer.Exit(1)
typer.echo("Studio not set up. Run install.sh first.")
raise typer.Exit(1)
from studio.backend.run import run_server