Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ irm https://unsloth.ai/install.ps1 | iex

#### Launch
```bash
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```
> For cloud VMs or LAN access, add `-H 0.0.0.0` to bind on all interfaces.

#### Update
To update, use the same install commands as above. Or run (does not work on Windows):
Expand Down Expand Up @@ -151,7 +152,7 @@ The below advanced instructions are for Unsloth Studio. For Unsloth Core advance
git clone https://github.com/unslothai/unsloth
cd unsloth
./install.sh --local
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```
Then to update :
```bash
Expand All @@ -164,7 +165,7 @@ git clone https://github.com/unslothai/unsloth.git
cd unsloth
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\install.ps1 --local
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```
Then to update :
```bash
Expand All @@ -177,11 +178,11 @@ git clone https://github.com/unslothai/unsloth
cd unsloth
git checkout nightly
./install.sh --local
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```
Then to launch every time:
```bash
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```

#### Nightly: Windows:
Expand All @@ -192,11 +193,11 @@ cd unsloth
git checkout nightly
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\install.ps1 --local
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```
Then to launch every time:
```bash
unsloth studio -H 0.0.0.0 -p 8888
unsloth studio
```

#### Uninstall
Expand Down
18 changes: 14 additions & 4 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -958,15 +958,25 @@ shell.Run cmd, 0, False
step "path" "added unsloth to PATH"
}

# Launch studio automatically in interactive terminals;
# in non-interactive environments (CI, Docker) just print instructions.
# In interactive terminals, ask the user before starting Studio.
# In non-interactive environments (CI, Docker) just print instructions.
$IsInteractive = [Environment]::UserInteractive -and (-not [Console]::IsInputRedirected)
if ($IsInteractive) {
& $UnslothExe studio -H 0.0.0.0 -p 8888
Write-Host ""
$reply = Read-Host " Start Unsloth Studio now? [Y/n]"
if ([string]::IsNullOrWhiteSpace($reply) -or $reply -match '^[Yy]') {
& $UnslothExe studio -p 8888
} else {
step "launch" "to start later, run:"
substep "unsloth studio"
substep "(add -H 0.0.0.0 to allow network / cloud access)"
Write-Host ""
}
} else {
step "launch" "manual commands:"
substep "& `"$VenvDir\Scripts\Activate.ps1`""
substep "unsloth studio -H 0.0.0.0 -p 8888"
substep "unsloth studio"
substep "(add -H 0.0.0.0 to allow network / cloud access)"
Write-Host ""
}
}
Expand Down
54 changes: 34 additions & 20 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,11 @@ if [ -t 1 ]; then
) &
# Clear traps so exec does not trigger _release_lock (the subshell owns it)
trap - EXIT INT TERM
exec "$UNSLOTH_EXE" studio -H 0.0.0.0 -p "$_launch_port"
exec "$UNSLOTH_EXE" studio -p "$_launch_port"
else
# ── Background mode (no TTY) ──
# Used by macOS .app and headless invocations.
_launch_cmd=$(printf '%q ' "$UNSLOTH_EXE" studio -H 0.0.0.0 -p "$_launch_port")
_launch_cmd=$(printf '%q ' "$UNSLOTH_EXE" studio -p "$_launch_port")
_launch_cmd=${_launch_cmd% }
_spawn_terminal "$_launch_cmd"

Expand Down Expand Up @@ -1211,28 +1211,42 @@ printf " ${C_TITLE}%s${C_RST}\n" "Unsloth Studio installed!"
printf " ${C_DIM}%s${C_RST}\n" "$RULE"
echo ""

# Launch studio automatically in interactive terminals;
# in non-interactive environments (Docker, CI, cloud-init) just print instructions.
# In interactive terminals, ask the user before starting Studio.
# In non-interactive environments (Docker, CI, cloud-init) just print instructions.
if [ -t 1 ]; then
step "launch" "starting Unsloth Studio..."
"$VENV_DIR/bin/unsloth" studio -H 0.0.0.0 -p 8888
_LAUNCH_EXIT=$?
if [ "$_LAUNCH_EXIT" -ne 0 ] && [ "$_MIGRATED" = true ]; then
echo ""
echo "⚠️ Unsloth Studio failed to start after migration."
echo " Your migrated environment may be incompatible."
echo " To fix, remove the environment and reinstall:"
echo ""
echo " rm -rf $VENV_DIR"
echo " curl -fsSL https://unsloth.ai/install.sh | sh"
echo ""
fi
exit "$_LAUNCH_EXIT"
echo ""
printf " Start Unsloth Studio now? [Y/n] "
read -r _reply </dev/tty || _reply="y"
case "${_reply:-y}" in
[Yy]*|"")
step "launch" "starting Unsloth Studio..."
"$VENV_DIR/bin/unsloth" studio -p 8888
_LAUNCH_EXIT=$?
if [ "$_LAUNCH_EXIT" -ne 0 ] && [ "$_MIGRATED" = true ]; then
echo ""
echo "⚠️ Unsloth Studio failed to start after migration."
echo " Your migrated environment may be incompatible."
echo " To fix, remove the environment and reinstall:"
echo ""
echo " rm -rf $VENV_DIR"
echo " curl -fsSL https://unsloth.ai/install.sh | sh"
echo ""
fi
exit "$_LAUNCH_EXIT"
;;
*)
step "launch" "to start later, run:"
substep "unsloth studio"
substep "(add -H 0.0.0.0 to allow network / cloud access)"
echo ""
;;
esac
else
step "launch" "manual commands:"
substep "unsloth studio -H 0.0.0.0 -p 8888"
substep "unsloth studio"
substep "or activate env first:"
substep "source ${VENV_DIR}/bin/activate"
substep "unsloth studio -H 0.0.0.0 -p 8888"
substep "unsloth studio"
substep "(add -H 0.0.0.0 to allow network / cloud access)"
echo ""
fi
41 changes: 27 additions & 14 deletions studio/backend/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def _graceful_shutdown(server = None):


def run_server(
host: str = "0.0.0.0",
host: str = "127.0.0.1",
port: int = 8888,
frontend_path: Path = Path(__file__).resolve().parent.parent / "frontend" / "dist",
silent: bool = False,
Expand Down Expand Up @@ -357,21 +357,20 @@ def _trigger_shutdown():
return app


# For direct execution (also invoked by CLI via os.execvp / subprocess)
if __name__ == "__main__":
import argparse
import signal
import traceback
def _make_argument_parser() -> "argparse.ArgumentParser":
"""Build and return the ArgumentParser for standalone run.py execution.

# Ensure stderr can handle Unicode on Windows (tracebacks with non-ASCII paths)
if sys.platform == "win32" and hasattr(sys.stderr, "reconfigure"):
try:
sys.stderr.reconfigure(encoding = "utf-8", errors = "replace")
except Exception:
pass
Extracted from ``__main__`` so it can be imported and tested without
executing the full startup sequence.
"""
import argparse

parser = argparse.ArgumentParser(description = "Run Unsloth UI Backend server")
parser.add_argument("--host", default = "0.0.0.0", help = "Host to bind to")
parser.add_argument(
"--host",
default = "127.0.0.1",
help = "Host to bind to (default: 127.0.0.1; use 0.0.0.0 for network/cloud access)",
)
parser.add_argument("--port", type = int, default = 8888, help = "Port to bind to")
parser.add_argument(
"--frontend",
Expand All @@ -380,8 +379,22 @@ def _trigger_shutdown():
help = "Path to frontend build",
)
parser.add_argument("--silent", action = "store_true", help = "Suppress output")
return parser


# For direct execution (also invoked by CLI via os.execvp / subprocess)
if __name__ == "__main__":
import signal
import traceback

# Ensure stderr can handle Unicode on Windows (tracebacks with non-ASCII paths)
if sys.platform == "win32" and hasattr(sys.stderr, "reconfigure"):
try:
sys.stderr.reconfigure(encoding = "utf-8", errors = "replace")
except Exception:
pass

args = parser.parse_args()
args = _make_argument_parser().parse_args()

kwargs = dict(host = args.host, port = args.port, silent = args.silent)
if args.frontend is not None:
Expand Down
2 changes: 1 addition & 1 deletion unsloth_cli/commands/studio.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _find_setup_script() -> Optional[Path]:
def studio_default(
ctx: typer.Context,
port: int = typer.Option(8888, "--port", "-p"),
host: str = typer.Option("0.0.0.0", "--host", "-H"),
host: str = typer.Option("127.0.0.1", "--host", "-H"),
frontend: Optional[Path] = typer.Option(None, "--frontend", "-f"),
silent: bool = typer.Option(False, "--silent", "-q"),
):
Expand Down
Loading