Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ Unsloth Studio (Beta) works on **Windows, Linux, WSL** and **macOS**.
#### MacOS, Linux, WSL:
For MacOS, ensure you have `cmake` installed. If not, run `brew install cmake`.
```bash
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/install.sh | sh
```
If you don't have `curl`, use `wget`:
```bash
wget -qO- https://raw.githubusercontent.com/unslothai/unsloth/main/install.sh | sh
```
Or manually:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv unsloth_studio --python 3.13
source unsloth_studio/bin/activate
Expand All @@ -67,9 +75,12 @@ source unsloth_studio/bin/activate
unsloth studio -H 0.0.0.0 -p 8888
```

#### Windows:
Run in Windows Powershell:
```bash
#### Windows PowerShell (One time):
```powershell
irm https://raw.githubusercontent.com/unslothai/unsloth/main/install.ps1 | iex
```
Or manually:
```powershell
winget install -e --id Python.Python.3.13
winget install --id=astral-sh.uv -e
uv venv unsloth_studio --python 3.13
Expand All @@ -79,7 +90,7 @@ unsloth studio setup
unsloth studio -H 0.0.0.0 -p 8888
```
Then to launch every time:
```bash
```powershell
.\unsloth_studio\Scripts\activate
unsloth studio -H 0.0.0.0 -p 8888
```
Expand Down
99 changes: 99 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 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

function Install-UnslothStudio {
$ErrorActionPreference = "Stop"

$VenvName = "unsloth_studio"
$PythonVersion = "3.13"

Write-Host ""
Write-Host "========================================="
Write-Host " Unsloth Studio Installer (Windows)"
Write-Host "========================================="
Write-Host ""

# ── Helper: refresh PATH from registry (preserving current session entries) ──
function Refresh-SessionPath {
$machine = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
$user = [System.Environment]::GetEnvironmentVariable("Path", "User")
$env:Path = "$machine;$user;$env:Path"
}

# ── Check winget ──
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
Write-Host "Error: winget is not available." -ForegroundColor Red
Write-Host " Install it from https://aka.ms/getwinget" -ForegroundColor Yellow
Write-Host " or install Python $PythonVersion and uv manually, then re-run." -ForegroundColor Yellow
return
Comment on lines +25 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exit PowerShell installer non-zero on fatal failures

This failure path returns from Install-UnslothStudio instead of terminating with a non-zero status, so callers can observe a successful command completion even when install prerequisites are missing. That matters for scripted installs (CI/bootstrap scripts) that rely on exit codes to detect failure. Use throw/exit 1 (or propagate an explicit non-zero return code) for fatal branches to avoid silent false-success outcomes.

Useful? React with 👍 / 👎.

}

# ── Install Python 3.13 if no compatible Python found ──
# setup.ps1 accepts Python 3.11-3.13; we install 3.13 if nothing compatible is found
$NeedPython = $true
if (Get-Command python -ErrorAction SilentlyContinue) {
$pyVer = python --version 2>&1
if ($pyVer -match "Python 3\.1[1-3]\.") {
Write-Host "==> Python already installed: $pyVer"
$NeedPython = $false
}
}
if ($NeedPython) {
Write-Host "==> Installing Python ${PythonVersion}..."
winget install -e --id Python.Python.3.13 --accept-package-agreements --accept-source-agreements
Refresh-SessionPath
}

# ── Install uv if not present ──
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host "==> Installing uv package manager..."
winget install --id=astral-sh.uv -e --accept-package-agreements --accept-source-agreements
Refresh-SessionPath
# Fallback: if winget didn't put uv on PATH, try the PowerShell installer
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host " Trying alternative uv installer..."
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Refresh-SessionPath
}
}

if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
Write-Host "Error: uv could not be installed." -ForegroundColor Red
Write-Host " Install it from https://docs.astral.sh/uv/" -ForegroundColor Yellow
return
}

# ── Create venv (skip if it already exists) ──
$VenvPython = Join-Path $VenvName "Scripts\python.exe"
if (-not (Test-Path $VenvName)) {
Write-Host "==> Creating Python ${PythonVersion} virtual environment (${VenvName})..."
uv venv $VenvName --python $PythonVersion

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use discovered Python version for venv creation

The installer marks Python 3.11–3.13 as compatible ($NeedPython = $false), but venv creation always requests --python 3.13. On machines that only have 3.11/3.12 and cannot download managed interpreters through uv, this fails even though the earlier compatibility check passed; use the detected compatible interpreter/version for uv venv instead of hard-coding 3.13 in this path.

Useful? React with 👍 / 👎.

} else {
Write-Host "==> Virtual environment ${VenvName} already exists, skipping creation."
}

# ── Install unsloth directly into the venv (no activation needed) ──
Write-Host "==> Installing unsloth (this may take a few minutes)..."
uv pip install --python $VenvPython unsloth --torch-backend=auto

# ── Run studio setup ──
# setup.ps1 will handle installing Git, CMake, Visual Studio Build Tools,
# CUDA Toolkit, Node.js, and other dependencies automatically via winget.
Write-Host "==> Running unsloth studio setup..."
$UnslothExe = Join-Path $VenvName "Scripts\unsloth.exe"
& $UnslothExe studio setup

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail install when studio setup returns non-zero

This script relies on $ErrorActionPreference = "Stop", but native executables do not automatically stop the script on non-zero exit in standard PowerShell behavior, and this call does not check $LASTEXITCODE. If unsloth studio setup fails (for example due to a dependency install failure), the script can still continue to the success banner, giving users a false-positive "installed" result; add an explicit exit-code check immediately after this command.

Useful? React with 👍 / 👎.


Write-Host ""
Write-Host "========================================="
Write-Host " Unsloth Studio installed!"
Write-Host "========================================="
Write-Host ""
Write-Host " To launch, run:"
Write-Host ""
Write-Host " .\${VenvName}\Scripts\activate"
Write-Host " unsloth studio -H 0.0.0.0 -p 8888"
Write-Host ""
}

Install-UnslothStudio
152 changes: 152 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/bin/sh
# Unsloth Studio Installer
# Usage (curl): curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/install.sh | sh
# Usage (wget): wget -qO- https://raw.githubusercontent.com/unslothai/unsloth/main/install.sh | sh
set -e

VENV_NAME="unsloth_studio"
PYTHON_VERSION="3.13"

# ── Helper: download a URL to a file (supports curl and wget) ──
download() {
if command -v curl >/dev/null 2>&1; then
curl -LsSf "$1" -o "$2"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$2" "$1"
else
echo "Error: neither curl nor wget found. Install one and re-run."
exit 1
fi
}

echo ""
echo "========================================="
echo " Unsloth Studio Installer"
echo "========================================="
echo ""

# ── Detect platform ──
OS="linux"
if [ "$(uname)" = "Darwin" ]; then
OS="macos"
elif grep -qi microsoft /proc/version 2>/dev/null; then
OS="wsl"
fi
echo "==> Platform: $OS"

# ── 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.
MISSING=""

command -v cmake >/dev/null 2>&1 || MISSING="$MISSING cmake"
command -v git >/dev/null 2>&1 || MISSING="$MISSING git"

case "$OS" in
macos)
# Xcode Command Line Tools provide the C/C++ compiler
if ! xcode-select -p >/dev/null 2>&1; then
echo ""
echo "==> Xcode Command Line Tools are required."
echo " Installing (a system dialog will appear)..."
xcode-select --install 2>/dev/null || true
echo " After the installation completes, please re-run this script."
exit 1
fi
;;
linux|wsl)
# curl or wget is needed for downloads; check both
if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
MISSING="$MISSING curl"
fi
command -v gcc >/dev/null 2>&1 || MISSING="$MISSING build-essential"
# libcurl dev headers for llama.cpp HTTPS support
if command -v dpkg >/dev/null 2>&1; then
dpkg -s libcurl4-openssl-dev >/dev/null 2>&1 || MISSING="$MISSING libcurl4-openssl-dev"
fi
;;
esac

MISSING=$(echo "$MISSING" | sed 's/^ *//')

if [ -n "$MISSING" ]; then
echo ""
echo "==> Unsloth Studio needs these packages: $MISSING"
echo " These are needed to build the GGUF inference engine."

case "$OS" in
macos)
if ! command -v brew >/dev/null 2>&1; then
echo ""
echo " Homebrew is required to install them."
echo " Install Homebrew from https://brew.sh then re-run this script."
exit 1
fi
printf " Install via Homebrew? [Y/n] "
read -r REPLY </dev/tty 2>/dev/null || REPLY="y"
case "$REPLY" in
[nN]*) echo " Skipping -- GGUF inference may not be available." ;;
*) brew install $MISSING ;;
esac
;;
linux|wsl)
if command -v apt-get >/dev/null 2>&1; then
echo " We need elevated permissions (sudo) to install them."
printf " Allow? [Y/n] "
read -r REPLY </dev/tty 2>/dev/null || REPLY="y"
case "$REPLY" in
[nN]*) echo " Skipping -- GGUF inference may not be available." ;;
*)
sudo apt-get update -y
sudo apt-get install -y $MISSING

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip sudo when running apt-get as root

In the Linux/WSL dependency install path, the script always runs sudo apt-get ... when packages are missing. On Debian/Ubuntu environments that run as root and do not have sudo installed (common in containers/CI), this exits with sudo: command not found and aborts the one-liner before uv/Studio setup can finish. The installer should detect root (id -u = 0) and call apt-get directly in that case.

Useful? React with 👍 / 👎.

;;
esac
else
echo " Please install them with your package manager, then re-run."
fi
Comment on lines +160 to +165

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Abort installer when Linux deps cannot be auto-installed

When required packages are missing on Linux/WSL and apt-get is unavailable, this branch only prints a message and then continues the one-liner install. On non-Debian distros (for example Fedora/Arch), that means the script proceeds into uv/venv/studio setup with known-missing prerequisites, causing later-stage failures and a misleading flow despite explicitly saying to re-run after manual install. This path should terminate immediately (non-zero) after the warning.

Useful? React with 👍 / 👎.

;;
esac
echo ""
else
echo "==> All system dependencies found."
fi

# ── Install uv ──
if ! command -v uv >/dev/null 2>&1; then
echo "==> Installing uv package manager..."
_uv_tmp=$(mktemp)
download "https://astral.sh/uv/install.sh" "$_uv_tmp"
sh "$_uv_tmp"
rm -f "$_uv_tmp"
if [ -f "$HOME/.local/bin/env" ]; then
. "$HOME/.local/bin/env"
fi
export PATH="$HOME/.local/bin:$PATH"
fi

# ── Create venv (skip if it already exists) ──
if [ ! -d "$VENV_NAME" ]; then
echo "==> Creating Python ${PYTHON_VERSION} virtual environment (${VENV_NAME})..."
uv venv "$VENV_NAME" --python "$PYTHON_VERSION"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Select an available Python instead of forcing 3.13

This installer always runs uv venv ... --python 3.13, even though Studio setup supports Python 3.11–3.13. On hosts that already have only 3.11/3.12 and cannot fetch managed interpreters (for example restricted networks or UV_PYTHON_DOWNLOADS=never), the install fails despite a compatible interpreter being present; the script should discover and use an installed compatible Python first.

Useful? React with 👍 / 👎.

else
echo "==> Virtual environment ${VENV_NAME} already exists, skipping creation."
fi

# ── Install unsloth directly into the venv (no activation needed) ──
echo "==> Installing unsloth (this may take a few minutes)..."
uv pip install --python "$VENV_NAME/bin/python" unsloth --torch-backend=auto

# ── Run studio setup ──
echo "==> Running unsloth studio setup..."
"$VENV_NAME/bin/unsloth" studio setup </dev/null

echo ""
echo "========================================="
echo " Unsloth Studio installed!"
echo "========================================="
echo ""
echo " To launch, run:"
echo ""
echo " source ${VENV_NAME}/bin/activate"
echo " unsloth studio -H 0.0.0.0 -p 8888"
echo ""
Loading