Compare commits

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

24 commits

Author SHA1 Message Date
Roland Tannous
8a531e3290 removed flash attention 2026-04-16 01:21:18 +04:00
Roland Tannous
c3cec00ca8 spark studio v0.13.6 2026.4.5 2026-04-16 00:48:14 +04:00
Roland Tannous
fce1a1e845 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-16 00:42:43 +04:00
Roland Tannous
c379471e45 update 2026-04-16 00:42:43 +04:00
pre-commit-ci[bot]
fe842482a2 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-16 00:42:43 +04:00
Roland Tannous
c3c03388ea clean venv_t5 dirs before re-install in setup.sh, clarify version alias comment 2026-04-16 00:42:43 +04:00
Roland Tannous
bd4aa24bc6 narrow Nemotron trust_remote_code to nemotron_h/nemotron-3-nano, add to export worker 2026-04-16 00:42:43 +04:00
Roland Tannous
74ca59487f extract shared activate_transformers_for_subprocess into transformers_version.py 2026-04-16 00:42:43 +04:00
Roland Tannous
ed694d67ee reorder tier checks: all substring matches before config.json fetches 2026-04-16 00:42:43 +04:00
pre-commit-ci[bot]
ee47f7c4f4 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-16 00:42:43 +04:00
Roland Tannous
2efca0c43e add unsloth/nvidia namespace guard to Nemotron trust_remote_code auto-enable 2026-04-16 00:42:43 +04:00
Roland Tannous
cef87885ab Revert "use config.json model_type for tier detection, add unsloth/nvidia namespace guard"
This reverts commit fc49ae2453.
2026-04-16 00:42:43 +04:00
Roland Tannous
e7c2072258 Revert "[pre-commit.ci] auto fixes from pre-commit.com hooks"
This reverts commit fb43d468e2.
2026-04-16 00:42:43 +04:00
pre-commit-ci[bot]
332b8e9e36 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-16 00:42:43 +04:00
Roland Tannous
8cc132dc6e use config.json model_type for tier detection, add unsloth/nvidia namespace guard 2026-04-16 00:42:43 +04:00
pre-commit-ci[bot]
0586aefdde [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-16 00:42:43 +04:00
Roland Tannous
fd098a3f3a restrict trust_remote_code auto-enable to Nemotron models only 2026-04-16 00:42:40 +04:00
Roland Tannous
ff8a65bc3f revert FORCE_FLOAT32 dtype change 2026-04-16 00:39:52 +04:00
Roland Tannous
ea1cce41ad fix bfloat16 crash on T4 for FORCE_FLOAT32 models and disable trust_remote_code auto-enable for native t5 models 2026-04-16 00:39:52 +04:00
Roland Tannous
09eef9b225 split venv_t5 into venv_t5_530 and venv_t5_550 for tiered transformers 5.x support 2026-04-16 00:39:52 +04:00
Roland Tannous
cf563d1a51 Skip llama.cpp install in Docker mode 2026-04-16 00:39:52 +04:00
pre-commit-ci[bot]
60f4ad4f8f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-04-16 00:39:52 +04:00
Roland Tannous
9c2b6a2560 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-16 00:39:52 +04:00
Roland Tannous
526c96980b add Docker support: skip venv, install only missing deps 2026-04-16 00:39:52 +04:00
8 changed files with 463 additions and 269 deletions

1
.worktreeinclude Normal file
View file

@ -0,0 +1 @@
CLAUDE.md

View file

@ -64,6 +64,97 @@ def _model_wants_causal_conv1d(model_name: str) -> bool:
)
def _causal_conv1d_platform_tag() -> str | None:
machine = platform.machine().lower()
if sys.platform.startswith("linux"):
if machine in {"x86_64", "amd64"}:
return "linux_x86_64"
if machine in {"aarch64", "arm64"}:
return "linux_aarch64"
return None
# No prebuilt wheels published for macOS or Windows
return None
def _probe_causal_conv1d_env() -> dict[str, str] | None:
try:
probe = _sp.run(
[
sys.executable,
"-c",
(
"import json, sys, re, torch; "
"parts = torch.__version__.split('+', 1)[0].split('.')[:2]; "
"minor = re.sub(r'[^0-9].*', '', parts[1]) if len(parts) > 1 else '0'; "
"torch_mm = parts[0] + '.' + minor; "
"print(json.dumps({"
"'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 '', "
"'cxx11abi': str(torch._C._GLIBCXX_USE_CXX11_ABI).upper()"
"}))"
),
],
stdout = _sp.PIPE,
stderr = _sp.PIPE,
text = True,
timeout = 30,
)
except _sp.TimeoutExpired:
logger.warning("Torch environment probe timed out after 30s")
return None
if probe.returncode != 0:
logger.warning(
"Failed to probe torch environment for causal-conv1d wheel:\n%s",
probe.stdout,
)
return None
try:
return json.loads(probe.stdout.strip())
except json.JSONDecodeError:
logger.warning(
"Failed to parse torch environment probe output: %s", probe.stdout
)
return None
def _direct_wheel_url(
*,
filename_prefix: str,
package_version: str,
release_tag: str,
release_base_url: str,
env: dict[str, str] | None = None,
) -> str | None:
env = env or _probe_causal_conv1d_env()
platform_tag = _causal_conv1d_platform_tag()
if env is None or platform_tag is None or not env.get("cuda_major"):
return None
filename = (
f"{filename_prefix}-{package_version}"
f"+cu{env['cuda_major']}torch{env['torch_mm']}"
f"cxx11abi{env['cxx11abi']}-{env['python_tag']}-{env['python_tag']}-{platform_tag}.whl"
)
return f"{release_base_url}/{release_tag}/{filename}"
def _url_exists(url: str) -> bool:
try:
request = urllib.request.Request(url, method = "HEAD")
with urllib.request.urlopen(request, timeout = 10):
return True
except urllib.error.HTTPError as exc:
if exc.code == 404:
return False
logger.warning("Unexpected HTTP error while probing %s: %s", url, exc)
return False
except Exception as exc:
logger.warning("Failed to probe %s: %s", url, exc)
return False
def _install_package_wheel_first(
*,
event_queue: Any,
@ -397,10 +488,10 @@ def run_training_process(
try:
_ensure_causal_conv1d_fast_path(event_queue, model_name)
_ensure_mamba_ssm(event_queue, model_name)
_ensure_flash_attn_for_long_context(
event_queue,
int(config.get("max_seq_length", 2048)),
)
#_ensure_flash_attn_for_long_context(
# event_queue,
# int(config.get("max_seq_length", 2048)),
#)
except Exception as exc:
event_queue.put(
{

View file

@ -193,10 +193,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

@ -841,220 +841,157 @@ def install_python_stack() -> int:
base_total += 3
_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))
# # 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,
# )
_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,
)
if not IS_WINDOWS and not IS_MACOS and not NO_TORCH:
_progress("flash-attn")
_ensure_flash_attn()
#if not IS_WINDOWS and not IS_MACOS and not NO_TORCH:
# _progress("flash-attn")
# _ensure_flash_attn()
# # 6. Patch: override llama_cpp.py with fix from unsloth-zoo feature/llama-cpp-windows-support branch
# patch_package_file(

View file

@ -1581,6 +1581,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

@ -1562,7 +1562,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

@ -158,56 +158,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