Memory planning and dense-quant path: size a local diffusers base's resident companions from its on-disk VAE and text-encoder weights instead of folding them to zero, feed the distilled variant hint into the runtime headroom estimate so turbo and schnell models are not over-reserved, place group-offload companions resident before attaching the transformer hooks so a failed placement falls back to whole-module offload instead of crashing, and bail out of the dense transformer download before it starts when the requested quant scheme is unsupported so the load falls back to GGUF cleanly. sd.cpp stack: scrub the native path lease secret from sd-cli child env, redact native load-progress errors, forward the resolved accelerator when auto-installing a forced-native binary, release stale diffusion GPU ownership on CPU-native loads, and remove the sd.cpp install tree on uninstall. Prequant and scripts: reject prequant artifacts missing base_model_id when a base is requested, expanduser before checkpoint existence checks, record and validate the int8 exclusion filter and fp8 fast-accum in checkpoint metadata, make verify_prequant_backend allowlist its local checkpoint and fail on missing or bad LPIPS and on load-peak regressions, average only finite PSNR values in diffusion_quality, and reset the process-wide attention backend between perf probe variants. API and UI: normalize attention_backend casing before Literal validation, close hidden popovers when leaving the Images page, and clear the stale quant label when loading a direct local GGUF file.
431 lines
21 KiB
Bash
Executable file
431 lines
21 KiB
Bash
Executable file
#!/usr/bin/env sh
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
#
|
|
# Unsloth Studio uninstaller (macOS / Linux / WSL).
|
|
# Stops running servers and removes install dir, launcher data,
|
|
# CLI shim, desktop shortcut, .app bundle, and Launch Services entry.
|
|
# Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME at
|
|
# install time (read back from studio.conf).
|
|
#
|
|
# Usage: curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.sh | sh
|
|
|
|
set -e
|
|
|
|
# Stop a Studio server via its PID file (written by install.sh's _spawn_terminal).
|
|
_kill_pid_file() {
|
|
_pid_file="$1"
|
|
[ -f "$_pid_file" ] || return 0
|
|
_pid=$(sed -n '1s/[^0-9].*//p' "$_pid_file" 2>/dev/null || true)
|
|
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
|
|
kill -TERM "$_pid" 2>/dev/null || true
|
|
# Wait up to 10s for graceful shutdown.
|
|
_i=0
|
|
while kill -0 "$_pid" 2>/dev/null && [ "$_i" -lt 20 ]; do
|
|
sleep 0.5
|
|
_i=$((_i + 1))
|
|
done
|
|
kill -0 "$_pid" 2>/dev/null && kill -KILL "$_pid" 2>/dev/null || true
|
|
fi
|
|
rm -f "$_pid_file" 2>/dev/null || true
|
|
}
|
|
|
|
# BRE-escape a path so it can be embedded in a pkill -f regex.
|
|
_pkill_escape() {
|
|
printf '%s' "$1" | sed -e 's:[][\\.^$*+?{|}()/]:\\&:g'
|
|
}
|
|
|
|
_pkill_studio() {
|
|
# Prefer PID files written by _spawn_terminal so we only touch our own installs.
|
|
for _data_dir in "$HOME/.local/share/unsloth" $(_custom_studio_data_dirs); do
|
|
[ -d "$_data_dir" ] || continue
|
|
for _pf in "$_data_dir"/studio-*.pid; do
|
|
[ -f "$_pf" ] && _kill_pid_file "$_pf"
|
|
done
|
|
done
|
|
|
|
command -v pkill >/dev/null 2>&1 || return 0
|
|
|
|
# Scope fallback patterns to the install roots we are removing so a
|
|
# different Studio install (different UNSLOTH_STUDIO_HOME) is not touched.
|
|
_kill_roots="$HOME/.unsloth/studio"
|
|
_roots_from_conf=$(_custom_studio_roots 2>/dev/null || true)
|
|
[ -n "$_roots_from_conf" ] && _kill_roots="$_kill_roots
|
|
$_roots_from_conf"
|
|
|
|
printf '%s\n' "$_kill_roots" | while IFS= read -r _root; do
|
|
[ -n "$_root" ] || continue
|
|
[ -d "$_root" ] || continue
|
|
_re=$(_pkill_escape "$_root")
|
|
# `unsloth studio` (default port) + `-p N` + `--port N` forms, all
|
|
# anchored on the install root's venv path.
|
|
for _pat in \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio( |\$|.*-p[ =][0-9])" \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio.*--port[ =][0-9]" \
|
|
"${_re}/.*studio/backend/run\.py"
|
|
do
|
|
pkill -TERM -f "$_pat" 2>/dev/null || true
|
|
done
|
|
done
|
|
sleep 0.5
|
|
printf '%s\n' "$_kill_roots" | while IFS= read -r _root; do
|
|
[ -n "$_root" ] || continue
|
|
[ -d "$_root" ] || continue
|
|
_re=$(_pkill_escape "$_root")
|
|
for _pat in \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio( |\$|.*-p[ =][0-9])" \
|
|
"${_re}/unsloth_studio/bin/[^ ]* studio.*--port[ =][0-9]" \
|
|
"${_re}/.*studio/backend/run\.py"
|
|
do
|
|
pkill -KILL -f "$_pat" 2>/dev/null || true
|
|
done
|
|
done
|
|
}
|
|
|
|
_remove_path() {
|
|
_p="$1"
|
|
if [ -e "$_p" ] || [ -L "$_p" ]; then
|
|
rm -rf "$_p" 2>/dev/null && echo " removed: $_p" || echo " could not remove: $_p" >&2
|
|
fi
|
|
}
|
|
|
|
# Accept as Studio root only if Studio sentinels exist (matches install.sh's
|
|
# env-mode ownership guard at install.sh:1358-1361). A bare unsloth_studio/
|
|
# directory is NOT enough -- require the install-time owner marker so a user
|
|
# directory that happens to contain a folder named "unsloth_studio" is safe.
|
|
_is_studio_root() {
|
|
_r="$1"
|
|
[ -n "$_r" ] || return 1
|
|
[ -f "$_r/share/studio.conf" ] && return 0
|
|
[ -f "$_r/unsloth_studio/.unsloth-studio-owned" ] && return 0
|
|
if [ -L "$_r/bin/unsloth" ]; then
|
|
_t=$(readlink "$_r/bin/unsloth" 2>/dev/null || true)
|
|
case "$_t" in *unsloth_studio/bin/unsloth) return 0 ;; esac
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Hard deny list: never delete /, $HOME, $HOME's parent, or system paths.
|
|
_is_unsafe_root() {
|
|
_r="$1"
|
|
[ -z "$_r" ] && return 0
|
|
case "$_r" in /|""|"$HOME"|"$HOME/") return 0 ;; esac
|
|
case "$_r" in /bin|/sbin|/etc|/usr|/usr/*|/var|/var/*|/opt|/opt/*|/Library|/Library/*|/System|/System/*|/Applications|/Applications/*) return 0 ;; esac
|
|
_parent=$(dirname "$HOME" 2>/dev/null || echo "")
|
|
[ -n "$_parent" ] && [ "$_r" = "$_parent" ] && return 0
|
|
return 1
|
|
}
|
|
|
|
# Print share/ dirs of known custom roots (where PID files live).
|
|
_custom_studio_data_dirs() {
|
|
_custom_studio_roots 2>/dev/null | while IFS= read -r _r; do
|
|
[ -d "$_r/share" ] && printf '%s\n' "$_r/share"
|
|
done
|
|
}
|
|
|
|
# Resolve a custom install root from any of:
|
|
# 1. UNSLOTH_STUDIO_HOME / STUDIO_HOME env vars at uninstall time
|
|
# 2. Default-mode studio.conf at $HOME/.local/share/unsloth/studio.conf
|
|
# 3. Env-mode studio.conf at $<root>/share/studio.conf (discovered via 1)
|
|
# install.sh writes UNSLOTH_EXE='<root>/unsloth_studio/bin/unsloth', so
|
|
# the install root is three dirnames up. Prints each discovered non-default
|
|
# root on its own line; the caller iterates and de-duplicates.
|
|
_custom_studio_roots() {
|
|
_seen=""
|
|
_emit() {
|
|
_r="$1"
|
|
[ -z "$_r" ] && return 0
|
|
# Tilde expansion (env vars are not subject to it on quoted assignment),
|
|
# matches install.sh's _resolve_studio_destinations. The literal "~/"
|
|
# pattern is intentional; SC2088 is a false positive here.
|
|
# shellcheck disable=SC2088
|
|
case "$_r" in
|
|
"~") _r="$HOME" ;;
|
|
"~/"*) _r="$HOME/${_r#'~/'}" ;;
|
|
esac
|
|
# Canonicalize so syntactic variants ($HOME/../$USER, trailing slash)
|
|
# resolve to the same path and hit the _is_unsafe_root deny list.
|
|
# shellcheck disable=SC1007
|
|
_canon=$(CDPATH= cd -P -- "$_r" 2>/dev/null && pwd -P)
|
|
[ -n "$_canon" ] && _r="$_canon"
|
|
case "$_r" in "$HOME/.unsloth/studio"|/|"") return 0 ;; esac
|
|
case ":$_seen:" in *":$_r:"*) return 0 ;; esac
|
|
_seen="$_seen:$_r"
|
|
printf '%s\n' "$_r"
|
|
}
|
|
_from_conf() {
|
|
[ -f "$1" ] || return 0
|
|
# Tolerate paths containing apostrophes (install.sh emits '\'' for them).
|
|
_exe=$(sed -n "s/^UNSLOTH_EXE='\(.*\)'\$/\1/p" "$1" | head -n1)
|
|
_exe=$(printf '%s' "$_exe" | sed "s/'\\\\''/'/g")
|
|
[ -n "$_exe" ] || return 0
|
|
_emit "$(dirname "$(dirname "$(dirname "$_exe")")")"
|
|
}
|
|
# Mirror install.sh's precedence: UNSLOTH_STUDIO_HOME wins, STUDIO_HOME is
|
|
# ignored when both are set. Otherwise uninstalling install A could also
|
|
# delete install B if the user has STUDIO_HOME left over from B.
|
|
if [ -n "${UNSLOTH_STUDIO_HOME:-}" ]; then
|
|
_emit "$UNSLOTH_STUDIO_HOME"
|
|
_from_conf "$UNSLOTH_STUDIO_HOME/share/studio.conf"
|
|
elif [ -n "${STUDIO_HOME:-}" ]; then
|
|
_emit "$STUDIO_HOME"
|
|
_from_conf "$STUDIO_HOME/share/studio.conf"
|
|
fi
|
|
# Default-mode conf.
|
|
_from_conf "$HOME/.local/share/unsloth/studio.conf"
|
|
}
|
|
|
|
# Remove $HOME/.local/bin/unsloth only if it's a Studio-managed symlink.
|
|
# Studio's install.sh writes this as a symlink into the studio venv
|
|
# (install.sh: `ln -sfn "$VENV_DIR/bin/unsloth" "$_shim_path"`). A
|
|
# pip-installed `unsloth` CLI is a regular file — leave it alone to avoid
|
|
# wiping an unrelated install.
|
|
_remove_cli_shim() {
|
|
_shim="$HOME/.local/bin/unsloth"
|
|
[ -L "$_shim" ] || return 0
|
|
_target=$(readlink "$_shim" 2>/dev/null || true)
|
|
case "$_target" in
|
|
*/unsloth_studio/bin/unsloth) _remove_path "$_shim" ;;
|
|
*) ;;
|
|
esac
|
|
}
|
|
|
|
_uid=$(id -u 2>/dev/null || echo 0)
|
|
_os=$(uname 2>/dev/null || echo unknown)
|
|
_is_wsl=0
|
|
[ "$_os" = "Linux" ] && grep -qi microsoft /proc/version 2>/dev/null && _is_wsl=1
|
|
|
|
echo "Stopping any running Unsloth Studio servers..."
|
|
_pkill_studio
|
|
|
|
echo "Removing data and install directories..."
|
|
_custom_studio_roots | while IFS= read -r _custom_root; do
|
|
[ -n "$_custom_root" ] || continue
|
|
if _is_unsafe_root "$_custom_root"; then
|
|
echo " refusing to remove unsafe path: $_custom_root" >&2
|
|
continue
|
|
fi
|
|
if ! _is_studio_root "$_custom_root"; then
|
|
echo " refusing to remove non-Studio path: $_custom_root" >&2
|
|
continue
|
|
fi
|
|
_remove_path "$_custom_root"
|
|
done
|
|
_remove_path "$HOME/.unsloth/studio"
|
|
# Default-mode shared llama.cpp build + cache are siblings of studio (not removed
|
|
# by deleting it). No-op in env/custom mode (they nest under the custom root) and
|
|
# when absent. A user-set UNSLOTH_LLAMA_CPP_PATH is intentionally kept.
|
|
_remove_path "$HOME/.unsloth/llama.cpp"
|
|
# Default-mode native diffusion (stable-diffusion.cpp / sd-cli) build, a sibling of
|
|
# studio like llama.cpp (install_sd_cpp_prebuilt.default_install_dir()). No-op in
|
|
# env/custom mode and when absent. A user-set UNSLOTH_SD_CPP_PATH is kept.
|
|
_remove_path "$HOME/.unsloth/stable-diffusion.cpp"
|
|
_remove_path "$HOME/.unsloth/.cache"
|
|
# Isolated Node.js runtime (install_node_prebuilt.py), a sibling of studio in
|
|
# default mode. No-op in env/custom mode (nested under the custom root) and absent.
|
|
_remove_path "$HOME/.unsloth/node"
|
|
# llama.cpp atomic-install staging root (install_llama_prebuilt.py .staging).
|
|
# Normally pruned after activate, but an interrupted build can leave it behind;
|
|
# removing it lets the rmdir below succeed. No-op in env/custom mode and absent.
|
|
_remove_path "$HOME/.unsloth/.staging"
|
|
# llama.cpp install lock (serializes the shared build); a stray one keeps ~/.unsloth
|
|
# from being pruned below. No-op in env/custom mode and when absent.
|
|
_remove_path "$HOME/.unsloth/.llama.cpp.install.lock"
|
|
# ROCm-on-WSL helper artifacts (librocdxg build clone + smoke-test venv). No-op
|
|
# where they don't exist; removing them lets the rmdir below succeed.
|
|
_remove_path "$HOME/.unsloth/librocdxg"
|
|
_remove_path "$HOME/.unsloth/rocm-smoketest"
|
|
# Drop ~/.unsloth only if now empty (rmdir refuses non-empty, so user content is kept).
|
|
rmdir "$HOME/.unsloth" 2>/dev/null || true
|
|
_remove_path "$HOME/.local/share/unsloth"
|
|
# CLI shim: only the symlink Studio created, never a pip-installed file.
|
|
_remove_cli_shim
|
|
|
|
echo "Removing desktop shortcut and launcher lock..."
|
|
# install.sh creates Desktop/Unsloth Studio as a symlink. If the user has an
|
|
# unrelated regular directory by that name, leave it alone.
|
|
_desktop_link="$HOME/Desktop/Unsloth Studio"
|
|
if [ -L "$_desktop_link" ] || [ ! -e "$_desktop_link" ]; then
|
|
_remove_path "$_desktop_link"
|
|
else
|
|
echo " refusing to remove non-symlink Desktop path: $_desktop_link" >&2
|
|
fi
|
|
_remove_path "$HOME/Desktop/unsloth-studio.desktop"
|
|
# Locks are namespaced per-uid; env-mode adds an extra suffix.
|
|
_lock_glob="${XDG_RUNTIME_DIR:-/tmp}/unsloth-studio-launcher-${_uid}"
|
|
for _lock in "$_lock_glob".lock "$_lock_glob"-*.lock; do
|
|
[ -e "$_lock" ] && _remove_path "$_lock"
|
|
done
|
|
|
|
case "$_os" in
|
|
Darwin)
|
|
echo "Removing macOS .app bundle and Launch Services entry..."
|
|
_remove_path "$HOME/Applications/Unsloth Studio.app"
|
|
_lsr="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
|
|
if [ -x "$_lsr" ]; then
|
|
"$_lsr" -u "$HOME/Applications/Unsloth Studio.app" 2>/dev/null || true
|
|
fi
|
|
;;
|
|
Linux)
|
|
if [ "$_is_wsl" = "1" ]; then
|
|
echo "Removing WSL Windows-side shortcuts..."
|
|
# install.sh creates per-distro 'Unsloth Studio (WSL - <distro>).lnk'
|
|
# on the Windows Desktop + Start Menu via powershell.exe. Scope removal
|
|
# to THIS distro (passed as $args[0]) so a multi-distro install keeps the
|
|
# other distros' launchers; the TARGET=wsl.exe check still spares a
|
|
# native install's "Unsloth Studio.lnk". Prefer powershell.exe; test it
|
|
# can EXECUTE (`command -v` succeeds even with interop OFF -- .exe then
|
|
# fails "Exec format error", common on systemd-enabled distros).
|
|
_wsl_distro="${WSL_DISTRO_NAME:-}"
|
|
_ps_ran=0
|
|
if command -v powershell.exe >/dev/null 2>&1 && \
|
|
powershell.exe -NoProfile -Command "exit 0" >/dev/null 2>&1; then
|
|
_ps_ran=1
|
|
# Inject the distro into the command: a -Command string does not
|
|
# receive trailing tokens as $args. WSL distro names are safe to
|
|
# embed (no quotes/$/backtick).
|
|
# shellcheck disable=SC2016
|
|
powershell.exe -NoProfile -Command '$distro = "'"$_wsl_distro"'";
|
|
$dirs = @(
|
|
[Environment]::GetFolderPath("Desktop"),
|
|
(Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs")
|
|
);
|
|
$ws = New-Object -ComObject WScript.Shell;
|
|
foreach ($d in $dirs) {
|
|
if (-not $d -or -not (Test-Path -LiteralPath $d)) { continue }
|
|
Get-ChildItem -LiteralPath $d -Filter "Unsloth Studio*.lnk" -ErrorAction SilentlyContinue | ForEach-Object {
|
|
try {
|
|
$sc = $ws.CreateShortcut($_.FullName);
|
|
if ("$($sc.TargetPath) $($sc.Arguments)" -notmatch "wsl\.exe") { return }
|
|
# When the distro is known, require the per-distro
|
|
# name for this distro or its -d "<distro>" argument
|
|
# so launchers for other distros are not removed.
|
|
if ($distro) {
|
|
$nameMatch = ($_.Name -eq "Unsloth Studio (WSL - $distro).lnk");
|
|
$argMatch = ($sc.Arguments -match ("-d\s+`"?" + [regex]::Escape($distro) + "`"?"));
|
|
if (-not ($nameMatch -or $argMatch)) { return }
|
|
}
|
|
Remove-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue
|
|
} catch { }
|
|
}
|
|
}
|
|
# Keep the shared icon while any Unsloth shortcut still uses it (native
|
|
# install or another WSL distro); drop it only with the last one.
|
|
$iconInUse = $false;
|
|
foreach ($d in $dirs) {
|
|
if (-not $d -or -not (Test-Path -LiteralPath $d)) { continue }
|
|
if (Get-ChildItem -LiteralPath $d -Filter "Unsloth Studio*.lnk" -ErrorAction SilentlyContinue) { $iconInUse = $true; break }
|
|
}
|
|
# Guard LOCALAPPDATA: empty on a service/SYSTEM account makes
|
|
# Join-Path throw, aborting the icon cleanup (mirror uninstall.ps1).
|
|
if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) {
|
|
$iconDir = Join-Path $env:LOCALAPPDATA "Unsloth Studio";
|
|
$ico = Join-Path $iconDir "unsloth.ico";
|
|
if ((-not $iconInUse) -and (Test-Path -LiteralPath $ico)) { Remove-Item -LiteralPath $ico -Force -ErrorAction SilentlyContinue }
|
|
if ((Test-Path -LiteralPath $iconDir) -and -not (Get-ChildItem -LiteralPath $iconDir -Force -ErrorAction SilentlyContinue)) { Remove-Item -LiteralPath $iconDir -Recurse -Force -ErrorAction SilentlyContinue }
|
|
}' >/dev/null 2>&1 || true
|
|
fi
|
|
# Remove $1's shared unsloth.ico only if no Unsloth shortcut (native install
|
|
# or another WSL distro) still uses it, then drop the dir if empty. Reciprocal
|
|
# of uninstall.ps1's _RemoveDataDirKeepingWslIcon (keeps the icon for a
|
|
# surviving WSL shortcut when the native side is removed).
|
|
_drop_shared_icon_if_unused() {
|
|
_du="$1"
|
|
_icodir="$_du/AppData/Local/Unsloth Studio"
|
|
_icon_in_use=0
|
|
for _sd in \
|
|
"$_du/Desktop" \
|
|
"$_du/OneDrive/Desktop" \
|
|
"$_du"/OneDrive*/Desktop \
|
|
"$_du/AppData/Roaming/Microsoft/Windows/Start Menu/Programs"; do
|
|
[ -d "$_sd" ] || continue
|
|
for _any in "$_sd"/"Unsloth Studio"*.lnk; do
|
|
[ -e "$_any" ] && { _icon_in_use=1; break; }
|
|
done
|
|
[ "$_icon_in_use" = "1" ] && break
|
|
done
|
|
if [ "$_icon_in_use" = "0" ]; then
|
|
[ -f "$_icodir/unsloth.ico" ] && rm -f "$_icodir/unsloth.ico" 2>/dev/null || true
|
|
fi
|
|
[ -d "$_icodir" ] && rmdir "$_icodir" 2>/dev/null || true
|
|
}
|
|
# Fallback when powershell.exe can't run (interop disabled): remove WSL .lnk
|
|
# files via drvfs. The "Unsloth Studio (WSL..." name is WSL-specific, so a
|
|
# native install's "Unsloth Studio.lnk" never matches.
|
|
if [ "$_ps_ran" = "0" ]; then
|
|
for _drive in /mnt/c /mnt/d /mnt/e; do
|
|
[ -d "$_drive/Users" ] || continue
|
|
for _udir in "$_drive"/Users/*; do
|
|
[ -d "$_udir" ] || continue
|
|
for _scdir in \
|
|
"$_udir/Desktop" \
|
|
"$_udir/OneDrive/Desktop" \
|
|
"$_udir"/OneDrive*/Desktop \
|
|
"$_udir/AppData/Roaming/Microsoft/Windows/Start Menu/Programs"; do
|
|
[ -d "$_scdir" ] || continue
|
|
if [ -n "$_wsl_distro" ]; then
|
|
# Exact per-distro name (no glob) so other distros survive.
|
|
_lnk="$_scdir/Unsloth Studio (WSL - ${_wsl_distro}).lnk"
|
|
[ -e "$_lnk" ] && rm -f "$_lnk" 2>/dev/null && echo " removed: $_lnk" || true
|
|
else
|
|
# Distro unknown: fall back to the broad WSL prefix.
|
|
for _lnk in "$_scdir"/"Unsloth Studio (WSL"*.lnk; do
|
|
[ -e "$_lnk" ] && rm -f "$_lnk" 2>/dev/null && echo " removed: $_lnk" || true
|
|
done
|
|
fi
|
|
done
|
|
# Drop the shared icon only when no shortcut still needs it.
|
|
_drop_shared_icon_if_unused "$_udir"
|
|
done
|
|
done
|
|
fi
|
|
# ── ROCm-on-WSL config (install_rocm_wsl_strixhalo.sh) ──
|
|
# Remove Unsloth's own ROCDXG config (the env it persisted). The system
|
|
# ROCm userspace is a shared prereq (like CUDA) and is LEFT IN PLACE by
|
|
# default; set UNSLOTH_UNINSTALL_ROCM=1 to remove it too.
|
|
echo "Removing ROCm-on-WSL config..."
|
|
_sudo=""
|
|
if [ "$_uid" != "0" ] && command -v sudo >/dev/null 2>&1; then _sudo="sudo"; fi
|
|
$_sudo rm -f /etc/profile.d/unsloth-rocm-wsl.sh 2>/dev/null || true
|
|
if [ -f "$HOME/.bashrc" ] && grep -q "Unsloth ROCm-on-WSL" "$HOME/.bashrc" 2>/dev/null; then
|
|
_bk=$(mktemp 2>/dev/null || echo "$HOME/.bashrc.unsloth.tmp")
|
|
if sed '/# >>> Unsloth ROCm-on-WSL/,/# <<< Unsloth ROCm-on-WSL/d' "$HOME/.bashrc" > "$_bk" 2>/dev/null; then
|
|
cat "$_bk" > "$HOME/.bashrc" 2>/dev/null || true
|
|
echo " cleaned ROCm-on-WSL block from ~/.bashrc"
|
|
fi
|
|
rm -f "$_bk" 2>/dev/null || true
|
|
fi
|
|
if [ "${UNSLOTH_UNINSTALL_ROCM:-0}" = "1" ]; then
|
|
echo " removing system ROCm (UNSLOTH_UNINSTALL_ROCM=1)..."
|
|
$_sudo rm -f /etc/apt/sources.list.d/rocm.list /etc/apt/preferences.d/rocm-pin-600 \
|
|
/etc/apt/keyrings/rocm.gpg /etc/ld.so.conf.d/rocm.conf 2>/dev/null || true
|
|
$_sudo sh -c 'rm -rf /opt/rocm /opt/rocm-*' 2>/dev/null || true
|
|
if command -v ldconfig >/dev/null 2>&1; then $_sudo ldconfig 2>/dev/null || true; fi
|
|
elif [ -d /opt/rocm ]; then
|
|
echo " Note: ROCm userspace (/opt/rocm*) left in place (shared prereq)."
|
|
echo " Remove it by re-running with UNSLOTH_UNINSTALL_ROCM=1, or manually:"
|
|
echo " sudo rm -rf /opt/rocm /opt/rocm-* && sudo ldconfig"
|
|
fi
|
|
fi
|
|
echo "Removing Linux .desktop entry..."
|
|
_remove_path "$HOME/.local/share/applications/unsloth-studio.desktop"
|
|
if command -v update-desktop-database >/dev/null 2>&1; then
|
|
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Unsloth Studio uninstalled."
|
|
echo "Note: Hugging Face model cache at ~/.cache/huggingface was left in place."
|
|
echo "Remove it manually with 'rm -rf ~/.cache/huggingface/hub' if desired."
|
|
# Env-mode installs leave no breadcrumb in $HOME, so a custom root can
|
|
# only be located if the user re-exports the variable. Print a hint when
|
|
# neither var is set so the bare `curl | sh` flow doesn't silently miss.
|
|
if [ -z "${UNSLOTH_STUDIO_HOME:-}" ] && [ -z "${STUDIO_HOME:-}" ]; then
|
|
echo ""
|
|
echo "If you installed Unsloth Studio with UNSLOTH_STUDIO_HOME or STUDIO_HOME"
|
|
echo "pointing at a custom directory, re-run this script with the same variable"
|
|
echo "set to also remove that install tree, e.g.:"
|
|
echo " UNSLOTH_STUDIO_HOME=/your/path sh -c \"\$(curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.sh)\""
|
|
fi
|