Skip to content

studio: unify Windows installer/setup logging style, verbosity controls, and startup messaging#4651

Merged
danielhanchen merged 62 commits into
unslothai:mainfrom
Imagineer99:fix/studio-setup-log-verbose
Mar 30, 2026
Merged

studio: unify Windows installer/setup logging style, verbosity controls, and startup messaging#4651
danielhanchen merged 62 commits into
unslothai:mainfrom
Imagineer99:fix/studio-setup-log-verbose

Conversation

@Imagineer99
Copy link
Copy Markdown
Collaborator

@Imagineer99 Imagineer99 commented Mar 27, 2026

Standardizes Studio setup/install UX and logging behavior across Windows + shell scripts, while preserving newer upstream functionality merged from main (including no-torch install support).

  • Aligned install.ps1 output style with Studio setup style:
    • Added VT/ANSI-aware styling helpers to match setup.ps1 palette.
    • Standardized to step/substep formatting.
    • Removed mixed legacy-style status strings and inconsistent banner output.
  • Reduced default installer verbosity and added explicit verbose mode behavior:
    • Quiet-by-default command execution for heavy install commands.
    • --verbose / -v / UNSLOTH_VERBOSE=1 preserves full command output.
    • Error paths still surface explicit [ERROR] ... (exit code N) messages by default.
  • Updated Studio startup banner tip text:
    • Uses dynamic localhost URL based on runtime port instead of hardcoded :8888.
    • Keeps user-facing browser guidance clearer.
  • Removed noisy/non-essential messaging in setup path:
    • Removed stray “already installed — skipping base packages” chatter.
  • Merged latest upstream/main into branch and resolved conflicts by preserving:
    • upstream no-torch flow (--no-torch, no-torch runtime deps path),
    • this branch’s logging/UX consistency improvements.

Imagineer99 and others added 30 commits March 19, 2026 19:31
Resolve merge conflicts in setup.sh and setup.ps1 while preserving
both main's behavioral additions and the PR's styling refactor:

setup.sh:
- Keep main's _run_quiet/run_quiet/run_quiet_no_exit architecture
  with PR's step() styled error messages
- Restore REQUESTED_PYTHON_VERSION support from main (install.sh
  contract) with substep() output style
- Restore graceful run_quiet_no_exit || BUILD_OK=false for llama.cpp
  clone/cmake/build steps (instead of hard exit)
- Keep PR's try_quiet for optional llama-quantize step
- Keep PR's compact python version check one-liners

setup.ps1:
- Restore main's cpu-first/CUDA-else PyTorch install structure with
  PR's step/substep/verbose styling
- Combine main's conda+URL detail in Python-not-found error with
  PR's step/substep format
- Style main's stale-venv and conda-skip messages to match PR
  conventions

Auto-merged from main (no conflicts):
- _platform_compat import and UTF-8 reconfigure in run.py
- Test-IsConda, py.exe launcher, Get-Command -All Python discovery
- Stale-venv rebuild logic
- Get-PytorchCudaTag with cu118/cpu fallbacks
- Bracket IPv6 display_host in external_url to produce clickable URLs
- Redirect try_quiet failure log to stderr instead of stdout
- Clamp _step label to column width to prevent negative padding
Simulation harness (tests/simulate_pr4494.py) creates an isolated uv
venv, copies the real source files into it, and runs subprocess tests
for all three fixes with visual before/after demos and edge cases.

Standalone bash test (tests/test_try_quiet.sh) validates try_quiet
stderr redirect across 8 scenarios including broken-version contrast.

39 integration tests total (14 IPv6 + 15 try_quiet + 10 _step), all
existing 75 unit tests still pass.
The %-15s printf format pads short labels but does not truncate long
ones.  Change to %-15.15s so labels wider than 15 chars are clipped,
matching the PowerShell .Substring(0,15) and Python label[:15] logic.
These test files are not part of the styling fix and should not
ship with this PR.
Resolve conflicts in install_python_stack.py: keep the PR's column-
layout output system (_step, _COL, _LABEL) and incorporate main's
_safe_print Unicode fallback for Windows consoles.
- install_python_stack.py: restore _red for patch_package_file
  warnings (was downgraded to _dim)
- setup.ps1: capture winget output and show on failure for CUDA,
  Node, Python, and OpenSSL installs (was piped to Out-Null)
- setup.ps1: always show git pull failure warning, not just in
  verbose mode
Same capture-and-print-on-failure pattern already used for
Node, Python, CUDA, and OpenSSL winget installs.
Resolve conflicts in studio/setup.sh, studio/setup.ps1, and
unsloth_cli/commands/studio.py by taking main's logic as the base
and applying the PR's styling changes (colors, step/substep helpers,
banner/footer) on top.

- setup.sh: kept main's Vite 8 node checks, bun install, prebuilt
  llama.cpp, Colab venv, ROCm support, version skip logic.
  Applied PR's color palette, step()/substep() helpers, styled
  banner/footer, and converted status messages.
- setup.ps1: kept main's full logic (stop command, update command,
  prebuilt llama.cpp, etc.). Added PR's VT/ANSI infrastructure
  (Enable-StudioVirtualTerminal, Get-StudioAnsi, step, substep),
  styled banner/footer, and converted key status messages.
- studio.py: took main's version entirely (stop, _run_setup_script,
  deprecated setup, update commands). The PR's --verbose flag is a
  feature addition beyond styling scope.
The step() helper writes to stdout, but _run_quiet's error header
was originally sent to stderr (>&2). Without the redirect, callers
that separate stdout/stderr would miss the failure headline while
still seeing the log body on stderr. Add >&2 to both step calls
inside _run_quiet to match main's behavior.
Wire UNSLOTH_VERBOSE=1 through _run_setup_script() so that
'unsloth studio update --verbose' (and the deprecated 'setup')
passes the flag to setup.sh / setup.ps1 / install_python_stack.py.
Fail fast when bash is unavailable before invoking setup.sh, and replace remaining nvidia-smi pipeline checks with stream redirection patterns that preserve reliable native exit-code handling.
Ensure PowerShell wrapper helpers in install/update stream native command output to host without returning it as function output, so npm logs no longer corrupt exit-code checks in verbose mode.
@danielhanchen
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request unifies logging and verbosity controls across the installation and setup scripts for Windows and Linux/macOS by introducing standardized logging functions, a verbose flag, and centralized command execution helpers. The changes also refine exception handling in the Python backend and implement tracking for degraded installation states. Feedback was provided regarding code duplication in the install.ps1 script, suggesting a refactor to improve maintainability.

Comment thread install.ps1
Comment on lines 860 to 877
} elseif ($StudioLocalInstall) {
uv pip install --python $VenvPython --upgrade-package unsloth "unsloth>=2026.3.14" unsloth-zoo
Write-Host "==> Overlaying local repo (editable)..."
uv pip install --python $VenvPython -e $RepoRoot --no-deps
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --upgrade-package unsloth "unsloth>=2026.3.14" unsloth-zoo }
} else {
uv pip install --python $VenvPython --upgrade-package unsloth "$PackageName"
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --upgrade-package unsloth "$PackageName" }
}
if ($baseInstallExit -ne 0) {
Write-Host "[ERROR] Failed to install unsloth (exit code $baseInstallExit)" -ForegroundColor Red
return
}

if ($StudioLocalInstall) {
substep "overlaying local repo (editable)..."
$overlayExit = Invoke-InstallCommand { uv pip install --python $VenvPython -e $RepoRoot --no-deps }
if ($overlayExit -ne 0) {
Write-Host "[ERROR] Failed to overlay local repo (exit code $overlayExit)" -ForegroundColor Red
return
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This block of code for installing unsloth and overlaying the local repo appears to be duplicated within the elseif ($TorchIndexUrl) block. Consider refactoring to avoid repetition and improve maintainability. You have similar logic for if ($SkipTorch) and elseif ($StudioLocalInstall) and then a general else block, followed by another if ($StudioLocalInstall) block. This could be streamlined.

… verbose

- setup.sh: Do not exit non-zero when llama.cpp is unavailable; the footer
  already reports the limitation, and install.sh runs under set -e so a
  non-zero exit aborts the entire install including PATH/shortcuts/launch.
- setup.ps1: Remove $? check in Invoke-SetupCommand verbose path; PS 5.1
  sets $? = $false when native commands write to stderr even with exit 0.
  Merge stderr into stdout with 2>&1 and rely solely on $LASTEXITCODE.
- startup_banner.py: Show the actual bound address when Studio is bound to
  a non-loopback interface instead of always showing 127.0.0.1/localhost.
- setup.sh: Use run_quiet_no_exit instead of run_quiet_no_exit_always for
  npm install steps so --verbose correctly surfaces npm output.
…one verbose

- install.ps1: Apply same Invoke-InstallCommand fix as setup.ps1 -- merge
  stderr into stdout with 2>&1 and drop the $? check that misclassifies
  successful native commands on PS 5.1.
- install.ps1 + setup.ps1: Export UNSLOTH_VERBOSE=1 to the process env
  when --verbose is passed so child processes like install_python_stack.py
  also run in verbose mode.
- setup.sh: Use run_quiet_no_exit for git clone llama.cpp so --verbose
  correctly surfaces clone diagnostics during source-build fallback.
…fix banner

- setup.sh: Use tee in verbose mode for prebuilt llama.cpp installer so
  users can see download/validation progress while still capturing the log
  for structured error reporting on failure.
- setup.ps1: Same fix for Windows -- use Tee-Object in verbose mode.
- setup.sh: Remove run_quiet_no_exit_always() which has no remaining callers.
- startup_banner.py: Avoid printing the same URL twice when Studio is
  bound to a specific non-loopback address that matches the display host.
Copy link
Copy Markdown
Member

@danielhanchen danielhanchen left a comment

Choose a reason for hiding this comment

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

Thank you for the PR! The goal of this PR is to standardize Studio installer/setup logging across Windows and Linux, add explicit verbose mode controls, and update the startup banner messaging. As a summary, this PR replaces mixed legacy-style status strings with shared step/substep formatting, threads --verbose / UNSLOTH_VERBOSE through the CLI and child processes, and moves the startup banner into a reusable Python helper.

I pushed several fix commits directly to the branch after running 3 rounds of automated review (15 independent reviewers total). Here is the consolidated summary of issues found and fixed:

Reviewers Severity Finding Status
15/15 High setup.sh exits non-zero for degraded llama.cpp installs, aborting the entire install.sh flow under set -e Fixed -- replaced exit 1 with a comment explaining why degraded installs must stay successful
13/15 High install.ps1 Invoke-InstallCommand verbose wrapper treats stderr-on-success as failure on PS 5.1 Fixed -- merged stderr into stdout with 2>&1 and dropped the $? check, matching setup.ps1
10/15 High Prebuilt llama.cpp installer output suppressed even in verbose mode (Linux + Windows) Fixed -- use tee/Tee-Object in verbose mode so users see live progress while still capturing the log
6/15 Medium UNSLOTH_VERBOSE not exported in install.ps1, setup.ps1 -- child processes like install_python_stack.py stay quiet Fixed -- added $env:UNSLOTH_VERBOSE = '1' export when --verbose is set
5/15 Medium install.sh UNSLOTH_VERBOSE assigned but never exported Fixed in earlier commit
5/15 Medium install.sh verbose branch in run_install_cmd runs bare "$@" under set -e -- structured error message never shown Fixed in earlier commit
8/15 Medium Startup banner shows wrong URLs for non-loopback and IPv6 binds Fixed -- use actual bound address instead of always defaulting to 127.0.0.1/localhost
8/15 Medium --silent flag on first-run launch hides startup banner and port-conflict notice Fixed in earlier commit
5/15 Low npm install and git clone llama.cpp still suppressed in verbose mode via run_quiet_no_exit_always Fixed -- switched to run_quiet_no_exit and removed the unused run_quiet_no_exit_always helper
3/15 Low cmake build steps used run_quiet_no_exit_always instead of run_quiet_no_exit Fixed in earlier commit
2/15 Low Duplicate "Bound address" URL shown when bind host matches display host Fixed -- added guard condition
1/15 Info Duplicate comment line in install.ps1 ("Tell setup.ps1 to skip base package installation") Fixed

All merge conflicts with origin/main have been resolved (kept PR's logging wrappers, updated version pins to 2026.3.16).

The previous pattern 'if "$@"; then return 0; fi; _rc=$?' always captured
$? = 0 because $? reflects the if-statement result, not the command's exit
code. Switch to '"$@" && return 0; _rc=$?' which preserves the actual
command exit code on failure. Applies to both verbose and quiet branches.
@danielhanchen
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the installation and setup scripts for Windows and Unix-like systems to provide a cleaner, more standardized user interface and improved diagnostic capabilities. Key enhancements include a new verbose mode that propagates to child processes, refined exception handling in Python components, and a graceful degradation mechanism that allows the installation to proceed even if the llama.cpp build fails. I have no feedback to provide as the existing comments were purely explanatory or validating.

- setup.sh: Fix _run_quiet verbose path that always captured exit code 0
  due to $? resetting after if-then-fi with no else. Switch to the same
  '"$@" && return 0; exit_code=$?' pattern used in install.sh.
- setup.sh: Consolidate the two uv install branches (verbose + quiet)
  into a single attempt with conditional output. Previously, when verbose
  mode was on and the install failed, a second silent attempt was made.
- install.ps1: Pass --local flag to 'unsloth studio update' when
  $StudioLocalInstall is true. Without this, studio.py's update() command
  overwrites STUDIO_LOCAL_INSTALL to "0", which could cause issues if
  setup.ps1 or install_python_stack.py later checks that variable.
- Revert SKIP_STUDIO_BASE from 0 to 1 for --no-torch. install.sh already
  installs unsloth+unsloth-zoo and no-torch-runtime.txt before calling
  setup.sh, so letting install_python_stack.py redo it was redundant and
  slowed down --no-torch installs for no benefit.
- Restore the "Unsloth Studio installed!" success banner and "starting
  Unsloth Studio..." launch message so users get clear install completion
  feedback before the server starts.
…ose + degraded footer

- Keep main's PyPI version-check optimization ($SkipPythonDeps) that skips
  Python dependency work when the installed version already matches latest.
- Keep PR's verbose-aware pip upgrade inside the $SkipPythonDeps guard.
- Combine main's dynamic $DoneLabel with PR's degraded llama.cpp variant
  in the footer so both "Setup Complete"/"Updated" and the llama.cpp
  limitation warning are shown correctly.
- setup.sh: Restore exit 1 when _LLAMA_CPP_DEGRADED is true. GGUF
  inference requires a working llama.cpp build, so this should be a
  hard failure, not a silent degradation.
- install.sh: Catch setup.sh's non-zero exit with '|| _SETUP_EXIT=$?'
  instead of letting set -e abort immediately. This ensures PATH setup,
  symlinks, and shortcuts still get created so the user can fix the
  build deps and retry with 'unsloth studio update'. After post-install
  steps, propagate the failure with a clear error message.
'studio update' pops SKIP_STUDIO_BASE from the environment, which
defeats the fast-path version check added in PR unslothai#4667. When called
from install.ps1 (which already installed packages), SKIP_STUDIO_BASE=1
must survive into setup.ps1 so it skips the redundant PyPI check and
package reinstallation. 'studio setup' does not modify env vars.
install.ps1 uses 'studio setup' (not 'studio update') to preserve
SKIP_STUDIO_BASE. The deprecation message was confusing during first
install since the user never typed the command.
…nslothai#4651

- install.ps1: Always set STUDIO_LOCAL_INSTALL and clear STUDIO_LOCAL_REPO
  when not using --local, to prevent stale values from a previous --local
  run in the same PowerShell session. Fix log messages to say 'setup' not
  'update' since we call 'studio setup'.
- setup.sh: Only exit non-zero for degraded llama.cpp when called from the
  installer (SKIP_STUDIO_BASE=1). Direct 'unsloth studio update' keeps
  degraded installs successful since Studio is still usable for non-GGUF
  workflows and the footer already reports the limitation.
- install.sh: Make the setup failure error message generic instead of
  GGUF-specific, so unrelated failures (npm, Python deps) do not show
  misleading cmake/git recovery advice.
Copy link
Copy Markdown
Member

@danielhanchen danielhanchen left a comment

Choose a reason for hiding this comment

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

Round 4 review (5 independent reviewers). Fixed 3 real issues, dismissed 2 false positives.

Reviewers Severity Finding Status
4/5 High install.ps1 does not clear stale STUDIO_LOCAL_INSTALL / STUDIO_LOCAL_REPO from previous --local runs in the same PS session Fixed -- always set STUDIO_LOCAL_INSTALL and clear STUDIO_LOCAL_REPO when not --local
4/5 High setup.sh exit 1 for degraded llama.cpp fires on direct unsloth studio update too, not just installer Fixed -- gated on SKIP_STUDIO_BASE=1 so only the installer path triggers it
1/5 High install.sh error message assumes GGUF failure for any setup.sh exit Fixed -- generic error message pointing to output above
1/5 High Colab falls through to llama.cpp setup False positive -- main has the same behavior, the comment says "continue to llama.cpp install so GGUF inference is available"
2/5 Medium bash preflight in install.sh runs too late False positive -- install.sh is a bash script, so if bash is missing it cannot run at all
4/5 Info Log says "running unsloth studio update" but calls studio setup Fixed -- log now says "setup"

@danielhanchen
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request unifies the installation and setup experience for Unsloth Studio by standardizing logging, output styling, and error handling across PowerShell and Bash scripts. It introduces a verbose mode, ANSI color support, and refined logic for dependency verification and llama.cpp builds. The review feedback suggests enhancing the PowerShell command wrappers to display captured output upon failure in quiet mode, ensuring functional parity with the newly implemented shell script logging behavior.

Comment thread install.ps1
Comment on lines +176 to +197
function Invoke-InstallCommand {
param(
[Parameter(Mandatory = $true)][ScriptBlock]$Command
)
$prevEap = $ErrorActionPreference
$ErrorActionPreference = "Continue"
try {
# Reset to avoid stale values from prior native commands.
$global:LASTEXITCODE = 0
if ($script:UnslothVerbose) {
# Merge stderr into stdout so progress/warning output stays visible
# without flipping $? on successful native commands (PS 5.1 treats
# stderr records as errors that set $? = $false even on exit code 0).
& $Command 2>&1 | Out-Host
} else {
& $Command *> $null
}
return [int]$LASTEXITCODE
} finally {
$ErrorActionPreference = $prevEap
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To improve debugging on failure, this function should display the captured output when a command fails in quiet mode. This aligns with the behavior of run_install_cmd in install.sh, which was also introduced in this PR to unify logging behavior.

    function Invoke-InstallCommand {
        param(
            [Parameter(Mandatory = $true)][ScriptBlock]$Command
        )
        $prevEap = $ErrorActionPreference
        $ErrorActionPreference = "Continue"
        try {
            # Reset to avoid stale values from prior native commands.
            $global:LASTEXITCODE = 0
            if ($script:UnslothVerbose) {
                # Merge stderr into stdout so progress/warning output stays visible
                # without flipping $? on successful native commands (PS 5.1 treats
                # stderr records as errors that set $? = $false even on exit code 0).
                & $Command 2>&1 | Out-Host
            } else {
                $output = & $Command 2>&1 | Out-String
                if ($LASTEXITCODE -ne 0) {
                    Write-Host $output -ForegroundColor Red
                }
            }
            return [int]$LASTEXITCODE
        } finally {
            $ErrorActionPreference = $prevEap
        }
    }

Comment thread studio/setup.ps1
Comment on lines +339 to +361
function Invoke-SetupCommand {
param(
[Parameter(Mandatory = $true)][scriptblock]$Command,
[switch]$AlwaysQuiet
)
$prevEap = $ErrorActionPreference
$ErrorActionPreference = "Continue"
try {
# Reset to avoid stale values from prior native commands.
$global:LASTEXITCODE = 0
if ($script:UnslothVerbose -and -not $AlwaysQuiet) {
# Merge stderr into stdout so progress/warning output stays visible
# without flipping $? on successful native commands (PS 5.1 treats
# stderr records as errors that set $? = $false even on exit code 0).
& $Command 2>&1 | Out-Host
} else {
& $Command *> $null
}
return [int]$LASTEXITCODE
} finally {
$ErrorActionPreference = $prevEap
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To improve debugging on failure, this function should display the captured output when a command fails in quiet mode. This aligns with the behavior of run_install_cmd in install.sh and would make the PowerShell scripts more consistent with the PR's goal of unifying logging.

function Invoke-SetupCommand {
    param(
        [Parameter(Mandatory = $true)][scriptblock]$Command,
        [switch]$AlwaysQuiet
    )
    $prevEap = $ErrorActionPreference
    $ErrorActionPreference = "Continue"
    try {
        # Reset to avoid stale values from prior native commands.
        $global:LASTEXITCODE = 0
        if ($script:UnslothVerbose -and -not $AlwaysQuiet) {
            # Merge stderr into stdout so progress/warning output stays visible
            # without flipping $? on successful native commands (PS 5.1 treats
            # stderr records as errors that set $? = $false even on exit code 0).
            & $Command 2>&1 | Out-Host
        } else {
            $output = & $Command 2>&1 | Out-String
            if ($LASTEXITCODE -ne 0) {
                Write-Host $output -ForegroundColor Red
            }
        }
        return [int]$LASTEXITCODE
    } finally {
        $ErrorActionPreference = $prevEap
    }
}

Both Invoke-InstallCommand (install.ps1) and Invoke-SetupCommand
(setup.ps1) now capture command output in quiet mode and display it
in red when the command fails. This matches the behavior of
run_install_cmd in install.sh where failure output is surfaced even
in quiet mode, making cross-platform error debugging consistent.
…or PR unslothai#4651

- setup.ps1: Exit non-zero for degraded llama.cpp when called from
  install.ps1 (SKIP_STUDIO_BASE=1), matching setup.sh behavior. Direct
  'unsloth studio update' keeps degraded installs successful.
- install.sh: Show 'unsloth studio update --local' in the recovery
  message when the install was run with --local, so users retry with
  the correct flag instead of losing local checkout context.
Copy link
Copy Markdown
Member

@danielhanchen danielhanchen left a comment

Choose a reason for hiding this comment

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

Round 5 review (5 independent reviewers). Fixed 2 real issues, dismissed 3 false positives.

Reviewers Severity Finding Status
3/5 High Windows setup.ps1 does not exit non-zero for degraded llama.cpp, unlike Linux setup.sh Fixed -- added exit 1 gated on SKIP_STUDIO_BASE=1 to match setup.sh
2/5 High Recovery message shows unsloth studio update even for --local installs Fixed -- shows --local flag when appropriate
1/5 High install.sh shebang /bin/sh conflicts with bash requirement False positive -- install.sh has no shebang, the bash check guards bash "$SETUP_SH" not the installer itself
2/5 Medium UNSLOTH_VERBOSE env leak across runs False positive -- process-scoped, dies with the session
1/5 Medium Out-String buffers memory in quiet mode False positive -- standard PS pattern, negligible overhead

@danielhanchen
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a unified verbose logging system and a consistent UI style across the installation and setup scripts for both Windows and Unix-like systems. Key changes include the introduction of step and substep functions for standardized output, improved error reporting for llama.cpp builds, and the addition of a --verbose flag to control command output. A logic error was identified in the startup banner's URL display logic, where a condition meant to show the bound address was rendered unreachable.

]
)
elif not listen_all and bind_host not in ("127.0.0.1", "localhost", "::1"):
elif not listen_all and not loopback_bind and external_url != primary_url:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The condition to display the 'Bound address' for specific non-loopback IP bindings appears to be incorrect. The external_url != primary_url part of the elif statement will always evaluate to false when not listen_all and not loopback_bind is true, because primary_url is assigned external_url in that case. This makes the block unreachable, preventing the bound address from being displayed when it should be.

Suggested change
elif not listen_all and not loopback_bind and external_url != primary_url:
elif not listen_all and not loopback_bind:

@danielhanchen danielhanchen merged commit 5557e1f into unslothai:main Mar 30, 2026
1 check passed
shibizhao pushed a commit to shibizhao/unsloth-npu that referenced this pull request Apr 7, 2026
…ls, and startup messaging (unslothai#4651)

* refactor(studio): unify setup terminal output style and add verbose setup mode

* studio(windows): align setup.ps1 banner/steps with setup.sh (ANSI, verbose)

* studio(setup): revert nvcc path reordering to match main

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* studio(setup): restore fail-fast llama.cpp setup flow

* studio(banner): use IPv6 loopback URL when binding :: or ::1

* Fix IPv6 URL bracketing, try_quiet stderr, _step label clamp

- Bracket IPv6 display_host in external_url to produce clickable URLs
- Redirect try_quiet failure log to stderr instead of stdout
- Clamp _step label to column width to prevent negative padding

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add sandbox integration tests for PR unslothai#4494 UX fixes

Simulation harness (tests/simulate_pr4494.py) creates an isolated uv
venv, copies the real source files into it, and runs subprocess tests
for all three fixes with visual before/after demos and edge cases.

Standalone bash test (tests/test_try_quiet.sh) validates try_quiet
stderr redirect across 8 scenarios including broken-version contrast.

39 integration tests total (14 IPv6 + 15 try_quiet + 10 _step), all
existing 75 unit tests still pass.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Truncate step() labels in setup.sh to match PS1 and Python

The %-15s printf format pads short labels but does not truncate long
ones.  Change to %-15.15s so labels wider than 15 chars are clipped,
matching the PowerShell .Substring(0,15) and Python label[:15] logic.

* Remove sandbox integration tests from PR

These test files are not part of the styling fix and should not
ship with this PR.

* Show error output on failure instead of suppressing it

- install_python_stack.py: restore _red for patch_package_file
  warnings (was downgraded to _dim)
- setup.ps1: capture winget output and show on failure for CUDA,
  Node, Python, and OpenSSL installs (was piped to Out-Null)
- setup.ps1: always show git pull failure warning, not just in
  verbose mode

* Show winget error output for Git and CMake installs on failure

Same capture-and-print-on-failure pattern already used for
Node, Python, CUDA, and OpenSSL winget installs.

* fix: preserve stderr for _run_quiet error messages in setup.sh

The step() helper writes to stdout, but _run_quiet's error header
was originally sent to stderr (>&2). Without the redirect, callers
that separate stdout/stderr would miss the failure headline while
still seeing the log body on stderr. Add >&2 to both step calls
inside _run_quiet to match main's behavior.

* feat: add --verbose flag to setup and update commands

Wire UNSLOTH_VERBOSE=1 through _run_setup_script() so that
'unsloth studio update --verbose' (and the deprecated 'setup')
passes the flag to setup.sh / setup.ps1 / install_python_stack.py.

* fix(studio): honor verbose logging and keep llama.cpp failures non-blocking

* fix(studio): switch installer to 'studio update' and normalize Windows setup logs

* chore(studio): refine localhost tip and remove skip-base setup nois

* fix(studio): align Windows setup logs with Linux style and improve startup tips

* fix(studio): align Windows setup logs with Linux style

* refactor(windows-installer): align install/setup logs with Linux style and silence auto-launch output

* refactor(windows): align installer/setup output with Linux style and reduce default verbosity

* refactor(windows): match install.ps1 output style/colors to setup and quiet default logs

* fix(studio-banner): update personal-computer localhost tip

* fix(setup.sh): restore verbose llama.cpp build output while keeping default quiet mode

* fix(install.sh): align installer logging with setup style and restore POSIX-safe color output

* fix(install.sh): preserve installer reliability and launch visibility

Export verbose mode for child setup processes, harden install command handling under set -e, and keep first-run studio launch non-silent so users can always see URL and port fallback output.

* fix(windows installer): keep exit semantics and degrade status accurate

Use quiet command redirection that preserves native exit codes, keep startup output visible on first launch, and report limited install status when llama.cpp is unavailable.

* fix(setup.sh): improve log clarity and enforce GGUF degraded signaling

Restore clean default setup output, add verbose-only diagnostics, fail fast on Colab dependency install errors, and return non-zero when GGUF prerequisites or llama.cpp artifacts are unavailable.

* fix(installer): harden bash preflight and PowerShell GPU checks

Fail fast when bash is unavailable before invoking setup.sh, and replace remaining nvidia-smi pipeline checks with stream redirection patterns that preserve reliable native exit-code handling.

* fix(windows): keep verbose output visible while preserving exit codes

Ensure PowerShell wrapper helpers in install/update stream native command output to host without returning it as function output, so npm logs no longer corrupt exit-code checks in verbose mode.

* fix(windows): avoid sticky UNSLOTH_VERBOSE and gate studio update verbosity

* Fix degraded llama.cpp exit code, PS verbose stderr, banner URLs, npm verbose

- setup.sh: Do not exit non-zero when llama.cpp is unavailable; the footer
  already reports the limitation, and install.sh runs under set -e so a
  non-zero exit aborts the entire install including PATH/shortcuts/launch.
- setup.ps1: Remove $? check in Invoke-SetupCommand verbose path; PS 5.1
  sets $? = $false when native commands write to stderr even with exit 0.
  Merge stderr into stdout with 2>&1 and rely solely on $LASTEXITCODE.
- startup_banner.py: Show the actual bound address when Studio is bound to
  a non-loopback interface instead of always showing 127.0.0.1/localhost.
- setup.sh: Use run_quiet_no_exit instead of run_quiet_no_exit_always for
  npm install steps so --verbose correctly surfaces npm output.

* Fix install.ps1 verbose stderr, propagate UNSLOTH_VERBOSE, fix git clone verbose

- install.ps1: Apply same Invoke-InstallCommand fix as setup.ps1 -- merge
  stderr into stdout with 2>&1 and drop the $? check that misclassifies
  successful native commands on PS 5.1.
- install.ps1 + setup.ps1: Export UNSLOTH_VERBOSE=1 to the process env
  when --verbose is passed so child processes like install_python_stack.py
  also run in verbose mode.
- setup.sh: Use run_quiet_no_exit for git clone llama.cpp so --verbose
  correctly surfaces clone diagnostics during source-build fallback.

* Surface prebuilt llama.cpp output in verbose mode, remove dead code, fix banner

- setup.sh: Use tee in verbose mode for prebuilt llama.cpp installer so
  users can see download/validation progress while still capturing the log
  for structured error reporting on failure.
- setup.ps1: Same fix for Windows -- use Tee-Object in verbose mode.
- setup.sh: Remove run_quiet_no_exit_always() which has no remaining callers.
- startup_banner.py: Avoid printing the same URL twice when Studio is
  bound to a specific non-loopback address that matches the display host.

* Fix run_install_cmd exit code after failed if-statement

The previous pattern 'if "$@"; then return 0; fi; _rc=$?' always captured
$? = 0 because $? reflects the if-statement result, not the command's exit
code. Switch to '"$@" && return 0; _rc=$?' which preserves the actual
command exit code on failure. Applies to both verbose and quiet branches.

* Fix _run_quiet exit code, double uv install, missing --local flag

- setup.sh: Fix _run_quiet verbose path that always captured exit code 0
  due to $? resetting after if-then-fi with no else. Switch to the same
  '"$@" && return 0; exit_code=$?' pattern used in install.sh.
- setup.sh: Consolidate the two uv install branches (verbose + quiet)
  into a single attempt with conditional output. Previously, when verbose
  mode was on and the install failed, a second silent attempt was made.
- install.ps1: Pass --local flag to 'unsloth studio update' when
  $StudioLocalInstall is true. Without this, studio.py's update() command
  overwrites STUDIO_LOCAL_INSTALL to "0", which could cause issues if
  setup.ps1 or install_python_stack.py later checks that variable.

* Revert SKIP_STUDIO_BASE change for --no-torch, restore install banners

- Revert SKIP_STUDIO_BASE from 0 to 1 for --no-torch. install.sh already
  installs unsloth+unsloth-zoo and no-torch-runtime.txt before calling
  setup.sh, so letting install_python_stack.py redo it was redundant and
  slowed down --no-torch installs for no benefit.
- Restore the "Unsloth Studio installed!" success banner and "starting
  Unsloth Studio..." launch message so users get clear install completion
  feedback before the server starts.

* Make llama.cpp build failure a hard error with proper cleanup

- setup.sh: Restore exit 1 when _LLAMA_CPP_DEGRADED is true. GGUF
  inference requires a working llama.cpp build, so this should be a
  hard failure, not a silent degradation.
- install.sh: Catch setup.sh's non-zero exit with '|| _SETUP_EXIT=$?'
  instead of letting set -e abort immediately. This ensures PATH setup,
  symlinks, and shortcuts still get created so the user can fix the
  build deps and retry with 'unsloth studio update'. After post-install
  steps, propagate the failure with a clear error message.

* Revert install.ps1 to 'studio setup' to preserve SKIP_STUDIO_BASE

'studio update' pops SKIP_STUDIO_BASE from the environment, which
defeats the fast-path version check added in PR unslothai#4667. When called
from install.ps1 (which already installed packages), SKIP_STUDIO_BASE=1
must survive into setup.ps1 so it skips the redundant PyPI check and
package reinstallation. 'studio setup' does not modify env vars.

* Remove deprecation message from 'studio setup' command

install.ps1 uses 'studio setup' (not 'studio update') to preserve
SKIP_STUDIO_BASE. The deprecation message was confusing during first
install since the user never typed the command.

* Fix stale env vars, scope degraded exit, generic error message for PR unslothai#4651

- install.ps1: Always set STUDIO_LOCAL_INSTALL and clear STUDIO_LOCAL_REPO
  when not using --local, to prevent stale values from a previous --local
  run in the same PowerShell session. Fix log messages to say 'setup' not
  'update' since we call 'studio setup'.
- setup.sh: Only exit non-zero for degraded llama.cpp when called from the
  installer (SKIP_STUDIO_BASE=1). Direct 'unsloth studio update' keeps
  degraded installs successful since Studio is still usable for non-GGUF
  workflows and the footer already reports the limitation.
- install.sh: Make the setup failure error message generic instead of
  GGUF-specific, so unrelated failures (npm, Python deps) do not show
  misleading cmake/git recovery advice.

* Show captured output on failure in quiet mode for PR unslothai#4651

Both Invoke-InstallCommand (install.ps1) and Invoke-SetupCommand
(setup.ps1) now capture command output in quiet mode and display it
in red when the command fails. This matches the behavior of
run_install_cmd in install.sh where failure output is surfaced even
in quiet mode, making cross-platform error debugging consistent.

* Match degraded llama.cpp exit on Windows, fix --local recovery hint for PR unslothai#4651

- setup.ps1: Exit non-zero for degraded llama.cpp when called from
  install.ps1 (SKIP_STUDIO_BASE=1), matching setup.sh behavior. Direct
  'unsloth studio update' keeps degraded installs successful.
- install.sh: Show 'unsloth studio update --local' in the recovery
  message when the install was run with --local, so users retry with
  the correct flag instead of losing local checkout context.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants