Compare commits

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

2 commits

Author SHA1 Message Date
Roland Tannous
cf65e488d8 install local branch in Docker setup.sh path 2026-03-26 03:55:35 +00:00
Roland Tannous
4eace0d5ae add Docker support: skip venv, install only missing deps 2026-03-26 02:36:28 +00:00
3 changed files with 206 additions and 174 deletions

View file

@ -354,132 +354,125 @@ def install_python_stack() -> int:
base_total = 10 if IS_WINDOWS else 11 base_total = 10 if IS_WINDOWS else 11
_TOTAL = (base_total - 1) if skip_base else base_total _TOTAL = (base_total - 1) if skip_base else base_total
# 1. Try to use uv for faster installs (must happen before pip upgrade # # 1. Try to use uv for faster installs (must happen before pip upgrade
# because uv venvs don't include pip by default) # # because uv venvs don't include pip by default)
USE_UV = _bootstrap_uv() # USE_UV = _bootstrap_uv()
# 2. Ensure pip is available (uv venvs created by install.sh don't include pip) # # 2. Ensure pip is available (uv venvs created by install.sh don't include pip)
_progress("pip bootstrap") # _progress("pip bootstrap")
if USE_UV: # if USE_UV:
run( # run(
"Bootstrapping pip via uv", # "Bootstrapping pip via uv",
[ # [
"uv", # "uv",
"pip", # "pip",
"install", # "install",
"--python", # "--python",
sys.executable, # sys.executable,
"pip", # "pip",
], # ],
) # )
else: # else:
# pip may not exist yet (uv-created venvs omit it). Try ensurepip # # pip may not exist yet (uv-created venvs omit it). Try ensurepip
# first, then upgrade. Only fall back to a direct upgrade when pip # # first, then upgrade. Only fall back to a direct upgrade when pip
# is already present. # # is already present.
_has_pip = ( # _has_pip = (
subprocess.run( # subprocess.run(
[sys.executable, "-m", "pip", "--version"], # [sys.executable, "-m", "pip", "--version"],
stdout = subprocess.DEVNULL, # stdout = subprocess.DEVNULL,
stderr = subprocess.DEVNULL, # stderr = subprocess.DEVNULL,
).returncode # ).returncode
== 0 # == 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: # # 3. Core packages: unsloth-zoo + unsloth (or custom package name)
run( # if skip_base:
"Bootstrapping pip via ensurepip", # print(_green(f"✅ {package_name} already installed — skipping base packages"))
[sys.executable, "-m", "ensurepip", "--upgrade"], # elif local_repo:
) # _progress("base packages")
else: # pip_install(
run( # "Updating base packages",
"Upgrading pip", # "--no-cache-dir",
[sys.executable, "-m", "pip", "install", "--upgrade", "pip"], # "--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) # # 3. Extra dependencies
if skip_base: # _progress("unsloth extras")
print(_green(f"{package_name} already installed — skipping base packages")) # pip_install(
elif local_repo: # "Installing additional unsloth dependencies",
# Local dev install: update deps from base.txt, then overlay the # "--no-cache-dir",
# local checkout as an editable install (--no-deps so torch is # req = REQ_ROOT / "extras.txt",
# 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",
)
# 3. Extra dependencies # # 3b. Extra dependencies (no-deps) -- audio model support etc.
_progress("unsloth extras") # _progress("extra codecs")
pip_install( # pip_install(
"Installing additional unsloth dependencies", # "Installing extras (no-deps)",
"--no-cache-dir", # "--no-deps",
req = REQ_ROOT / "extras.txt", # "--no-cache-dir",
) # req = REQ_ROOT / "extras-no-deps.txt",
# )
# 3b. Extra dependencies (no-deps) -- audio model support etc. # # 4. Overrides (torchao, transformers) -- force-reinstall
_progress("extra codecs") # _progress("dependency overrides")
pip_install( # pip_install(
"Installing extras (no-deps)", # "Installing dependency overrides",
"--no-deps", # "--force-reinstall",
"--no-cache-dir", # "--no-cache-dir",
req = REQ_ROOT / "extras-no-deps.txt", # req = REQ_ROOT / "overrides.txt",
) # )
# 4. Overrides (torchao, transformers) -- force-reinstall # # 5. Triton kernels (no-deps, from source)
_progress("dependency overrides") # if not IS_WINDOWS:
pip_install( # _progress("triton kernels")
"Installing dependency overrides", # pip_install(
"--force-reinstall", # "Installing triton kernels",
"--no-cache-dir", # "--no-deps",
req = REQ_ROOT / "overrides.txt", # "--no-cache-dir",
) # req = REQ_ROOT / "triton-kernels.txt",
# constrain = False,
# 5. Triton kernels (no-deps, from source) # )
if not IS_WINDOWS:
_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 # # 6. Patch: override llama_cpp.py with fix from unsloth-zoo feature/llama-cpp-windows-support branch
# patch_package_file( # patch_package_file(

View file

@ -282,7 +282,7 @@ fi
# The venv must already exist (created by install.sh). # The venv must already exist (created by install.sh).
# This script (setup.sh / "unsloth studio update") only updates packages. # This script (setup.sh / "unsloth studio update") only updates packages.
STUDIO_HOME="$HOME/.unsloth/studio" STUDIO_HOME="${UNSLOTH_STUDIO_HOME:-$HOME/.unsloth/studio}"
VENV_DIR="$STUDIO_HOME/unsloth_studio" VENV_DIR="$STUDIO_HOME/unsloth_studio"
VENV_T5_DIR="$STUDIO_HOME/.venv_t5" VENV_T5_DIR="$STUDIO_HOME/.venv_t5"
@ -293,7 +293,12 @@ VENV_T5_DIR="$STUDIO_HOME/.venv_t5"
# Note: do NOT delete $STUDIO_HOME/.venv here — install.sh handles migration # Note: do NOT delete $STUDIO_HOME/.venv here — install.sh handles migration
_COLAB_NO_VENV=false _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 if [ "$IS_COLAB" = true ]; then
# On Colab there is no Studio venv -- install backend deps into system Python. # On Colab there is no Studio venv -- install backend deps into system Python.
# Strip all version constraints so pip keeps Colab's pre-installed # Strip all version constraints so pip keeps Colab's pre-installed
@ -355,6 +360,38 @@ if [ "$_COLAB_NO_VENV" = true ]; then
exit 0 exit 0
fi 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
echo ""
echo " Pre-installing transformers 5.x for newer model support..."
mkdir -p "$VENV_T5_DIR"
pip install --target "$VENV_T5_DIR" --no-deps "transformers==5.3.0" 2>/dev/null
pip install --target "$VENV_T5_DIR" --no-deps "huggingface_hub==1.7.1" 2>/dev/null
pip install --target "$VENV_T5_DIR" --no-deps "hf_xet==1.4.2" 2>/dev/null
pip install --target "$VENV_T5_DIR" "tiktoken" 2>/dev/null
echo "✅ Transformers 5.x pre-installed to $VENV_T5_DIR/"
echo ""
echo "╔══════════════════════════════════════╗"
echo "║ Docker Studio Setup Complete! ║"
echo "╚══════════════════════════════════════╝"
exit 0
fi
# ── Check if Python deps need updating ── # ── Check if Python deps need updating ──
# Compare installed package version against PyPI latest. # Compare installed package version against PyPI latest.
# Skip all Python dependency work if versions match (fast update path). # Skip all Python dependency work if versions match (fast update path).

View file

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