diff --git a/studio/backend/core/inference/llama_cpp.py b/studio/backend/core/inference/llama_cpp.py index 7bcf02dc35..8ba195bdd6 100644 --- a/studio/backend/core/inference/llama_cpp.py +++ b/studio/backend/core/inference/llama_cpp.py @@ -3651,8 +3651,9 @@ class LlamaCppBackend: logger.warning( "Requested MTP speculative decoding but " "llama-server lacks --spec-type mtp/draft-mtp; " - "run `unsloth studio update`. Loading without " - "speculative decoding." + "update by running curl -fsSL https://unsloth.ai/install.sh | sh " + "(macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex " + "(Windows). Loading without speculative decoding." ) return False draft_n_max = _resolved_draft_n_max() diff --git a/studio/backend/main.py b/studio/backend/main.py index 10fbbfddf0..0e1af5a305 100644 --- a/studio/backend/main.py +++ b/studio/backend/main.py @@ -344,7 +344,9 @@ async def lifespan(app: FastAPI): if _caps.get("found") and not _caps.get("supports_mtp"): _msg = ( "llama.cpp prebuilt lacks MTP support " - "(--spec-type mtp/draft-mtp). Run `unsloth studio update`. " + "(--spec-type mtp/draft-mtp). Update by running " + "curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or " + "irm https://unsloth.ai/install.ps1 | iex (Windows). " "MTP GGUFs will load without speculative decoding." ) _log.warning(_msg) diff --git a/studio/backend/tests/test_llama_cpp_freshness.py b/studio/backend/tests/test_llama_cpp_freshness.py index b32aeefcdb..d41f8ce308 100644 --- a/studio/backend/tests/test_llama_cpp_freshness.py +++ b/studio/backend/tests/test_llama_cpp_freshness.py @@ -317,7 +317,8 @@ def test_format_stale_warning_contains_actionable_command(): assert "b9190" in msg assert "b9300" in msg assert "5 days" in msg - assert "unsloth studio update" in msg + assert "install.sh" in msg + assert "install.ps1" in msg def test_format_stale_warning_singular_day(): diff --git a/studio/backend/utils/llama_cpp_freshness.py b/studio/backend/utils/llama_cpp_freshness.py index 2c781f4a7b..e16b2228a6 100644 --- a/studio/backend/utils/llama_cpp_freshness.py +++ b/studio/backend/utils/llama_cpp_freshness.py @@ -233,8 +233,9 @@ def format_stale_warning(info: dict) -> str: age_str = f"{age} day{'s' if age != 1 else ''}" if age is not None else "some time" return ( f"llama.cpp prebuilt is {age_str} behind: installed " - f"{installed}, latest {latest}. Run `unsloth studio update` " - f"to refresh." + f"{installed}, latest {latest}. Update to refresh by running " + f"curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or " + f"irm https://unsloth.ai/install.ps1 | iex (Windows)." ) diff --git a/studio/frontend/src/components/tauri/update-banner.tsx b/studio/frontend/src/components/tauri/update-banner.tsx index 62038f92a9..332ca7bdcb 100644 --- a/studio/frontend/src/components/tauri/update-banner.tsx +++ b/studio/frontend/src/components/tauri/update-banner.tsx @@ -102,7 +102,7 @@ export function UpdateBanner({ : isManualLinuxPackage ? "Open the GitHub release page to install the Linux package" : isExternalServer - ? "Run `unsloth studio update` from your terminal" + ? "To update, run curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows) from your terminal" : "A new app update is available"}

diff --git a/studio/frontend/src/components/web/update-banner.tsx b/studio/frontend/src/components/web/update-banner.tsx index 685a8aa3dc..0e3b0c541a 100644 --- a/studio/frontend/src/components/web/update-banner.tsx +++ b/studio/frontend/src/components/web/update-banner.tsx @@ -8,7 +8,11 @@ import { copyToClipboard } from "@/lib/copy-to-clipboard"; import { AnimatePresence, motion } from "motion/react"; import { type ReactElement, useEffect, useRef, useState } from "react"; -const STUDIO_UPDATE_CMD = "unsloth studio update"; +const STUDIO_UPDATE_UNIX_CMD = "curl -fsSL https://unsloth.ai/install.sh | sh"; +const STUDIO_UPDATE_WINDOWS_CMD = "irm https://unsloth.ai/install.ps1 | iex"; +// Match "windows" (every Windows UA carries "Windows NT") rather than "win", +// which also matches "Darwin" and would mis-detect macOS as Windows. +const WINDOWS_UA_RE = /windows/i; const RELEASE_NOTES_URL = "https://unsloth.ai/docs/new/changelog"; const EASE_OUT_QUART: [number, number, number, number] = [0.165, 0.84, 0.44, 1]; @@ -36,7 +40,15 @@ export function WebUpdateBanner({ } async function handleCopyCommand() { - if (!(await copyToClipboard(STUDIO_UPDATE_CMD))) { + // Copy only the command for the user's OS: pasting both (Unix + PowerShell) + // into a single terminal runs the wrong one and errors out. + const isWindows = + typeof navigator !== "undefined" && + WINDOWS_UA_RE.test(navigator.userAgent || ""); + const command = isWindows + ? STUDIO_UPDATE_WINDOWS_CMD + : STUDIO_UPDATE_UNIX_CMD; + if (!(await copyToClipboard(command))) { return; } setCopiedVersion(status?.latestVersion ?? null); @@ -90,8 +102,23 @@ export function WebUpdateBanner({

Installed package: {status.currentVersion}. To update Studio, - run this in your terminal, then restart Studio. + run the command for your OS in a terminal, then restart + Studio.

+
+

+ macOS / Linux +

+ + {STUDIO_UPDATE_UNIX_CMD} + +

+ Windows (PowerShell) +

+ + {STUDIO_UPDATE_WINDOWS_CMD} + +
diff --git a/studio/frontend/src/features/settings/components/update-studio-instructions.tsx b/studio/frontend/src/features/settings/components/update-studio-instructions.tsx index 90f66483b7..7bbdd524c3 100644 --- a/studio/frontend/src/features/settings/components/update-studio-instructions.tsx +++ b/studio/frontend/src/features/settings/components/update-studio-instructions.tsx @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -import { copyToClipboard } from "@/lib/copy-to-clipboard"; import { useT } from "@/i18n"; +import { copyToClipboard } from "@/lib/copy-to-clipboard"; import { cn } from "@/lib/utils"; import { Copy01Icon, Tick02Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; @@ -10,13 +10,9 @@ import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import type { ReactElement } from "react"; import { useEffect, useRef, useState } from "react"; -const STUDIO_UPDATE_CMD = "unsloth studio update"; -const STUDIO_UPDATE_FALLBACK_UNIX_CMD = - "curl -fsSL https://unsloth.ai/install.sh | sh"; -const STUDIO_UPDATE_FALLBACK_WINDOWS_CMD = - "irm https://unsloth.ai/install.ps1 | iex"; +const STUDIO_UPDATE_UNIX_CMD = "curl -fsSL https://unsloth.ai/install.sh | sh"; +const STUDIO_UPDATE_WINDOWS_CMD = "irm https://unsloth.ai/install.ps1 | iex"; const STUDIO_LOCAL_PULL_CMD = "git pull --ff-only"; -const STUDIO_LOCAL_UPDATE_CMD = "unsloth studio update --local"; const STUDIO_LOCAL_FALLBACK_UNIX_CMD = "./install.sh --local"; const STUDIO_LOCAL_FALLBACK_WINDOWS_CMD = ".\\install.ps1 --local"; @@ -145,6 +141,9 @@ export function UpdateStudioInstructions({ const shell = shellOverride ?? defaultShell; const prefersReducedMotion = useReducedMotion(); const windows = shell === "windows"; + const updateCmd = windows + ? STUDIO_UPDATE_WINDOWS_CMD + : STUDIO_UPDATE_UNIX_CMD; const localInstallSource = isLocalInstallSource(installSource); const checkoutInstallSource = installSource === "editable" || installSource === "local_repo"; @@ -224,16 +223,9 @@ export function UpdateStudioInstructions({ command={STUDIO_LOCAL_PULL_CMD} copyLabel={t("settings.about.update.gitPullCommand")} /> - -

- {t("settings.about.update.localInstallerFallback")} -

@@ -291,17 +283,20 @@ export function UpdateStudioInstructions({

{t("settings.about.update.curlOrPypi")}

- -

- {t("settings.about.update.localCheckout")} -

- + + + + +

{t("settings.about.update.restartAfterUpdate")}

@@ -320,28 +315,17 @@ export function UpdateStudioInstructions({ {getStudioUpdateInstructionLine(shell, t)} - -

- {t("settings.about.update.fallbackInstruction")} -

diff --git a/studio/frontend/src/hooks/use-tauri-backend.ts b/studio/frontend/src/hooks/use-tauri-backend.ts index c7628c0fd0..8feffd7da1 100644 --- a/studio/frontend/src/hooks/use-tauri-backend.ts +++ b/studio/frontend/src/hooks/use-tauri-backend.ts @@ -71,8 +71,8 @@ function externalConflictMessage(preflight: DesktopPreflightResult) { } return preflight.port - ? `A Studio server for this install is already running from a terminal on port ${preflight.port}. Stop that server, or run \`unsloth studio update\` from that terminal before using the desktop app.` - : "A Studio server for this install is already running from a terminal. Stop that server, or run `unsloth studio update` from that terminal before using the desktop app."; + ? `A Studio server for this install is already running from a terminal on port ${preflight.port}. Stop that server, or update by running curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows) from that terminal before using the desktop app.` + : "A Studio server for this install is already running from a terminal. Stop that server, or update by running curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows) from that terminal before using the desktop app."; } async function waitForManagedServerReady( @@ -248,8 +248,8 @@ export function useTauriBackend() { } else { setBackendError( preflight.disposition === "owned_stale" - ? "Desktop-owned Studio backend is too old for this desktop app. Run `unsloth studio update`, then restart Studio." - : "Managed Studio install is too old. Run `unsloth studio update`.", + ? "Desktop-owned Studio backend is too old for this desktop app. Update by running curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows), then restart Studio." + : "Managed Studio install is too old. Update by running curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows).", ); } return; diff --git a/studio/frontend/src/i18n/locales/en.ts b/studio/frontend/src/i18n/locales/en.ts index 83a04b60c1..97412e967d 100644 --- a/studio/frontend/src/i18n/locales/en.ts +++ b/studio/frontend/src/i18n/locales/en.ts @@ -307,8 +307,6 @@ export const en = { "Pull latest changes from your Unsloth repo checkout, then update Studio locally:", gitPullCommand: "git pull command", localUpdateCommand: "local update command", - localInstallerFallback: - "If the Studio update command is unavailable, run the local installer from that checkout:", localInstallerCommand: "local installer command", sourceInstallDetected: "This looks like a source or VCS package install. Reinstall from the original local path or Git URL you used.", @@ -320,11 +318,6 @@ export const en = { "Studio could not detect how it was installed. Check how you installed Studio first, then choose the matching update path.", curlOrPypi: "For curl or PyPI installs, run:", updateCommand: "update command", - localCheckout: - "For local checkout installs, update from that checkout instead and use the local update command:", - fallbackInstruction: - "If that fails or unsloth studio update is unavailable, run:", - fallbackCommand: "fallback command", }, }, }, diff --git a/studio/frontend/src/i18n/locales/zh-CN.ts b/studio/frontend/src/i18n/locales/zh-CN.ts index f7075eb887..bc0e24028b 100644 --- a/studio/frontend/src/i18n/locales/zh-CN.ts +++ b/studio/frontend/src/i18n/locales/zh-CN.ts @@ -288,8 +288,6 @@ export const zhCN = { "从你的 Unsloth 仓库 checkout 拉取最新变更,然后本地更新 Studio:", gitPullCommand: "git pull 命令", localUpdateCommand: "本地更新命令", - localInstallerFallback: - "如果 Studio 更新命令不可用,请从该 checkout 运行本地安装器:", localInstallerCommand: "本地安装器命令", sourceInstallDetected: "这看起来是源码或 VCS 包安装。请从最初使用的本地路径或 Git URL 重新安装。", @@ -300,11 +298,6 @@ export const zhCN = { "Studio 无法检测安装方式。请先确认你如何安装 Studio,然后选择匹配的更新方式。", curlOrPypi: "对于 curl 或 PyPI 安装,请运行:", updateCommand: "更新命令", - localCheckout: - "对于本地 checkout 安装,请改为从该 checkout 更新并使用本地更新命令:", - fallbackInstruction: - "如果失败,或 unsloth studio update 不可用,请运行:", - fallbackCommand: "备用命令", }, }, }, diff --git a/studio/src-tauri/src/commands.rs b/studio/src-tauri/src/commands.rs index 06f2dca941..0c796694d8 100644 --- a/studio/src-tauri/src/commands.rs +++ b/studio/src-tauri/src/commands.rs @@ -30,7 +30,7 @@ fn external_conflict_message(conflict: &crate::preflight::ExternalBackendConflic ); } format!( - "A Studio server for this install is already running from a terminal on port {}. Stop that server, or run `unsloth studio update` from that terminal before using desktop repair/update.", + "A Studio server for this install is already running from a terminal on port {}. Stop that server, or update by running curl -fsSL https://unsloth.ai/install.sh | sh (macOS/Linux) or irm https://unsloth.ai/install.ps1 | iex (Windows) from that terminal before using desktop repair/update.", conflict.port ) }