diff --git a/.gitattributes b/.gitattributes index 5f04b5e9d1..0025f2a697 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,7 +6,7 @@ # them when run in WSL/Linux (e.g. `set -e` -> "set: Illegal option -"). *.sh text eol=lf -# Normalize Studio frontend sources to LF. Scoped to the frontend tree (rather +# Normalize Unsloth frontend sources to LF. Scoped to the frontend tree (rather # than repo-wide *.ts/*.tsx/... rules) so the policy can't force LF on files # elsewhere. text=auto lets Git detect and leave binary assets (logos, fonts) # untouched while text files (.ts/.tsx/.json/.html/.svg/...) are stored as LF. diff --git a/.github/scripts/agent-guides-drive.sh b/.github/scripts/agent-guides-drive.sh index f4189a159e..2457f08407 100755 --- a/.github/scripts/agent-guides-drive.sh +++ b/.github/scripts/agent-guides-drive.sh @@ -166,8 +166,8 @@ parse_connect() { echo "[$AGENT] connect --no-launch printed:"; cat_redacted "$raw" CONNECT_ENV="$(grep -E '^(export |unset )' "$raw" || true)" # The launch command is the last non-export, non-status line. start.py - # prints "Studio · model " and "Updated ..." status lines first. - CONNECT_CMD="$(grep -vE '^(export |unset |Studio |Updated |Disabled |Warning|Loading)' "$raw" \ + # prints "Unsloth · model " and "Updated ..." status lines first. + CONNECT_CMD="$(grep -vE '^(export |unset |Unsloth |Updated |Disabled |Warning|Loading)' "$raw" \ | grep -E '[^[:space:]]' | tail -1)" [ -n "$CONNECT_CMD" ] || guide_fail "could not parse a launch command from connect --no-launch output" redact "$raw" diff --git a/.github/scripts/assert-llama-loads.sh b/.github/scripts/assert-llama-loads.sh index c2ffe27469..62ef80d364 100755 --- a/.github/scripts/assert-llama-loads.sh +++ b/.github/scripts/assert-llama-loads.sh @@ -2,7 +2,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. # -# Assert Studio installed a llama.cpp that loads and runs on THIS macOS. Tests +# Assert Unsloth installed a llama.cpp that loads and runs on THIS macOS. Tests # the contract that matters (binaries load and their minimum-OS is <= this host) # instead of the old "did install.sh fall back to a source build?" grep, since a # source build with a correct deployment target is a valid outcome. diff --git a/.github/scripts/assert-prompt-cache.sh b/.github/scripts/assert-prompt-cache.sh index f5b6b075eb..8c28569f77 100755 --- a/.github/scripts/assert-prompt-cache.sh +++ b/.github/scripts/assert-prompt-cache.sh @@ -31,7 +31,7 @@ # (llama_cpp.py:337-340). So default: ~/.unsloth/studio/logs/llama-server/. # #

is the INTERNAL llama-server port (self._find_free_port(), -# llama_cpp.py:3489 / :4641) -- a RANDOM port, NOT the Studio port. So we must +# llama_cpp.py:3489 / :4641) -- a RANDOM port, NOT the Unsloth port. So we must # NOT filter the log glob by STUDIO_PORT (the brief's `port-` # glob would never match). We pick the newest llama-*.log instead. # diff --git a/.github/scripts/hf-download-with-retry.sh b/.github/scripts/hf-download-with-retry.sh index 013a459f46..6dec93356a 100755 --- a/.github/scripts/hf-download-with-retry.sh +++ b/.github/scripts/hf-download-with-retry.sh @@ -3,7 +3,7 @@ # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 # # Download a single file from a Hugging Face repo with a stall-retry -# watchdog. Used by the Studio CI workflows so a hung hf-xet transfer +# watchdog. Used by the Unsloth CI workflows so a hung hf-xet transfer # kills + retries instead of silently consuming the job's timeout. # # Usage: hf-download-with-retry.sh REPO FILE LOCAL_DIR @@ -35,7 +35,7 @@ REPO="${1:?usage: hf-download-with-retry.sh REPO FILE [LOCAL_DIR]}" FILE="${2:?usage: hf-download-with-retry.sh REPO FILE [LOCAL_DIR]}" # LOCAL_DIR is optional. If empty, hf falls back to HF_HUB_CACHE # (~/.cache/huggingface/hub) which is the desired path for callers -# that populate HF_HOME for a downstream Studio model load. +# that populate HF_HOME for a downstream Unsloth model load. LOCAL_DIR="${3:-}" # Stall threshold per attempt, in seconds. Override with diff --git a/.github/workflows/lint-ci.yml b/.github/workflows/lint-ci.yml index bd859a6e9e..e1f0afd299 100644 --- a/.github/workflows/lint-ci.yml +++ b/.github/workflows/lint-ci.yml @@ -13,10 +13,10 @@ # committed YAML / JSON config. # # TypeScript and Rust are NOT duplicated here on purpose: -# - Studio Frontend CI runs `npm run typecheck` (= `tsc --noEmit`) +# - Unsloth Frontend CI runs `npm run typecheck` (= `tsc --noEmit`) # and `npm run build` (vite/swc) on every studio/frontend/** # change, which is a full TS AST + type check. -# - Studio Tauri CI runs `tauri build --debug --no-bundle` on +# - Unsloth Tauri CI runs `tauri build --debug --no-bundle` on # every studio/src-tauri/** or studio/frontend/** change, which # compiles the Rust crate (= cargo check + cargo build). # Each is a stricter check than a parse-only step would be, so a diff --git a/.github/workflows/local-agent-guides-ci.yml b/.github/workflows/local-agent-guides-ci.yml index 25796bd5cf..c48328e90f 100644 --- a/.github/workflows/local-agent-guides-ci.yml +++ b/.github/workflows/local-agent-guides-ci.yml @@ -154,7 +154,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-v1 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Gated off PR (see note above); public GGUF still downloads. @@ -256,7 +256,7 @@ jobs: done fi - - name: Stop Studio + - name: Stop Unsloth if: always() run: | # Guard the PID: an unset/zero UNSLOTH_SERVER_PID would make @@ -359,7 +359,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-v1 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Gated off PR (see note above); public GGUF still downloads. @@ -448,7 +448,7 @@ jobs: done fi - - name: Stop Studio + - name: Stop Unsloth if: always() run: | # Guard the PID: an unset/zero UNSLOTH_SERVER_PID would make @@ -543,7 +543,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-v1 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} HF_TOKEN: ${{ secrets.HF_TOKEN }} @@ -620,7 +620,7 @@ jobs: done fi - - name: Stop Studio + - name: Stop Unsloth if: always() run: | if [ -n "${UNSLOTH_SERVER_PID:-}" ] && [ "${UNSLOTH_SERVER_PID}" != "0" ]; then @@ -706,7 +706,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Gated off PR (see note above); public GGUF still downloads. @@ -764,7 +764,7 @@ jobs: done fi - - name: Stop Studio + - name: Stop Unsloth if: always() run: | # Guard the PID: an unset/zero UNSLOTH_SERVER_PID would make diff --git a/.github/workflows/mlx-ci.yml b/.github/workflows/mlx-ci.yml index a2f716a93c..aadf0b54e6 100644 --- a/.github/workflows/mlx-ci.yml +++ b/.github/workflows/mlx-ci.yml @@ -130,7 +130,7 @@ jobs: # MLX support landed after the most recent unsloth-zoo PyPI # release; the wheel still raises NotImplementedError on # Apple Silicon when device_type.get_device_type() runs - # unguarded. Studio's own install.sh overlays unsloth-zoo + # unguarded. Unsloth's own install.sh overlays unsloth-zoo # from git main for the same reason. Pulling deps lets pip # resolve the platform-conditional MLX-only wheels (mlx, # mlx-lm, mlx-vlm gated on darwin+arm64 in unsloth-zoo's @@ -317,13 +317,13 @@ jobs: echo done - # Validates the macOS prebuilt path Studio's setup.sh uses (#5963): install the + # Validates the macOS prebuilt path Unsloth's setup.sh uses (#5963): install the # unslothai/llama.cpp fork's latest release, download a small public GGUF, and # check llama-server /completion end to end. Split and placed last so the # untrusted binary runs only in the final smoke step, after every HF_TOKEN step, # leaving no token-bearing step or shared workspace for a tampered prebuilt to # corrupt. GH_TOKEN: releases API; HF_TOKEN (withheld on PR): probe + GGUF fetch. - - name: Studio prebuilt llama.cpp install + GGUF download (Mac M1) + - name: Unsloth prebuilt llama.cpp install + GGUF download (Mac M1) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -344,12 +344,12 @@ jobs: # Final step: runs the downloaded binaries with no secrets present, and clears # the GitHub Actions command files so a tampered prebuilt cannot influence the job. - - name: Studio prebuilt llama.cpp GGUF inference smoke (Mac M1) + - name: Unsloth prebuilt llama.cpp GGUF inference smoke (Mac M1) run: | set -euo pipefail unset GITHUB_ENV GITHUB_PATH GITHUB_OUTPUT GITHUB_STEP_SUMMARY INSTALL_DIR="$HOME/.unsloth-studio-prebuilt-test/llama.cpp" - # Studio bundles only llama-server + llama-quantize (not llama-cli); + # Unsloth bundles only llama-server + llama-quantize (not llama-cli); # inference goes through llama-server's HTTP /completion endpoint. LLAMA_SERVER="$INSTALL_DIR/build/bin/llama-server" LLAMA_QUANT="$INSTALL_DIR/build/bin/llama-quantize" @@ -400,4 +400,4 @@ jobs: tail -40 /tmp/llama-server.log exit 1 fi - echo "OK: Studio prebuilt llama.cpp on Mac M1 + GGUF /completion works" + echo "OK: Unsloth prebuilt llama.cpp on Mac M1 + GGUF /completion works" diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 4daafae35d..081eda4e32 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: studio_version: - description: 'Studio version tag to release (for example, v0.1.39-beta)' + description: 'Unsloth version tag to release (for example, v0.1.39-beta)' type: string required: true pypi_version: @@ -69,7 +69,7 @@ jobs: if not studio_version: sys.exit('studio_version is required, for example v0.1.39-beta') if re.fullmatch(r'v?20\d{2}\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?', studio_version): - sys.exit(f'studio_version must be a Studio SemVer tag, not a date-style backend version: {studio_version}') + sys.exit(f'studio_version must be an Unsloth SemVer tag, not a date-style backend version: {studio_version}') semver_tag = re.compile( r'^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)' @@ -146,7 +146,7 @@ jobs: print(f'pypi_version={pypi_version}', file=output) PY - - name: Verify PyPI package and Studio stamp + - name: Verify PyPI package and Unsloth stamp shell: bash env: STUDIO_VERSION: ${{ steps.prepare.outputs.studio_version }} @@ -211,7 +211,7 @@ jobs: fi python3 scripts/stamp_studio_release.py --verify-dist "$RUNNER_TEMP/pypi-unsloth-dist" --expected "$STUDIO_VERSION" else - echo "scripts/stamp_studio_release.py not found; release-desktop requires #5308 to verify the PyPI Studio stamp." >&2 + echo "scripts/stamp_studio_release.py not found; release-desktop requires #5308 to verify the PyPI Unsloth stamp." >&2 exit 1 fi diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index 1275d12216..27eafbedea 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -36,8 +36,8 @@ # - unsloth `huggingfacenotorch` extras (the canonical install path # for fine-tuning users; pulls transformers / peft / accelerate / # trl / datasets / diffusers / sentence-transformers / etc.) -# - all six Studio backend requirements files -# - Studio frontend (npm) and Tauri shell (cargo) +# - all six Unsloth backend requirements files +# - Unsloth frontend (npm) and Tauri shell (cargo) # Each Python step builds a filtered dep list from pyproject.toml + # requirements/*.txt before auditing. We do NOT install any of these # -- pip-audit resolves through PyPI metadata, scan_packages.py @@ -218,7 +218,7 @@ jobs: # on the runner). A comment line is left in place so the # skipped specs are obvious in the artifact. # The `huggingface` extra is `huggingfacenotorch` plus torch / - # torchvision / triton, deliberately skipped: Studio backend + # torchvision / triton, deliberately skipped: Unsloth backend # already pins a torch and the +cu* / +cpu local-version tags # trip up the PyPI resolver in `-r` mode. run: | @@ -253,7 +253,7 @@ jobs: # `-r requirements.txt` resolves the requirements through pip's # dependency resolver against PyPI metadata and audits the # resolved tree without ever executing setup.py / install - # hooks. Way faster than installing the full Studio runtime + # hooks. Way faster than installing the full Unsloth runtime # and -- critically -- safer: an attacker who has compromised # a transitive dep cannot run code in this job. # @@ -326,9 +326,9 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" # ───────────────────────────────────────────────────────────── - # npm: Studio frontend + # npm: Unsloth frontend # ───────────────────────────────────────────────────────────── - - name: npm audit (Studio frontend) + - name: npm audit (Unsloth frontend) # `npm audit` resolves the lockfile through the npmjs.com # advisory DB. `--audit-level=high` filters the noise floor # to only HIGH and CRITICAL. We do NOT pass --omit=dev: a @@ -342,7 +342,7 @@ jobs: # Always also write the full JSON for grep-ability. npm audit --json > ../../logs-npm-audit.json || true { - echo "## npm audit (Studio frontend)" + echo "## npm audit (Unsloth frontend)" echo echo '```' tail -200 ../../logs-npm-audit.txt @@ -350,9 +350,9 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" # ───────────────────────────────────────────────────────────── - # cargo: Studio Tauri shell + # cargo: Unsloth Tauri shell # ───────────────────────────────────────────────────────────── - - name: cargo audit (Studio Tauri) + - name: cargo audit (Unsloth Tauri) # `--deny warnings` would make the job fail on any advisory. # Keep non-blocking initially; drop continue-on-error after # the baseline closes. @@ -362,7 +362,7 @@ jobs: set +e cargo audit | tee ../../logs-cargo-audit.txt { - echo "## cargo audit (Studio Tauri)" + echo "## cargo audit (Unsloth Tauri)" echo echo '```' tail -200 ../../logs-cargo-audit.txt @@ -559,7 +559,7 @@ jobs: # ───────────────────────────────────────────────────────────── # CycloneDX SBOM. Lets downstream consumers audit what's - # actually shipped in unsloth wheels and the Studio backend + # actually shipped in unsloth wheels and the Unsloth backend # runtime. Generates one JSON file per requirements input plus # a combined SBOM keyed off pyproject.toml; uploads as a build # artifact (and a future step can attest it via SLSA). @@ -740,7 +740,7 @@ jobs: # `--with-deps` makes the scan transitive: every package the # declared set resolves to gets fetched and pattern-scanned, not # just the top-level pins. Resolving the full transitive closure - # of the unsloth + Studio dep tree downloads several hundred + # of the unsloth + Unsloth dep tree downloads several hundred # archives, hence the longer timeout. # # Sharded across runners for wall-clock parallelism. Each shard @@ -749,7 +749,7 @@ jobs: # composition tries to balance load: # - hf-stack: pyproject extras + no-torch-runtime # (~150 archives, transformers/peft/accelerate/...) - # - studio: FastAPI/Studio backend + overrides + extras-no-deps + # - studio: FastAPI/Unsloth backend + overrides + extras-no-deps # (~150 archives, smaller scientific stack) # - extras: the heavy openai-whisper / scikit-learn / librosa # stack (~250 archives, dominant cost) @@ -964,7 +964,7 @@ jobs: # documented at scripts/scan_npm_packages.py top-of-file. The # script is stdlib-only so adding it does not increase the # transitive supply-chain surface. - name: npm scan-packages (Studio frontend tarballs) + name: npm scan-packages (Unsloth frontend tarballs) runs-on: ubuntu-latest timeout-minutes: 30 needs: [] @@ -1173,7 +1173,7 @@ jobs: with: python-version: '3.12' - - name: Install Studio frontend deps (--ignore-scripts) + - name: Install Unsloth frontend deps (--ignore-scripts) # `npm audit signatures` requires node_modules to be populated. # `--ignore-scripts` is mandatory: this is exactly the lever the # new-install-script gate below protects against, and we must diff --git a/.github/workflows/studio-api-smoke.yml b/.github/workflows/studio-api-smoke.yml index 15efee382e..cdf1f6bf12 100644 --- a/.github/workflows/studio-api-smoke.yml +++ b/.github/workflows/studio-api-smoke.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# Studio API & Auth Tests -- HTTP-level integration tests for the +# Unsloth API & Auth Tests -- HTTP-level integration tests for the # FastAPI surface. No Playwright, no model UI; tests/studio/test_studio_api_smoke.py # runs ~30 s and asserts: # - CORS hardening (no wildcard + credentials, no bootstrap leak) @@ -15,7 +15,7 @@ # Reuses the GGUF cache key from studio-ui-smoke.yml so the model # download is one cache-hit on the second job. -name: Studio API CI +name: Unsloth API CI on: pull_request: @@ -40,7 +40,7 @@ permissions: jobs: api-smoke: - name: Studio API & Auth Tests + name: Unsloth API & Auth Tests runs-on: ubuntu-latest timeout-minutes: 12 env: @@ -98,7 +98,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -111,7 +111,7 @@ jobs: - name: Install pyjwt for the JWT-expiry forge test run: pip install 'pyjwt>=2.6' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -144,7 +144,7 @@ jobs: echo "STUDIO_NEW_PW=$NEW" >> "$GITHUB_ENV" echo "STUDIO_NEW2_PW=$NEW2" >> "$GITHUB_ENV" - - name: Run Studio API & Auth tests + - name: Run Unsloth API & Auth tests # The script is named WITHOUT a `test_` prefix so it isn't # auto-collected by pytest in Backend CI's `tests/` walk # (which doesn't set BASE_URL and would crash at import). @@ -153,7 +153,7 @@ jobs: STUDIO_AUTH_DIR: /home/runner/.unsloth/studio/auth run: python tests/studio/studio_api_smoke.py - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-backend-ci.yml b/.github/workflows/studio-backend-ci.yml index 3022127a2b..b8f587b63e 100644 --- a/.github/workflows/studio-backend-ci.yml +++ b/.github/workflows/studio-backend-ci.yml @@ -64,7 +64,7 @@ jobs: - name: Install backend test dependencies (CPU only) run: | python -m pip install --upgrade pip - # Studio's declared backend deps: + # Unsloth's declared backend deps: pip install -r studio/backend/requirements/studio.txt # Extras that studio.txt does not list but the import chain needs # (python-multipart for FastAPI form/file uploads, sqlalchemy/cryptography diff --git a/.github/workflows/studio-export-capability-ci.yml b/.github/workflows/studio-export-capability-ci.yml index 1ee6489209..83df3ed476 100644 --- a/.github/workflows/studio-export-capability-ci.yml +++ b/.github/workflows/studio-export-capability-ci.yml @@ -9,7 +9,7 @@ # export is validated separately. No GPU / model / llama.cpp: the tests mock the probes and block # torch/unsloth, so the job installs only a CPU PyTorch plus import deps. -name: Studio export capability +name: Unsloth export capability on: pull_request: diff --git a/.github/workflows/studio-frontend-ci.yml b/.github/workflows/studio-frontend-ci.yml index b42086f191..3a9e373915 100644 --- a/.github/workflows/studio-frontend-ci.yml +++ b/.github/workflows/studio-frontend-ci.yml @@ -136,7 +136,7 @@ jobs: - name: Build run: npm run build - - name: Built bundle must not contain Studio's unstable_Provider call site + - name: Built bundle must not contain Unsloth's unstable_Provider call site run: | set -e JS=$(ls dist/assets/index-*.js | head -1) @@ -144,7 +144,7 @@ jobs: echo "main bundle: $JS" echo "unstable_Provider: hits=$HITS (assistant-ui internals contribute up to 3)" if [ "$HITS" -gt 3 ]; then - echo "::error file=studio/frontend/src/features/chat/runtime-provider.tsx::Studio bundle still passes unstable_Provider through useRemoteThreadListRuntime; this is the 2026.5.1 chat-history regression. Pass adapters directly into useLocalRuntime instead." + echo "::error file=studio/frontend/src/features/chat/runtime-provider.tsx::Unsloth bundle still passes unstable_Provider through useRemoteThreadListRuntime; this is the 2026.5.1 chat-history regression. Pass adapters directly into useLocalRuntime instead." exit 1 fi diff --git a/.github/workflows/studio-inference-smoke.yml b/.github/workflows/studio-inference-smoke.yml index 58ef2558f3..c2d52eac22 100644 --- a/.github/workflows/studio-inference-smoke.yml +++ b/.github/workflows/studio-inference-smoke.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# Three end-to-end smoke jobs that boot a freshly-installed Studio and +# Three end-to-end smoke jobs that boot a freshly-installed Unsloth and # exercise the surfaces real users hit through the OpenAI / Anthropic # SDKs and curl. Each job picks the smallest model that exercises the # behaviour under test, primes HF_HOME via actions/cache, and shares @@ -27,7 +27,7 @@ # All three jobs run in parallel. Total wall time is dominated by job 3 # on a cold cache; warm cache cuts that to ~3 min. -name: Studio GGUF CI +name: Unsloth GGUF CI on: pull_request: @@ -112,7 +112,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -125,7 +125,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -142,7 +142,7 @@ jobs: fi sleep 1 done - echo "Studio did not become healthy in 180s" + echo "Unsloth did not become healthy in 180s" tail -200 logs/studio.log exit 1 @@ -229,11 +229,11 @@ jobs: return replies def run_anthropic(): - # Two SDK quirks vs. Studio: + # Two SDK quirks vs. Unsloth: # 1. base_url must NOT include /v1 -- the SDK appends # /v1/messages itself; otherwise the request hits # /v1/v1/messages and 405s. - # 2. The SDK sends `x-api-key` by default, but Studio's + # 2. The SDK sends `x-api-key` by default, but Unsloth's # auth layer is HTTPBearer-only. Override via # default_headers so Authorization: Bearer ... is # sent instead. @@ -276,7 +276,7 @@ jobs: print( f"[{label}] WARN non-determinism at temperature=0.0 across " f"{len(determinism_failures)} of {len(first)} turn(s); " - f"small-quant model drift, not a Studio regression. " + f"small-quant model drift, not an Unsloth regression. " f"Details: " + " | ".join(determinism_failures) ) # Sanity: turn-2 reply should mention the earlier question, and @@ -290,7 +290,7 @@ jobs: print(f"[{label}] {status_word} -- 4 turns, history grounded ('paris' present)") PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true @@ -323,7 +323,7 @@ jobs: # store xet chunks + blobs + snapshots = ~4 GiB compressed -- # 4-5x file-size inflation, dominated by xet chunks. Use main's # `--local-dir gguf-cache` pattern to cache the flat .gguf only. - # Studio's /api/inference/load accepts either a HF repo (which + # Unsloth's /api/inference/load accepts either a HF repo (which # uses HF_HOME) or an absolute file path; passing the absolute # path keeps the test off HF_HOME entirely so the cache size # tracks the GGUF file 1:1. The OpenAI/Anth and JSON+images @@ -380,7 +380,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-v1 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -390,7 +390,7 @@ jobs: set -o pipefail bash install.sh --local --no-torch 2>&1 | tee logs/install.log - - name: Reset auth + boot Studio (API-only, default tool policy) + - name: Reset auth + boot Unsloth (API-only, default tool policy) # We deliberately use the API-only mode rather than # `unsloth studio run` because the latter calls # `set_tool_policy(...)` with a resolved bool: on loopback the @@ -503,7 +503,7 @@ jobs: that the tool path executed. A shared CI runner can stall the stream transport (the - connection opening, or a mid-stream read) even when Studio + connection opening, or a mid-stream read) even when Unsloth is healthy, so retry a stall once with a fresh request capped at 300s. A stall means the stream did NOT complete, so partial events are normally NOT returned (an early @@ -575,11 +575,11 @@ jobs: def _tool_invoked(events): """Structural check: True iff some SSE payload is a real - tool envelope (Studio tool_start/tool_end, Anthropic + tool envelope (Unsloth tool_start/tool_end, Anthropic tool_use/tool_result, OpenAI non-empty delta.tool_calls / message.tool_calls / finish_reason='tool_calls' / role:'tool' / function_call). tool_status is NOT - evidence: Studio emits empty tool_status events on + evidence: Unsloth emits empty tool_status events on iteration boundaries even when no tool ran. """ for raw in events: @@ -698,7 +698,7 @@ jobs: attempt has structural invocation evidence. WARN (not FAIL) if invoked but no attempt produces the expected literal in tool_end.result -- small-quant Qwen3.5-2B can - emit OpenAI tool_calls deltas without Studio's GGUF + emit OpenAI tool_calls deltas without Unsloth's GGUF agentic loop intercepting them, and that GGUF-vs-OpenAI format mismatch is out of scope for #5642. """ @@ -811,7 +811,7 @@ jobs: # because (a) the search may legitimately return no results, # and (b) DuckDuckGo upstream blocks GHA IP ranges often # enough that requiring a tool_call marker would create - # red-herring failures from infra rather than from Studio. + # red-herring failures from infra rather than from Unsloth. try: # Best-effort and bounded: a single 180s attempt keeps a stall # from eating the job's timeout-minutes (it already WARNs, so a @@ -834,7 +834,7 @@ jobs: print(f"[tools] WARN web_search probe failed (non-blocking): {exc}") # ── 5. Thinking on / off ───────────────────────────────────── - # Studio strips think blocks from message.content for tools-mode + # Unsloth strips think blocks from message.content for tools-mode # responses, so we toggle plain chat (no enable_tools) and look # at the surfaced reasoning_content / message.thinking field. def thinking_call(enable): @@ -848,7 +848,7 @@ jobs: }) assert status == 200 msg = data["choices"][0]["message"] - # Studio surfaces thinking via reasoning_content (OpenAI + # Unsloth surfaces thinking via reasoning_content (OpenAI # extension). Fall back to inline markers for # robustness across template versions. raw = (msg.get("content") or "") + (msg.get("reasoning_content") or "") @@ -868,7 +868,7 @@ jobs: print(f"[tools] PASS thinking on/off (on={len(on_text)} chars, off={len(off_text)} chars)") PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true @@ -960,7 +960,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -973,7 +973,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) # See Job 2's comment: API-only mode keeps tool_policy=None so # response_format requests aren't routed through the agentic # tool loop. @@ -1076,13 +1076,13 @@ jobs: # llama.cpp's HTTP server supports OpenAI-compatible JSON # mode: `response_format: {"type": "json_object"}` constrains # the model to emit syntactically-valid JSON. We use raw HTTP - # rather than the OpenAI SDK so that the field shape Studio + # rather than the OpenAI SDK so that the field shape Unsloth # forwards to llama-server is unambiguous (the SDK rewrites # response_format depending on which variant it recognises). # We deliberately do NOT pass a strict JSON schema -- on # small Gemma-4 quants the GBNF-from-schema path occasionally # produces empty output, and JSON mode is the surface we care - # about exposing through Studio. + # about exposing through Unsloth. status, data = post("/v1/chat/completions", { "model": "default", "messages": [ @@ -1112,7 +1112,7 @@ jobs: print(f"[json] PASS json_object -> {parsed}") # ── 2. OpenAI image_url (data URI base64) ─────────────────── - # 64x64 solid-red PNG. stb_image (used by Studio's image + # 64x64 solid-red PNG. stb_image (used by Unsloth's image # normaliser at routes/inference.py:3410) rejects 4x4 or # smaller PNGs as truncated, so we go up to 64x64 -- still # tiny in token cost. The assertion is loose: any non-empty @@ -1148,9 +1148,9 @@ jobs: print("[image/openai] PASS image_url accepted, non-empty response") # ── 3. Anthropic source/base64 image ──────────────────────── - # Two SDK quirks vs. Studio: base_url must NOT include /v1 + # Two SDK quirks vs. Unsloth: base_url must NOT include /v1 # (the SDK appends it itself; otherwise /v1/v1/messages -> 405), - # and Studio's auth is HTTPBearer-only so the SDK's default + # and Unsloth's auth is HTTPBearer-only so the SDK's default # x-api-key header is ignored -- send Authorization: Bearer # via default_headers. anthropic = Anthropic( @@ -1184,7 +1184,7 @@ jobs: print("[image/anthropic] PASS source/base64 accepted, non-empty response") PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-load-orchestrator-ci.yml b/.github/workflows/studio-load-orchestrator-ci.yml index 93d1a7742d..8710efc2bd 100644 --- a/.github/workflows/studio-load-orchestrator-ci.yml +++ b/.github/workflows/studio-load-orchestrator-ci.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. # -# Event-loop regression test for the Studio model-load orchestrator. +# Event-loop regression test for the Unsloth model-load orchestrator. # Pins down issue #5642 (Win10 UI freeze on model load): the /load # route calls LlamaCppBackend.detect_audio_type synchronously, blocking # the FastAPI event loop on a chain of sync httpx.Client.post() probes. @@ -14,7 +14,7 @@ # danielhanchen/unsloth-staging-2 (Ubuntu / macOS / Windows all # green at PR time). -name: Studio load-orchestrator CI +name: Unsloth load-orchestrator CI on: pull_request: diff --git a/.github/workflows/studio-mac-api-smoke.yml b/.github/workflows/studio-mac-api-smoke.yml index 617ce189dc..1968885a1d 100644 --- a/.github/workflows/studio-mac-api-smoke.yml +++ b/.github/workflows/studio-mac-api-smoke.yml @@ -33,7 +33,7 @@ permissions: jobs: api-smoke: - name: Studio API & Auth Tests + name: Unsloth API & Auth Tests runs-on: macos-14 timeout-minutes: 25 env: @@ -83,7 +83,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -99,7 +99,7 @@ jobs: - name: Install pyjwt for the JWT-expiry forge test run: pip install 'pyjwt>=2.6' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -129,13 +129,13 @@ jobs: echo "STUDIO_NEW_PW=$NEW" >> "$GITHUB_ENV" echo "STUDIO_NEW2_PW=$NEW2" >> "$GITHUB_ENV" - - name: Run Studio API & Auth tests + - name: Run Unsloth API & Auth tests env: BASE_URL: http://127.0.0.1:18895 STUDIO_AUTH_DIR: /Users/runner/.unsloth/studio/auth run: python tests/studio/studio_api_smoke.py - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-mac-inference-smoke.yml b/.github/workflows/studio-mac-inference-smoke.yml index 946681706a..ce15eed5c8 100644 --- a/.github/workflows/studio-mac-inference-smoke.yml +++ b/.github/workflows/studio-mac-inference-smoke.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# Three end-to-end smoke jobs that boot a freshly-installed Studio and +# Three end-to-end smoke jobs that boot a freshly-installed Unsloth and # exercise the surfaces real users hit through the OpenAI / Anthropic # SDKs and curl. Each job picks the smallest model that exercises the # behaviour under test, primes a model cache via actions/cache, and @@ -108,7 +108,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -124,7 +124,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -141,7 +141,7 @@ jobs: fi sleep 1 done - echo "Studio did not become healthy in 180s" + echo "Unsloth did not become healthy in 180s" tail -200 logs/studio.log exit 1 @@ -228,11 +228,11 @@ jobs: return replies def run_anthropic(): - # Two SDK quirks vs. Studio: + # Two SDK quirks vs. Unsloth: # 1. base_url must NOT include /v1 -- the SDK appends # /v1/messages itself; otherwise the request hits # /v1/v1/messages and 405s. - # 2. The SDK sends `x-api-key` by default, but Studio's + # 2. The SDK sends `x-api-key` by default, but Unsloth's # auth layer is HTTPBearer-only. Override via # default_headers so Authorization: Bearer ... is # sent instead. @@ -283,7 +283,7 @@ jobs: print(f"[{label}] OK -- 4 turns, run1 == run2, history grounded") PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true @@ -363,7 +363,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-v1 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -376,7 +376,7 @@ jobs: - name: Assert llama.cpp loads on this macOS run: bash .github/scripts/assert-llama-loads.sh - - name: Reset auth + boot Studio (API-only, default tool policy) + - name: Reset auth + boot Unsloth (API-only, default tool policy) # We deliberately use the API-only mode rather than # `unsloth studio run` because the latter calls # `set_tool_policy(...)` with a resolved bool: on loopback the @@ -478,7 +478,7 @@ jobs: call with enable_tools=true must use this helper. A shared CI runner can stall the stream transport (the - connection opening, or a mid-stream read) even when Studio + connection opening, or a mid-stream read) even when Unsloth is healthy, so harden the read three ways: retry a stall once with a fresh request capped at 300s; return any text already streamed before a stall (a stall on the trailing @@ -574,11 +574,11 @@ jobs: assert status == 200, f"tool call status {status}: {data}" choice = data["choices"][0] tool_calls = (choice.get("message") or {}).get("tool_calls") or [] - # Studio's contract: when tool_choice='required', llama.cpp's + # Unsloth's contract: when tool_choice='required', llama.cpp's # grammar should force a tool_calls payload. On Mac that # contract is sometimes broken by the underlying quant; the # PASS path is "tool_calls present + correct schema", the - # WARN path documents Studio still returned 200 with a + # WARN path documents Unsloth still returned 200 with a # well-formed choices[] envelope. if tool_calls: tc = tool_calls[0] @@ -660,7 +660,7 @@ jobs: print(f"[tools] WARN web_search probe failed (non-blocking): {exc}") # ── 4. Thinking on / off ───────────────────────────────────── - # Studio strips think blocks from message.content for tools-mode + # Unsloth strips think blocks from message.content for tools-mode # responses, so we toggle plain chat (no enable_tools) and look # at the surfaced reasoning_content / message.thinking field. def thinking_call(enable): @@ -678,7 +678,7 @@ jobs: }, timeout = 180) assert status == 200 msg = data["choices"][0]["message"] - # Studio surfaces thinking via reasoning_content (OpenAI + # Unsloth surfaces thinking via reasoning_content (OpenAI # extension). Fall back to inline markers for # robustness across template versions. raw = (msg.get("content") or "") + (msg.get("reasoning_content") or "") @@ -704,7 +704,7 @@ jobs: print(f"[tools] PASS thinking on/off (on={len(on_text)} chars, off={len(off_text)} chars)") PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true @@ -810,7 +810,7 @@ jobs: path: gguf-cache key: ${{ runner.os }}-gguf-${{ env.GGUF_REPO }}-${{ env.GGUF_FILE }}-${{ env.MMPROJ_FILE }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -826,7 +826,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) # See Job 2's comment: API-only mode keeps tool_policy=None so # response_format requests aren't routed through the agentic # tool loop. @@ -929,13 +929,13 @@ jobs: # llama.cpp's HTTP server supports OpenAI-compatible JSON # mode: `response_format: {"type": "json_object"}` constrains # the model to emit syntactically-valid JSON. We use raw HTTP - # rather than the OpenAI SDK so that the field shape Studio + # rather than the OpenAI SDK so that the field shape Unsloth # forwards to llama-server is unambiguous (the SDK rewrites # response_format depending on which variant it recognises). # We deliberately do NOT pass a strict JSON schema -- on # small Gemma-4 quants the GBNF-from-schema path occasionally # produces empty output, and JSON mode is the surface we care - # about exposing through Studio. + # about exposing through Unsloth. status, data = post("/v1/chat/completions", { "model": "default", "messages": [ @@ -1007,7 +1007,7 @@ jobs: ) # ── 2. OpenAI image_url (data URI base64) ─────────────────── - # 64x64 solid-red PNG. stb_image (used by Studio's image + # 64x64 solid-red PNG. stb_image (used by Unsloth's image # normaliser at routes/inference.py:3410) rejects 4x4 or # smaller PNGs as truncated, so we go up to 64x64 -- still # tiny in token cost. The assertion is loose: any non-empty @@ -1023,11 +1023,11 @@ jobs: # The Mac prebuilt llama.cpp server has a known crash when # processing image inputs alongside the gemma-4-E2B mmproj # (server disconnects mid-completion). This is upstream - # llama.cpp behaviour, not Studio. Wrap both SDK calls in + # llama.cpp behaviour, not Unsloth. Wrap both SDK calls in # try/except so an upstream crash registers as a WARN rather - # than failing the whole job. Studio's contract (OpenAI/ + # than failing the whole job. Unsloth's contract (OpenAI/ # Anthropic image fields are accepted and forwarded) is - # validated by the request body Studio constructs, not by + # validated by the request body Unsloth constructs, not by # whether llama.cpp can decode it on Mac Metal. client = OpenAI(base_url = f"{BASE}/v1", api_key = KEY) try: @@ -1053,14 +1053,14 @@ jobs: except Exception as exc: print( f"[image/openai] WARN image_url SDK call raised: {type(exc).__name__}: " - f"{exc}. Likely upstream llama.cpp Mac+vision crash, NOT a Studio " - f"regression. Studio successfully forwarded the request." + f"{exc}. Likely upstream llama.cpp Mac+vision crash, NOT an Unsloth " + f"regression. Unsloth successfully forwarded the request." ) # ── 3. Anthropic source/base64 image ──────────────────────── - # Two SDK quirks vs. Studio: base_url must NOT include /v1 + # Two SDK quirks vs. Unsloth: base_url must NOT include /v1 # (the SDK appends it itself; otherwise /v1/v1/messages -> 405), - # and Studio's auth is HTTPBearer-only so the SDK's default + # and Unsloth's auth is HTTPBearer-only so the SDK's default # x-api-key header is ignored -- send Authorization: Bearer # via default_headers. anthropic = Anthropic( @@ -1099,11 +1099,11 @@ jobs: print( f"[image/anthropic] WARN anthropic image SDK call raised: " f"{type(exc).__name__}: {exc}. Likely upstream llama.cpp Mac+vision " - f"crash, NOT a Studio regression." + f"crash, NOT an Unsloth regression." ) PY - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-mac-install-matrix.yml b/.github/workflows/studio-mac-install-matrix.yml index 362305cdd4..e990f752d4 100644 --- a/.github/workflows/studio-mac-install-matrix.yml +++ b/.github/workflows/studio-mac-install-matrix.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# Proves Studio's llama.cpp install loads on every supported macOS. The heavy +# Proves Unsloth's llama.cpp install loads on every supported macOS. The heavy # app smokes stay single-OS; this matrix covers the OS-version dimension cheaply # (install.sh + binary-load assert). Regression guard for the macOS-version # selection in studio/install_llama_prebuilt.py. @@ -60,7 +60,7 @@ jobs: with: python-version: '3.12' - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. diff --git a/.github/workflows/studio-mac-ui-smoke.yml b/.github/workflows/studio-mac-ui-smoke.yml index 20ca247b9f..378e8ee5a6 100644 --- a/.github/workflows/studio-mac-ui-smoke.yml +++ b/.github/workflows/studio-mac-ui-smoke.yml @@ -83,7 +83,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -143,7 +143,7 @@ jobs: print(f"pipeTransport.js: patched JSON.parse calls in {path}") PY - - name: Reset auth + boot Studio + - name: Reset auth + boot Unsloth run: | unsloth studio reset-password mkdir -p logs @@ -188,7 +188,7 @@ jobs: # dies mid-test, (2) Chromium net::ERR_NO_BUFFER_SPACE when the # runner's kernel briefly runs out of socket buffers, and (3) a # goto 'interrupted by another navigation' when the SPA auth - # guard redirects mid-navigation. The retry FULLY resets Studio + # guard redirects mid-navigation. The retry FULLY resets Unsloth # (kill, reset-password, reboot, wait /api/health, re-export # bootstrap pw) before re-running the script. A real test failure # (assertion / timeout) does NOT match any pattern so it bypasses @@ -209,7 +209,7 @@ jobs: || grep -q "ERR_NO_BUFFER_SPACE" logs/playwright_attempt_${attempt}.log \ || grep -q "interrupted by another navigation" logs/playwright_attempt_${attempt}.log; } \ && [ "$attempt" -lt "$max_attempts" ]; then - echo "::warning::Playwright flake on attempt ${attempt}; resetting Studio and retrying..." + echo "::warning::Playwright flake on attempt ${attempt}; resetting Unsloth and retrying..." kill "${STUDIO_PID}" 2>/dev/null || true sleep 2 unsloth studio reset-password @@ -238,13 +238,13 @@ jobs: exit "$rc" done - - name: Stop Studio (chat-ui ends with Shutdown click; this is belt-and-suspenders) + - name: Stop Unsloth (chat-ui ends with Shutdown click; this is belt-and-suspenders) if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true sleep 2 - - name: Reset auth + boot Studio for extra UI tests (port 18897) + - name: Reset auth + boot Unsloth for extra UI tests (port 18897) run: | unsloth studio reset-password mkdir -p logs @@ -271,7 +271,7 @@ jobs: echo "STUDIO_EXTRA_OLD_PW=$OLD" >> "$GITHUB_ENV" echo "STUDIO_EXTRA_NEW_PW=$NEW" >> "$GITHUB_ENV" - - name: Drive Compare/Recipes/Export/Studio/Settings with Playwright + - name: Drive Compare/Recipes/Export/Unsloth/Settings with Playwright env: BASE_URL: http://127.0.0.1:18897 STUDIO_OLD_PW: ${{ env.STUDIO_EXTRA_OLD_PW }} @@ -300,7 +300,7 @@ jobs: || grep -q "ERR_NO_BUFFER_SPACE" logs/playwright_extra_attempt_${attempt}.log \ || grep -q "interrupted by another navigation" logs/playwright_extra_attempt_${attempt}.log; } \ && [ "$attempt" -lt "$max_attempts" ]; then - echo "::warning::Playwright flake on attempt ${attempt}; resetting Studio and retrying..." + echo "::warning::Playwright flake on attempt ${attempt}; resetting Unsloth and retrying..." kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true sleep 2 unsloth studio reset-password @@ -327,7 +327,7 @@ jobs: exit "$rc" done - - name: Stop second Studio + - name: Stop second Unsloth if: always() run: | kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-mac-update-smoke.yml b/.github/workflows/studio-mac-update-smoke.yml index d104306c7e..fe9880f3ca 100644 --- a/.github/workflows/studio-mac-update-smoke.yml +++ b/.github/workflows/studio-mac-update-smoke.yml @@ -4,15 +4,15 @@ # Mac counterpart to studio-update-smoke.yml. Verifies that on a real # Apple Silicon (macos-14, M1) runner: # -# 1. install.sh --local --no-torch installs Studio AND auto-fetches +# 1. install.sh --local --no-torch installs Unsloth AND auto-fetches # the prebuilt llama.cpp Mac binary (llama-bNNNN-bin-macos-arm64 # from ggml-org/llama.cpp). Hitting the source-build fallback is -# treated as an Unsloth bug -- Studio must always pick the +# treated as an Unsloth bug -- Unsloth must always pick the # prebuilt on Mac. # 2. unsloth studio update --local is idempotent. Two consecutive # runs both report "prebuilt up to date and validated", no # source-build fallback. -# 3. The installed Studio still boots and /api/health returns +# 3. The installed Unsloth still boots and /api/health returns # healthy after the update path. name: Mac Studio Update CI @@ -42,7 +42,7 @@ permissions: jobs: update-idempotency: - name: Studio Updating Tests + name: Unsloth Updating Tests runs-on: macos-14 timeout-minutes: 30 steps: @@ -59,7 +59,7 @@ jobs: python-version: '3.12' cache: 'pip' - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -106,7 +106,7 @@ jobs: grep -qE "prebuilt up to date and validated|prebuilt installed and validated" logs/update2.log echo "second update was clean" - - name: Boot Studio briefly to confirm the install is still usable + - name: Boot Unsloth briefly to confirm the install is still usable run: | mkdir -p logs UNSLOTH_API_ONLY=1 unsloth studio -H 127.0.0.1 -p 18891 \ @@ -123,13 +123,13 @@ jobs: sleep 1 done if [ -z "$HEALTHY" ]; then - echo "Studio failed to come up after \`update\`" + echo "Unsloth failed to come up after \`update\`" tail -200 logs/studio.log kill "$PID" 2>/dev/null || true exit 1 fi kill "$PID" 2>/dev/null || true - echo "post-update Studio /api/health OK" + echo "post-update Unsloth /api/health OK" - name: Uninstall and verify clean # Round-trip through scripts/uninstall.sh on real macOS. As a side diff --git a/.github/workflows/studio-tauri-smoke.yml b/.github/workflows/studio-tauri-smoke.yml index 018857de68..8e26b9fd0c 100644 --- a/.github/workflows/studio-tauri-smoke.yml +++ b/.github/workflows/studio-tauri-smoke.yml @@ -12,7 +12,7 @@ # stay in release-desktop.yml (manual `workflow_dispatch`) because they need # code-signing secrets and ~30 min of runner time each. -name: Studio Tauri CI +name: Unsloth Tauri CI on: pull_request: diff --git a/.github/workflows/studio-ui-smoke.yml b/.github/workflows/studio-ui-smoke.yml index 297a585430..b6d6d7d6e2 100644 --- a/.github/workflows/studio-ui-smoke.yml +++ b/.github/workflows/studio-ui-smoke.yml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# End-to-end Studio chat UI smoke via Playwright + Chromium against a -# headless Linux runner. Boots Studio with the smallest GGUF +# End-to-end Unsloth chat UI smoke via Playwright + Chromium against a +# headless Linux runner. Boots Unsloth with the smallest GGUF # (gemma-3-270m-it UD-Q4_K_XL, ~254 MiB), drives the actual frontend # bundle, and asserts the full bootstrap-password / change-password / # send-message / persist-on-reload journey works end to end. @@ -14,7 +14,7 @@ # frontend-only CI happily pass while the actual user-visible UI is # broken (cf. the 2026.5.1 chat-history release). -name: Studio UI CI +name: Unsloth UI CI on: pull_request: @@ -97,7 +97,7 @@ jobs: path: hf-cache key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Withheld on PR: this step runs checked-out PR code; public GGUF still downloads. @@ -115,7 +115,7 @@ jobs: # warm runner. python -m playwright install --with-deps chromium - - name: Reset auth + boot Studio + - name: Reset auth + boot Unsloth run: | unsloth studio reset-password mkdir -p logs @@ -147,7 +147,7 @@ jobs: # NEW + NEW2 are generated freshly per CI run via secrets.token_urlsafe # rather than hardcoded. If a workflow gets compromised, the # attacker can't replay a known-good rotated password against - # any future / parallel Studio install -- the rotated value + # any future / parallel Unsloth install -- the rotated value # only ever exists for the lifetime of this single job, masked # in the log via ::add-mask::. run: | @@ -165,18 +165,18 @@ jobs: env: BASE_URL: http://127.0.0.1:18892 # The test file lives in the repo so it can be run locally - # against a freshly-installed Studio (BASE_URL=...; STUDIO_OLD_PW= + # against a freshly-installed Unsloth (BASE_URL=...; STUDIO_OLD_PW= # $(cat ~/.unsloth/studio/auth/.bootstrap_password); python ...). PW_ART_DIR: logs/playwright # Strict mode: in CI a missing button / nav / dialog must # FAIL the test. Locally the test still runs against partial - # Studio installs without STUDIO_UI_STRICT. + # Unsloth installs without STUDIO_UI_STRICT. STUDIO_UI_STRICT: '1' run: | mkdir -p logs/playwright python tests/studio/playwright_chat_ui.py - - name: Stop Studio (chat-ui ends with Shutdown click; this is belt-and-suspenders) + - name: Stop Unsloth (chat-ui ends with Shutdown click; this is belt-and-suspenders) if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true @@ -184,10 +184,10 @@ jobs: # The chat UI test ends by clicking the Shutdown menuitem, which # leaves the server dead. The extra UI test (Compare / Recipes / - # Export / Studio / Settings) needs a fresh Studio, so we boot a + # Export / Unsloth / Settings) needs a fresh Unsloth, so we boot a # second one on a different port. Boot is fast (~3-5s on the # warm install we already did) so this adds little wall time. - - name: Reset auth + boot Studio for extra UI tests (port 18894) + - name: Reset auth + boot Unsloth for extra UI tests (port 18894) run: | unsloth studio reset-password mkdir -p logs @@ -214,7 +214,7 @@ jobs: echo "STUDIO_EXTRA_OLD_PW=$OLD" >> "$GITHUB_ENV" echo "STUDIO_EXTRA_NEW_PW=$NEW" >> "$GITHUB_ENV" - - name: Drive Compare/Recipes/Export/Studio/Settings with Playwright + - name: Drive Compare/Recipes/Export/Unsloth/Settings with Playwright env: BASE_URL: http://127.0.0.1:18894 STUDIO_OLD_PW: ${{ env.STUDIO_EXTRA_OLD_PW }} @@ -227,16 +227,16 @@ jobs: mkdir -p logs/playwright_extra python tests/studio/playwright_extra_ui.py - - name: Stop second Studio + - name: Stop second Unsloth if: always() run: | kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true sleep 2 # IME + multilingual paste regression (issue #5318 / PR #5327). - # Third Studio on its own port so a hang here cannot poison the + # Third Unsloth on its own port so a hang here cannot poison the # earlier UI tests. No GGUF -- the bug surface is the composer. - - name: Reset auth + boot Studio for IME / i18n tests (port 18896) + - name: Reset auth + boot Unsloth for IME / i18n tests (port 18896) run: | unsloth studio reset-password mkdir -p logs @@ -256,7 +256,7 @@ jobs: - name: Pass bootstrap pw for IME / i18n test # IME smoke does the change-password against the bootstrap that - # Studio's frontend injects into the page, so it only needs the + # Unsloth's frontend injects into the page, so it only needs the # NEW password. run: | NEW="CIIme-$(python -c 'import secrets; print(secrets.token_urlsafe(16))')" @@ -273,7 +273,7 @@ jobs: mkdir -p logs/playwright_ime python tests/studio/playwright_chat_ime_i18n.py - - name: Stop third Studio + - name: Stop third Unsloth if: always() run: | kill "${STUDIO_IME_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-update-smoke.yml b/.github/workflows/studio-update-smoke.yml index 08a79afacd..625c2c7811 100644 --- a/.github/workflows/studio-update-smoke.yml +++ b/.github/workflows/studio-update-smoke.yml @@ -9,7 +9,7 @@ # This catches regressions in setup.sh's update path that the existing # GGUF / wheel jobs would miss because they only invoke install.sh once. -name: Studio Update CI +name: Unsloth Update CI on: pull_request: @@ -36,7 +36,7 @@ permissions: jobs: update-idempotency: - name: Studio Updating Tests + name: Unsloth Updating Tests runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -63,7 +63,7 @@ jobs: # post-step then fatal-errors with "Cache folder path is # retrieved for pip but doesn't exist on disk". - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) # Pass the workflow token so the llama.cpp prebuilt installer's # GitHub-API call to list releases isn't rate-limited (60/hr # unauthenticated). Without this, three consecutive install + @@ -122,7 +122,7 @@ jobs: grep -qE "prebuilt up to date and validated|prebuilt installed and validated" logs/update2.log echo "second update was clean" - - name: Boot Studio briefly to confirm the install is still usable + - name: Boot Unsloth briefly to confirm the install is still usable # If `update --local` accidentally broke the venv or wiped the # llama-server binary, the server would fail to start here. run: | @@ -138,13 +138,13 @@ jobs: sleep 1 done if ! jq -e '.status == "healthy"' /tmp/health.json 2>/dev/null; then - echo "Studio failed to come up after `update`" + echo "Unsloth failed to come up after `update`" tail -200 logs/studio.log kill "$PID" 2>/dev/null || true exit 1 fi kill "$PID" 2>/dev/null || true - echo "post-update Studio /api/health OK" + echo "post-update Unsloth /api/health OK" - name: Uninstall and verify clean # Round-trip the installer through scripts/uninstall.sh: confirms the diff --git a/.github/workflows/studio-windows-api-smoke.yml b/.github/workflows/studio-windows-api-smoke.yml index e9abd2d669..6dbcceebbd 100644 --- a/.github/workflows/studio-windows-api-smoke.yml +++ b/.github/workflows/studio-windows-api-smoke.yml @@ -9,7 +9,7 @@ # (Section 6) is Linux-only and short-circuits on non-POSIX; the rest # is platform-portable. -name: Windows Studio API CI +name: Windows Unsloth API CI on: pull_request: @@ -34,7 +34,7 @@ permissions: jobs: api-smoke: - name: Studio API & Auth Tests + name: Unsloth API & Auth Tests runs-on: windows-latest timeout-minutes: 30 defaults: @@ -105,7 +105,7 @@ jobs: # studio-windows-update-smoke.yml for the full rationale -- # creating an empty studio/frontend/dist trips setup.ps1's # mtime-based staleness check into "frontend up to date, skip - # rebuild" and Studio boots with an empty dist directory. + # rebuild" and Unsloth boots with an empty dist directory. # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", @@ -121,7 +121,7 @@ jobs: } } - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -161,7 +161,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH # install.ps1's User-PATH update doesn't propagate to a # running Git Bash session; export the shim dir so the # next `unsloth ...` invocation finds it. @@ -177,7 +177,7 @@ jobs: - name: Install pyjwt for the JWT-expiry forge test run: python -m pip install 'pyjwt>=2.6' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -207,7 +207,7 @@ jobs: echo "STUDIO_NEW_PW=$NEW" >> "$GITHUB_ENV" echo "STUDIO_NEW2_PW=$NEW2" >> "$GITHUB_ENV" - - name: Run Studio API & Auth tests + - name: Run Unsloth API & Auth tests # Do NOT pin STUDIO_AUTH_DIR here. The Mac/Linux mirrors # hardcode runner-specific paths (/Users/runner/..., # /home/runner/...), but on Windows the path is @@ -219,7 +219,7 @@ jobs: BASE_URL: http://127.0.0.1:18895 run: python tests/studio/studio_api_smoke.py - - name: Stop Studio + - name: Stop Unsloth if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-windows-inference-smoke.yml b/.github/workflows/studio-windows-inference-smoke.yml index 63a7e9dc8f..3ebe442f52 100644 --- a/.github/workflows/studio-windows-inference-smoke.yml +++ b/.github/workflows/studio-windows-inference-smoke.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -# Three end-to-end smoke jobs that boot a freshly-installed Studio and +# Three end-to-end smoke jobs that boot a freshly-installed Unsloth and # exercise the surfaces real users hit through the OpenAI / Anthropic # SDKs and curl, on the FREE windows-latest runner. Each job picks the # smallest model that exercises the behaviour under test, primes @@ -16,7 +16,7 @@ # Qwen3-VL-2B-Instruct UD-IQ2_XXS + mmproj-F16 (~1.4 GiB total). # Within the 14 GB windows-latest SSD budget. -name: Windows Studio GGUF CI +name: Windows Unsloth GGUF CI on: pull_request: @@ -57,7 +57,7 @@ jobs: STUDIO_PORT: '18888' HF_HOME: ${{ github.workspace }}/hf-cache # Force UTF-8 for stdio (Windows defaults to cp1252; hf - # download / Studio CLI print "✓" checkmarks and crash + # download / Unsloth CLI print "✓" checkmarks and crash # otherwise). PYTHONIOENCODING: utf-8 PYTHONUTF8: '1' @@ -160,7 +160,7 @@ jobs: # studio-windows-update-smoke.yml for the full rationale -- # creating an empty studio/frontend/dist trips setup.ps1's # mtime-based staleness check into "frontend up to date, skip - # rebuild" and Studio boots with an empty dist directory. + # rebuild" and Unsloth boots with an empty dist directory. # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", @@ -176,7 +176,7 @@ jobs: } } - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -214,7 +214,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH run: | SHIM_DIR=~/.unsloth/studio/bin if [ ! -f "$SHIM_DIR/unsloth.exe" ]; then @@ -227,7 +227,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: python -m pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -244,7 +244,7 @@ jobs: fi sleep 1 done - echo "Studio did not become healthy in 180s" + echo "Unsloth did not become healthy in 180s" tail -200 logs/studio.log exit 1 @@ -281,7 +281,7 @@ jobs: # Retry the load step a few times so a transient TCP RST during # llama-server warm-up (Windows runner image churn, # windows-latest -> windows-2025-vs2026 rollout) doesn't fail - # the whole job. The Studio backend's _wait_for_health now + # the whole job. The Unsloth backend's _wait_for_health now # catches httpx.ReadError too; this retry layer covers the # cases the backend can't recover from on its own. LOAD_OK=0 @@ -382,15 +382,15 @@ jobs: print(f"[{label}] OK -- 4 turns, run1 == run2, history grounded") PY - - name: Stop Studio + - name: Stop Unsloth if: always() # Run as cmd so we are not running through the Git Bash shell; # Git Bash on windows-latest has been observed to exit 143 # (SIGTERM) from any inline kill/sleep block, masking a green - # test run. The runner reclaims the Studio child process at + # test run. The runner reclaims the Unsloth child process at # job end either way, so just emit a marker and exit 0. shell: cmd - run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) + run: echo Stop Unsloth (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) - name: Collect llama-server logs if: always() @@ -398,10 +398,10 @@ jobs: # copy must not fail an otherwise-green job. continue-on-error: true shell: bash - # Copy llama-server's own stdout/stderr (teed by Studio under + # Copy llama-server's own stdout/stderr (teed by Unsloth under # ~/.unsloth/studio/logs/llama-server/) into the workspace so # upload-artifact can pick it up. Crucial for diagnosing a - # subprocess crash where Studio's traceback only shows the + # subprocess crash where Unsloth's traceback only shows the # symptom (httpx ReadError) but not the cause. run: | mkdir -p logs/llama-server @@ -439,14 +439,14 @@ jobs: # (211 s on first run; subsequent runs hit the cache, but the # one-time cost recurs every time the cache key bumps). Use # main's `--local-dir gguf-cache` pattern: cache the flat .gguf - # only, pass an absolute path to Studio's /api/inference/load. + # only, pass an absolute path to Unsloth's /api/inference/load. # The OpenAI/Anth and JSON+images jobs still cover the # gguf_variant resolution path. GGUF_REPO: unsloth/Qwen3.5-2B-GGUF GGUF_FILE: Qwen3.5-2B-UD-Q4_K_XL.gguf STUDIO_PORT: '18898' # Force UTF-8 for stdio (Windows defaults to cp1252; hf - # download / Studio CLI print "✓" checkmarks and crash + # download / Unsloth CLI print "✓" checkmarks and crash # otherwise). PYTHONIOENCODING: utf-8 PYTHONUTF8: '1' @@ -507,7 +507,7 @@ jobs: # studio-windows-update-smoke.yml for the full rationale -- # creating an empty studio/frontend/dist trips setup.ps1's # mtime-based staleness check into "frontend up to date, skip - # rebuild" and Studio boots with an empty dist directory. + # rebuild" and Unsloth boots with an empty dist directory. # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", @@ -523,7 +523,7 @@ jobs: } } - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -561,7 +561,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH run: | SHIM_DIR=~/.unsloth/studio/bin if [ ! -f "$SHIM_DIR/unsloth.exe" ]; then @@ -571,7 +571,7 @@ jobs: fi cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Reset auth + boot Studio (API-only, default tool policy) + - name: Reset auth + boot Unsloth (API-only, default tool policy) run: | unsloth studio reset-password mkdir -p logs @@ -607,7 +607,7 @@ jobs: # raw string, but we cannot embed `\a` etc. in JSON without # JSON-string-escaping every backslash. Replace `\` with `/` # via bash parameter expansion -- pathlib.Path on Windows - # accepts forward slashes natively, so Studio's loader sees + # accepts forward slashes natively, so Unsloth's loader sees # a normal path. GGUF_PATH="${GITHUB_WORKSPACE//\\//}/gguf-cache/${GGUF_FILE}" ls -lh "$GGUF_PATH" @@ -680,7 +680,7 @@ jobs: def post_sse(path, body, *, timeout = 600, retries = 1, soft = False): # The server-side agentic loop always answers over SSE. A # shared CI runner can stall the stream transport (the - # connection opening, or a mid-stream read) even when Studio + # connection opening, or a mid-stream read) even when Unsloth # is healthy, so harden the read three ways: # * retry a transport stall once with a fresh request, # capped at 300s (a healthy server answers a retry @@ -882,15 +882,15 @@ jobs: print(f"[tools] PASS thinking on/off (on={len(on_text)} chars, off={len(off_text)} chars)") PY - - name: Stop Studio + - name: Stop Unsloth if: always() # Run as cmd so we are not running through the Git Bash shell; # Git Bash on windows-latest has been observed to exit 143 # (SIGTERM) from any inline kill/sleep block, masking a green - # test run. The runner reclaims the Studio child process at + # test run. The runner reclaims the Unsloth child process at # job end either way, so just emit a marker and exit 0. shell: cmd - run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) + run: echo Stop Unsloth (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) - name: Collect llama-server logs if: always() @@ -898,10 +898,10 @@ jobs: # copy must not fail an otherwise-green job. continue-on-error: true shell: bash - # Copy llama-server's own stdout/stderr (teed by Studio under + # Copy llama-server's own stdout/stderr (teed by Unsloth under # ~/.unsloth/studio/logs/llama-server/) into the workspace so # upload-artifact can pick it up. Crucial for diagnosing a - # subprocess crash where Studio's traceback only shows the + # subprocess crash where Unsloth's traceback only shows the # symptom (httpx ReadError) but not the cause. run: | mkdir -p logs/llama-server @@ -939,7 +939,7 @@ jobs: STUDIO_PORT: '18899' HF_HOME: ${{ github.workspace }}/hf-cache # Force UTF-8 for stdio (Windows defaults to cp1252; hf - # download / Studio CLI print "✓" checkmarks and crash + # download / Unsloth CLI print "✓" checkmarks and crash # otherwise). PYTHONIOENCODING: utf-8 PYTHONUTF8: '1' @@ -1005,7 +1005,7 @@ jobs: # studio-windows-update-smoke.yml for the full rationale -- # creating an empty studio/frontend/dist trips setup.ps1's # mtime-based staleness check into "frontend up to date, skip - # rebuild" and Studio boots with an empty dist directory. + # rebuild" and Unsloth boots with an empty dist directory. # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", @@ -1021,7 +1021,7 @@ jobs: } } - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -1059,7 +1059,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH run: | SHIM_DIR=~/.unsloth/studio/bin if [ ! -f "$SHIM_DIR/unsloth.exe" ]; then @@ -1072,7 +1072,7 @@ jobs: - name: Install OpenAI + Anthropic Python SDKs run: python -m pip install 'openai>=1.50' 'anthropic>=0.40' - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -1262,7 +1262,7 @@ jobs: except Exception as exc: print( f"[image/openai] WARN image_url SDK call raised: {type(exc).__name__}: " - f"{exc}. Studio successfully forwarded the request; failure here is " + f"{exc}. Unsloth successfully forwarded the request; failure here is " f"upstream llama.cpp vision behaviour." ) @@ -1303,19 +1303,19 @@ jobs: print( f"[image/anthropic] WARN anthropic image SDK call raised: " f"{type(exc).__name__}: {exc}. Likely upstream llama.cpp vision " - f"behaviour, NOT a Studio regression." + f"behaviour, NOT an Unsloth regression." ) PY - - name: Stop Studio + - name: Stop Unsloth if: always() # Run as cmd so we are not running through the Git Bash shell; # Git Bash on windows-latest has been observed to exit 143 # (SIGTERM) from any inline kill/sleep block, masking a green - # test run. The runner reclaims the Studio child process at + # test run. The runner reclaims the Unsloth child process at # job end either way, so just emit a marker and exit 0. shell: cmd - run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) + run: echo Stop Unsloth (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) - name: Collect llama-server logs if: always() @@ -1323,10 +1323,10 @@ jobs: # copy must not fail an otherwise-green job. continue-on-error: true shell: bash - # Copy llama-server's own stdout/stderr (teed by Studio under + # Copy llama-server's own stdout/stderr (teed by Unsloth under # ~/.unsloth/studio/logs/llama-server/) into the workspace so # upload-artifact can pick it up. Crucial for diagnosing a - # subprocess crash where Studio's traceback only shows the + # subprocess crash where Unsloth's traceback only shows the # symptom (httpx ReadError) but not the cause. run: | mkdir -p logs/llama-server @@ -1348,7 +1348,7 @@ jobs: # ── folded from studio-windows-no-vs-smoke.yml: install + run with no Visual Studio ── no-vs-cpu: - name: Studio install + inference without Visual Studio + name: Unsloth install + inference without Visual Studio runs-on: windows-latest timeout-minutes: 35 defaults: @@ -1502,7 +1502,7 @@ jobs: python -m pip install torch --index-url https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple python -c "import torch; print('torch', torch.__version__, 'cuda?', torch.cuda.is_available())" - - name: Install Studio (--local, --no-torch) with no build tools present + - name: Install Unsloth (--local, --no-torch) with no build tools present shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -1538,13 +1538,13 @@ jobs: echo "Prebuilt installed with no build tools:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH run: | SHIM_DIR=~/.unsloth/studio/bin [ -f "$SHIM_DIR/unsloth.exe" ] || { echo "::error::unsloth.exe shim not found"; ls -la ~/.unsloth/studio/ || true; exit 1; } cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - - name: Reset auth + boot Studio (API-only) + - name: Reset auth + boot Unsloth (API-only) run: | unsloth studio reset-password mkdir -p logs @@ -1613,10 +1613,10 @@ jobs: } Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue - - name: Stop Studio + - name: Stop Unsloth if: always() shell: cmd - run: echo Stop Studio (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) + run: echo Stop Unsloth (no-op; runner reclaims STUDIO_PID=%STUDIO_PID% at job end) - name: Collect llama-server logs if: always() diff --git a/.github/workflows/studio-windows-ui-smoke.yml b/.github/workflows/studio-windows-ui-smoke.yml index 405309916a..12d7475b53 100644 --- a/.github/workflows/studio-windows-ui-smoke.yml +++ b/.github/workflows/studio-windows-ui-smoke.yml @@ -4,11 +4,11 @@ # Windows counterpart to studio-ui-smoke.yml / studio-mac-ui-smoke.yml. # Same Playwright + Chromium end-to-end chat UI flow + extra UI flow, # but on the FREE windows-latest runner so we catch Windows-specific -# regressions in the install path (install.ps1), the Studio CLI's +# regressions in the install path (install.ps1), the Unsloth CLI's # Windows process-management branches, and the llama.cpp prebuilt's # Windows HTTP layer. -name: Windows Studio UI CI +name: Windows Unsloth UI CI on: pull_request: @@ -49,7 +49,7 @@ jobs: GGUF_FILE: gemma-3-270m-it-UD-Q4_K_XL.gguf STUDIO_PORT: '18896' HF_HOME: ${{ github.workspace }}/hf-cache - # Force UTF-8 for stdio so Python tools (hf download, Studio + # Force UTF-8 for stdio so Python tools (hf download, Unsloth # CLI, etc.) can print Unicode characters like the success # checkmark "✓". Windows defaults to cp1252 / charmap and # any tool that prints "OK ✓" hits a UnicodeEncodeError. @@ -121,7 +121,7 @@ jobs: # studio-windows-update-smoke.yml for the full rationale -- # creating an empty studio/frontend/dist trips setup.ps1's # mtime-based staleness check into "frontend up to date, skip - # rebuild" and Studio boots with an empty dist directory. + # rebuild" and Unsloth boots with an empty dist directory. # Add-MpPreference accepts paths that do not yet exist. foreach ($p in @( "$env:USERPROFILE\.unsloth", @@ -148,7 +148,7 @@ jobs: Set-Content -LiteralPath (Join-Path $appDir 'launch-studio.vbs') -Value 'WScript.Echo "legacy"' -Encoding Unicode Write-Host "seeded legacy launch-studio.vbs at $appDir" - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) # install.ps1 is the supported Windows installer. install.sh # has no Windows branch (apt-get / brew calls). The PS1 # script's `Install-UnslothStudio @args` line at the bottom @@ -205,7 +205,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Assert Studio launcher chain (no VBS, hidden PowerShell shortcut) + - name: Assert Unsloth launcher chain (no VBS, hidden PowerShell shortcut) # The shortcut launch path is otherwise untested here (the steps below # boot `unsloth studio` directly). Guard against re-introducing the VBS # that tripped Kaspersky HEUR:Trojan.VBS.Agent.gen and against the .lnk @@ -234,7 +234,7 @@ jobs: } Write-Host "launcher chain OK (no VBS; hidden powershell over launch-studio.ps1)" - - name: Launch Studio via the shortcut and assert health + - name: Launch Unsloth via the shortcut and assert health # Run the exact command the .lnk stores (hidden PowerShell over # launch-studio.ps1) and confirm it brings the backend up. This is the # only step that proves the shortcut launch is not silently broken. @@ -265,10 +265,10 @@ jobs: $owner = (Get-NetTCPConnection -LocalPort $foundPort -State Listen -ErrorAction Stop | Select-Object -First 1).OwningProcess if ($owner) { taskkill /PID $owner /T /F 2>$null | Out-Null } } catch {} - if (-not $foundPort) { throw "Studio did not become healthy when launched via the shortcut" } - Write-Host "Studio healthy on port $foundPort (launched via the shortcut)" + if (-not $foundPort) { throw "Unsloth did not become healthy when launched via the shortcut" } + Write-Host "Unsloth healthy on port $foundPort (launched via the shortcut)" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH # install.ps1 puts unsloth.exe at $StudioHome\bin\unsloth.exe # and adds that dir to the User PATH via the Windows registry. # Registry-level PATH updates don't propagate to a running @@ -284,7 +284,7 @@ jobs: fi # GITHUB_PATH wants Windows-style paths; convert via cygpath. cygpath -w "$SHIM_DIR" >> "$GITHUB_PATH" - echo "Added Studio shim dir to PATH: $(cygpath -w "$SHIM_DIR")" + echo "Added Unsloth shim dir to PATH: $(cygpath -w "$SHIM_DIR")" - name: Install Playwright + Chromium # No --with-deps on Windows: that flag installs Linux apt @@ -294,7 +294,7 @@ jobs: python -m pip install 'playwright>=1.45' python -m playwright install chromium - - name: Reset auth + boot Studio + - name: Reset auth + boot Unsloth run: | unsloth studio reset-password mkdir -p logs @@ -339,13 +339,13 @@ jobs: mkdir -p logs/playwright python tests/studio/playwright_chat_ui.py - - name: Stop Studio (chat-ui ends with Shutdown click; this is belt-and-suspenders) + - name: Stop Unsloth (chat-ui ends with Shutdown click; this is belt-and-suspenders) if: always() run: | kill "${STUDIO_PID}" 2>/dev/null || true sleep 2 - - name: Reset auth + boot Studio for extra UI tests (port 18897) + - name: Reset auth + boot Unsloth for extra UI tests (port 18897) run: | unsloth studio reset-password mkdir -p logs @@ -372,7 +372,7 @@ jobs: echo "STUDIO_EXTRA_OLD_PW=$OLD" >> "$GITHUB_ENV" echo "STUDIO_EXTRA_NEW_PW=$NEW" >> "$GITHUB_ENV" - - name: Drive Compare/Recipes/Export/Studio/Settings with Playwright + - name: Drive Compare/Recipes/Export/Unsloth/Settings with Playwright env: BASE_URL: http://127.0.0.1:18897 STUDIO_OLD_PW: ${{ env.STUDIO_EXTRA_OLD_PW }} @@ -386,7 +386,7 @@ jobs: mkdir -p logs/playwright_extra python tests/studio/playwright_extra_ui.py - - name: Stop second Studio + - name: Stop second Unsloth if: always() run: | kill "${STUDIO_EXTRA_PID}" 2>/dev/null || true diff --git a/.github/workflows/studio-windows-update-smoke.yml b/.github/workflows/studio-windows-update-smoke.yml index 5b92f1a3e0..42d74d47d2 100644 --- a/.github/workflows/studio-windows-update-smoke.yml +++ b/.github/workflows/studio-windows-update-smoke.yml @@ -5,19 +5,19 @@ # studio-mac-update-smoke.yml. Verifies that on the FREE # windows-latest runner: # -# 1. install.ps1 --local --no-torch installs Studio AND auto-fetches +# 1. install.ps1 --local --no-torch installs Unsloth AND auto-fetches # the prebuilt llama.cpp Windows binary (app--windows-x64-cpu # from unslothai/llama.cpp). Hitting the source-build fallback is -# treated as an Unsloth bug -- Studio must always pick the +# treated as an Unsloth bug -- Unsloth must always pick the # prebuilt on Windows. # 2. unsloth studio update --local is idempotent. Two consecutive # runs both report "prebuilt up to date and validated", no # source-build fallback. The CLI's _find_setup_script picks # setup.ps1 on Windows automatically. -# 3. The installed Studio still boots and /api/health returns +# 3. The installed Unsloth still boots and /api/health returns # healthy after the update path. -name: Windows Studio Update CI +name: Windows Unsloth Update CI on: pull_request: @@ -45,7 +45,7 @@ permissions: jobs: update-idempotency: - name: Studio Updating Tests + name: Unsloth Updating Tests runs-on: windows-latest timeout-minutes: 30 defaults: @@ -53,7 +53,7 @@ jobs: shell: bash env: # Force UTF-8 for stdio (Windows defaults to cp1252; hf - # download / Studio CLI print "✓" checkmarks and crash + # download / Unsloth CLI print "✓" checkmarks and crash # otherwise). PYTHONIOENCODING: utf-8 PYTHONUTF8: '1' @@ -90,7 +90,7 @@ jobs: # reuses the existing Node with no download. # # (2) Defender. windows-latest's real-time scan opens / hashes - # every file Studio writes during install (Vite output = + # every file Unsloth writes during install (Vite output = # thousands of small chunks, uv pip = wheel-extraction = # thousands of small files). The latency dominates the # 200 s frontend build and the 90 s deps install. Adding @@ -109,7 +109,7 @@ jobs: # setup.ps1 line 1281-1296's mtime-based "is the frontend # stale?" check into "up to date, skip rebuild", because the # newly-created dist's mtime is younger than every source - # file. Studio then boots with an empty dist and 500s on + # file. Unsloth then boots with an empty dist and 500s on # GET / with FileNotFoundError: dist\index.html. See run # 25546676715 / job 74984469728. # Add-MpPreference accepts paths that do not yet exist; the @@ -129,7 +129,7 @@ jobs: } } - - name: Install Studio (--local, --no-torch) + - name: Install Unsloth (--local, --no-torch) shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -168,7 +168,7 @@ jobs: echo "install.ps1 installed the Windows prebuilt llama.cpp:" cat "$INFO" - - name: Add Studio shim to GITHUB_PATH + - name: Add Unsloth shim to GITHUB_PATH run: | SHIM_DIR=~/.unsloth/studio/bin if [ ! -f "$SHIM_DIR/unsloth.exe" ]; then @@ -212,7 +212,7 @@ jobs: grep -qE "prebuilt up to date and validated|prebuilt installed and validated" logs/update2.log echo "second update was clean" - - name: Boot Studio briefly to confirm the install is still usable + - name: Boot Unsloth briefly to confirm the install is still usable run: | mkdir -p logs UNSLOTH_API_ONLY=1 unsloth studio -H 127.0.0.1 -p 18891 \ @@ -239,13 +239,13 @@ jobs: sleep 1 done if [ -z "$HEALTHY" ]; then - echo "Studio failed to come up after \`update\`" + echo "Unsloth failed to come up after \`update\`" tail -200 logs/studio.log kill "$PID" 2>/dev/null || true exit 1 fi kill "$PID" 2>/dev/null || true - echo "post-update Studio /api/health OK" + echo "post-update Unsloth /api/health OK" - name: Uninstall and verify clean # Round-trip through scripts/uninstall.ps1 against the default diff --git a/.github/workflows/wheel-smoke.yml b/.github/workflows/wheel-smoke.yml index 3de3c33ca2..cdad617027 100644 --- a/.github/workflows/wheel-smoke.yml +++ b/.github/workflows/wheel-smoke.yml @@ -3,7 +3,7 @@ # Builds the PyPI wheel from the PR branch, then verifies the built wheel # actually contains what we expect to ship and does NOT contain the broken -# Studio bundle that 2026.5.1 published. This is the single workflow that +# Unsloth bundle that 2026.5.1 published. This is the single workflow that # would have blocked the 2026.5.1 release before twine upload. # # Verified locally end-to-end against this branch: @@ -12,7 +12,7 @@ # lockfile shipped, frontend dist shipped, # no node_modules in wheel, no bun.lock in wheel, # main bundle has unstable_Provider hits=1 (assistant-ui internals only). -# - Studio backend imports cleanly from the installed wheel with the +# - Unsloth backend imports cleanly from the installed wheel with the # lightweight dep set below. name: Wheel CI @@ -101,7 +101,7 @@ jobs: hits = data.count("unstable_Provider:") print(f"main bundle: {js[0]}") print(f"unstable_Provider hits: {hits} (>=4 indicates 2026.5.1 regression)") - checks["bundle has no Studio unstable_Provider call site"] = (hits < 4) + checks["bundle has no Unsloth unstable_Provider call site"] = (hits < 4) print() for k, v in checks.items(): @@ -109,7 +109,7 @@ jobs: sys.exit(0 if all(checks.values()) else 1) PY - - name: Studio backend import smoke + - name: Unsloth backend import smoke # Imports `studio.backend.main:app` from the freshly-installed wheel in # a clean venv. This catches the class of bug that 2026.5.1 shipped with: # frontend dist missing, package-lock.json missing, or the wheel's Python @@ -125,7 +125,7 @@ jobs: /tmp/v/bin/pip install --no-deps dist/unsloth-*.whl # Run from /tmp so Python imports the installed package, not the source tree. cd /tmp - /tmp/v/bin/python -c "from studio.backend.main import app; print('Studio backend OK:', app.title)" + /tmp/v/bin/python -c "from studio.backend.main import app; print('Unsloth backend OK:', app.title)" - name: Upload wheel on failure if: failure() diff --git a/README.md b/README.md index ef45b91430..085c7718e5 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Unsloth Studio (Beta) works on **Windows, Linux, WSL** and **macOS**. * **CPU:** Supported for Chat and Data Recipes currently * **NVIDIA:** Training works on RTX 30/40/50, Blackwell, DGX Spark, Station and more * **macOS:** Training, MLX and GGUF inference are ALL supported. -* **AMD:** Chat + Data works. Train with [Unsloth Core](#unsloth-core-code-based). Studio support is out soon. +* **AMD:** Chat + Data works. Train with [Unsloth Core](#unsloth-core-code-based). Unsloth Studio support is out soon. * **Multi-GPU:** Available now, with a major upgrade on the way #### macOS, Linux, WSL: @@ -86,7 +86,7 @@ unsloth studio -p 8888 ``` For LAN or cloud access, add `-H 0.0.0.0` (raw port only; add `--cloudflare` for a public URL). By default, Unsloth is accessible only locally. -To reach Studio over HTTPS, use `unsloth studio --secure`. Studio stays bound to localhost and is reached only through a free Cloudflare tunnel, which publishes it at a public `https://*.trycloudflare.com` URL (it fails closed if the tunnel can't start, so the raw port is never exposed). This makes Studio reachable from the internet, so anyone with the link and API key can use it and run code: keep your API key private (see Remote access below). +To reach Unsloth over HTTPS, use `unsloth studio --secure`. Unsloth stays bound to localhost and is reached only through a free Cloudflare tunnel, which publishes it at a public `https://*.trycloudflare.com` URL (it fails closed if the tunnel can't start, so the raw port is never exposed). This makes Unsloth reachable from the internet, so anyone with the link and API key can use it and run code: keep your API key private (see Remote access below). #### Docker Use our [Docker image](https://hub.docker.com/r/unsloth/unsloth) ```unsloth/unsloth``` container. Run: @@ -208,7 +208,7 @@ unsloth studio -p 8888 #### Remote access: `--secure` (HTTPS tunnel) vs raw port By default `unsloth studio` binds to `127.0.0.1` (this machine only). To reach it from another device, pick one of: -- `--secure` (recommended): serve **only** through a free Cloudflare HTTPS link. Studio stays bound to localhost and the tunnel provides the public URL; it fails closed (does not start) if the tunnel can't come up, so the raw port is never exposed. +- `--secure` (recommended): serve **only** through a free Cloudflare HTTPS link. Unsloth stays bound to localhost and the tunnel provides the public URL; it fails closed (does not start) if the tunnel can't come up, so the raw port is never exposed. ```bash unsloth studio --secure -p 8888 ``` @@ -218,7 +218,7 @@ unsloth studio -H 0.0.0.0 -p 8888 ``` The Cloudflare tunnel is **off by default**: `-H 0.0.0.0` exposes the raw port only, not a public internet URL. Pair the wildcard bind with `--cloudflare` (`unsloth studio -H 0.0.0.0 --cloudflare`) to also publish a public `https://*.trycloudflare.com` link, or prefer `--secure` (above), which keeps the raw port private. `--cloudflare` has no effect on a loopback bind. -The first time Studio is published on a public URL (`--secure` or `--cloudflare`) with the auto-generated admin password still in place, it asks for a new admin password in the terminal (masked input with confirmation) before the public link goes up. Without an attached terminal it warns instead and keeps the bootstrap deadline: Studio shuts down after `UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT` (default 1 hour) unless the password is changed in the web UI. +The first time Unsloth is published on a public URL (`--secure` or `--cloudflare`) with the auto-generated admin password still in place, it asks for a new admin password in the terminal (masked input with confirmation) before the public link goes up. Without an attached terminal it warns instead and keeps the bootstrap deadline: Unsloth shuts down after `UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT` (default 1 hour) unless the password is changed in the web UI. For headless setups that cannot answer that prompt, set the initial admin password non-interactively with `--password` (only takes effect when no password is set yet; if one already exists it is a hard error, so rotate later with `unsloth studio reset-password`): @@ -230,7 +230,7 @@ printf '%s\n' 'your-strong-password' | unsloth studio --secure --password - # A literal `--password VALUE` is visible in the process list and shell history, so prefer the `UNSLOTH_STUDIO_PASSWORD` env var or `--password -` (stdin) for automation. This applies to any launch (public or a headless `-H 0.0.0.0` bind), and the password is set in the parent before the server binds, so it never reaches a re-executed child process. -Server-side tools (web search, Python and terminal code execution) run as your user and are on by default. Anyone who can reach the server with the API key can run code on this machine, so keep your API key private and pass `--disable-tools` when exposing Studio. +Server-side tools (web search, Python and terminal code execution) run as your user and are on by default. Anyone who can reach the server with the API key can run code on this machine, so keep your API key private and pass `--disable-tools` when exposing Unsloth. #### Advanced launch options Installer options can be passed as environment variables. On macOS, Linux and WSL place the variable after the pipe so the shell passes it to `sh`; on Windows set it with `$env:` before piping to `iex`. @@ -243,7 +243,7 @@ curl -fsSL https://unsloth.ai/install.sh | UNSLOTH_NO_TORCH=1 sh $env:UNSLOTH_NO_TORCH=1; irm https://unsloth.ai/install.ps1 | iex ``` -Skip the post-install prompt that starts Studio (useful for automated installs): +Skip the post-install prompt that starts Unsloth (useful for automated installs): ```bash curl -fsSL https://unsloth.ai/install.sh | UNSLOTH_SKIP_AUTOSTART=1 sh ``` @@ -279,9 +279,9 @@ UNSLOTH_NPM_REGISTRY=https://artifactory.example.com/api/npm/npm/ ./install.sh - ```powershell $env:UNSLOTH_NPM_REGISTRY='https://artifactory.example.com/api/npm/npm/'; .\install.ps1 --local ``` -It is threaded as `--registry` into the Studio frontend `npm`/`bun` installs; the supply-chain locks (7-day `min-release-age`, exact version pins) stay in force. +It is threaded as `--registry` into the Unsloth frontend `npm`/`bun` installs; the supply-chain locks (7-day `min-release-age`, exact version pins) stay in force. -Cap Studio's native CPU thread pools on high-core hosts: `UNSLOTH_CPU_THREADS=8 unsloth studio -p 8888`. +Cap Unsloth's native CPU thread pools on high-core hosts: `UNSLOTH_CPU_THREADS=8 unsloth studio -p 8888`. #### Uninstall The recommended way to fully remove Unsloth Studio is the matching uninstall script for your OS. It stops any running servers, removes the install dir, the launcher data dir, the desktop shortcut, and any platform-specific entries (macOS `.app` bundle + Launch Services on Mac; Start Menu, `HKCU\Software\Unsloth` registry key and user `PATH` entries on Windows): diff --git a/build.sh b/build.sh index dc272f0de1..2a836e19d9 100644 --- a/build.sh +++ b/build.sh @@ -4,9 +4,9 @@ set -euo pipefail -# PyPI/Studio release publishing must use `./build.sh publish` (or an -# equivalent stamp -> build -> verify-dist -> upload flow) so packaged Studio -# artifacts include the display-only Studio release version. +# PyPI/Unsloth release publishing must use `./build.sh publish` (or an +# equivalent stamp -> build -> verify-dist -> upload flow) so packaged Unsloth +# artifacts include the display-only Unsloth release version. # 1. Build frontend (Vite outputs to dist/) cd studio/frontend @@ -87,7 +87,7 @@ cd ../.. # 2. Clean old artifacts rm -rf build dist *.egg-info -# 3. Stamp display-only Studio release metadata for packaged builds. +# 3. Stamp display-only Unsloth release metadata for packaged builds. _STUDIO_BUILD_INFO="studio/backend/utils/_studio_release_build.py" _STUDIO_BUILD_INFO_BACKUP="$(mktemp)" cp "$_STUDIO_BUILD_INFO" "$_STUDIO_BUILD_INFO_BACKUP" diff --git a/install.ps1 b/install.ps1 index c25d7e7b7f..df49414620 100644 --- a/install.ps1 +++ b/install.ps1 @@ -176,7 +176,7 @@ function Install-UnslothStudio { $envOverride = $env:STUDIO_HOME.Trim() } - # Custom Studio roots are not supported with --tauri (desktop app still + # Custom Unsloth roots are not supported with --tauri (desktop app still # resolves %USERPROFILE%\.unsloth\studio). Pass through if override == legacy. if ($TauriMode -and $envOverride) { $_tauriOverride = $envOverride @@ -756,7 +756,7 @@ function Find-FreeLaunchPort { return `$null } -# If Studio is already healthy on any expected port, just open it and exit. +# If Unsloth is already healthy on any expected port, just open it and exit. `$existingPort = Find-HealthyStudioPort if (`$existingPort) { Start-Process "http://localhost:`$existingPort" @@ -772,7 +772,7 @@ try { `$haveMutex = `$true } if (-not `$haveMutex) { - # Another launcher is already running; wait for it to bring Studio up + # Another launcher is already running; wait for it to bring Unsloth up `$deadline = (Get-Date).AddSeconds(`$timeoutSec) while ((Get-Date) -lt `$deadline) { `$port = Find-HealthyStudioPort @@ -1438,7 +1438,7 @@ exit 0 if (Test-Path -LiteralPath $VenvPython) { # why: matching guard to the .venv branch below -- in env-mode # $StudioHome is a user-chosen workspace, so refuse to nuke an - # existing $StudioHome\unsloth_studio that lacks Studio sentinels. + # existing $StudioHome\unsloth_studio that lacks Unsloth sentinels. # -PathType Leaf rejects a directory at the sentinel path. Accept the # in-VENV ownership marker so partial-install retries are not blocked. if ( @@ -1449,7 +1449,7 @@ exit 0 ) { Write-Host "[ERROR] $VenvDir already exists but does not look like an Unsloth Studio install." -ForegroundColor Red Write-Host " Move it aside or choose an empty UNSLOTH_STUDIO_HOME." -ForegroundColor Yellow - throw "Refusing to delete non-Studio venv at $VenvDir" + throw "Refusing to delete non-Unsloth venv at $VenvDir" } # New layout already exists -- replace only after preserving rollback copy. substep "preserving existing environment for rollback..." @@ -1468,7 +1468,7 @@ exit 0 # workspace root (e.g. user's existing project Python venv). $OldVenv = Join-Path $StudioHome ".venv" $OldPy = Join-Path $OldVenv "Scripts\python.exe" - substep "found legacy Studio environment, validating..." + substep "found legacy Unsloth environment, validating..." $prevEAP2 = $ErrorActionPreference $ErrorActionPreference = "Continue" try { @@ -1498,7 +1498,7 @@ exit 0 # Skip in env-mode so we don't relocate the default-install venv into # the workspace root. $CwdVenv = Join-Path $env:USERPROFILE "unsloth_studio" - substep "found CWD-relative Studio environment, migrating to $VenvDir..." + substep "found CWD-relative Unsloth environment, migrating to $VenvDir..." Move-Item -LiteralPath $CwdVenv -Destination $VenvDir -Force substep "moved ~/unsloth_studio -> ~/.unsloth/studio/unsloth_studio" $_Migrated = $true @@ -1517,7 +1517,7 @@ exit 0 substep "$VenvDir" } - # Mark the freshly-created venv as Studio-owned so a partial install can be + # Mark the freshly-created venv as Unsloth-owned so a partial install can be # repaired by re-running install.ps1; the env-mode deletion guard above # accepts this marker as the primary sentinel. if (Test-Path -LiteralPath $VenvDir -PathType Container) { @@ -1526,7 +1526,7 @@ exit 0 # ── Helper: run amd-smi without triggering a UAC elevation prompt ── # amd-smi on Windows auto-elevates to read GPU/APU memory, surfacing a confusing - # DiskPart UAC prompt mid-install (Studio backend amd.py hits the same). + # DiskPart UAC prompt mid-install (Unsloth backend amd.py hits the same). # __COMPAT_LAYER=RunAsInvoker forces it (and helpers it spawns) to run # un-elevated; on failure the WMI name -> gfx fallback still resolves the arch. function Invoke-AmdSmiNoElevate { @@ -1653,7 +1653,7 @@ exit 0 function Test-HipinfoIsVenvInternal { param([AllowNull()][string]$HipinfoPath) if ([string]::IsNullOrWhiteSpace($HipinfoPath)) { return $false } - # Also derive the venv from the setup python + default Studio home, so + # Also derive the venv from the setup python + default Unsloth home, so # the venv hipInfo is caught when VenvDir/VIRTUAL_ENV are unset. $venvRoots = @() if ($env:VIRTUAL_ENV) { $venvRoots += $env:VIRTUAL_ENV } @@ -1663,7 +1663,7 @@ exit 0 try { $venvRoots += (Split-Path -Parent (Split-Path -Parent $env:UNSLOTH_SETUP_PYTHON)) } catch {} } if ($env:USERPROFILE) { $venvRoots += (Join-Path $env:USERPROFILE ".unsloth\studio\unsloth_studio") } - # A custom Studio home (UNSLOTH_STUDIO_HOME / STUDIO_HOME alias) moves the + # A custom Unsloth home (UNSLOTH_STUDIO_HOME / STUDIO_HOME alias) moves the # venv off the default path; seed it too or its hipInfo escapes the filter. $studioHomeEnv = if (-not [string]::IsNullOrWhiteSpace($env:UNSLOTH_STUDIO_HOME)) { $env:UNSLOTH_STUDIO_HOME.Trim() } elseif (-not [string]::IsNullOrWhiteSpace($env:STUDIO_HOME)) { $env:STUDIO_HOME.Trim() } else { $null } if ($studioHomeEnv) { @@ -1942,7 +1942,7 @@ exit 0 substep " Ensure the ROCm compute driver is installed alongside the display driver:" "Yellow" substep " https://rocm.docs.amd.com/en/latest/deploy/windows/index.html" "Yellow" } elseif ($ROCmGfxArch) { - # Known arch: Studio setup installs AMD's bundled-runtime ROCm PyTorch wheels + # Known arch: Unsloth setup installs AMD's bundled-runtime ROCm PyTorch wheels # (repo.amd.com), which ship their own runtime -- HIP SDK optional. step "gpu" "AMD ROCm ($ROCmGfxArch)" "Cyan" substep "Detected: $ROCmGpuLabel" "Cyan" @@ -2219,8 +2219,8 @@ exit 0 $torchInstallExit = Invoke-InstallCommandRetry -Label "install PyTorch (AMD ROCm)" { uv pip install --python $VenvPython --force-reinstall --default-index $ROCmIndexUrl $torchSpec $visionSpec $audioSpec } if ($torchInstallExit -ne 0) { # Transient AMD-index failure: fall back to a CPU base so the install - # still completes; Studio setup retries ROCm afterwards. - substep "ROCm PyTorch install failed (exit $torchInstallExit); using a CPU base, Studio setup retries ROCm." "Yellow" + # still completes; Unsloth setup retries ROCm afterwards. + substep "ROCm PyTorch install failed (exit $torchInstallExit); using a CPU base, Unsloth setup retries ROCm." "Yellow" # --force-reinstall: a failed ROCm install can leave an unpinned ROCm # torch (e.g. 2.10.0+rocm on gfx110X/gfx90a) that still satisfies the CPU # torch>= range, so without it uv would keep the ROCm build and only swap @@ -2422,7 +2422,7 @@ exit 0 Write-TauriLog "ERROR" "unsloth CLI was not installed correctly" Write-Host "[ERROR] unsloth CLI was not installed correctly." -ForegroundColor Red Write-Host " Expected: $UnslothExe" -ForegroundColor Yellow - Write-Host " This usually means an older unsloth version was installed that does not include the Studio CLI." -ForegroundColor Yellow + Write-Host " This usually means an older unsloth version was installed that does not include the Unsloth CLI." -ForegroundColor Yellow Write-Host " Try re-running the installer or see: https://github.com/unslothai/unsloth?tab=readme-ov-file#-quickstart" -ForegroundColor Yellow return (Exit-InstallFailure "unsloth CLI was not installed correctly") } @@ -2533,7 +2533,7 @@ exit 0 Write-Host " Move or remove it manually, then re-run the installer." -ForegroundColor Yellow throw "Cannot create unsloth launcher: $ShimExe is a directory." } - # try/catch: if unsloth.exe is locked (Studio running), keep the old shim. + # try/catch: if unsloth.exe is locked (Unsloth running), keep the old shim. $shimUpdated = $false try { if (Test-Path -LiteralPath $ShimExe) { Remove-Item -LiteralPath $ShimExe -Force -ErrorAction Stop } @@ -2551,7 +2551,7 @@ exit 0 if (Test-Path -LiteralPath $ShimExe) { Write-Host "[WARN] Could not refresh unsloth launcher at $ShimExe." -ForegroundColor Yellow Write-Host " This usually means a running 'unsloth studio' process still holds the file open." -ForegroundColor Yellow - Write-Host " Close Studio and re-run the installer to pick up the latest launcher." -ForegroundColor Yellow + Write-Host " Close Unsloth and re-run the installer to pick up the latest launcher." -ForegroundColor Yellow Write-Host " Continuing with the existing launcher." -ForegroundColor Yellow } else { Write-Host "[WARN] Could not create unsloth launcher at $ShimExe" -ForegroundColor Yellow @@ -2616,7 +2616,7 @@ exit 0 # Diagnostic only; never block install on a probe failure. } - # In interactive terminals, ask the user before starting Studio unless the + # In interactive terminals, ask the user before starting Unsloth unless the # caller explicitly disabled the post-install prompt. # In non-interactive environments (CI, Docker) just print instructions. $IsInteractive = (-not $SkipAutostart) -and [Environment]::UserInteractive -and (-not [Console]::IsInputRedirected) diff --git a/install.sh b/install.sh index 4a3c4471fa..5972379d26 100755 --- a/install.sh +++ b/install.sh @@ -97,7 +97,7 @@ if [ "$_VERBOSE" = true ]; then export UNSLOTH_VERBOSE=1 fi -# Custom Studio roots are not supported with --tauri (desktop app still +# Custom Unsloth roots are not supported with --tauri (desktop app still # resolves ~/.unsloth/studio). Pass through if the override == legacy default. if [ "$TAURI_MODE" = true ]; then _tauri_override_var="" @@ -663,7 +663,7 @@ POLL_INTERVAL_SEC=0.25 LOG_FILE="$DATA_DIR/studio.log" # why: in env-override mode multiple installs share an OS user; namespace the # lock and remember our own healthy port so we never attach to an unrelated -# Studio listening on the global 8888..8908 range. +# Unsloth listening on the global 8888..8908 range. LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp}/unsloth-studio-launcher-$(id -u).lock" PORT_FILE="" # why: gate on the install-time mode (baked above) instead of the runtime env @@ -734,7 +734,7 @@ _candidate_ports() { _find_healthy_port() { if [ -n "$PORT_FILE" ] && [ -f "$PORT_FILE" ]; then # why: env-mode installs only attach to a port we previously launched - # ourselves; never to a sibling Studio that happens to be healthy. + # ourselves; never to a sibling Unsloth that happens to be healthy. _p=$(cat "$PORT_FILE" 2>/dev/null || true) case "$_p" in ''|*[!0-9]*) ;; @@ -901,7 +901,7 @@ _acquire_lock() { # Lock dir exists -- check if owner is still alive _old_pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || true) if [ -n "$_old_pid" ] && kill -0 "$_old_pid" 2>/dev/null; then - # Another launcher is running; wait for it to bring Studio up + # Another launcher is running; wait for it to bring Unsloth up _deadline=$(($(date +%s) + TIMEOUT_SEC)) while [ "$(date +%s)" -lt "$_deadline" ]; do _port=$(_find_healthy_port) && { @@ -1371,7 +1371,7 @@ WSLPS1_EOF # shortcut wasn't created; tell the user how to launch / re-enable it. if [ "$_css_created" -ne 1 ]; then substep "Couldn't create the Windows shortcut (WSL interop may be disabled)." "$C_WARN" - substep " Launch Studio from Windows: wsl -d \"$_css_distro\" -- bash -lc 'unsloth studio'" "$C_WARN" + substep " Launch Unsloth from Windows: wsl -d \"$_css_distro\" -- bash -lc 'unsloth studio'" "$C_WARN" substep " (re-enable shortcuts: turn WSL interop back on, e.g. run 'wsl --shutdown' then reopen WSL.)" "$C_WARN" fi fi @@ -1439,7 +1439,7 @@ if [ "$MAC_INTEL" = true ]; then echo "" echo " NOTE: Intel Mac (x86_64) detected." echo " PyTorch is unavailable for this platform (dropped Jan 2024)." - echo " Studio will install in GGUF-only mode." + echo " Unsloth will install in GGUF-only mode." echo " Chat, inference via GGUF, and data recipes will work." echo " Training requires Apple Silicon or Linux with GPU." echo "" @@ -1671,7 +1671,7 @@ _maybe_reroute_strixhalo_to_2404() { _maybe_reroute_strixhalo_to_2404 || true # ── Check system dependencies ── -# cmake/git are only needed to *build* llama.cpp from source. Studio downloads a +# cmake/git are only needed to *build* llama.cpp from source. Unsloth downloads a # prebuilt by default, and setup.sh self-skips the source build when they're # absent -- so macOS doesn't block on cmake (requiring it would force a manual # Homebrew install). Linux keeps requiring them; its package manager has them. @@ -1825,7 +1825,7 @@ _MIGRATED=false if [ -x "$VENV_DIR/bin/python" ]; then # why: matching guard to the .venv branch below -- in env-mode # $STUDIO_HOME is a user-chosen workspace, so refuse to nuke an - # existing $STUDIO_HOME/unsloth_studio that lacks Studio sentinels. + # existing $STUDIO_HOME/unsloth_studio that lacks Unsloth sentinels. # Accept the in-VENV ownership marker so partial-install retries are # not blocked. Sentinels must be regular files: -f follows symlinks # to files (the legitimate ln -s shim shape) but rejects directories @@ -1846,7 +1846,7 @@ elif [ "$_STUDIO_HOME_REDIRECT" != "env" ] && [ -x "$STUDIO_HOME/.venv/bin/pytho # Skip in env-mode so we don't rm -rf an unrelated .venv at the # workspace root (e.g. user's existing project Python venv). # In no-torch mode, a missing torch package is expected; validate Python only. - substep "found legacy Studio environment, validating..." + substep "found legacy Unsloth environment, validating..." _legacy_ok=false if [ "$SKIP_TORCH" = true ]; then if "$STUDIO_HOME/.venv/bin/python" -c "import sys; print(sys.executable)" >/dev/null 2>&1; then @@ -1903,7 +1903,7 @@ if [ ! -x "$VENV_DIR/bin/python" ]; then fi fi -# Mark the freshly-created venv as Studio-owned so a partial install can be +# Mark the freshly-created venv as Unsloth-owned so a partial install can be # repaired by re-running install.sh; the env-mode deletion guard above accepts # this marker as the primary sentinel. if [ -x "$VENV_DIR/bin/python" ]; then @@ -2335,7 +2335,7 @@ _pick_radeon_wheel() { # the installer -- always returns 0. Runs the idempotent helper (ROCm 7.2 + # librocdxg), then sources the env it persisted so detection finds the GPU. # Export the ROCm-on-WSL env into this process and persist it to /etc/profile.d -# so non-login Studio/llama launches inherit it. Idempotent (writes only when +# so non-login Unsloth/llama launches inherit it. Idempotent (writes only when # the drop-in is missing); no-op without librocdxg, so never fires off WSL. # /etc/profile.d is root-owned -- sudo-tee when not root, else ROCm vanishes # after this shell on a non-root reinstall. Best-effort either way. @@ -2380,7 +2380,7 @@ _maybe_bootstrap_rocm_wsl() { rocminfo 2>/dev/null | awk '/Name:[[:space:]]*gfx[1-9]/ && !/generic/{found=1} END{exit !found}'; then # rocminfo may work only via the transient env _ensure_rocm_probe_env # just set, which dies with the installer. Persist the drop-in so login - # shells (Studio, llama.cpp) inherit it -- else a reinstall over an + # shells (Unsloth, llama.cpp) inherit it -- else a reinstall over an # existing /opt/rocm (uninstall keeps ROCm but drops it) loses the GPU. _persist_rocm_wsl_dropin return 0 @@ -2402,7 +2402,7 @@ _maybe_bootstrap_rocm_wsl() { # shellcheck disable=SC1091 . /etc/profile.d/unsloth-rocm-wsl.sh || true else - # librocdxg present but the env drop-in is gone (e.g. a Studio + # librocdxg present but the env drop-in is gone (e.g. an Unsloth # uninstall removed it while keeping shared ROCm). Restore the env. _persist_rocm_wsl_dropin fi @@ -3033,7 +3033,7 @@ if [ "$SKIP_TORCH" = false ] && [ -n "${TORCH_INDEX_URL:-}" ]; then fi # ── Run studio setup ── -tauri_log "STEP" "Running Studio setup" +tauri_log "STEP" "Running Unsloth setup" # When --local, use the repo's own setup.sh directly. # Otherwise, find it inside the installed package. SETUP_SH="" @@ -3227,7 +3227,7 @@ printf " ${C_TITLE}%s${C_RST}\n" "Unsloth Studio installed!" printf " ${C_DIM}%s${C_RST}\n" "$RULE" echo "" -# In interactive terminals, ask the user before starting Studio unless the +# In interactive terminals, ask the user before starting Unsloth unless the # caller explicitly disabled the post-install prompt. # In non-interactive environments (Docker, CI, cloud-init) just print instructions. if [ "$_SKIP_AUTOSTART" != true ] && [ -t 1 ]; then diff --git a/scripts/install_rocm_wsl_strixhalo.sh b/scripts/install_rocm_wsl_strixhalo.sh index aa560fc432..697aae933f 100644 --- a/scripts/install_rocm_wsl_strixhalo.sh +++ b/scripts/install_rocm_wsl_strixhalo.sh @@ -219,7 +219,7 @@ fi echo "${ROCM_DIR}/lib" | $SUDO tee /etc/ld.so.conf.d/rocm.conf >/dev/null $SUDO ldconfig -# ── Step 4: persist environment (system-wide so Studio's worker inherits it) ── +# ── Step 4: persist environment (system-wide so Unsloth's worker inherits it) ── say "Persisting ROCm-on-WSL environment" _envfile="/etc/profile.d/unsloth-rocm-wsl.sh" $SUDO tee "$_envfile" >/dev/null < str: return f'''# SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Build-stamped Studio release metadata.""" +"""Build-stamped Unsloth release metadata.""" STUDIO_RELEASE_VERSION = {literal} ''' @@ -168,7 +168,7 @@ def stamp(require_release: bool) -> int: version, source = resolve_version() if version is not None and not is_valid_version(version): print( - f"Invalid Studio release version from {source}: {version!r}", + f"Invalid Unsloth release version from {source}: {version!r}", file = sys.stderr, ) return 2 @@ -196,9 +196,9 @@ def stamp(require_release: bool) -> int: if version is None: if require_release: print( - "No Studio release version available. Set " + "No Unsloth release version available. Set " "UNSLOTH_STUDIO_RELEASE_VERSION, build from a GitHub tag, " - "or run from an exact local Studio release tag.", + "or run from an exact local Unsloth release tag.", file = sys.stderr, ) return 2 @@ -207,7 +207,7 @@ def stamp(require_release: bool) -> int: return 0 _atomic_write_text(BUILD_INFO_PATH, build_info_source(version), encoding = "utf-8") - print(f"Stamping Studio release version {version} from {source}", file = sys.stderr) + print(f"Stamping Unsloth release version {version} from {source}", file = sys.stderr) print(version) return 0 @@ -233,7 +233,7 @@ def _read_sdist_member(path: Path) -> str | None: def verify_dist(expected: str, dist_dir: Path) -> int: if not is_valid_version(expected): - print(f"Invalid expected Studio release version: {expected!r}", file = sys.stderr) + print(f"Invalid expected Unsloth release version: {expected!r}", file = sys.stderr) return 2 artifacts = list(dist_dir.glob("*.whl")) + list(dist_dir.glob("*.tar.gz")) @@ -251,14 +251,14 @@ def verify_dist(expected: str, dist_dir: Path) -> int: if content is None: failures.append(f"{artifact.name}: missing {BUILD_INFO_SUFFIX}") elif expected_line not in content: - failures.append(f"{artifact.name}: Studio release version mismatch") + failures.append(f"{artifact.name}: Unsloth release version mismatch") if failures: for failure in failures: print(failure, file = sys.stderr) return 2 - print(f"Verified Studio release version {expected} in {len(artifacts)} artifact(s)") + print(f"Verified Unsloth release version {expected} in {len(artifacts)} artifact(s)") return 0 diff --git a/scripts/uninstall.ps1 b/scripts/uninstall.ps1 index 88defb9ea0..9b6e6ebb86 100644 --- a/scripts/uninstall.ps1 +++ b/scripts/uninstall.ps1 @@ -83,7 +83,7 @@ function Uninstall-UnslothStudio { } } - # A path is a Studio-owned root iff one of install.ps1's sentinels exists: + # A path is an Unsloth-owned root iff one of install.ps1's sentinels exists: # \share\studio.conf, \unsloth_studio\.unsloth-studio-owned, # or \bin\unsloth.exe. function _IsStudioRoot { @@ -164,7 +164,7 @@ function Uninstall-UnslothStudio { return $p } - # Discover non-default Studio roots from env vars + studio.conf files. + # Discover non-default Unsloth roots from env vars + studio.conf files. # Mirrors install.ps1's precedence: UNSLOTH_STUDIO_HOME wins, STUDIO_HOME # is ignored when both are set, so uninstalling install A doesn't also # delete install B if the user has a stale STUDIO_HOME pointing at B. @@ -207,7 +207,7 @@ function Uninstall-UnslothStudio { # Return $true iff the PID's image path lives under one of $KnownRoots. # Prevents killing an unrelated process that happens to listen on a stale - # Studio port. + # Unsloth port. function _PidUnderKnownRoot { param([int]$Pid_, [string[]]$KnownRoots) if (-not $KnownRoots -or $KnownRoots.Count -eq 0) { return $false } @@ -223,8 +223,8 @@ function Uninstall-UnslothStudio { return $false } - # Stop a Studio backend whose port is recorded in \studio.port. - # Only kills if the listening PID's exe path is under a known Studio root. + # Stop an Unsloth backend whose port is recorded in \studio.port. + # Only kills if the listening PID's exe path is under a known Unsloth root. function _StopByPortFile { param([string]$PortFile, [string[]]$KnownRoots) if (-not (Test-Path -LiteralPath $PortFile -PathType Leaf)) { return } @@ -372,7 +372,7 @@ function Uninstall-UnslothStudio { continue } if (-not (_IsStudioRoot $r)) { - _Substep "refusing to remove non-Studio path: $r" "Yellow" + _Substep "refusing to remove non-Unsloth path: $r" "Yellow" continue } _RemovePath $r @@ -436,7 +436,7 @@ function Uninstall-UnslothStudio { $entries = $rawPath -split ';' $kept = New-Object System.Collections.ArrayList $removedAny = $false - # Only remove PATH entries that live inside a Studio root we + # Only remove PATH entries that live inside an Unsloth root we # actually own (default or env-mode). A literal substring # match on `unsloth_studio` would clobber unrelated user # virtualenvs that happen to share the name. diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index 31e851fcbb..957d2b7af2 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -12,7 +12,7 @@ set -e -# Stop a Studio server via its PID file (written by install.sh's _spawn_terminal). +# Stop an Unsloth server via its PID file (written by install.sh's _spawn_terminal). _kill_pid_file() { _pid_file="$1" [ -f "$_pid_file" ] || return 0 @@ -47,7 +47,7 @@ _pkill_studio() { 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. + # different Unsloth 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 @@ -89,7 +89,7 @@ _remove_path() { fi } -# Accept as Studio root only if Studio sentinels exist (matches install.sh's +# Accept as Unsloth root only if Unsloth 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. @@ -175,8 +175,8 @@ _custom_studio_roots() { _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 +# Remove $HOME/.local/bin/unsloth only if it's an Unsloth-managed symlink. +# Unsloth'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. @@ -206,7 +206,7 @@ _custom_studio_roots | while IFS= read -r _custom_root; do continue fi if ! _is_studio_root "$_custom_root"; then - echo " refusing to remove non-Studio path: $_custom_root" >&2 + echo " refusing to remove non-Unsloth path: $_custom_root" >&2 continue fi _remove_path "$_custom_root" @@ -234,7 +234,7 @@ _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. +# CLI shim: only the symlink Unsloth created, never a pip-installed file. _remove_cli_shim echo "Removing desktop shortcut and launcher lock..." diff --git a/studio/MCP.md b/studio/MCP.md index 91b39fcc69..127a85a116 100644 --- a/studio/MCP.md +++ b/studio/MCP.md @@ -1,10 +1,10 @@ # Unsloth Studio MCP server -Studio can expose a local MCP server so an MCP client can inspect models and +Unsloth can expose a local MCP server so an MCP client can inspect models and GPU state, validate recipes, start or stop training, inspect recipe output, and export a loaded model. -The server is disabled by default. Enable it for a local Studio process with: +The server is disabled by default. Enable it for a local Unsloth process with: ```bash UNSLOTH_STUDIO_ENABLE_MCP=1 \ @@ -12,8 +12,8 @@ UNSLOTH_STUDIO_MCP_TOKEN='use-a-local-secret' \ unsloth studio ``` -The endpoint is `http://127.0.0.1:8888/mcp/` when Studio uses its default port -(a request to `/mcp` redirects to the canonical `/mcp/`). Use the actual Studio +The endpoint is `http://127.0.0.1:8888/mcp/` when Unsloth uses its default port +(a request to `/mcp` redirects to the canonical `/mcp/`). Use the actual Unsloth port when it is configured differently. The high-impact tools are: @@ -23,9 +23,9 @@ The high-impact tools are: - `validate_recipe`, `get_recipe_job_status`, and `get_recipe_job_dataset` - `load_checkpoint` and `export_gguf` -`start_training` accepts the same fields as the Studio `TrainingStartRequest`. +`start_training` accepts the same fields as the Unsloth `TrainingStartRequest`. The request is validated by the existing Pydantic model before a subprocess is -started. Export paths use the existing Studio validation as well. +started. Export paths use the existing Unsloth validation as well. The endpoint always requires `UNSLOTH_STUDIO_MCP_TOKEN` and checks an exact Bearer token for both HTTP and WebSocket connections. Keep it on localhost diff --git a/studio/Unsloth_Studio_Colab.ipynb b/studio/Unsloth_Studio_Colab.ipynb index 619395bd6d..44282b2255 100644 --- a/studio/Unsloth_Studio_Colab.ipynb +++ b/studio/Unsloth_Studio_Colab.ipynb @@ -33,7 +33,7 @@ "\n", "We are actively working on making Unsloth Studio install on Colab T4 GPUs faster.\n", "\n", - "[Features](https://unsloth.ai/docs/new/unsloth-studio#features) • [Quickstart](https://unsloth.ai/docs/new/unsloth-studio/start) • [Data Recipes](https://unsloth.ai/docs/new/unsloth-studio/data-recipe) • [Studio Chat](https://unsloth.ai/docs/new/unsloth-studio/chat) • [Export](https://unsloth.ai/docs/new/unsloth-studio/export)" + "[Features](https://unsloth.ai/docs/new/unsloth-studio#features) • [Quickstart](https://unsloth.ai/docs/new/unsloth-studio/start) • [Data Recipes](https://unsloth.ai/docs/new/unsloth-studio/data-recipe) • [Unsloth Chat](https://unsloth.ai/docs/new/unsloth-studio/chat) • [Export](https://unsloth.ai/docs/new/unsloth-studio/export)" ] }, { diff --git a/studio/backend/assets/chat_templates/gemma-4-edge.jinja b/studio/backend/assets/chat_templates/gemma-4-edge.jinja index 0266127233..74fa73ddd3 100644 --- a/studio/backend/assets/chat_templates/gemma-4-edge.jinja +++ b/studio/backend/assets/chat_templates/gemma-4-edge.jinja @@ -3,7 +3,7 @@ Source: google/gemma-4-31B-it HF discussion/PR #118 (adds the preserve_thinking flag plus null-rendering, string-arguments validation, balanced turn tags, empty messages handling, and OpenAI image_url/input_audio aliases). - Studio-local changes vs PR #118: + Unsloth-local changes vs PR #118: 1. preserve_thinking defaults to false (see SETUP block below). 2. The empty "<|channel>thought\n" block on enable_thinking=false is NOT emitted. Google ships a distinct template for E2B/E4B (google/gemma-4-E2B-it, diff --git a/studio/backend/assets/chat_templates/gemma-4.jinja b/studio/backend/assets/chat_templates/gemma-4.jinja index 65ab39df57..cc5f98065f 100644 --- a/studio/backend/assets/chat_templates/gemma-4.jinja +++ b/studio/backend/assets/chat_templates/gemma-4.jinja @@ -3,7 +3,7 @@ Source: google/gemma-4-31B-it HF discussion/PR #118 (adds the preserve_thinking flag plus null-rendering, string-arguments validation, balanced turn tags, empty messages handling, and OpenAI image_url/input_audio aliases). - Studio-local change: preserve_thinking defaults to false (see SETUP block below). + Unsloth-local change: preserve_thinking defaults to false (see SETUP block below). Applied to unsloth/gemma-4-*-GGUF models so the embedded GGUF template does not need re-downloading. Keep in sync with upstream if PR #118 changes. -#} diff --git a/studio/backend/auth/authentication.py b/studio/backend/auth/authentication.py index b13cd1c851..dfb8fc513e 100644 --- a/studio/backend/auth/authentication.py +++ b/studio/backend/auth/authentication.py @@ -148,7 +148,7 @@ async def authenticated_via_api_key( ) -> bool: """True when the caller used an sk-unsloth API key, not a UI session JWT. - Lets routes treat programmatic API callers differently from the Studio UI + Lets routes treat programmatic API callers differently from the Unsloth UI (e.g. refuse a teardown the UI would allow). """ return bool(credentials and credentials.credentials.startswith(API_KEY_PREFIX)) diff --git a/studio/backend/auth/bootstrap_timeout.py b/studio/backend/auth/bootstrap_timeout.py index 728433dc54..97a8086f04 100644 --- a/studio/backend/auth/bootstrap_timeout.py +++ b/studio/backend/auth/bootstrap_timeout.py @@ -1,13 +1,13 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Auto-shutdown for an exposed first-run Studio whose admin password is unchanged. +"""Auto-shutdown for an exposed first-run Unsloth whose admin password is unchanged. On a fresh install the seeded bootstrap admin password stays a valid login credential until first login changes it. When the web UI is put on the network (``--secure`` / ``0.0.0.0``) and nobody completes that first-login change within -a deadline, tear Studio down so a fresh, unconfigured instance does not stay -publicly reachable indefinitely. If the password was changed, Studio keeps +a deadline, tear Unsloth down so a fresh, unconfigured instance does not stay +publicly reachable indefinitely. If the password was changed, Unsloth keeps running. Scope: web UI launches only (never ``--api-only``, which authenticates by API @@ -98,7 +98,7 @@ def enforce_bootstrap_password_deadline( ) -> bool: """Deadline handler: shut down iff the seeded admin password is still unchanged. - Returns True if it shut Studio down, False if it left it running (the + Returns True if it shut Unsloth down, False if it left it running (the password was changed in time). """ try: @@ -106,7 +106,7 @@ def enforce_bootstrap_password_deadline( except Exception: return False if not still_default: - return False # password changed in time -> leave Studio running + return False # password changed in time -> leave Unsloth running message = ( "\nUnsloth Studio was exposed on the network but its default admin " diff --git a/studio/backend/auth/storage.py b/studio/backend/auth/storage.py index 9bb3ab5735..39fa691304 100644 --- a/studio/backend/auth/storage.py +++ b/studio/backend/auth/storage.py @@ -146,7 +146,7 @@ def get_connection() -> sqlite3.Connection: pass conn.row_factory = sqlite3.Row # WAL lets token reads run concurrently with refresh-token writes; - # busy_timeout bounds lock waits. Matches the other Studio SQLite stores. + # busy_timeout bounds lock waits. Matches the other Unsloth SQLite stores. # Set busy_timeout first: switching journal_mode needs a lock, so if a # refresh-token write already holds one, journal_mode=WAL raises SQLITE_BUSY; # with busy_timeout already in effect it waits instead of failing and leaving @@ -305,8 +305,8 @@ def get_or_create_identity_secret() -> bytes: def compute_identity_proof(nonce: bytes, host: str, port: int) -> str: """HMAC-SHA256 proof that the caller holds this install's identity secret, bound to the loopback address and port the connection landed on. A proof - relayed from a Studio on a different address/port (a squatter proxying to the - real one, e.g. localhost resolving to ::1 while Studio is on 127.0.0.1) was + relayed from an Unsloth on a different address/port (a squatter proxying to the + real one, e.g. localhost resolving to ::1 while Unsloth is on 127.0.0.1) was computed for that other endpoint and won't match the one the client dialed.""" try: host = ipaddress.ip_address(host).compressed # normalise 127.0.0.1 / ::1 forms diff --git a/studio/backend/auth/terminal_prompt.py b/studio/backend/auth/terminal_prompt.py index 8491019ae9..e855f4078b 100644 --- a/studio/backend/auth/terminal_prompt.py +++ b/studio/backend/auth/terminal_prompt.py @@ -2,14 +2,14 @@ # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 """Interactive terminal prompt that forces a bootstrap password change before -Studio is exposed on a public Cloudflare URL (``--secure`` / ``--cloudflare``). +Unsloth is exposed on a public Cloudflare URL (``--secure`` / ``--cloudflare``). Masked input echoes one ``*`` per keystroke (unlike ``getpass``). Works on Windows (``msvcrt``) and Linux/macOS (``termios``). All output goes to stderr so redirected stdout never swallows the prompt. Mirrored for the CLI at ``unsloth_cli/commands/_password_prompt.py`` (the CLI -cannot import the Studio backend package); keep the two in sync. +cannot import the Unsloth backend package); keep the two in sync. """ from __future__ import annotations @@ -252,7 +252,7 @@ def prompt_for_password_change( out.flush() return True except (KeyboardInterrupt, EOFError): - out.write("Password change aborted; not exposing Studio.\n") + out.write("Password change aborted; not exposing Unsloth.\n") out.flush() return False diff --git a/studio/backend/cloudflare_tunnel.py b/studio/backend/cloudflare_tunnel.py index ef7bacba67..b1ddc74c32 100644 --- a/studio/backend/cloudflare_tunnel.py +++ b/studio/backend/cloudflare_tunnel.py @@ -1,13 +1,13 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Free Cloudflare quick tunnel for Studio's 0.0.0.0 launches. +"""Free Cloudflare quick tunnel for Unsloth's 0.0.0.0 launches. The raw http://: is often unreachable (https-vs-http, blocked ports, closed security groups); a cloudflared quick tunnel gives a free https://*.trycloudflare.com URL that works anywhere, with no account or domain. -Best-effort throughout: any failure collapses to "no URL" and Studio keeps +Best-effort throughout: any failure collapses to "no URL" and Unsloth keeps running. Stdlib only (back-end imports are lazy) so it is safe to import early. """ @@ -95,7 +95,7 @@ def _cache_path() -> Optional[Path]: def find_cloudflared() -> Optional[str]: - """Locate an existing cloudflared: PATH first, then the Studio bin cache.""" + """Locate an existing cloudflared: PATH first, then the Unsloth bin cache.""" on_path = shutil.which("cloudflared") if on_path: return on_path @@ -309,7 +309,7 @@ class CloudflareTunnel: pass -# Single serving process per Studio launch, so one module-level tunnel handle is +# Single serving process per Unsloth launch, so one module-level tunnel handle is # enough; the lock guards the start/stop/shutdown races. _active_tunnel: Optional[CloudflareTunnel] = None _active_lock = threading.Lock() diff --git a/studio/backend/colab.py b/studio/backend/colab.py index e04543b3aa..1762469bcf 100644 --- a/studio/backend/colab.py +++ b/studio/backend/colab.py @@ -129,7 +129,7 @@ def start_cloudflare_tunnel(port: int) -> "str | None": logger.warning( "Cloudflare link not started: the admin account still has its temporary " "bootstrap password, which is exposed to anyone who can load the page. " - "Open Studio in this tab, log in and change the admin password, then re-run " + "Open Unsloth in this tab, log in and change the admin password, then re-run " "start(cloudflare=True) to get the shareable link." ) return None @@ -203,7 +203,7 @@ def _shareable_link_html(cloudflare_url: str) -> str: display: flex; align-items: center; gap: 12px;"> - Shareable Studio Link is Ready! + Shareable Unsloth Link is Ready! ParsedUpdate | None: source = "github", status = "rate_limited", retry_after_sec = seconds, - message = ("Waiting for GitHub rate limit. Studio will resume automatically."), + message = ("Waiting for GitHub rate limit. Unsloth will resume automatically."), ), ) @@ -147,7 +147,7 @@ def parse_log_message(msg: str) -> ParsedUpdate | None: status = "rate_limited", retry_after_sec = seconds, message = ( - "Waiting for GitHub secondary rate limit. Studio will resume automatically." + "Waiting for GitHub secondary rate limit. Unsloth will resume automatically." ), ), ) @@ -161,7 +161,7 @@ def parse_log_message(msg: str) -> ParsedUpdate | None: source = "github", status = "rate_limited", retry_after_sec = seconds, - message = ("Waiting for GitHub rate limit. Studio will resume automatically."), + message = ("Waiting for GitHub rate limit. Unsloth will resume automatically."), ), ) diff --git a/studio/backend/core/data_recipe/local_callable_validators.py b/studio/backend/core/data_recipe/local_callable_validators.py index ebb1d39dfb..ffc81669ae 100644 --- a/studio/backend/core/data_recipe/local_callable_validators.py +++ b/studio/backend/core/data_recipe/local_callable_validators.py @@ -238,7 +238,7 @@ def _run_oxc_batch( if not node_executable: return _fallback_results( len(code_values), - "Node.js not found (install Node >= 20.19, or re-run Studio setup to provision it).", + "Node.js not found (install Node >= 20.19, or re-run Unsloth setup to provision it).", ) try: tmp_dir = ensure_dir(oxc_validator_tmp_root()) diff --git a/studio/backend/core/data_recipe/service.py b/studio/backend/core/data_recipe/service.py index 4647dc098d..9770e88b7f 100644 --- a/studio/backend/core/data_recipe/service.py +++ b/studio/backend/core/data_recipe/service.py @@ -280,8 +280,8 @@ def create_data_designer(recipe: dict[str, Any], *, artifact_path: str | None = from data_designer.interface.data_designer import DataDesigner # pyright: ignore[reportMissingImports] if artifact_path is None: - # DataDesigner defaults to cwd/artifacts; packaged Studio can run with - # cwd=/, so keep default callers on Studio's writable recipe artifact root. + # DataDesigner defaults to cwd/artifacts; packaged Unsloth can run with + # cwd=/, so keep default callers on Unsloth's writable recipe artifact root. artifact_path = str(recipe_datasets_root()) recipe = _strip_frontend_model_config_metadata(recipe) diff --git a/studio/backend/core/inference/__init__.py b/studio/backend/core/inference/__init__.py index ad78157418..1491dfa749 100644 --- a/studio/backend/core/inference/__init__.py +++ b/studio/backend/core/inference/__init__.py @@ -11,7 +11,7 @@ subprocess and can be imported directly from .inference when needed. Public names are resolved lazily (PEP 562): importing this package -- or a dependency-light leaf like ``core.inference.chat_eos`` -- must NOT eagerly pull the orchestrator / llama_cpp import chain (httpx, subprocess plumbing, the ML -backend and its Studio dependencies). Those load only when a public name is +backend and its Unsloth dependencies). Those load only when a public name is actually accessed, so standalone helpers stay unit-testable without the full inference stack. """ diff --git a/studio/backend/core/inference/anthropic_compat.py b/studio/backend/core/inference/anthropic_compat.py index 3c7a4cb182..34445cc58e 100644 --- a/studio/backend/core/inference/anthropic_compat.py +++ b/studio/backend/core/inference/anthropic_compat.py @@ -539,7 +539,7 @@ class AnthropicPassthroughEmitter: Only calls naming a tool in ``allowed_tools`` (the client's declared tools) are promoted; everything else streams as text exactly as before. - Never enabled for Studio's own tool loop. + Never enabled for Unsloth's own tool loop. """ from core.inference.passthrough_healing import StreamToolCallHealer diff --git a/studio/backend/core/inference/chat_template_helpers.py b/studio/backend/core/inference/chat_template_helpers.py index 5113eebb36..528c059fbc 100644 --- a/studio/backend/core/inference/chat_template_helpers.py +++ b/studio/backend/core/inference/chat_template_helpers.py @@ -150,7 +150,7 @@ def _split_partial_marker(text: str, marker: str) -> tuple[str, str]: class ReasoningChannelNormalizer: """Incrementally convert one native reasoning channel to ````. - The parser follows mlx-vlm's streaming boundary behavior but emits Studio's + The parser follows mlx-vlm's streaming boundary behavior but emits Unsloth's established canonical text contract. Only the configured opening and closing markers are consumed; tool-call and other control markers remain available to downstream parsers. diff --git a/studio/backend/core/inference/chat_templates.py b/studio/backend/core/inference/chat_templates.py index 58f63ff61b..04c0db6aae 100644 --- a/studio/backend/core/inference/chat_templates.py +++ b/studio/backend/core/inference/chat_templates.py @@ -4,13 +4,13 @@ """Bundled chat-template selection for GGUF inference. Some shipped GGUF quants embed an older chat template. Rather than re-cutting and -asking users to re-download every quant, Studio can override the embedded template +asking users to re-download every quant, Unsloth can override the embedded template at llama-server launch time with a bundled, up-to-date Jinja template for known model families. The override is wired through the existing ``chat_template_override`` -> ``--chat-template-file`` path in ``LlamaCppBackend.load_model``. Currently this covers ``unsloth/gemma-4-*-GGUF``, which gains the upstream PR #118 -``preserve_thinking`` flag (defaulted OFF here) so the Studio "Preserve thinking" +``preserve_thinking`` flag (defaulted OFF here) so the Unsloth "Preserve thinking" toggle appears while staying disabled by default. """ diff --git a/studio/backend/core/inference/external_provider.py b/studio/backend/core/inference/external_provider.py index 20312e067c..2debf946e9 100644 --- a/studio/backend/core/inference/external_provider.py +++ b/studio/backend/core/inference/external_provider.py @@ -473,7 +473,7 @@ def _apply_mistral_reasoning_controls( # handles every provider without storing credentials. def _create_shared_http_client() -> httpx.AsyncClient: # Unsupported env proxy schemes (socks:// etc) raise at construction and - # would crash Studio startup (#6090); retry ignoring env proxies instead. + # would crash Unsloth startup (#6090); retry ignoring env proxies instead. try: return httpx.AsyncClient() except (ImportError, ValueError) as exc: @@ -858,7 +858,7 @@ class ExternalProviderClient: if not self._is_openai_compatible(): # Gemini speaks its own native REST shape (contents/parts); # `_stream_gemini` translates request/response into the OpenAI - # Chat Completions chunk format the rest of Studio expects. + # Chat Completions chunk format the rest of Unsloth expects. # API ref: https://ai.google.dev/gemini-api/docs if self.provider_type == "gemini": async for line in self._stream_gemini( @@ -1706,7 +1706,7 @@ class ExternalProviderClient: # Translate OpenAI multimodal parts -> Anthropic native shapes. # - `image_url` -> `{type:"image", source:...}` # - `input_document` -> `{type:"document", source:...}` - # (Studio extension; mirrors Anthropic's document block, + # (Unsloth extension; mirrors Anthropic's document block, # which supports PDFs as base64 or URL per # https://platform.claude.com/docs/en/build-with-claude/vision) anthropic_parts: list[dict[str, Any]] = [] @@ -1749,7 +1749,7 @@ class ExternalProviderClient: } ) elif part.get("type") == "input_document": - # Studio's normalised PDF/doc type (file_data data-URI or + # Unsloth's normalised PDF/doc type (file_data data-URI or # file_url) -> Anthropic's native `document` block. url = part.get("file_url") or "" data_uri = part.get("file_data") or "" @@ -4704,7 +4704,7 @@ class ExternalProviderClient: {"type": "image_generation_call", "id": call_id} ) elif part_type == "input_document": - # Map Studio's `input_document` onto Responses' `input_file`. + # Map Unsloth's `input_document` onto Responses' `input_file`. # https://developers.openai.com/api/docs/guides/images-vision file_url = part.get("file_url") file_data = part.get("file_data") @@ -6010,7 +6010,7 @@ class ExternalProviderClient: if not models and self.provider_type == "ollama": models = await self._list_ollama_native_models() # Gemini's native /v1beta/models uses a different shape; repackage - # into the OpenAI-compatible one Studio expects. + # into the OpenAI-compatible one Unsloth expects. if not models and self.provider_type == "gemini": models = self._parse_gemini_models(data) return models @@ -6213,7 +6213,7 @@ def _friendly_provider_error_text( *, model: str | None = None, ) -> str: - """Rewrite common provider errors into actionable Studio copy.""" + """Rewrite common provider errors into actionable Unsloth copy.""" if status_code == 404 and model: lowered = raw_message.lower() if "not found" in lowered or "not_found" in lowered: diff --git a/studio/backend/core/inference/llama_cpp.py b/studio/backend/core/inference/llama_cpp.py index fec5214cf9..c9ab7eb83b 100644 --- a/studio/backend/core/inference/llama_cpp.py +++ b/studio/backend/core/inference/llama_cpp.py @@ -116,7 +116,7 @@ LLAMA_SERVER_NOT_FOUND_DETAIL = ( # llama-server can serve HTTP 200 while running a model entirely on CPU when a # GPU backend fails to init (#5807 / #5106 / #5830). Classify the startup log so -# Studio can warn. Priority: explicit "offloaded N/M layers to GPU" counts +# Unsloth can warn. Priority: explicit "offloaded N/M layers to GPU" counts # (authoritative), then GPU "model buffer size" lines (host-pinned _Host # excluded), then the "device_info:" device table (disconfirm only). _GPU_OFFLOAD_MARKERS = ( @@ -1363,7 +1363,7 @@ def _kv_bytes_per_elem(cache_type: Optional[str]) -> float: def _env_main_cache_type_for_budget(env: Optional[Mapping[str, str]] = None) -> Optional[str]: """Heavier of the inherited LLAMA_ARG_CACHE_TYPE_K/_V env types when it - exceeds the f16 default, else None. Studio emits --cache-type only for the + exceeds the f16 default, else None. Unsloth emits --cache-type only for the param/extras path, so a heavier env (f32) would otherwise reach the child unbudgeted; quantized env types stay over-reserved by f16 (-> None).""" e = os.environ if env is None else env @@ -1682,7 +1682,7 @@ def _build_ngram_mod_flags( return [] -# Canonical Speculative Decoding modes exposed by the Studio chat UI. +# Canonical Speculative Decoding modes exposed by the Unsloth chat UI. # Dropdown renders five (auto, mtp, ngram, mtp+ngram, off); the load API # also accepts legacy values the original Switch and external callers emit # (default, draft-mtp, ngram-mod, ngram-simple). @@ -1731,7 +1731,7 @@ def _backfill_usage_from_timings(usage, timings): """Synthesize ``usage`` from llama-server's ``timings`` when the OpenAI-style usage block is missing or reports zero tokens. - The Studio chat UI computes generation t/s from + The Unsloth chat UI computes generation t/s from ``meta.usage.completion_tokens / totalStreamTime``. llama-server always populates ``timings.predicted_n`` (true decoded count) and ``timings.prompt_n``, but the final SSE chunk's ``usage`` can be absent @@ -1804,7 +1804,7 @@ def _llama_lib_dir(binary: str) -> Path: def _is_external_link(path: Path) -> bool: """True when ``path`` is a --with-llama-cpp-dir local link: a POSIX symlink or a Windows directory junction / reparse point. Such a link resolves into - the user's own llama.cpp checkout, which Studio does not own.""" + the user's own llama.cpp checkout, which Unsloth does not own.""" try: if os.path.islink(path): return True @@ -1960,7 +1960,7 @@ class LlamaCppBackend: # observes it (direct proxy endpoints, or nothing in flight). self._mtp_watchdog_thread: Optional[threading.Thread] = None self._mtp_watchdog_stop = threading.Event() - # True when the launch actually runs MTP+tensor (Studio- or user/env-driven); + # True when the launch actually runs MTP+tensor (Unsloth- or user/env-driven); # gates the probe, watchdog, and recovery so pass-through MTP is covered. self._mtp_runtime_fallback_active = False self._stdout_lines: list[str] = [] @@ -2353,7 +2353,7 @@ class LlamaCppBackend: @staticmethod def _resolved_studio_root_and_is_legacy() -> "tuple[Optional[Path], bool]": - """Resolve the Studio install root and classify it as the legacy + """Resolve the Unsloth install root and classify it as the legacy ~/.unsloth/studio root vs. a custom (env/venv-inferred) root. Returns (resolved_root, is_legacy). On any import/resolution failure the @@ -3241,7 +3241,7 @@ class LlamaCppBackend: return prev = curr - # Free-VRAM fraction at which Studio pins the GPU directly instead of + # Free-VRAM fraction at which Unsloth pins the GPU directly instead of # deferring to ``--fit on``. 3% headroom: the compute buffer is now modelled in # the fit, so this only guards fragmentation + multi-GPU per-device CUDA context # (~2-3%); kept >= 3% as a floor (0.90 dropped 91-94% fits to CPU offload, #5106). @@ -3800,7 +3800,7 @@ class LlamaCppBackend: return total if total > 0 else None return draft_kv + weights + target_ctx_copy - _DEFAULT_N_UBATCH = 512 # llama.cpp --ubatch default; Studio does not override it + _DEFAULT_N_UBATCH = 512 # llama.cpp --ubatch default; Unsloth does not override it _COMPUTE_BUFFER_SAFETY = 1.15 # upper-bound margin on the compute-buffer estimate # Soft VRAM the modeled terms omit; charged to the fit budget on tight tiers (#6682). _CUDA_CONTEXT_RESERVE_BYTES = 320 * 1024 * 1024 # CUDA ctx + cuBLAS workspace (~330 MiB) @@ -3940,7 +3940,7 @@ class LlamaCppBackend: n_ubatch: Optional[int] = None, ) -> tuple[Optional[list[int]], bool, int]: """Largest serving-slot count in [1, n_parallel) whose fully-on-GPU footprint fits, - so Studio keeps the model on GPU (-ngl -1) instead of --fit on, which offloads layers + so Unsloth keeps the model on GPU (-ngl -1) instead of --fit on, which offloads layers to host and collapses decode ~3x (oobabooga #6718). ``base_footprint_bytes`` is the slot-independent footprint (weights + soft overhead + MTP + context-linear compute, minus the folded compute buffer); each candidate re-adds the slot-sized compute buffer @@ -4416,7 +4416,7 @@ class LlamaCppBackend: ] # Otherwise hand off to the resolver (cache / bootstrap / transformers / HF). Diffusion models - # skip it: they do not use Studio's SWA pattern and the resolver can raise for them. + # skip it: they do not use Unsloth's SWA pattern and the resolver can raise for them. if ( self._sliding_window_pattern is None and self._sliding_window @@ -4536,7 +4536,7 @@ class LlamaCppBackend: ) -> bool: """Launch the OpenAI-compat diffusion shim (which drives the on-device visual decoder) and wait for health. Presents the same /v1 + /health - interface as llama-server, so the rest of Studio is unchanged. + interface as llama-server, so the rest of Unsloth is unchanged. """ assets = self._find_diffusion_assets() if assets is None: @@ -4608,7 +4608,7 @@ class LlamaCppBackend: logger.debug(f"Could not open diffusion runner log file: {e}") # The shim (and its visual server) die with this backend process, so a - # Studio crash/restart never orphans a GPU process. + # Unsloth crash/restart never orphans a GPU process. self._process = subprocess.Popen( cmd, stdout = subprocess.PIPE, @@ -5242,7 +5242,7 @@ class LlamaCppBackend: return ( f"'{arch}' is a diffusion (image-generation) GGUF, which " "llama-server cannot run as a chat/completion model. Use " - "Studio's Images page to generate with local diffusion " + "Unsloth's Images page to generate with local diffusion " "GGUFs such as FLUX and Qwen-Image." ) if is_ollama: @@ -6103,7 +6103,7 @@ class LlamaCppBackend: and not bool(mtp_draft_path) ) # LLAMA_ARG_SPEC_TYPE only reaches the child when neither extras - # nor Studio emit a spec flag (mode "off", no user --spec-type), + # nor Unsloth emit a spec flag (mode "off", no user --spec-type), # since _build_speculative_flags emits one for every other mode. # Consult the env for the reserve only then, else a stale MTP env # would over-reserve. @@ -6112,7 +6112,7 @@ class LlamaCppBackend: if (not _extra_args_set_spec_type(extra_args) and _mtp_canonical == "off") else {} ) - # Extras can run MTP even when Studio suppresses its own emission. + # Extras can run MTP even when Unsloth suppresses its own emission. _user_mtp_via_extras = _extra_args_requests_mtp(extra_args, env = _spec_env) # A non-MTP model-based draft mode (draft-simple/draft-eagle3) in # extras also loads a separate draft model that needs reserving; @@ -6178,7 +6178,7 @@ class LlamaCppBackend: _mtp_eff_n_max = 2 if gpus else 3 # Separate-drafter weights live on GPU (an embedded head is # already in model_size). Size the drafter the launch loads, by - # precedence: extras --model-draft (last-wins), else Studio's + # precedence: extras --model-draft (last-wins), else Unsloth's # emitted mtp_draft_path, else the env drafter. Sizing the wrong # one would under-reserve and OOM. _cli_draft_for_budget = _extra_args_mtp_draft_path(extra_args, env = {}) @@ -7097,12 +7097,12 @@ class LlamaCppBackend: # Vulkan pins via --device (a cmd arg, unlike the env-based # CUDA/ROCm pin below), emitted BEFORE user extras so llama.cpp's - # last-wins parsing lets a user --device override Studio's pick. + # last-wins parsing lets a user --device override Unsloth's pick. if is_vulkan_backend and gpu_indices is not None: cmd += LlamaCppBackend._vulkan_pin_args(gpu_indices) # User pass-through args go last so llama.cpp's last-wins parsing - # lets the user override Studio's auto-set flags. Already + # lets the user override Unsloth's auto-set flags. Already # validated by the route via validate_extra_args(). if extra_args: cmd.extend(str(a) for a in extra_args) @@ -7118,9 +7118,9 @@ class LlamaCppBackend: if "--threads" not in cmd: env.pop("LLAMA_ARG_THREADS", None) - # Reconcile the inherited LLAMA_ARG_* env with Studio's final + # Reconcile the inherited LLAMA_ARG_* env with Unsloth's final # decision: stripping CLI extras on a tensor->layer downgrade - # can't remove env vars, so the child could run a mode/KV Studio + # can't remove env vars, so the child could run a mode/KV Unsloth # didn't budget. if not tensor_parallel: # Layer split: clear a non-layer inherited split mode (and any @@ -7130,7 +7130,7 @@ class LlamaCppBackend: env.pop("LLAMA_ARG_SPLIT_MODE", None) env.pop("LLAMA_ARG_TENSOR_SPLIT", None) else: - # Studio owns the tensor split: it emits --tensor-split when it + # Unsloth owns the tensor split: it emits --tensor-split when it # picks an uneven one (CLI wins) and nothing when an even split # is safe. Clear any inherited LLAMA_ARG_TENSOR_SPLIT so the even # case can't be overridden by a stale env (the layer branch above @@ -7201,7 +7201,7 @@ class LlamaCppBackend: # 'on') even when -ngl is explicit. That step has aborted on # some ROCm hosts (ggml-cuda.cu ROCm error during worst-case # estimation, e.g. MTP + mmproj models on gfx1151). When - # Studio's own VRAM math already placed the model + # Unsloth's own VRAM math already placed the model # (use_fit=False), the step is redundant second-guessing -- # retry once with --fit off before declaring the load failed. # Never retry when fit was requested (use_fit) or the caller @@ -7284,7 +7284,7 @@ class LlamaCppBackend: and _startup_crashed and not _split_axis_crash ): - # We forced --fit off because Studio's (conservative) VRAM + # We forced --fit off because Unsloth's (conservative) VRAM # math placed the model fully on GPU. A startup crash here # means that estimate was optimistic, so fall back to --fit # on and let llama.cpp offload rather than fail the load. @@ -7296,7 +7296,7 @@ class LlamaCppBackend: self._process.returncode, self._llama_log_path, ) - # Flip Studio's own --fit off (added first, before any + # Flip Unsloth's own --fit off (added first, before any # user extra args) to on; a user's later --fit still wins # by last-arg. Defensive: if absent, the default is already # --fit on, so leave it. @@ -7313,7 +7313,7 @@ class LlamaCppBackend: ): logger.warning( "llama-server crashed during startup (exit code %s) " - "with the default memory-fit step enabled; Studio " + "with the default memory-fit step enabled; Unsloth " "already verified the model fits, retrying once " "with --fit off. Crash log: %s", self._process.returncode, @@ -7393,7 +7393,7 @@ class LlamaCppBackend: cmd = _fa_cmd healthy = _spawn_and_wait(_fa_cmd, label = "-noflash") - # MTP from Studio's spec flags or the user's (extra_args + # MTP from Unsloth's spec flags or the user's (extra_args # --spec-type / LLAMA_ARG_SPEC_TYPE). The env reaches the child # only when neither emits a spec flag, so consult it only then. _launch_spec_env: Mapping[str, str] = ( @@ -7587,11 +7587,11 @@ class LlamaCppBackend: if self._gpu_offload_active is False: logger.warning( "llama-server appears to have loaded the model entirely " - "on CPU even though Studio detected at least one GPU. " + "on CPU even though Unsloth detected at least one GPU. " "This usually means the prebuilt binary's GPU backend " "failed to load -- on Windows, cudart64_X.dll / " "cublas64_X.dll could not be resolved. Reinstall the " - "Studio llama.cpp prebuilt or install a matching CUDA " + "Unsloth llama.cpp prebuilt or install a matching CUDA " "toolkit (issue unslothai/unsloth#5106).", ) @@ -7888,7 +7888,7 @@ class LlamaCppBackend: logger.info( "Auto: MLA embedded-MTP model detected; llama.cpp's MLA/DSA " "MTP path is slower than no speculation, so using ngram-mod " - "instead. Override via the Studio Speculative Decoding " + "instead. Override via the Unsloth Speculative Decoding " "dropdown or UNSLOTH_MLA_MTP_ENABLED=1." ) _emit_ngram_mod() @@ -7916,7 +7916,7 @@ class LlamaCppBackend: f"MTP GGUF detected but model size {_mtp_size_b:.1f}B " "is below the 3B speedup threshold; using ngram-mod " "only (zero-VRAM, no draft head). Override via " - "--spec-type or the Studio Speculative Decoding " + "--spec-type or the Unsloth Speculative Decoding " "dropdown." ) _emit_ngram_mod() @@ -8294,7 +8294,7 @@ class LlamaCppBackend: def _pid_parent_is_alive(pid: int) -> bool: """True if the recorded server's parent is still running, i.e. the server is NOT orphaned. Lets the cross-session reap kill only a true orphan (parent - gone) and never a live server owned by a running Studio, regardless of which + gone) and never a live server owned by a running Unsloth, regardless of which process performs the sweep. Biased toward "alive" on uncertainty so a live server is never mistakenly reaped.""" try: @@ -8334,9 +8334,9 @@ class LlamaCppBackend: @classmethod def _reap_recorded_pid(cls) -> int: """Kill the exact llama-server PID recorded at spawn, but only when it is a - genuine orphan -- its parent (the Studio that spawned it) is gone. This is + genuine orphan -- its parent (the Unsloth that spawned it) is gone. This is the cross-session backstop the parent-death reaper (Job Object / - PR_SET_PDEATHSIG) cannot cover: an orphan left by an already-dead Studio + PR_SET_PDEATHSIG) cannot cover: an orphan left by an already-dead Unsloth (macOS, a best-effort failure, or a pre-existing orphan). Path-independent, so it also catches an orphan the install-root match would miss. @@ -8393,7 +8393,7 @@ class LlamaCppBackend: """Kill orphaned llama-server processes started by studio. Only kills processes whose resolved binary lives under a known - Studio install dir (or matches an exact env-var override), to avoid + Unsloth install dir (or matches an exact env-var override), to avoid terminating unrelated llama-server instances. Mirrors every location _find_llama_server_binary() can return, so orphans from any supported install path are cleaned up. @@ -8413,7 +8413,7 @@ class LlamaCppBackend: try: # -- Build the ownership allowlist -------------------------------- # exact_binaries -- env var overrides (exact path match). - # install_roots -- Studio-owned dir trees (binary must be under one). + # install_roots -- Unsloth-owned dir trees (binary must be under one). install_roots: list[Path] = [] # Env-mode custom root (mirrors _find_llama_server_binary). @@ -8423,7 +8423,7 @@ class LlamaCppBackend: install_roots.append(_resolved_sr / "llama.cpp") # Primary install dir (default mode only). Env-mode skips this so a - # custom-root Studio can't kill a default-install Studio's server. + # custom-root Unsloth can't kill a default-install Unsloth's server. if not _is_custom_root: install_roots.append(Path.home() / ".unsloth" / "llama.cpp") @@ -8497,7 +8497,7 @@ class LlamaCppBackend: if not is_ours: continue - # A live parent means a running Studio (or the user's + # A live parent means a running Unsloth (or the user's # shell) still owns it -- not an orphan. if LlamaCppBackend._pid_parent_is_alive(proc.info["pid"]): continue @@ -8577,7 +8577,7 @@ class LlamaCppBackend: def _fit_off_retry_eligible(cmd: "list[str]", use_fit: bool) -> bool: """Whether a llama-server startup crash may be retried with --fit off. - Only when Studio's own VRAM math placed the model (use_fit=False) + Only when Unsloth's own VRAM math placed the model (use_fit=False) and nothing on the command line set the fit mode explicitly (-fit / --fit, space- or equals-form). --fit-ctx / --fit-target / -fitc / -fitt tune the fit step but do not select the mode, so @@ -8821,7 +8821,7 @@ class LlamaCppBackend: return None def _reconcile_effective_ctx_with_server(self) -> None: - """Adopt the server's real ``n_ctx`` when it is below Studio's value. + """Adopt the server's real ``n_ctx`` when it is below Unsloth's value. Keeps ``context_length`` (load response, status route, passthrough ``max_tokens`` ceiling) honest; clients sized to the requested value diff --git a/studio/backend/core/inference/llama_keepwarm.py b/studio/backend/core/inference/llama_keepwarm.py index 4ce663c3ce..86a8c8a404 100644 --- a/studio/backend/core/inference/llama_keepwarm.py +++ b/studio/backend/core/inference/llama_keepwarm.py @@ -59,7 +59,7 @@ _INFERENCE_SUFFIXES = ( "/messages/count_tokens", # counts via the loaded tokenizer; protect like /messages "/embeddings", "/responses", - "/generate/stream", # Studio's own streaming route on the same llama-server + "/generate/stream", # Unsloth's own streaming route on the same llama-server "/audio/generate", # direct GGUF TTS; can outlive the idle TTL ) diff --git a/studio/backend/core/inference/llama_server_args.py b/studio/backend/core/inference/llama_server_args.py index f400d2ae40..70d0dc774d 100644 --- a/studio/backend/core/inference/llama_server_args.py +++ b/studio/backend/core/inference/llama_server_args.py @@ -3,10 +3,10 @@ """Boundary validator for user-supplied llama-server pass-through args. -Reject only flags Studio manages (model identity, auth, network, parallel +Reject only flags Unsloth manages (model identity, auth, network, parallel slots). Everything else (sampling, ``-c``, ``-ngl``, ``--flash-attn``, ``--cache-type-*``, ``--spec-*``, ``--jinja``, ...) is appended after -Studio's auto-set flags so llama.cpp's last-wins parser lets the user override. +Unsloth's auto-set flags so llama.cpp's last-wins parser lets the user override. Ref: https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md """ @@ -22,12 +22,12 @@ _DENYLIST_GROUPS: tuple[frozenset[str], ...] = ( # Parallel slots: owned by typer --parallel; a pass-through would desync # app.state.llama_parallel_slots from llama-server. frozenset({"-np", "--parallel", "--n-parallel"}), - # Model identity: Studio resolves it from LoadRequest; a second -m would - # load a different model than Studio thinks it loaded. + # Model identity: Unsloth resolves it from LoadRequest; a second -m would + # load a different model than Unsloth thinks it loaded. frozenset({"-m", "--model"}), - # Public model id: Studio sets a sanitized --alias so the OpenAI API never + # Public model id: Unsloth sets a sanitized --alias so the OpenAI API never # exposes the local .gguf path. A user-supplied alias is appended after - # Studio's and, with llama.cpp's last-wins parsing, would reintroduce the + # Unsloth's and, with llama.cpp's last-wins parsing, would reintroduce the # path leak this is meant to prevent. frozenset({"-a", "--alias"}), frozenset({"-mu", "--model-url"}), @@ -39,14 +39,14 @@ _DENYLIST_GROUPS: tuple[frozenset[str], ...] = ( frozenset({"-hft", "--hf-token"}), frozenset({"-mm", "--mmproj"}), frozenset({"-mmu", "--mmproj-url"}), - # Networking: Studio binds + proxies; retargeting orphans the proxy. + # Networking: Unsloth binds + proxies; retargeting orphans the proxy. frozenset({"--host"}), frozenset({"--port"}), frozenset({"--path"}), frozenset({"--api-prefix"}), frozenset({"--reuse-port"}), - # Auth / TLS: Studio terminates auth; upstream --api-key / TLS shadows - # Studio's key and breaks the proxy hop. + # Auth / TLS: Unsloth terminates auth; upstream --api-key / TLS shadows + # Unsloth's key and breaks the proxy hop. frozenset({"--api-key"}), frozenset({"--api-key-file"}), frozenset({"--ssl-key-file"}), @@ -64,11 +64,11 @@ _DENYLIST_GROUPS: tuple[frozenset[str], ...] = ( frozenset({"--models-max"}), frozenset({"--models-autoload", "--no-models-autoload"}), # Server-mode flips: --embedding / --rerank restrict llama-server to - # those endpoints, breaking Studio's /v1/chat/completions hop. + # those endpoints, breaking Unsloth's /v1/chat/completions hop. frozenset({"--embedding", "--embeddings"}), frozenset({"--rerank", "--reranking"}), # llama-server's own built-in tools flag would silently stack on top of - # Studio's --enable-tools / --disable-tools policy resolver. + # Unsloth's --enable-tools / --disable-tools policy resolver. frozenset({"--tools"}), ) @@ -120,7 +120,7 @@ def validate_extra_args(args: Optional[Iterable[str]]) -> list[str]: def is_managed_flag(flag: str) -> bool: - """True if ``flag`` is Studio-managed. Normalises via ``_flag_name`` so + """True if ``flag`` is Unsloth-managed. Normalises via ``_flag_name`` so `-np8` / `--parallel=8` classify like the canonical tokens.""" normalised = _flag_name(flag) return normalised is not None and normalised in _DENYLIST @@ -142,7 +142,7 @@ _SPEC_FLAGS: frozenset[str] = frozenset( "--draft-min", "--draft-max", # MTP path (llama.cpp #22673). The drafter selectors (local --model-draft - # and HF --spec-draft-hf aliases) are Studio-managed since the separate- + # and HF --spec-draft-hf aliases) are Unsloth-managed since the separate- # drafter support (Gemma 4): an inherited copy must not last-wins-override # the auto-detected drafter. Explicit extras for the current load are never # stripped. The per-drafter tuning knobs (--spec-draft-type-*, -ngld, @@ -179,9 +179,9 @@ _TEMPLATE_FLAGS: frozenset[str] = frozenset( # (--split-mode tensor). Pass-through stays allowed so users keep the # row/none/layer modes the toggle doesn't expose, but it's stripped on # inherit and reconciled into the round-tripped tensor_parallel state. -# --tensor-split is coupled to the split mode and is stripped with it: Studio +# --tensor-split is coupled to the split mode and is stripped with it: Unsloth # owns the tensor-mode split ratios, so an inherited/stale --tensor-split must -# not last-wins-override Studio's computed asymmetric split. +# not last-wins-override Unsloth's computed asymmetric split. _SPLIT_MODE_FLAGS: frozenset[str] = frozenset({"-sm", "--split-mode"}) _TENSOR_SPLIT_FLAGS: frozenset[str] = frozenset({"-ts", "--tensor-split"}) _SPLIT_SHADOWING_FLAGS: frozenset[str] = _SPLIT_MODE_FLAGS | _TENSOR_SPLIT_FLAGS @@ -197,7 +197,7 @@ _BOOLEAN_SHADOWING_FLAGS: frozenset[str] = frozenset({"--spec-default", "--jinja def parse_ctx_override(args: Optional[Iterable[str]]) -> Optional[int]: """Return the last user-supplied ``-c`` / ``--ctx-size`` value. - Mirrors llama.cpp's last-wins parsing for the one numeric knob Studio's + Mirrors llama.cpp's last-wins parsing for the one numeric knob Unsloth's load-time fit logic needs. """ if not args: @@ -286,7 +286,7 @@ def parse_cache_override(args: Optional[Iterable[str]]) -> Optional[str]: Mirrors parse_ctx_override but for cache type. Recognises both -ctk (key) and -ctv (value). When both flags appear, returns the last-wins value, treating key and value cache flags as the same setting because - Studio's KV estimate has a single cache_type_kv knob. + Unsloth's KV estimate has a single cache_type_kv knob. """ return _last_flag_value(args, _CACHE_FLAGS) @@ -341,7 +341,7 @@ def resolve_tensor_parallel(args: Optional[Iterable[str]], fallback_tensor_paral def _env_split_mode_is_tensor(env: Optional[Mapping[str, str]] = None) -> bool: - """True when the inherited LLAMA_ARG_SPLIT_MODE env selects tensor. Studio + """True when the inherited LLAMA_ARG_SPLIT_MODE env selects tensor. Unsloth emits --split-mode only on its tensor branch, so a tensor env on the layer path would run the child tensor-parallel unbudgeted; this flips the budget to tensor. Only tensor is heavier, so other modes are ignored.""" @@ -425,7 +425,7 @@ def strip_shadowing_flags( strip_template: bool = True, strip_split_mode: bool = True, ) -> list[str]: - """Strip flags that shadow first-class Studio settings. + """Strip flags that shadow first-class Unsloth settings. Used when inheriting a previous load's ``llama_extra_args`` so an inherited `-c 4096` can't override the current `max_seq_length` diff --git a/studio/backend/core/inference/llama_stats.py b/studio/backend/core/inference/llama_stats.py index 6047aedbc0..ab0d287e8c 100644 --- a/studio/backend/core/inference/llama_stats.py +++ b/studio/backend/core/inference/llama_stats.py @@ -5,7 +5,7 @@ engine-stats log line (generation/prompt throughput, requests in flight). llama-server already computes these (it needs `--metrics`); this lifts them -into Studio's structured log so the terminal shows serving health, not just +into Unsloth's structured log so the terminal shows serving health, not just per-request access lines. Emitted only while there is activity. """ diff --git a/studio/backend/core/inference/local_model_resolver.py b/studio/backend/core/inference/local_model_resolver.py index 002cafe2c8..86ad8b9fd8 100644 --- a/studio/backend/core/inference/local_model_resolver.py +++ b/studio/backend/core/inference/local_model_resolver.py @@ -130,7 +130,7 @@ def info_has_local_gguf(info) -> bool: def _build_index() -> dict[str, _LocalGgufEntry]: """Map normalized id/model_id/display_name -> local GGUF entry. - Scans the same roots Studio's model picker lists (./models, the active plus + Scans the same roots Unsloth's model picker lists (./models, the active plus legacy/default HF caches, LM Studio dirs, and user scan folders) so a named local model is never missed and silently served as the loaded one. Ollama's scanner is skipped: it creates symlinks as a side effect and this runs on the @@ -199,7 +199,7 @@ def _build_index() -> dict[str, _LocalGgufEntry]: raw_id = getattr(info, "id", None) if not raw_id: continue - # Skip what Studio hides from its pickers (validation probe, RAG embed + # Skip what Unsloth hides from its pickers (validation probe, RAG embed # weights): not chat models, so never an auto-switch target. if _is_hidden_model(raw_id, getattr(info, "path", None)): continue diff --git a/studio/backend/core/inference/mcp_client.py b/studio/backend/core/inference/mcp_client.py index 6b5ce02216..0256df944e 100644 --- a/studio/backend/core/inference/mcp_client.py +++ b/studio/backend/core/inference/mcp_client.py @@ -906,7 +906,7 @@ def _call_stdio_tool( def _remaining() -> Optional[float]: return None if deadline is None else max(0.0, deadline - time.monotonic()) - # Callers without a Studio session id must retain the former one-shot + # Callers without an Unsloth session id must retain the former one-shot # behavior: no browser/cookie/tool state can leak into another request. # Use an ephemeral key (and close it below) rather than the shared empty # scope that the persistent-session cache used previously. diff --git a/studio/backend/core/inference/passthrough_healing.py b/studio/backend/core/inference/passthrough_healing.py index ed7c7ecfcf..e6da0a22b0 100644 --- a/studio/backend/core/inference/passthrough_healing.py +++ b/studio/backend/core/inference/passthrough_healing.py @@ -5,7 +5,7 @@ With server-side tools disabled (``unsloth run --disable-tools``, every ``unsloth start`` coding agent), requests carrying the client's own ``tools`` -bypass Studio's tool loop and are relayed to/from llama-server verbatim. Small +bypass Unsloth's tool loop and are relayed to/from llama-server verbatim. Small GGUF models often emit their tool calls as TEXT (``{...}``, Gemma ``<|tool_call>...``, ```` XML) instead of structured ``tool_calls`` -- on the passthrough that text reaches the agent as prose and @@ -18,7 +18,7 @@ promotes calls whose function name exactly matches a declared tool. Promotion removes EXACTLY the promoted calls' markup spans (the parser reports them): undeclared calls, unparseable blocks, and suppressed alternate formats keep every byte and relay as text, so healing can never silently delete model -output. Responses without a tool signal, requests without tools, and Studio's +output. Responses without a tool signal, requests without tools, and Unsloth's own enable-tools loop are untouched. Per-request opt-out: ``auto_heal_tool_calls: false``. Process kill-switch: ``UNSLOTH_DISABLE_TOOL_CALL_HEALING=1``. diff --git a/studio/backend/core/inference/pricing.py b/studio/backend/core/inference/pricing.py index 3b611d3596..30fec47723 100644 --- a/studio/backend/core/inference/pricing.py +++ b/studio/backend/core/inference/pricing.py @@ -122,12 +122,12 @@ def calculate_cost(provider: str, model: str, usage: dict[str, Any]) -> dict[str "priced": bool(prices), } - # Accept raw (input_tokens/output_tokens) and Studio chat-style + # Accept raw (input_tokens/output_tokens) and Unsloth chat-style # (prompt_tokens/completion_tokens) envelopes. Cache buckets differ: # raw Anthropic: input_tokens EXCLUDES cache buckets # raw OpenAI: input_tokens INCLUDES cache_read - # Studio Anthropic: prompt_tokens INCLUDES cache_creation + cache_read - # Studio OpenAI: prompt_tokens == raw input_tokens + # Unsloth Anthropic: prompt_tokens INCLUDES cache_creation + cache_read + # Unsloth OpenAI: prompt_tokens == raw input_tokens # Clamp >=0 so corrupted payloads can't produce a negative bill. cache_creation = max(0, int(usage.get("cache_creation_input_tokens") or 0)) cache_read_native_present = ( @@ -160,7 +160,7 @@ def calculate_cost(provider: str, model: str, usage: dict[str, Any]) -> dict[str output_tokens = max(0, int(usage.get("completion_tokens") or 0)) if provider == "openai": # Cached tokens land on input_tokens_details (raw Responses) or - # prompt_tokens_details (Studio chat-style). + # prompt_tokens_details (Unsloth chat-style). for key in ("input_tokens_details", "prompt_tokens_details"): details = usage.get(key) or {} if isinstance(details, dict): diff --git a/studio/backend/core/inference/safetensors_agentic.py b/studio/backend/core/inference/safetensors_agentic.py index 9110315815..40731de57b 100644 --- a/studio/backend/core/inference/safetensors_agentic.py +++ b/studio/backend/core/inference/safetensors_agentic.py @@ -995,7 +995,7 @@ def run_safetensors_tool_loop( if not safety_tc: # Re-prompt once on plan-without-action, before any tool runs # (GGUF loop parity). The retry is gated on nudge_tool_calls so - # Studio callers (which send True) always nudge, while API callers + # Unsloth callers (which send True) always nudge, while API callers # who omit the flag keep today's no-reprompt behavior (opt-in). intent_text = _reprompt_intent_text( content_accum, diff --git a/studio/backend/core/inference/sandbox_site/sitecustomize.py b/studio/backend/core/inference/sandbox_site/sitecustomize.py index d655e8e35a..244fa95145 100644 --- a/studio/backend/core/inference/sandbox_site/sitecustomize.py +++ b/studio/backend/core/inference/sandbox_site/sitecustomize.py @@ -4,7 +4,7 @@ """Sandbox-side compatibility shim for ChatGPT code-interpreter paths. Models habitually write to /mnt/data (or /mnt/outputs, /home/sandbox, -/workspace), none of which exist in the Studio sandbox. This module sits on the +/workspace), none of which exist in the Unsloth sandbox. This module sits on the sandbox subprocess PYTHONPATH (see ``tools._build_safe_env``), so it loads at interpreter startup in every sandboxed ``python`` run and any Python the ``terminal`` tool launches. diff --git a/studio/backend/core/inference/tool_loop_controller.py b/studio/backend/core/inference/tool_loop_controller.py index f7ed450d11..61643b5795 100644 --- a/studio/backend/core/inference/tool_loop_controller.py +++ b/studio/backend/core/inference/tool_loop_controller.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Shared controller state for Studio local agentic tool loops. +"""Shared controller state for Unsloth local agentic tool loops. This module is intentionally dependency-light: it owns only per-response ledger state and value objects used by the GGUF and safetensors loops. diff --git a/studio/backend/core/inference/tools.py b/studio/backend/core/inference/tools.py index 5fd57e1b2c..bc9ffe85c2 100644 --- a/studio/backend/core/inference/tools.py +++ b/studio/backend/core/inference/tools.py @@ -2502,7 +2502,7 @@ def _build_safe_env(workdir: str) -> dict[str, str]: shim directory. """ # Start from the running interpreter's dir so 'python'/'pip' resolve to the - # same environment the Studio server runs in. + # same environment the Unsloth server runs in. exe_dir = os.path.dirname(sys.executable) path_entries = [exe_dir] if exe_dir else [] @@ -2792,7 +2792,7 @@ def _bypass_preexec(): """Minimal pre-exec for bypass exec: os.setsid() only. Required, not a restriction: _kill_process_tree does killpg(getpgid(child)), - so without a new session a timeout/cancel would kill the Studio server too. + so without a new session a timeout/cancel would kill the Unsloth server too. """ try: os.setsid() @@ -2800,13 +2800,13 @@ def _bypass_preexec(): pass -# Hardening the Studio parent is done once (PR_SET_DUMPABLE is process-global +# Hardening the Unsloth parent is done once (PR_SET_DUMPABLE is process-global # and sticky); guarded so repeated bypass calls do not re-issue the prctl. _parent_proc_hardened = False def _harden_parent_against_proc_env_leak() -> bool: - """Make the Studio process's /proc//environ unreadable to its children. + """Make the Unsloth process's /proc//environ unreadable to its children. Stripping the child env is not enough on Linux: a bypassed same-UID child can read /proc//environ to recover the parent's unfiltered @@ -5482,7 +5482,7 @@ def _truncate(text: str, limit: int = _MAX_OUTPUT_CHARS) -> str: # ChatGPT code-interpreter path conventions models write out of habit; none -# exist in the Studio sandbox, so a failure on one earns the retry hint. +# exist in the Unsloth sandbox, so a failure on one earns the retry hint. _MISSING_PATH_PREFIXES = ( "/mnt/data", "/mnt/outputs", @@ -5688,7 +5688,7 @@ def _python_exec( # Close the /proc//environ secret-recovery path first; if it # cannot be applied, fail closed rather than leak the parent environ. return ( - "Execution error: could not harden the Studio process against " + "Execution error: could not harden the Unsloth process against " "/proc environment reads; refusing bypass execution." ) @@ -5833,7 +5833,7 @@ def _bash_exec( # Close the /proc//environ secret-recovery path first; if it # cannot be applied, fail closed rather than leak the parent environ. return ( - "Execution error: could not harden the Studio process against " + "Execution error: could not harden the Unsloth process against " "/proc environment reads; refusing bypass execution." ) diff --git a/studio/backend/core/rag/captioner.py b/studio/backend/core/rag/captioner.py index 6d1512a770..8398506f21 100644 --- a/studio/backend/core/rag/captioner.py +++ b/studio/backend/core/rag/captioner.py @@ -6,7 +6,7 @@ Both turn pixels into indexable text and are a no-op (never raise) without a loaded vision model. They reuse the chat model's vision endpoint, so it must be served with ``--ubatch-size`` >= one image's tokens (some encoders, e.g. Gemma, attend -non-causally and abort otherwise); Studio's vision chat already requires this.""" +non-causally and abort otherwise); Unsloth's vision chat already requires this.""" from __future__ import annotations diff --git a/studio/backend/core/rag/embed_llama_server.py b/studio/backend/core/rag/embed_llama_server.py index 46a282c939..b141e59422 100644 --- a/studio/backend/core/rag/embed_llama_server.py +++ b/studio/backend/core/rag/embed_llama_server.py @@ -10,7 +10,7 @@ Opt-in (``RAG_EMBED_BACKEND=llama-server``). Runs a dedicated Device is ``auto`` (GPU when present, else CPU, falling back to CPU if a GPU start fails); ``RAG_EMBED_DEVICE`` forces it. We call only llama_cpp's *static* helpers (no torch), copying the instance-coupled bits locally, since constructing a -``LlamaCppBackend`` runs an ``__init__`` reaper that kills any Studio llama-server +``LlamaCppBackend`` runs an ``__init__`` reaper that kills any Unsloth llama-server -- so each request re-spawns ours if it died (self-heal). """ diff --git a/studio/backend/core/rag/embeddings.py b/studio/backend/core/rag/embeddings.py index b0ecedd593..15be7f1249 100644 --- a/studio/backend/core/rag/embeddings.py +++ b/studio/backend/core/rag/embeddings.py @@ -39,7 +39,7 @@ _model = None _name: str | None = None -# Studio device -> torch device string. Apple has no torch device -> CPU. +# Unsloth device -> torch device string. Apple has no torch device -> CPU. _TORCH_DEVICE = {DeviceType.CUDA: "cuda", DeviceType.XPU: "xpu"} diff --git a/studio/backend/core/training/resume.py b/studio/backend/core/training/resume.py index 2a4a198610..bbd9a895ab 100644 --- a/studio/backend/core/training/resume.py +++ b/studio/backend/core/training/resume.py @@ -53,7 +53,7 @@ def get_resume_checkpoint_path(path_value: str) -> Optional[str]: def normalize_resume_output_dir(path_value: str) -> str: path = resolve_output_dir(path_value) if not _is_under_outputs(path): - raise ValueError("Resume checkpoint must be inside Studio outputs.") + raise ValueError("Resume checkpoint must be inside Unsloth outputs.") return str(path) diff --git a/studio/backend/core/training/trainer.py b/studio/backend/core/training/trainer.py index 883a535a89..26720865f4 100644 --- a/studio/backend/core/training/trainer.py +++ b/studio/backend/core/training/trainer.py @@ -797,7 +797,7 @@ class UnslothTrainer: ) logger.info("Loaded text model") - raise_if_offloaded(self.model, device_map, "Studio training") + raise_if_offloaded(self.model, device_map, "Unsloth training") if self.should_stop: return False diff --git a/studio/backend/core/training/training.py b/studio/backend/core/training/training.py index 38f6b92f6d..b407ba39a5 100644 --- a/studio/backend/core/training/training.py +++ b/studio/backend/core/training/training.py @@ -140,7 +140,7 @@ def should_use_mlx_training_backend(*, device: Optional[Any] = None) -> bool: def _build_training_worker_config(values: dict[str, Any]) -> dict[str, Any]: - """Build the normalized worker config shared by Studio and the CLI adapter.""" + """Build the normalized worker config shared by Unsloth and the CLI adapter.""" config = { "model_name": values["model_name"], "project_name": values.get("project_name"), @@ -307,7 +307,7 @@ PLOT_HEIGHT = 3.5 @dataclass class TrainingProgress: - """Shared training progress payload for Studio and backend-aware trainers.""" + """Shared training progress payload for Unsloth and backend-aware trainers.""" epoch: float = 0 step: int = 0 @@ -328,7 +328,7 @@ class TrainingProgress: class _MLXTrainerAdapter: - """Adapts the legacy UnslothTrainer API to the shared Studio MLX worker path.""" + """Adapts the legacy UnslothTrainer API to the shared Unsloth MLX worker path.""" def __init__(self): self.model = None diff --git a/studio/backend/core/training/worker.py b/studio/backend/core/training/worker.py index c52adbe8fa..111f4fdd0f 100644 --- a/studio/backend/core/training/worker.py +++ b/studio/backend/core/training/worker.py @@ -1100,7 +1100,7 @@ _MLX_VLM_RESIZED_IMAGE_LAYOUT_CACHE = {} def _mlx_vlm_resized_image_layout(processor = None) -> str | None: - """Return the numpy image layout expected after Studio-side VLM resizing.""" + """Return the numpy image layout expected after Unsloth-side VLM resizing.""" image_processor = getattr(processor, "image_processor", None) if image_processor is None: return None @@ -1257,7 +1257,7 @@ _MLX_STUDIO_LR_SCHEDULERS = {"linear", "cosine", "constant"} # Fallback alias map mirroring unsloth_zoo._normalize_mlx_optimizer_name, used -# only when mlx (Apple Silicon) is not importable so Studio config validation +# only when mlx (Apple Silicon) is not importable so Unsloth config validation # still works on non-MLX hosts. The zoo function stays the source of truth. _MLX_STUDIO_ADAMW_ALIASES = frozenset( ( @@ -1309,7 +1309,7 @@ def _normalize_mlx_studio_scheduler(value): def _resolve_mlx_local_dataset_files(file_paths: list) -> list[str]: - """Resolve CLI paths and Studio local dataset uploads without importing the GPU trainer.""" + """Resolve CLI paths and Unsloth local dataset uploads without importing the GPU trainer.""" from utils.paths import resolve_dataset_path all_files: list[str] = [] @@ -1912,7 +1912,7 @@ def _run_mlx_training(event_queue, stop_queue, config): if "max_grad_leaf_norm" in _supported_fields: mlx_config_kwargs["max_grad_leaf_norm"] = max_grad_leaf_norm if "append_eos" in _supported_fields: - # Studio SFT formatting owns rendered examples; raw/CPT text still + # Unsloth SFT formatting owns rendered examples; raw/CPT text still # needs MLX to append EOS like the CUDA raw-text path. mlx_config_kwargs["append_eos"] = bool(raw_text_mode) @@ -2121,7 +2121,7 @@ def run_mlx_training_process( config: dict, transformers_activated: bool = False, ) -> None: - """MLX worker entrypoint shared by Studio subprocesses and the CLI adapter.""" + """MLX worker entrypoint shared by Unsloth subprocesses and the CLI adapter.""" model_name = config["model_name"] backend_path = str(Path(__file__).resolve().parent.parent.parent) @@ -2780,7 +2780,7 @@ def run_training_process(*, event_queue: Any, stop_queue: Any, config: dict) -> ) # Unified Windows APUs: the WDDM budget is user-raisable, but # nothing on the box says so -- users see "48 GB VRAM" on a - # 96 GB machine and assume a Studio bug. Say where the limit + # 96 GB machine and assume an Unsloth bug. Say where the limit # comes from and how to raise it. if _is_unified and sys.platform == "win32": try: diff --git a/studio/backend/hub/services/download_lifecycle.py b/studio/backend/hub/services/download_lifecycle.py index e5d48872c1..23f8c7c911 100644 --- a/studio/backend/hub/services/download_lifecycle.py +++ b/studio/backend/hub/services/download_lifecycle.py @@ -76,7 +76,7 @@ def spawn_worker( env["HF_HUB_DISABLE_PROGRESS_BARS"] = "1" env["HF_HUB_DISABLE_TELEMETRY"] = "1" env["HF_HUB_DISABLE_XET"] = "0" if use_xet else "1" - # No token in Studio settings: fall back to the backend's own HF_TOKEN so + # No token in Unsloth settings: fall back to the backend's own HF_TOKEN so # private repos stay downloadable (needed while inkling repos are private). if not hf_token: hf_token = os.environ.get("HF_TOKEN") or None diff --git a/studio/backend/hub/services/models/folder_browser.py b/studio/backend/hub/services/models/folder_browser.py index d56b62c318..7d9c3ac665 100644 --- a/studio/backend/hub/services/models/folder_browser.py +++ b/studio/backend/hub/services/models/folder_browser.py @@ -165,7 +165,7 @@ def _looks_like_model_dir(directory: Path) -> bool: def _build_browse_allowlist( media_roots: Optional[list[Path]] = None, drive_roots: Optional[list[Path]] = None ) -> list[Path]: - """Root directories the browser may walk (also seeds the suggestion chips): HOME, resolved HF cache dirs, Studio outputs/exports/root, registered scan folders, and well-known local-LLM dirs. Each is added only if it resolves to a real directory so the sandbox has no dead boundary. + """Root directories the browser may walk (also seeds the suggestion chips): HOME, resolved HF cache dirs, Unsloth outputs/exports/root, registered scan folders, and well-known local-LLM dirs. Each is added only if it resolves to a real directory so the sandbox has no dead boundary. *media_roots* / *drive_roots* let the caller pass already-probed removable-media and Windows drive roots so they aren't scanned again (a diff --git a/studio/backend/hub/services/models/ollama.py b/studio/backend/hub/services/models/ollama.py index 96a4114620..2ccdbb44f1 100644 --- a/studio/backend/hub/services/models/ollama.py +++ b/studio/backend/hub/services/models/ollama.py @@ -85,7 +85,7 @@ def _contained_link_path(link_dir: Path, link_name: str) -> Optional[Path]: def _ollama_links_dir(ollama_dir: Path) -> Optional[Path]: - """Writable directory for Ollama ``.gguf`` symlinks. Prefers ``/.studio_links/`` next to the blobs; falls back to Studio's cache (read-only system installs), then the temp dir (sandboxed installs).""" + """Writable directory for Ollama ``.gguf`` symlinks. Prefers ``/.studio_links/`` next to the blobs; falls back to Unsloth's cache (read-only system installs), then the temp dir (sandboxed installs).""" def _ensure_writable_dir(path: Path) -> Optional[Path]: try: diff --git a/studio/backend/hub/utils/state_dir.py b/studio/backend/hub/utils/state_dir.py index 183e934724..898c03c87d 100644 --- a/studio/backend/hub/utils/state_dir.py +++ b/studio/backend/hub/utils/state_dir.py @@ -3,7 +3,7 @@ """Filesystem layout for Hub download state. -State directory sits beside HF's cache (under Studio's own cache root) +State directory sits beside HF's cache (under Unsloth's own cache root) so it survives ``huggingface-cli delete-cache`` and any other HF-side cache lifecycle. Two subdirectories: diff --git a/studio/backend/main.py b/studio/backend/main.py index 6e16dc00ca..4797764ce7 100644 --- a/studio/backend/main.py +++ b/studio/backend/main.py @@ -19,7 +19,7 @@ os.environ["PYTHONWARNINGS"] = "ignore" # Pin GPU index ordering to PCI bus id before any torch import creates a CUDA # context. Without this, torch/CUDA default to FASTEST_FIRST while nvidia-smi -# (and Studio's VRAM probes) use PCI-bus order, so a GPU index chosen from +# (and Unsloth's VRAM probes) use PCI-bus order, so a GPU index chosen from # nvidia-smi data can resolve to a different physical card via # CUDA_VISIBLE_DEVICES. setdefault so an explicit user override wins. See # utils/hardware/hardware.py for the full rationale; set here too so the entry @@ -93,7 +93,7 @@ if sys.platform == "win32": # ── Windows AMD ROCm: make hipInfo.exe resolvable for subprocess probes ── # bitsandbytes' get_rocm_gpu_arch() runs `hipinfo.exe` via PATH at import # time; the AMD torch wheel ships it in the venv Scripts dir, which is on - # PATH only when the venv is activated -- Studio launches python directly. + # PATH only when the venv is activated -- Unsloth launches python directly. # Without this, every bitsandbytes import logs a scary (but harmless) # "Could not detect ROCm GPU architecture: [WinError 2]" ERROR + WARNING. # Gated on the file existing: only AMD ROCm wheels ship hipInfo.exe, so @@ -252,7 +252,7 @@ def _read_studio_install_id() -> str: Returns "" when absent or not a 64-char lowercase-hex token; then /api/health emits "" and the launcher accepts any healthy backend. - Carries no install-path info (matters when Studio runs -H 0.0.0.0).""" + Carries no install-path info (matters when Unsloth runs -H 0.0.0.0).""" try: token = (_STUDIO_ROOT_RESOLVED / "share" / "studio_install_id").read_text().strip() except (OSError, ValueError): @@ -573,7 +573,7 @@ async def lifespan(app: FastAPI): print("DEFAULT ADMIN ACCOUNT CREATED") print(f" username: {storage.DEFAULT_ADMIN_USERNAME}") print(f" password saved to: {bootstrap_path}") - print(" Open the Studio UI to sign in and change it.") + print(" Open the Unsloth UI to sign in and change it.") print("=" * 60 + "\n") else: app.state.bootstrap_password = ( @@ -613,7 +613,7 @@ app = FastAPI( ) # The MCP surface is opt-in because it can start GPU jobs and write model -# artifacts. Mount it only when explicitly enabled by the Studio process. +# artifacts. Mount it only when explicitly enabled by the Unsloth process. if os.environ.get("UNSLOTH_STUDIO_ENABLE_MCP") == "1": from fastmcp.utilities.lifespan import combine_lifespans @@ -973,7 +973,7 @@ app.include_router(training_router, prefix = "/api/train", tags = ["training"]) app.include_router(models_router, prefix = "/api/models", tags = ["models"]) app.include_router(chat_history_router, prefix = "/api/chat", tags = ["chat"]) app.include_router(inference_router, prefix = "/api/inference", tags = ["inference"]) -# Studio-only inference endpoints (cancel, etc.) are NOT exposed on the /v1 +# Unsloth-only inference endpoints (cancel, etc.) are NOT exposed on the /v1 # OpenAI-compat prefix below. app.include_router(inference_studio_router, prefix = "/api/inference", tags = ["inference"]) @@ -1080,7 +1080,7 @@ def studio_install_source(_current_subject: str = Depends(get_current_subject)): @app.get("/api/studio/update-status") def studio_update_status(_current_subject: str = Depends(get_current_subject)): - """Return source-aware manual update status for browser-served Studio.""" + """Return source-aware manual update status for browser-served Unsloth.""" return get_studio_update_status(UNSLOTH_VERSION) diff --git a/studio/backend/mcp_server.py b/studio/backend/mcp_server.py index f837f46425..e93490411d 100644 --- a/studio/backend/mcp_server.py +++ b/studio/backend/mcp_server.py @@ -3,7 +3,7 @@ """Curated MCP tools for driving an Unsloth Studio instance. -The MCP surface deliberately wraps the existing Studio services instead of +The MCP surface deliberately wraps the existing Unsloth services instead of duplicating training or export logic. It is opt-in because several tools can start GPU work or write model artifacts. """ @@ -17,14 +17,14 @@ from fastmcp import FastMCP class BearerTokenMiddleware: - """Require an exact bearer token when Studio MCP is exposed remotely.""" + """Require an exact bearer token when Unsloth MCP is exposed remotely.""" def __init__(self, app: Any, token: str) -> None: if not token or not token.strip(): - raise ValueError("Studio MCP bearer token must be a non-empty value") + raise ValueError("Unsloth MCP bearer token must be a non-empty value") if not token.isascii(): # A non-ASCII token cannot be sent in an HTTP header; reject it here. - raise ValueError("Studio MCP bearer token must contain ASCII characters only") + raise ValueError("Unsloth MCP bearer token must contain ASCII characters only") self.app = app # Compare on raw header bytes: str hmac.compare_digest raises on non-ASCII # input, which would surface as a 500 instead of a clean 401. @@ -76,18 +76,18 @@ def _dump(value: Any) -> Any: def _clamp(value: int, low: int, high: int) -> int: """Clamp an MCP-supplied integer into an inclusive range. - MCP tools call the Studio route functions directly, which skips FastAPI's + MCP tools call the Unsloth route functions directly, which skips FastAPI's Query(ge=, le=) validation, so we re-apply the same bounds here. """ return max(low, min(value, high)) def create_studio_mcp() -> FastMCP: - """Create the Studio MCP server and register the high-value tools.""" + """Create the Unsloth MCP server and register the high-value tools.""" mcp = FastMCP( "Unsloth Studio", instructions = ( - "Use read tools to inspect the local Studio state before starting GPU work. " + "Use read tools to inspect the local Unsloth state before starting GPU work. " "Training and export tools can consume substantial VRAM and write files. " "Never expose tokens or local paths from tool results unless the user asks." ), @@ -116,7 +116,7 @@ def create_studio_mcp() -> FastMCP: @mcp.tool async def list_local_models(models_dir: str = "./models") -> dict[str, Any]: - """List local and cached models available to Studio.""" + """List local and cached models available to Unsloth.""" from routes.models import list_local_models as list_models return _dump(await list_models(models_dir = models_dir, current_subject = "mcp")) @@ -128,9 +128,9 @@ def create_studio_mcp() -> FastMCP: @mcp.tool async def start_training(config: dict[str, Any]) -> dict[str, Any]: - """Start a validated Studio training job from a TrainingStartRequest-shaped object. + """Start a validated Unsloth training job from a TrainingStartRequest-shaped object. - The config is validated by the same Pydantic model used by the Studio UI. + The config is validated by the same Pydantic model used by the Unsloth UI. Call get_training_status first and do not start work while another job runs. """ from models import TrainingStartRequest @@ -138,7 +138,7 @@ def create_studio_mcp() -> FastMCP: request = TrainingStartRequest.model_validate(config) # Pass via_api_key explicitly (a direct call leaves it a Depends object). - # MCP drives Studio like the UI session, so it coexists and frees VRAM. + # MCP drives Unsloth like the UI session, so it coexists and frees VRAM. return _dump(await start(request, current_subject = "mcp", via_api_key = False)) @mcp.tool @@ -159,7 +159,7 @@ def create_studio_mcp() -> FastMCP: @mcp.tool def validate_recipe(recipe: dict[str, Any]) -> dict[str, Any]: - """Validate a Data Recipe with the same validator used by Studio.""" + """Validate a Data Recipe with the same validator used by Unsloth.""" from models.data_recipe import RecipePayload from routes.data_recipe.validate import validate @@ -225,7 +225,7 @@ def create_studio_mcp() -> FastMCP: imatrix: bool = False, imatrix_path: str | None = None, ) -> dict[str, Any]: - """Export the loaded model to GGUF using Studio's existing path validation. + """Export the loaded model to GGUF using Unsloth's existing path validation. quantization_method may be a single method or a list to produce several GGUFs from one load. Pass hf_token when push_to_hub is set (the backend diff --git a/studio/backend/models/inference.py b/studio/backend/models/inference.py index 3ae974448e..f3ae0f70df 100644 --- a/studio/backend/models/inference.py +++ b/studio/backend/models/inference.py @@ -105,7 +105,7 @@ class LoadRequest(BaseModel): description = ( "Extra arguments forwarded verbatim to llama-server for GGUF models. " "One token per list entry, e.g. ['--top-k', '20', '--seed', '42']. " - "Studio-managed flags (model identity, port, context length, GPU placement, " + "Unsloth-managed flags (model identity, port, context length, GPU placement, " "auth, UI/server mode) are rejected. Ignored for non-GGUF models." ), ) @@ -151,13 +151,13 @@ class TransformersUpgradeInfo(BaseModel): ) supported_in_pypi: bool = Field( False, - description = "True if the latest PyPI release ships this model_type; Studio can " + description = "True if the latest PyPI release ships this model_type; Unsloth can " "install it into a persistent sidecar after user consent.", ) supported_in_main: bool = Field( False, description = "True if transformers GitHub main ships this model_type (dev-only; " - "not installable through Studio yet).", + "not installable through Unsloth yet).", ) @@ -533,7 +533,7 @@ class ImageContentPart(BaseModel): class InputDocumentContentPart(BaseModel): """Document (PDF / file) content part in a multimodal message. - Studio-normalised shape (file_data or file_url, plus optional filename/media_type). + Unsloth-normalised shape (file_data or file_url, plus optional filename/media_type). Mapped onto Anthropic ``document`` / OpenAI ``input_file`` for vision providers; dropped for non-vision providers. """ @@ -689,7 +689,7 @@ class ThinkingConfig(BaseModel): """Anthropic-compatible thinking/reasoning configuration. Use type='disabled' to turn off thinking, or type='enabled' to turn it on. Only type is read; extra fields (e.g. budget_tokens) are ignored, since - Studio sets provider thinking budgets itself. + Unsloth sets provider thinking budgets itself. """ type: Literal["disabled", "enabled"] = "disabled" @@ -748,7 +748,7 @@ class ChatCompletionRequest(BaseModel): None, description = ( "OpenAI function-tool definitions. When provided without `enable_tools=true`, " - "Studio forwards the tools to the backend so the model returns structured " + "Unsloth forwards the tools to the backend so the model returns structured " "tool_calls for the client to execute (standard OpenAI function calling)." ), ) @@ -1160,7 +1160,7 @@ class ChatCompletionRequest(BaseModel): and (self.enable_tools is True or bool(self.mcp_enabled)) ): # "Ask" gates every call, so a direct API caller that omits the legacy - # confirm flag must still hit the confirmation gate for Studio's own + # confirm flag must still hit the confirmation gate for Unsloth's own # tool loop. An explicit confirm_tool_calls=False wins over the mode # (mirrors _permission_mode_confirm and the Anthropic pre-switch guard), # so only self-enable when the flag is unset. Only self-enable when that @@ -1168,7 +1168,7 @@ class ChatCompletionRequest(BaseModel): # (enable_tools / mcp_enabled) -- the router enters the loop on those # signals, not on enabled_tools alone (which merely filters which tools # run). A plain client-tool passthrough (client-supplied `tools` that - # Studio does not execute) must route verbatim, and external-provider + # Unsloth does not execute) must route verbatim, and external-provider # routing rejects confirm_tool_calls with tools, so skip the fold there. # # "auto" is deliberately NOT folded: it only prompts for a call the diff --git a/studio/backend/models/training.py b/studio/backend/models/training.py index ff815a2fa9..0b50f63b95 100644 --- a/studio/backend/models/training.py +++ b/studio/backend/models/training.py @@ -446,7 +446,7 @@ class TrainingStartRequest(BaseModel): random_seed: int = Field( 3407, description = ( - "Random seed; matches the Studio backend / MLX worker default " + "Random seed; matches the Unsloth backend / MLX worker default " "and unsloth's historical recommended value." ), ) diff --git a/studio/backend/plugins/data-designer-github-repo-seed/README.md b/studio/backend/plugins/data-designer-github-repo-seed/README.md index 346d94b305..44519496f5 100644 --- a/studio/backend/plugins/data-designer-github-repo-seed/README.md +++ b/studio/backend/plugins/data-designer-github-repo-seed/README.md @@ -4,7 +4,7 @@ A Data Designer seed-reader plugin for **Unsloth Studio** that scrapes real GitHub data (issues, pull requests, commits) from one or more repositories and hands it to the recipe pipeline as a seed dataset. -Designed to ship with Studio as a default seed source so any user with a +Designed to ship with Unsloth as a default seed source so any user with a GitHub token can build training datasets straight from live repos. ## What it does @@ -64,7 +64,7 @@ sleeps until reset when the budget drops below a safety threshold. ## Install -Shipped as a default Studio plugin. For development: +Shipped as a default Unsloth plugin. For development: ```bash pip install -e . diff --git a/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/__init__.py b/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/__init__.py index 62ecb2e280..d4d46da370 100644 --- a/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/__init__.py +++ b/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/__init__.py @@ -3,4 +3,4 @@ # Intentionally empty. Data-designer loads submodules lazily via qualified names # in plugin.py, so importing this package must not touch data_designer.engine.* -# during Studio bootstrap (circular import). +# during Unsloth bootstrap (circular import). diff --git a/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper.py b/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper.py index 637193e8b3..1af8133cc5 100644 --- a/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper.py +++ b/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Multi-repo GitHub scraper for the Studio seed plugin. +"""Multi-repo GitHub scraper for the Unsloth seed plugin. Drives the GraphQL scraper in `scraper_impl/` per repo, capped via trial_limits to stop at `limit` items per resource. Then reads the per-resource JSONL shards diff --git a/studio/backend/requirements/extras-no-deps.txt b/studio/backend/requirements/extras-no-deps.txt index 5830a47789..3361af50dd 100644 --- a/studio/backend/requirements/extras-no-deps.txt +++ b/studio/backend/requirements/extras-no-deps.txt @@ -5,7 +5,7 @@ julius torchcodec==0.10.0 snac -# peft 0.19.0 causes export subprocess shutdown issues in Studio; +# peft 0.19.0 causes export subprocess shutdown issues in Unsloth; # installing with --no-deps to avoid pulling in torch>=0.11.0 peft==0.18.1 diff --git a/studio/backend/requirements/no-torch-runtime.txt b/studio/backend/requirements/no-torch-runtime.txt index de321f80ed..378fb33a60 100644 --- a/studio/backend/requirements/no-torch-runtime.txt +++ b/studio/backend/requirements/no-torch-runtime.txt @@ -70,7 +70,7 @@ cut_cross_entropy pillow # RAG store + document parsing, mirroring studio.txt. Pinned here because -# this file installs --no-deps; without them Studio runs with RAG disabled. +# this file installs --no-deps; without them Unsloth runs with RAG disabled. sqlite-vec==0.1.9 pymupdf==1.27.2.3 # 0.3.x keeps pymupdf-layout (which pulls onnxruntime) an optional extra; the diff --git a/studio/backend/requirements/single-env/constraints.txt b/studio/backend/requirements/single-env/constraints.txt index 0ed2bf8b26..0a5619924a 100644 --- a/studio/backend/requirements/single-env/constraints.txt +++ b/studio/backend/requirements/single-env/constraints.txt @@ -4,7 +4,7 @@ transformers==4.57.6 trl==0.23.1 huggingface-hub==0.36.2 -# Studio stack +# Unsloth stack datasets==4.3.0 pyarrow==23.0.1 diff --git a/studio/backend/requirements/studio.txt b/studio/backend/requirements/studio.txt index 6f4a5c3292..0c7503a5ca 100644 --- a/studio/backend/requirements/studio.txt +++ b/studio/backend/requirements/studio.txt @@ -1,4 +1,4 @@ -# Studio UI backend dependencies +# Unsloth UI backend dependencies typer fastapi uvicorn @@ -9,7 +9,7 @@ pandas nest_asyncio datasets==4.3.0 pyjwt -# gradio>=4.0.0 # 148 MB - Studio uses React + FastAPI, not Gradio +# gradio>=4.0.0 # 148 MB - Unsloth uses React + FastAPI, not Gradio huggingface-hub==0.36.2 structlog>=24.1.0 diceware diff --git a/studio/backend/routes/auth.py b/studio/backend/routes/auth.py index c61c1a16e4..d779c8784e 100644 --- a/studio/backend/routes/auth.py +++ b/studio/backend/routes/auth.py @@ -338,11 +338,11 @@ def _clear_login_bucket(key: tuple[str, str]) -> None: # so FastAPI runs it in the threadpool rather than blocking the event loop. @router.get("/identity") def identity(nonce: str, request: Request) -> dict: - """Challenge-response proof this is the real local Studio: caller sends a nonce, + """Challenge-response proof this is the real local Unsloth: caller sends a nonce, gets HMAC(install identity secret, nonce, connection address + port). Unauthenticated and side-effect free; a process that can't read the same-user secret can't forge a proof, and binding to the address/port the connection - landed on stops a squatter relaying a proof from the real Studio elsewhere.""" + landed on stops a squatter relaying a proof from the real Unsloth elsewhere.""" try: raw = base64.urlsafe_b64decode(nonce) except Exception: diff --git a/studio/backend/routes/data_recipe/jobs.py b/studio/backend/routes/data_recipe/jobs.py index 59714380da..e870e8855e 100644 --- a/studio/backend/routes/data_recipe/jobs.py +++ b/studio/backend/routes/data_recipe/jobs.py @@ -37,7 +37,7 @@ def _resolve_local_v1_endpoint(request: Request) -> str: Resolution order: 1. ``app.state.server_port`` (run.py, post-bind) - survives proxies/tunnels. - 2. ``request.scope["server"]`` - when Studio starts outside ``run_server``. + 2. ``request.scope["server"]`` - when Unsloth starts outside ``run_server``. 3. parsed ``request.base_url`` - last resort for test fixtures. """ port: Any = getattr(request.app.state, "server_port", None) diff --git a/studio/backend/routes/datasets.py b/studio/backend/routes/datasets.py index 46319ca2ba..5456080f34 100644 --- a/studio/backend/routes/datasets.py +++ b/studio/backend/routes/datasets.py @@ -485,7 +485,7 @@ async def upload_dataset( # Stream to disk in chunks to avoid holding the whole file in memory. The # route-level cap gives a clear training-dataset error and avoids leaving - # oversized partial files in the Studio uploads directory. + # oversized partial files in the Unsloth uploads directory. upload_limit_bytes = get_upload_limit_bytes() total_bytes = 0 upload_complete = False diff --git a/studio/backend/routes/inference.py b/studio/backend/routes/inference.py index 9299e26d56..3d527bf317 100644 --- a/studio/backend/routes/inference.py +++ b/studio/backend/routes/inference.py @@ -92,7 +92,7 @@ def _mlx_distributed_launch_detected() -> bool: def _install_httpcore_asyncgen_silencer() -> None: """Silence benign httpx/httpcore asyncgen GC noise on Python 3.13. - When Studio proxies a llama-server stream via httpx, the innermost + When Unsloth proxies a llama-server stream via httpx, the innermost ``HTTP11ConnectionByteStream.__aiter__`` async generator is finalised by the asyncgen GC hook on a task different from the one that opened it. Its ``aclose`` calls ``anyio.Lock.acquire`` → ``cancel_shielded_checkpoint``, @@ -229,14 +229,14 @@ def _friendly_upstream_error(text: str) -> str: parse grammar" / "failed to initialize samplers"). This surfaces to coding agents as a hard 400 on every tool-bearing turn. It is a llama-server limitation with some model/quant + tool-schema combinations, and recent llama.cpp builds handle the common - coding-agent tools, so point the user at updating Studio rather than the raw body. + coding-agent tools, so point the user at updating Unsloth rather than the raw body. """ lowered = text.lower() if "failed to parse grammar" in lowered or "failed to initialize samplers" in lowered: return ( "The model couldn't compile a tool-calling grammar for this request. This is a " "llama-server limitation with some model/quant and tool-schema combinations. " - "Update Studio (it installs the latest llama.cpp, which handles the common " + "Update Unsloth (it installs the latest llama.cpp, which handles the common " "coding-agent tools) or try a different GGUF model." ) return f"llama-server error: {text}" @@ -731,7 +731,7 @@ def _openai_passthrough_sse_line_terminal_state(raw_line: str) -> Optional[str]: Some llama-server builds can emit the logical final chunk (``finish_reason``) and optional usage chunk, then keep the HTTP stream open without sending the - OpenAI ``data: [DONE]`` sentinel. Classifying those chunks lets Studio close + OpenAI ``data: [DONE]`` sentinel. Classifying those chunks lets Unsloth close the client stream promptly while preserving an optional trailing usage chunk. """ if not raw_line.startswith("data:"): @@ -1786,7 +1786,7 @@ import numpy as np from datetime import date as _date router = APIRouter() -# Studio-only router (not mounted on /v1 OpenAI-compat). +# Unsloth-only router (not mounted on /v1 OpenAI-compat). studio_router = APIRouter() @@ -2108,9 +2108,9 @@ def _effective_enable_tools(payload) -> Optional[bool]: def _explicit_studio_tool_loop_requested(payload) -> bool: - """True when the request itself asks Studio to execute local tools. + """True when the request itself asks Unsloth to execute local tools. - Process-wide CLI policy can default Studio's tool loop on for ordinary chat, + Process-wide CLI policy can default Unsloth's tool loop on for ordinary chat, but it must not steal OpenAI-compatible client tools or response_format requests from the llama-server passthrough path. A policy of ``False`` (--disable-tools) vetoes even an explicit ``enable_tools: true`` ask. @@ -2122,7 +2122,7 @@ def _explicit_studio_tool_loop_requested(payload) -> bool: def _permission_mode_confirm(payload) -> bool: - """Effective confirm-gate intent for Studio's own local tool loop. + """Effective confirm-gate intent for Unsloth's own local tool loop. Honors the documented default that an unset permission_mode behaves as "ask". An explicit confirm_tool_calls (True or False) wins; explicit @@ -2144,7 +2144,7 @@ def _permission_mode_confirm(payload) -> bool: def _confirm_gate_needs_stream(payload) -> bool: - """Whether Studio's local tool-loop confirm gate still requires stream=true. + """Whether Unsloth's local tool-loop confirm gate still requires stream=true. The gate can only prompt while streaming, so a non-streaming request that will prompt must 400 up front. auto ("Approve for me") only prompts for a call the @@ -3143,7 +3143,7 @@ def _is_explicit_tensor_drop(request: LoadRequest) -> bool: """True only when the request explicitly selects a non-tensor --split-mode (e.g. layer/row/none), a deliberate departure from a preserved tensor->layer fallback. - A bare tensor_parallel field is NOT a drop: the Studio UI always sends it and echoes + A bare tensor_parallel field is NOT a drop: the Unsloth UI always sends it and echoes the /load response's resolved value back, so after a fallback every reload carries tensor_parallel=false even though the user never changed it -- treating that as a drop would collapse the preserved multi-GPU placement on the next ctx/settings reload. An @@ -4197,8 +4197,8 @@ async def _load_model_impl(request: LoadRequest, fastapi_request: Request, curre raise HTTPException( status_code = 400, detail = ( - "Studio does not support distributed MLX inference under " - "mlx.launch. Use `mlx.launch ... unsloth chat` or run Studio " + "Unsloth does not support distributed MLX inference under " + "mlx.launch. Use `mlx.launch ... unsloth chat` or run Unsloth " "without the distributed launcher." ), ) @@ -4288,7 +4288,7 @@ async def _load_model_impl(request: LoadRequest, fastapi_request: Request, curre # omits chat_template_override, so strip the inherited # --chat-template-file in that case too -- otherwise the stale # extra arg (appended last) shadows the bundled template while - # Studio reports the bundled template's capabilities. + # Unsloth reports the bundled template's capabilities. fields_set = getattr(request, "model_fields_set", set()) stripped = strip_shadowing_flags( llama_backend.extra_args, @@ -4727,7 +4727,7 @@ def _requires_trust_remote_code_for_model( model_identifier: str, hf_token: Optional[str] = None ) -> bool: """Whether loading this model would execute custom repo code, so the consent - dialog must run first. True if the Studio YAML default enables + dialog must run first. True if the Unsloth YAML default enables ``trust_remote_code`` OR the raw config declares an ``auto_map`` (Hub/local, config.json or tokenizer_config.json). Reads raw JSON only; never imports model code.""" @@ -5360,7 +5360,7 @@ async def confirm_tool_call( @studio_router.get("/monitor") async def get_api_monitor(current_subject: str = Depends(get_current_subject)): - """Return recent OpenAI-compatible API activity for Studio.""" + """Return recent OpenAI-compatible API activity for Unsloth.""" active_model = _monitor_active_model() active_requests = api_monitor.active_count(subject = current_subject) if active_requests: @@ -5548,7 +5548,7 @@ async def get_status(current_subject: str = Depends(get_current_subject)): _display_model_id = os.path.basename(_model_id) _inference_cfg = load_inference_config(_model_id) if _model_id else None _audio_type = getattr(llama_backend, "_audio_type", None) - # Don't surface Studio's auto-applied bundled family template (e.g. the + # Don't surface Unsloth's auto-applied bundled family template (e.g. the # gemma-4 override) as a user-authored override: the frontend adopts # status.chat_template_override as editable state and would otherwise # re-send it as an explicit override for a later, unrelated model. Only @@ -6173,7 +6173,7 @@ def _build_external_messages( metadata; strip it for providers that can't parse the unknown key. 2. Marked server-side builtin cards (`_server_tool: true` on a canonical builtin name, or a Gemini `native_part` payload) are - Studio-internal tool cards from a prior native Gemini turn; + Unsloth-internal tool cards from a prior native Gemini turn; forwarding them to OpenAI / Anthropic / custom OAI-compat gateways sends an orphan `tool_calls` entry (no matching tool declaration, often no matching `role="tool"` reply) that can be rejected. We @@ -6859,7 +6859,7 @@ async def openai_chat_completions( # is invalid and must not evict the resident model first. # # Enter the local-loop arm exactly when the passthrough router below would - # run Studio's own tool loop. That gate is `_tools_on or _mcp_allowed` + # run Unsloth's own tool loop. That gate is `_tools_on or _mcp_allowed` # (see the use_tools block): _effective_enable_tools (which lets a # process-wide --enable-tools policy force the loop on) plus mcp_enabled # honoring --disable-tools, and tool_choice="none" disabling it unless the @@ -6884,7 +6884,7 @@ async def openai_chat_completions( or bool(payload.openai_code_exec_container_id) or bool(payload.anthropic_code_exec_container_id) # A JSON-schema response_format is guided-decoding structured output the - # router forwards to the llama-server passthrough, not Studio's tool + # router forwards to the llama-server passthrough, not Unsloth's tool # loop, so a --enable-tools policy must not 400 it as a local-confirm # request under ask/auto. or bool(_extract_response_format(payload)) @@ -6961,7 +6961,7 @@ async def openai_chat_completions( using_gguf = llama_backend.is_loaded # OpenAI-SDK clients send ``chat_template_kwargs`` via ``extra_body``, which - # the SDK spreads into the request body at the top level. Studio's + # the SDK spreads into the request body at the top level. Unsloth's # ChatCompletionRequest has ``extra="allow"`` so pydantic stashes them in # ``model_extra``, but downstream generators consume the typed # ``payload.enable_thinking``. Lift ``enable_thinking`` from the extra-body @@ -7215,7 +7215,7 @@ async def openai_chat_completions( # ── Standard OpenAI function-calling pass-through (GGUF only) ──── # When a client (opencode / Claude Code via OpenAI compat / Cursor / - # Continue / ...) sends standard OpenAI `tools` without Studio's + # Continue / ...) sends standard OpenAI `tools` without Unsloth's # `enable_tools` shorthand, forward the request to llama-server # verbatim so structured `tool_calls` flow back to the client. This # branch runs BEFORE `_extract_content_parts` because that helper is @@ -7238,7 +7238,7 @@ async def openai_chat_completions( _has_tool_catalog = bool(payload.tools and len(payload.tools) > 0) _has_active_tool_catalog = _has_tool_catalog and payload.tool_choice != "none" _has_client_tool_contract = _has_active_tool_catalog or _has_tool_messages - # The Studio tool loop needs a tool-capable backend, so a request that asks + # The Unsloth tool loop needs a tool-capable backend, so a request that asks # for it on a backend that can't run it (DiffusionGemma forces supports_tools # off) must not steal client tools from the passthrough (#6851). _studio_tool_loop_requested = ( @@ -7434,7 +7434,7 @@ async def openai_chat_completions( use_tools = False if use_tools: - # permission_mode ask/auto require the confirm gate for Studio's own + # permission_mode ask/auto require the confirm gate for Unsloth's own # tool loop. The request validator self-enables confirm only for # request-level tool signals (enable_tools/enabled_tools/mcp_enabled); # when a CLI policy (--enable-tools) forces the loop on without those, @@ -8711,7 +8711,7 @@ async def openai_chat_completions( _sf_model_info = backend.models.get(backend.active_model_name, {}) _sf_tpl = (_sf_model_info.get("chat_template_info") or {}).get("template") # Named templates may expose native reasoning only in their ``tool_use`` - # branch. Use a truthy placeholder for Studio-managed tools, whose concrete + # branch. Use a truthy placeholder for Unsloth-managed tools, whose concrete # schemas are selected below, and the request schemas for client passthrough. _sf_server_tool_intent = bool( _effective_enable_tools(payload) or _explicit_studio_tool_loop_requested(payload) @@ -8790,7 +8790,7 @@ async def openai_chat_completions( _sf_use_tools = False if _sf_use_tools: - # permission_mode ask/auto require the confirm gate for Studio's own tool + # permission_mode ask/auto require the confirm gate for Unsloth's own tool # loop; when a CLI policy (--enable-tools) forces the loop on without a # request-level tool signal, derive confirm here so the mode still gates # the call (matching the GGUF path). off/full never prompt. @@ -12050,7 +12050,7 @@ def _anthropic_requested_studio_tools(tools: Optional[list]) -> set[str]: def _select_anthropic_server_tools( all_tools: list[dict], requested_studio_tools: set[str], enabled_tools: Optional[list[str]] ) -> list[dict]: - """Select Studio tools requested through Anthropic tools and extensions.""" + """Select Unsloth tools requested through Anthropic tools and extensions.""" if not requested_studio_tools and enabled_tools is None: return all_tools @@ -12289,7 +12289,7 @@ async def anthropic_messages( ), ) - # Reject an unsupported confirm-gated permission mode for Studio's own + # Reject an unsupported confirm-gated permission mode for Unsloth's own # ("server") Anthropic tools before the switch, mirroring the malformed- and # mixed-tool checks above. ask always wants a per-call pause this passthrough # cannot offer, so it 400s whenever server tools are selected. auto only needs @@ -12778,11 +12778,11 @@ async def _anthropic_tool_stream( ends_on_tool_use = True elif etype == "tool_end": tool_blocks_emitted += 1 - # A tool_end means Studio executed the tool server-side, so + # A tool_end means Unsloth executed the tool server-side, so # the response no longer ends on a pending client action. # Without this, a server tool that produces no trailing text # would be mislabeled stop_reason "tool_use", telling the - # client to run a tool Studio already ran. + # client to run a tool Unsloth already ran. ends_on_tool_use = False elif etype == "content" and event.get("text"): ends_on_tool_use = False @@ -13698,7 +13698,7 @@ def _openai_messages_for_passthrough(payload) -> list[dict]: structured ``tool_calls``. Content-parts images already in the list are left untouched. - When a client uses Studio's legacy ``image_base64`` top-level field, the + When a client uses Unsloth's legacy ``image_base64`` top-level field, the image is re-encoded to PNG (llama-server's stb_image has limited format support) and spliced into the last user message as an OpenAI ``image_url`` content part so vision + function-calling requests work transparently. @@ -13832,7 +13832,7 @@ def _build_openai_passthrough_body( ) -> dict: """Assemble the llama-server request body from a ChatCompletionRequest. - Only known OpenAI / llama-server fields are forwarded, so Studio-specific + Only known OpenAI / llama-server fields are forwarded, so Unsloth-specific extensions (``enable_tools``, ``enabled_tools``, ``session_id``, ...) never leak to the backend. """ @@ -14082,7 +14082,7 @@ async def _openai_passthrough_stream_admitted( admission_lease: LlamaAdmissionLease, tracker, ): - """Streaming client-side pass-through after Studio granted an upstream slot. + """Streaming client-side pass-through after Unsloth granted an upstream slot. Forwards the client's OpenAI function-calling request to llama-server and relays the SSE stream back with minimal normalization (reasoning-only diff --git a/studio/backend/routes/mcp_servers.py b/studio/backend/routes/mcp_servers.py index 71f0fd2874..dc018d163a 100644 --- a/studio/backend/routes/mcp_servers.py +++ b/studio/backend/routes/mcp_servers.py @@ -82,7 +82,7 @@ def _validate_url(url: str) -> str: if _looks_like_command(trimmed): detail = ( "Local commands aren't enabled on this server. To allow them, " - "set UNSLOTH_STUDIO_ALLOW_STDIO_MCP=1 and restart Studio, or use " + "set UNSLOTH_STUDIO_ALLOW_STDIO_MCP=1 and restart Unsloth, or use " "an http:// or https:// URL instead." ) else: diff --git a/studio/backend/routes/models.py b/studio/backend/routes/models.py index b8526c75e7..742ecde3ba 100644 --- a/studio/backend/routes/models.py +++ b/studio/backend/routes/models.py @@ -544,7 +544,7 @@ def _ollama_links_dir(ollama_dir: Path) -> Optional[Path]: """Return a writable directory for Ollama ``.gguf`` symlinks. Prefers ``/.studio_links/`` so links sit next to their - blobs; falls back to a per-ollama-dir namespace under Studio's cache + blobs; falls back to a per-ollama-dir namespace under Unsloth's cache when the models dir is read-only (common for system installs). """ from utils.paths.storage_roots import cache_root @@ -555,7 +555,7 @@ def _ollama_links_dir(ollama_dir: Path) -> Optional[Path]: return primary except OSError as e: logger.debug( - "Ollama dir %s not writable for .studio_links (%s); falling back to Studio cache", + "Ollama dir %s not writable for .studio_links (%s); falling back to Unsloth cache", ollama_dir, e, ) @@ -594,7 +594,7 @@ def _scan_ollama_dir(ollama_dir: Path, limit: Optional[int] = None) -> List[Loca model, keyed by a short hash of the manifest path, so ``detect_mmproj_file`` only sees that model's projector). Links are symlinks when possible, else hardlinks; the link dir is - ``.studio_links/`` when writable, else Studio's cache. + ``.studio_links/`` when writable, else Unsloth's cache. """ manifests_root = ollama_dir / "manifests" if not manifests_root.is_dir(): @@ -1194,7 +1194,7 @@ def _build_browse_allowlist( """Return the root directories the folder browser may walk. The same list seeds the sidebar suggestion chips, so chip targets are - always reachable. Roots: HOME, resolved HF cache dirs, Studio's + always reachable. Roots: HOME, resolved HF cache dirs, Unsloth's outputs/exports/studio root, registered scan folders, and well-known local-LLM dirs (LM Studio, Ollama, ``~/models``); each added only if it resolves to a real directory. @@ -1486,7 +1486,7 @@ def browse_folders( "Directory to list. If omitted, defaults to the current user's " "home directory. Tilde (`~`) and relative paths are expanded. " "Must resolve inside the allowlist of browseable roots (HOME, " - "HF cache, Studio dirs, registered scan folders, well-known " + "HF cache, Unsloth dirs, registered scan folders, well-known " "model dirs)." ), ), @@ -2251,15 +2251,15 @@ async def delete_finetuned_model( gguf_variant: Optional[str] = Body(None), current_subject: str = Depends(get_current_subject), ): - """Delete a Studio-trained or exported model from disk. + """Delete an Unsloth-trained or exported model from disk. - Only paths under Studio's outputs/exports roots are accepted. + Only paths under Unsloth's outputs/exports roots are accepted. Exported GGUF entries can delete one quant variant at a time. """ if source not in {"training", "exported"}: raise HTTPException( status_code = 400, - detail = "Only trained or exported Studio models can be deleted", + detail = "Only trained or exported Unsloth models can be deleted", ) if not model_path or not model_path.strip(): @@ -2291,14 +2291,14 @@ async def delete_finetuned_model( if not _is_path_under_lexically(delete_path, allowed_root): raise HTTPException( status_code = 400, - detail = "Model path is outside Studio storage", + detail = "Model path is outside Unsloth storage", ) if export_type == "gguf" and gguf_variant: target_path = delete_path.resolve() if not _is_path_under(target_path, allowed_root): raise HTTPException( status_code = 400, - detail = "Model path is outside Studio storage", + detail = "Model path is outside Unsloth storage", ) else: target_path = delete_path @@ -2311,7 +2311,7 @@ async def delete_finetuned_model( if should_check_resolved_path and not _is_path_under(target_path, allowed_root): raise HTTPException( status_code = 400, - detail = "Model path is outside Studio storage", + detail = "Model path is outside Unsloth storage", ) if target_path == allowed_root: raise HTTPException( @@ -3456,7 +3456,7 @@ _EXPORT_SIZE_CACHE: dict[str, tuple[int, int, str]] = {} def _is_sizable_local_path(model: str) -> bool: - """True only for local paths under a Studio data root. + """True only for local paths under an Unsloth data root. Containment is decided lexically (no filesystem access) before the path is touched, then the path is symlink-resolved and re-checked so a symlink diff --git a/studio/backend/routes/training.py b/studio/backend/routes/training.py index d53e8f2bbc..53b1c4d991 100644 --- a/studio/backend/routes/training.py +++ b/studio/backend/routes/training.py @@ -127,9 +127,9 @@ async def start_training( try: logger.info(f"Starting training job with model: {request.model_name}") - # When Studio is driven as an inference API (API-key auth), refuse to start + # When Unsloth is driven as an inference API (API-key auth), refuse to start # training while a request is in flight: training frees VRAM by unloading - # the chat model, which would kill the stream. The Studio UI (session auth) + # the chat model, which would kill the stream. The Unsloth UI (session auth) # still starts training and coexists/frees VRAM as before. (A mixed UI+API # session is not yet special-cased.) if via_api_key is True: @@ -139,7 +139,7 @@ async def start_training( status_code = 409, detail = ( "Cannot start training over the API while an inference request is in " - "progress. Wait for it to finish, or start training from the Studio UI." + "progress. Wait for it to finish, or start training from the Unsloth UI." ), ) diff --git a/studio/backend/run.py b/studio/backend/run.py index 4f105b53b1..398943cc2c 100644 --- a/studio/backend/run.py +++ b/studio/backend/run.py @@ -232,7 +232,7 @@ def _working_local_url(port: int) -> "str | None": def _localhost_ipv6_mismatch_url(bind_host: str, port: int) -> "str | None": """Return the IPv4 loopback URL when localhost won't reach 127.0.0.1. - Local Studio binds to 127.0.0.1. Where localhost resolves to IPv6 only (::1), + Local Unsloth binds to 127.0.0.1. Where localhost resolves to IPv6 only (::1), http://localhost: fails (or hits a different process on ::1) even though http://127.0.0.1: works. Return the IPv4 URL for the caller to surface. """ @@ -243,7 +243,7 @@ def _localhost_ipv6_mismatch_url(bind_host: str, port: int) -> "str | None": ipv4_url = f"http://127.0.0.1:{port}" - # Only warn once Studio is confirmed answering on IPv4 loopback. + # Only warn once Unsloth is confirmed answering on IPv4 loopback. if _working_local_url(port) != ipv4_url: return None @@ -265,7 +265,7 @@ def _localhost_ipv6_mismatch_url(bind_host: str, port: int) -> "str | None": if host == "::1": has_ipv6_loopback = True - # A connection to ::1 is NOT evidence Studio is reachable there: Studio binds + # A connection to ::1 is NOT evidence Unsloth is reachable there: Unsloth binds # 127.0.0.1 only, so anything on ::1 is a different process. Dual-stack # localhost is fine (browsers fall back to 127.0.0.1), so only the IPv6-only # case strands the user. @@ -287,7 +287,7 @@ def _stdout_color_ok() -> bool: def _print_localhost_ipv6_mismatch_warning(local_url: str, port: int) -> None: - """Warn that localhost points at ::1 while Studio is bound to 127.0.0.1.""" + """Warn that localhost points at ::1 while Unsloth is bound to 127.0.0.1.""" use_color = _stdout_color_ok() warn_c = "\033[38;5;215;1m" if use_color else "" reset = "\033[0m" if use_color else "" @@ -303,7 +303,7 @@ def _print_localhost_ipv6_mismatch_warning(local_url: str, port: int) -> None: def _verify_global_reachability(display_host: str, port: int) -> None: """Probe check-host.net to confirm display_host:port is reachable from the public internet. Synchronous so output lands between the banner URLs and the - stop hint. Bounded at ~15s; failures swallowed (verifier failing != Studio + stop hint. Bounded at ~15s; failures swallowed (verifier failing != Unsloth failing). Only meaningful for a wildcard bind.""" global _public_reachable # Reset to "unknown" each run; set True/False only when the probe decides. @@ -563,15 +563,15 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 " Cloudflare tunnel: ON. This Cloudflare URL is PUBLIC, and the " "raw port is also publicly reachable. --no-cloudflare disables " f"only the Cloudflare URL; bind {loopback_host} or close firewall " - "access to keep Studio private.", + "access to keep Unsloth private.", warn, ) else: _emit( " Cloudflare tunnel: ON. This is a PUBLIC internet URL: anyone " - "who has it can reach this Studio. Relaunch with --no-cloudflare " + "who has it can reach this Unsloth. Relaunch with --no-cloudflare " f"to disable the Cloudflare URL; bind {loopback_host} or close " - "firewall access to keep Studio private.", + "firewall access to keep Unsloth private.", warn, ) return @@ -580,12 +580,12 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 _emit( " Cloudflare tunnel: requested but failed to start. The raw port is " "still reachable from the public internet (see the reachability check " - "above): anyone who can reach it can access this Studio.", + "above): anyone who can reach it can access this Unsloth.", warn, ) elif _public_reachable is False: _emit( - " Cloudflare tunnel: requested but failed to start. Studio is reachable " + " Cloudflare tunnel: requested but failed to start. Unsloth is reachable " "on your local network only (no public link).", warn, ) @@ -593,7 +593,7 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 _emit( " Cloudflare tunnel: requested but failed to start. There is no " "Cloudflare public link. Raw port reachability was not verified; " - f"bind {loopback_host} or close firewall access to keep Studio private.", + f"bind {loopback_host} or close firewall access to keep Unsloth private.", warn, ) elif _cloudflare_flag: @@ -601,19 +601,19 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 _emit( " Cloudflare tunnel: OFF for this mode. The raw port is still " "reachable from the public internet (see the reachability check above): " - "anyone who can reach it can access this Studio.", + "anyone who can reach it can access this Unsloth.", warn, ) elif _public_reachable is False: _emit( - " Cloudflare tunnel: OFF for this mode. Studio is reachable on your " + " Cloudflare tunnel: OFF for this mode. Unsloth is reachable on your " "local network only (no public link)." ) else: _emit( " Cloudflare tunnel: OFF for this mode. There is no Cloudflare public " "link. Raw port reachability was not verified; " - f"bind {loopback_host} or close firewall access to keep Studio private.", + f"bind {loopback_host} or close firewall access to keep Unsloth private.", warn, ) elif _cloudflare_flag is False or _cloudflare_flag is None: @@ -624,12 +624,12 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 f" Cloudflare tunnel: OFF ({_reason}). The raw port is still " "reachable from the public internet (see the reachability check above): " "pass --cloudflare to also expose a public Cloudflare HTTPS link, or " - f"bind {loopback_host} to keep Studio private.", + f"bind {loopback_host} to keep Unsloth private.", warn, ) elif _public_reachable is False: _emit( - f" Cloudflare tunnel: OFF ({_reason}). Studio is reachable on your " + f" Cloudflare tunnel: OFF ({_reason}). Unsloth is reachable on your " "local network only. Pass --cloudflare to expose a public " "Cloudflare HTTPS link." ) @@ -638,7 +638,7 @@ def _print_cloudflare_line(secure: bool = False, loopback_host: str = "127.0.0.1 f" Cloudflare tunnel: OFF ({_reason}). There is no Cloudflare " "public link. Raw port reachability was not verified; pass --cloudflare " "to expose a public Cloudflare HTTPS link, or " - f"bind {loopback_host} or close firewall access to keep Studio private.", + f"bind {loopback_host} or close firewall access to keep Unsloth private.", warn, ) @@ -674,7 +674,7 @@ def _is_port_free(host: str, port: int) -> bool: For a ``0.0.0.0`` wildcard host, also check whether anything is listening on ``127.0.0.1`` (and ``::1`` when IPv6 exists): an SSH tunnel may hold loopback - while the wildcard bind succeeds, making Studio unreachable via ``localhost``. + while the wildcard bind succeeds, making Unsloth unreachable via ``localhost``. """ import socket @@ -1087,7 +1087,7 @@ def _terminal_password_gate( ) -> Tuple[bool, bool]: """Force a terminal password change before the public tunnel goes up. - When the tunnel is about to publish Studio and the seeded admin password was + When the tunnel is about to publish Unsloth and the seeded admin password was never changed, ask for a new one (masked, confirmed) before any public URL exists. The CLI normally does this before re-exec'ing the backend; this is the backstop for direct `python run.py` launches and older-CLI installs. @@ -1147,7 +1147,7 @@ def _terminal_password_gate( ) if not deadline_arms: print( - "Refusing to publish Studio on a public Cloudflare URL: the " + "Refusing to publish Unsloth on a public Cloudflare URL: the " "default admin password was never changed, no terminal is " "attached to change it here, and the bootstrap shutdown " "deadline does not apply to this launch (api-only, or " @@ -1163,11 +1163,11 @@ def _terminal_password_gate( # terminal-attached run / reset-password instead of reading it from disk. print( " WARNING: the default admin password is still active while " - "Studio is about to be published on a public Cloudflare URL, and " + "Unsloth is about to be published on a public Cloudflare URL, and " "no terminal is attached to change it here. The public page will " "NOT auto-fill the bootstrap credential. Set a new password by " "running `unsloth studio` locally with a terminal attached, or " - "`unsloth studio reset-password`. Studio shuts down after the " + "`unsloth studio reset-password`. Unsloth shuts down after the " "bootstrap deadline (UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT, default 1h) " "unless the password is changed.", file = sys.stderr, @@ -1222,7 +1222,7 @@ def _apply_supplied_password(password_value: "Optional[str]") -> None: _auth_storage.ensure_default_admin() if not _auth_storage.requires_password_change(_admin): print( - "Error: a Studio admin password is already set; --password only sets " + "Error: an Unsloth admin password is already set; --password only sets " "the initial password. Run `unsloth studio reset-password` first.", file = sys.stderr, flush = True, @@ -1337,7 +1337,7 @@ def run_server( pass # Persist a session log + native-crash stacks BEFORE importing main, so - # even import-time failures leave evidence on disk. Field report: Studio + # even import-time failures leave evidence on disk. Field report: Unsloth # "terminates without a warning" -- a native crash in the GPU runtime # kills the process with no Python traceback, and a desktop-shortcut # console closes before anything can be read. Console-only logging made @@ -1406,7 +1406,7 @@ def run_server( ensure_studio_directories() logger.info( - "Ensured Studio directories in %.1fms", + "Ensured Unsloth directories in %.1fms", (time.perf_counter() - boot_started) * 1000, ) @@ -1455,7 +1455,7 @@ def run_server( installer_bin = home / "unsloth_studio" / "bin" / "unsloth" tried_lines = "\n".join(f" - {p}" for p in attempted) or " (none)" raise SystemExit( - "[ERROR] Studio frontend build not found.\n" + "[ERROR] Unsloth frontend build not found.\n" f"Tried:\n{tried_lines}\n" "\n" "Likely cause: another 'unsloth' on PATH is shadowing the " @@ -1557,7 +1557,7 @@ def run_server( ) if not _pw_proceed: print( - "Not starting Studio; set a new admin password first, or launch " + "Not starting Unsloth; set a new admin password first, or launch " "without --secure/--cloudflare.", file = sys.stderr, flush = True, @@ -1695,7 +1695,7 @@ def run_server( logger = logger, ) logger.info( - "Studio will shut down in %ds unless the default admin password is changed.", + "Unsloth will shut down in %ds unless the default admin password is changed.", _bootstrap_timeout, ) except Exception as e: # best-effort: never block startup on the timeout @@ -1753,11 +1753,11 @@ def _build_arg_parser(): "--cloudflare", action = argparse.BooleanOptionalAction, default = None, - help = "Expose Studio on a PUBLIC internet URL via a free Cloudflare HTTPS " + help = "Expose Unsloth on a PUBLIC internet URL via a free Cloudflare HTTPS " "tunnel, for non-api-only wildcard binds (0.0.0.0 or ::). Off by default; " "pass --cloudflare to enable it (--secure implies it), --no-cloudflare to " "force it off. It does not change a raw wildcard bind. If the admin " - "password was never changed, Studio asks for a new one in the terminal " + "password was never changed, Unsloth asks for a new one in the terminal " "before publishing the URL.", ) parser.add_argument( @@ -1767,7 +1767,7 @@ def _build_arg_parser(): help = "Expose ONLY a Cloudflare HTTPS link: bind localhost and fail closed " "if the tunnel can't start. Without it, --no-secure also serves the raw " "0.0.0.0 port, which is reachable from anywhere on the network. If the " - "admin password was never changed, Studio asks for a new one in the " + "admin password was never changed, Unsloth asks for a new one in the " "terminal before publishing the URL.", ) # Back-compat: accept --not-secure as a hidden alias for --no-secure. diff --git a/studio/backend/startup_banner.py b/studio/backend/startup_banner.py index ea951a4325..9ec7a4f91c 100644 --- a/studio/backend/startup_banner.py +++ b/studio/backend/startup_banner.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Terminal banner for Studio startup. +"""Terminal banner for Unsloth startup. Stdlib only -- safe to import without the rest of the backend. """ @@ -172,7 +172,7 @@ def print_studio_access_banner( secondary, ), style( - " Only on trusted networks -- anyone who reaches this machine can use Studio.", + " Only on trusted networks -- anyone who reaches this machine can use Unsloth.", secondary, ), ] diff --git a/studio/backend/tests/conftest.py b/studio/backend/tests/conftest.py index b0b9ee309c..c2216104a3 100644 --- a/studio/backend/tests/conftest.py +++ b/studio/backend/tests/conftest.py @@ -101,13 +101,13 @@ def studio_server(request): @pytest.fixture def base_url(studio_server): - """Base URL for the e2e Studio server (from ``studio_server``).""" + """Base URL for the e2e Unsloth server (from ``studio_server``).""" return studio_server[0] @pytest.fixture def api_key(studio_server): - """API key for the e2e Studio server (from ``studio_server``).""" + """API key for the e2e Unsloth server (from ``studio_server``).""" return studio_server[1] diff --git a/studio/backend/tests/test_amd_apu_unified_memory.py b/studio/backend/tests/test_amd_apu_unified_memory.py index 4df9e85b30..9fd8260bf2 100644 --- a/studio/backend/tests/test_amd_apu_unified_memory.py +++ b/studio/backend/tests/test_amd_apu_unified_memory.py @@ -91,7 +91,7 @@ class TestApuRamShortfall: """On a unified-memory APU the weights load into system RAM, so a model larger than available RAM (the field case: a 64.6 GB GGUF on a WSL VM capped well below the ROCm-reported APU budget) must be refused before spawning, - not left to OOM-kill the Studio process.""" + not left to OOM-kill the Unsloth process.""" def test_field_case_wsl_cap_refuses(self): # 64.6 GB weights, ~46 GB available (WSL VM): refuse with guidance. diff --git a/studio/backend/tests/test_anthropic_compaction.py b/studio/backend/tests/test_anthropic_compaction.py index 1528eebe8b..acc0acc2e0 100644 --- a/studio/backend/tests/test_anthropic_compaction.py +++ b/studio/backend/tests/test_anthropic_compaction.py @@ -4,7 +4,7 @@ """Unit tests for Anthropic server-side context compaction wiring. Compaction is a beta (header ``compact-2026-01-12``) gated to Opus 4.6/4.7, -Sonnet 4.6, and Mythos preview. When enabled, Studio attaches +Sonnet 4.6, and Mythos preview. When enabled, Unsloth attaches ``context_management.edits[{type:"compact_20260112", trigger:{type:"input_tokens", value:N}}]``; the 50k-token minimum is clamped up so the request doesn't 400. diff --git a/studio/backend/tests/test_anthropic_fast_mode_edge.py b/studio/backend/tests/test_anthropic_fast_mode_edge.py index dd69d77590..03f5d1c0eb 100644 --- a/studio/backend/tests/test_anthropic_fast_mode_edge.py +++ b/studio/backend/tests/test_anthropic_fast_mode_edge.py @@ -330,7 +330,7 @@ def test_refusal_chunk_is_proper_openai_delta_shape(monkeypatch): def test_refusal_tool_event_chunk_shape(monkeypatch): - """Drop signal rides a Studio `_toolEvent` envelope (delta={}, + """Drop signal rides an Unsloth `_toolEvent` envelope (delta={}, finish_reason=null); the frontend latches on `_toolEvent.type == "anthropic_refusal"`.""" _, lines = _capture(monkeypatch, sse = _refusal_sse(), model = "claude-opus-4-7") @@ -409,7 +409,7 @@ def _fast_speed_sse(model: str = "claude-opus-4-7", speed: str = "fast") -> byte def test_usage_speed_propagates_to_final_usage_chunk_fast(monkeypatch): - """``usage.speed == "fast"`` from upstream must reach the Studio usage chunk.""" + """``usage.speed == "fast"`` from upstream must reach the Unsloth usage chunk.""" _, lines = _capture(monkeypatch, sse = _fast_speed_sse(speed = "fast")) usage_lines = [l for l in lines if l.startswith("data: ") and '"usage"' in l] assert usage_lines, lines @@ -428,7 +428,7 @@ def test_usage_speed_propagates_to_final_usage_chunk_standard(monkeypatch): def test_usage_speed_absent_when_anthropic_does_not_report(monkeypatch): - """Studio must not invent ``usage.speed`` when upstream omits it.""" + """Unsloth must not invent ``usage.speed`` when upstream omits it.""" _, lines = _capture(monkeypatch) parsed = [ json.loads(l[len("data: ") :]) for l in lines if l.startswith("data: ") and '"usage"' in l diff --git a/studio/backend/tests/test_anthropic_messages.py b/studio/backend/tests/test_anthropic_messages.py index 3b0ea37372..9ccc3f44dd 100644 --- a/studio/backend/tests/test_anthropic_messages.py +++ b/studio/backend/tests/test_anthropic_messages.py @@ -1418,7 +1418,7 @@ class TestNormalizeAnthropicOpenAIImages: # ===================================================================== -# Studio-tool alias detection (/v1/messages tool routing) +# Unsloth-tool alias detection (/v1/messages tool routing) # ===================================================================== @@ -1436,7 +1436,7 @@ class TestAnthropicRequestedStudioTools: def test_client_tool_named_python_is_not_misclassified(self): # input_schema is the client-tool discriminator; its presence must - # prevent the name from being treated as a Studio alias. + # prevent the name from being treated as an Unsloth alias. tools = [ { "name": "python", @@ -1747,9 +1747,9 @@ class TestAnthropicMessagesToolRouting: assert "name" in exc.value.detail def test_alias_named_client_tool_without_schema_rejected_with_400(self, monkeypatch): - # Regression: a typo'd client tool whose name collides with a Studio + # Regression: a typo'd client tool whose name collides with an Unsloth # alias (e.g. a custom "python" tool missing input_schema) must - # surface a 400, not silently switch into Studio's built-in python + # surface a 400, not silently switch into Unsloth's built-in python # execution. _mock_backend(monkeypatch) payload = _basic_payload(tools = [{"name": "python"}]) @@ -1770,7 +1770,7 @@ class TestAnthropicMessagesToolRouting: def test_disable_tools_policy_overrides_server_tool_alias(self, monkeypatch): # CLI `unsloth run --disable-tools` sets policy=False. A request with - # a Studio server-tool alias must NOT enter the agentic loop then. + # an Unsloth server-tool alias must NOT enter the agentic loop then. backend = _mock_backend(monkeypatch) set_tool_policy(False) payload = _basic_payload( diff --git a/studio/backend/tests/test_compute_buffer.py b/studio/backend/tests/test_compute_buffer.py index 8408f8203d..3e95acc98d 100644 --- a/studio/backend/tests/test_compute_buffer.py +++ b/studio/backend/tests/test_compute_buffer.py @@ -152,7 +152,7 @@ class TestFallback: class TestParallel1Default: - """At Studio's default --parallel 1 the buffer is negligible in pipeline.""" + """At Unsloth's default --parallel 1 the buffer is negligible in pipeline.""" def test_default_n_parallel(self): est = _backend()._estimate_compute_buffer_bytes() / MIB diff --git a/studio/backend/tests/test_cpu_threads.py b/studio/backend/tests/test_cpu_threads.py index 2930c9f081..9d8795b6c0 100644 --- a/studio/backend/tests/test_cpu_threads.py +++ b/studio/backend/tests/test_cpu_threads.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for Studio's early CPU thread-pool configuration.""" +"""Tests for Unsloth's early CPU thread-pool configuration.""" import ast import os @@ -30,7 +30,7 @@ def test_cpu_thread_cap_seeds_native_pool_limits(): } -# Explicit per-library values win over the Studio knob via setdefault. +# Explicit per-library values win over the Unsloth knob via setdefault. def test_cpu_thread_cap_preserves_runtime_specific_override(): env = {"UNSLOTH_CPU_THREADS": "4", "OMP_NUM_THREADS": "2"} diff --git a/studio/backend/tests/test_frontend_resolution.py b/studio/backend/tests/test_frontend_resolution.py index c3e0524a30..7ac2717aae 100644 --- a/studio/backend/tests/test_frontend_resolution.py +++ b/studio/backend/tests/test_frontend_resolution.py @@ -218,7 +218,7 @@ def test_systemexit_message_contains_actionable_fixes(tmp_path, monkeypatch): installer_bin = home / "unsloth_studio" / "bin" / "unsloth" tried_lines = "\n".join(f" - {p}" for p in attempted) message = ( - "[ERROR] Studio frontend build not found.\n" + "[ERROR] Unsloth frontend build not found.\n" f"Tried:\n{tried_lines}\n" "\n" "Likely cause: another 'unsloth' on PATH is shadowing the " diff --git a/studio/backend/tests/test_gemini_provider.py b/studio/backend/tests/test_gemini_provider.py index 85ceb04d27..c6ffa798d0 100644 --- a/studio/backend/tests/test_gemini_provider.py +++ b/studio/backend/tests/test_gemini_provider.py @@ -768,7 +768,7 @@ def test_cached_content_pass_through(monkeypatch): def test_boolean_caching_does_not_set_cached_content(monkeypatch): - """Studio's existing True/False signals shouldn't fabricate a cache id.""" + """Unsloth's existing True/False signals shouldn't fabricate a cache id.""" captured = _capture_body(monkeypatch, enable_prompt_caching = True) assert "cachedContent" not in captured["body"] @@ -2613,7 +2613,7 @@ def test_gemini_native_skips_orphan_function_response_for_native_part_replay(mon def test_gemini_native_part_falls_back_to_args_google(monkeypatch): """Round 27: a direct OpenAI-compat API caller (or imported third-party - thread) cannot use Studio's non-standard `tool_calls[].extra_content` + thread) cannot use Unsloth's non-standard `tool_calls[].extra_content` field, so the native_part payload round-trips through `function.arguments` as `{"google": {"native_part": {...}}}`. The synthetic-builtin detector recognizes that location, but the replay branch was only reading from diff --git a/studio/backend/tests/test_gemma4_chat_template_override.py b/studio/backend/tests/test_gemma4_chat_template_override.py index f726741aa5..9fb24a4cf6 100644 --- a/studio/backend/tests/test_gemma4_chat_template_override.py +++ b/studio/backend/tests/test_gemma4_chat_template_override.py @@ -3,7 +3,7 @@ """Auto-override of the chat template for ``unsloth/gemma-4-*-GGUF``. -Studio ships a bundled ``gemma-4.jinja`` (PR #118 based, ``preserve_thinking`` +Unsloth ships a bundled ``gemma-4.jinja`` (PR #118 based, ``preserve_thinking`` defaulted off) and applies it to gemma-4 GGUF loads via the existing ``chat_template_override`` -> ``--chat-template-file`` path, so users do not need to re-download quants. Pins the family matcher, the resolver precedence, the diff --git a/studio/backend/tests/test_hf_xet_fallback.py b/studio/backend/tests/test_hf_xet_fallback.py index 2fff744b64..48aff29659 100644 --- a/studio/backend/tests/test_hf_xet_fallback.py +++ b/studio/backend/tests/test_hf_xet_fallback.py @@ -1,10 +1,10 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for the Studio shim over the shared unsloth_zoo Xet -> HTTP fallback. +"""Tests for the Unsloth shim over the shared unsloth_zoo Xet -> HTTP fallback. The transport-policy matrix is tested once in unsloth_zoo; here we assert only the -Studio seam: re-exporting the shared API and injecting the marker-aware +Unsloth seam: re-exporting the shared API and injecting the marker-aware prepare_cache_for_transport on the HTTP retry. CPU-only, no network, no real subprocess. """ @@ -69,7 +69,7 @@ def test_child_should_disable_xet_truth_table(): def test_shim_injects_studio_prepare_on_http_retry(monkeypatch): - """A Xet stall retries over HTTP and the shim runs Studio's marker-aware + """A Xet stall retries over HTTP and the shim runs Unsloth's marker-aware ``prepare_cache_for_transport(..., 'http')`` before the retry.""" _requires_shared() for var in ("UNSLOTH_DISABLE_XET", "UNSLOTH_STABLE_DOWNLOADS", "HF_HUB_DISABLE_XET"): @@ -107,11 +107,11 @@ def test_shim_injects_studio_prepare_on_http_retry(monkeypatch): out = xf.hf_hub_download_with_xet_fallback(DL_REPO, FILE, None) assert out == "/cache/model.gguf" assert seen_disable_xet == [False, True] # Xet first, then HTTP - assert prepared == [("model", DL_REPO, "http")], "shim must run Studio's marker-aware prep" + assert prepared == [("model", DL_REPO, "http")], "shim must run Unsloth's marker-aware prep" def test_shim_snapshot_injects_studio_prepare(monkeypatch): - """The snapshot wrapper forwards Studio's marker-aware prep, like the file wrapper.""" + """The snapshot wrapper forwards Unsloth's marker-aware prep, like the file wrapper.""" captured = {} def fake_snapshot(repo_id, **kwargs): @@ -127,7 +127,7 @@ def test_shim_snapshot_injects_studio_prepare(monkeypatch): def test_degrades_gracefully_without_shared_helper(monkeypatch): - """On an older unsloth_zoo lacking the shared helper, the shim still imports (Studio + """On an older unsloth_zoo lacking the shared helper, the shim still imports (Unsloth boots) and exposes stub API doing plain HF downloads with the watchdog disabled.""" import importlib @@ -206,7 +206,7 @@ def test_degrades_gracefully_without_shared_helper(monkeypatch): def test_degrades_when_unsloth_zoo_entirely_absent(): """When unsloth_zoo is absent entirely, the import raises ModuleNotFoundError(name='unsloth_zoo') (top-level package). Guard that the shim still - degrades and does not re-raise, breaking every Studio import that pulls it in.""" + degrades and does not re-raise, breaking every Unsloth import that pulls it in.""" import importlib class _BlockZoo: @@ -248,7 +248,7 @@ def test_degrades_when_unsloth_zoo_entirely_absent(): def test_degrades_when_shared_helper_import_raises_importerror(): """unsloth_zoo can be installed yet fail to import when torch is missing (llama.cpp/GGUF-only - Studio), raising ImportError not ModuleNotFoundError. The shim must degrade for that too.""" + Unsloth), raising ImportError not ModuleNotFoundError. The shim must degrade for that too.""" import importlib class _BlockWithImportError: @@ -329,7 +329,7 @@ def test_retries_under_light_gpu_init_when_import_fails(monkeypatch): # with it set); accessing DownloadStallError drives it via __getattr__. stall_error = degraded.DownloadStallError assert seen_env == [None, "1"], seen_env - # Both attempts raised -> Studio still boots in degraded mode. + # Both attempts raised -> Unsloth still boots in degraded mode. assert issubclass(stall_error, RuntimeError) # The env override must not leak past the load. assert os.environ.get("UNSLOTH_ZOO_DISABLE_GPU_INIT") is None diff --git a/studio/backend/tests/test_identity.py b/studio/backend/tests/test_identity.py index 1e84ddef35..712348f7ca 100644 --- a/studio/backend/tests/test_identity.py +++ b/studio/backend/tests/test_identity.py @@ -3,7 +3,7 @@ """Tests for the server identity handshake (`GET /api/auth/identity`). -The endpoint lets a client confirm an endpoint is really this Studio install +The endpoint lets a client confirm an endpoint is really this Unsloth install before sending it a credential: the client sends a random nonce and checks the returned HMAC against one computed from the install identity secret. A process that cannot read this same-user secret cannot forge a matching proof. diff --git a/studio/backend/tests/test_index_bootstrap_origin_extra.py b/studio/backend/tests/test_index_bootstrap_origin_extra.py index feda88c14c..e1c52a653e 100644 --- a/studio/backend/tests/test_index_bootstrap_origin_extra.py +++ b/studio/backend/tests/test_index_bootstrap_origin_extra.py @@ -26,7 +26,7 @@ def _build_request( def test_is_same_origin_request_ipv6_loopback_same_origin(): - """Studio supports ``-H ::1`` binds; netloc is ``[::1]:8902``. Bare + """Unsloth supports ``-H ::1`` binds; netloc is ``[::1]:8902``. Bare ``partition(":")`` mis-parses the bracketed form and would refuse the bootstrap on legitimate same-origin navigation. """ diff --git a/studio/backend/tests/test_llama_cpp_context_fit.py b/studio/backend/tests/test_llama_cpp_context_fit.py index d3a10df8ca..2a4f6d19d2 100644 --- a/studio/backend/tests/test_llama_cpp_context_fit.py +++ b/studio/backend/tests/test_llama_cpp_context_fit.py @@ -567,7 +567,7 @@ class TestClassifyGpuOffload: assert inst._classify_gpu_offload(False, []) is None def test_user_did_not_intend_gpu_returns_none(self): - # Studio called start_llama_server without expecting GPU; don't warn. + # Unsloth called start_llama_server without expecting GPU; don't warn. inst = self._backend( [ "load_tensors: CPU_Mapped model buffer size = 21000.0 MiB", diff --git a/studio/backend/tests/test_llama_cpp_mmproj_fallback.py b/studio/backend/tests/test_llama_cpp_mmproj_fallback.py index 04d4aac9e1..049058e511 100644 --- a/studio/backend/tests/test_llama_cpp_mmproj_fallback.py +++ b/studio/backend/tests/test_llama_cpp_mmproj_fallback.py @@ -222,7 +222,7 @@ class TestFlashAttnOff: assert _flash_off(["llama-server", "-fa=on"]) == ["llama-server", "-fa=off"] def test_flips_every_occurrence_last_wins(self): - # extra_args can re-enable FA after Studio's flag; llama.cpp is last-wins, + # extra_args can re-enable FA after Unsloth's flag; llama.cpp is last-wins, # so one leftover 'on' would re-crash the retry. Every enable must flip. cmd = ["llama-server", "--flash-attn", "on", "--mmproj", "/p", "--flash-attn", "on"] out = _flash_off(cmd) @@ -234,7 +234,7 @@ class TestFlashAttnOff: assert _flash_off(["llama-server", "--flash-attn=off"]) is None def test_none_when_user_off_wins_last(self): - # User appended 'off' after Studio's 'on'; effective (last-wins) is off, + # User appended 'off' after Unsloth's 'on'; effective (last-wins) is off, # so there is nothing to retry. assert _flash_off(["llama-server", "--flash-attn", "on", "--flash-attn", "off"]) is None diff --git a/studio/backend/tests/test_llama_cpp_mtp_detection.py b/studio/backend/tests/test_llama_cpp_mtp_detection.py index 3f9d2a8f50..8fe04c0e39 100644 --- a/studio/backend/tests/test_llama_cpp_mtp_detection.py +++ b/studio/backend/tests/test_llama_cpp_mtp_detection.py @@ -1014,7 +1014,7 @@ def test_already_in_target_state_2b_falls_back_to_ngram_below_threshold(monkeypa ) -# usage backfill from timings (Studio UI t/s widget fix). +# usage backfill from timings (Unsloth UI t/s widget fix). def test_backfill_usage_from_timings_fills_when_completion_tokens_zero(): @@ -1606,7 +1606,7 @@ def test_reload_forced_mtp_bounces_auto_mla(): ) -# ── Full named-repo resolver matrix (the shipping Studio families) ───── +# ── Full named-repo resolver matrix (the shipping Unsloth families) ───── # # Locks auto / off / forced-mtp routing for every Qwen3.5 (MTP + plain) and # gemma-4 (regular + QAT) GGUF repo, including the giant MoEs that stay diff --git a/studio/backend/tests/test_llama_cpp_no_context_shift.py b/studio/backend/tests/test_llama_cpp_no_context_shift.py index 10b1dc7ff6..f320d29a02 100644 --- a/studio/backend/tests/test_llama_cpp_no_context_shift.py +++ b/studio/backend/tests/test_llama_cpp_no_context_shift.py @@ -5,7 +5,7 @@ With llama-server's default context-shift behavior, the UI cannot tell the user the KV cache was rotated -- earlier turns silently vanish from the conversation. -The Studio backend always passes ``--no-context-shift`` so the server returns a +The Unsloth backend always passes ``--no-context-shift`` so the server returns a clean error instead, and the chat adapter can point the user at the ``Context Length`` input in the settings panel. diff --git a/studio/backend/tests/test_llama_cpp_props_readback.py b/studio/backend/tests/test_llama_cpp_props_readback.py index 316956325f..488645ee5a 100644 --- a/studio/backend/tests/test_llama_cpp_props_readback.py +++ b/studio/backend/tests/test_llama_cpp_props_readback.py @@ -4,7 +4,7 @@ """Tests for the post-launch /props context readback. llama-server's memory-fit step or --parallel slot split can allocate less -context than the requested -c while Studio keeps advertising the requested +context than the requested -c while Unsloth keeps advertising the requested value; clients sized to it then die on exceed_context_size_error 400s. ``_reconcile_effective_ctx_with_server`` must adopt the server's real ``default_generation_settings.n_ctx`` whenever it is smaller. @@ -223,7 +223,7 @@ _CAPS_NONE = {"supports_kv_unified": False, "supports_fit_ctx": False} def test_kv_unified_added_for_multi_slot(): """Explicit --parallel N disables llama-server's auto-slots kv-unified - default, splitting -c into per-slot windows of -c/N; Studio must restore + default, splitting -c into per-slot windows of -c/N; Unsloth must restore the shared pool so one request can use the full advertised context.""" flags = LlamaCppBackend._ctx_integrity_flags(4, False, 98304, 98304, _CAPS_ALL) assert "--kv-unified" in flags diff --git a/studio/backend/tests/test_llama_cpp_tool_loop.py b/studio/backend/tests/test_llama_cpp_tool_loop.py index bd2c008589..e99e227d40 100644 --- a/studio/backend/tests/test_llama_cpp_tool_loop.py +++ b/studio/backend/tests/test_llama_cpp_tool_loop.py @@ -122,7 +122,7 @@ def _structured_tool_call(tool_name: str, arguments: dict, call_id: str) -> list def test_structured_tool_call_after_visible_preface_is_executed(monkeypatch): """llama-server may emit content first and then native delta.tool_calls. - Studio must not drop that tool call after it has streamed the preface. + Unsloth must not drop that tool call after it has streamed the preface. """ tool_call_id = "call_render_late" diff --git a/studio/backend/tests/test_llama_cpp_wait_for_health.py b/studio/backend/tests/test_llama_cpp_wait_for_health.py index 82c5b4931a..423c3dd009 100644 --- a/studio/backend/tests/test_llama_cpp_wait_for_health.py +++ b/studio/backend/tests/test_llama_cpp_wait_for_health.py @@ -224,7 +224,7 @@ class TestRetryLogFilenameUnique: class TestFitOffRetryEligible: """Gate for the one-shot --fit off startup-crash retry. - Retry only when Studio's own VRAM math placed the model and nothing + Retry only when Unsloth's own VRAM math placed the model and nothing on the command line chose the fit mode explicitly.""" def test_eligible_for_plain_ngl_launch(self): diff --git a/studio/backend/tests/test_llama_cpp_wait_for_vram_settle.py b/studio/backend/tests/test_llama_cpp_wait_for_vram_settle.py index d0213f6079..b28df7ec3f 100644 --- a/studio/backend/tests/test_llama_cpp_wait_for_vram_settle.py +++ b/studio/backend/tests/test_llama_cpp_wait_for_vram_settle.py @@ -346,7 +346,7 @@ def test_helper_is_static_method_callable_off_class(): def test_kill_orphaned_servers_returns_count(): """The reaper reports how many owned orphans it killed, so __init__ can - arm the settle wait. Only Studio-owned llama-server procs count.""" + arm the settle wait. Only Unsloth-owned llama-server procs count.""" import os mypid = os.getpid() @@ -376,7 +376,7 @@ def test_kill_orphaned_servers_returns_count(): patch.object(LlamaCppBackend, "_pid_parent_is_alive", staticmethod(lambda pid: False)), ): n = LlamaCppBackend._kill_orphaned_servers() - assert n == 1, "only the Studio-owned orphan should be counted" + assert n == 1, "only the Unsloth-owned orphan should be counted" assert killed == [mypid + 1] # No owned orphans -> zero, so __init__ leaves the cold-start sentinel. @@ -392,8 +392,8 @@ def test_kill_orphaned_servers_returns_count(): def test_kill_orphaned_servers_spares_live_parent(): - """A Studio-owned llama-server whose parent is still running is not an - orphan (a live Studio or the user's shell owns it) and must never be + """An Unsloth-owned llama-server whose parent is still running is not an + orphan (a live Unsloth or the user's shell owns it) and must never be killed; only the true orphan (parent gone) is reaped.""" import os @@ -548,7 +548,7 @@ def test_record_then_reap_round_trip_identity_matches(tmp_path): def test_reap_recorded_pid_spares_live_server(tmp_path): - """A recorded server whose parent is still alive (the running Studio) is NEVER + """A recorded server whose parent is still alive (the running Unsloth) is NEVER reaped, and its pidfile is kept. This is the finding-3 guard: a helper backend constructed in-process must not kill the active chat server. Uses the REAL _pid_parent_is_alive (the child's parent is this live test process).""" diff --git a/studio/backend/tests/test_llama_cpp_windows_nvidia_path.py b/studio/backend/tests/test_llama_cpp_windows_nvidia_path.py index 957de4bad6..489d9eb8d1 100644 --- a/studio/backend/tests/test_llama_cpp_windows_nvidia_path.py +++ b/studio/backend/tests/test_llama_cpp_windows_nvidia_path.py @@ -3,7 +3,7 @@ """Tests for the Windows pip-nvidia DLL dir resolver. -Studio installs torch with bundled CUDA wheels (nvidia-cuda-runtime-cu13, +Unsloth installs torch with bundled CUDA wheels (nvidia-cuda-runtime-cu13, nvidia-cublas-cu13, etc.) and the prebuilt llama-server.exe must find those DLLs at runtime to load CUDA. Mirrors the Linux LD_LIBRARY_PATH block. See unslothai/unsloth#5106. diff --git a/studio/backend/tests/test_llama_server_args.py b/studio/backend/tests/test_llama_server_args.py index deeb228026..ba52afad1c 100644 --- a/studio/backend/tests/test_llama_server_args.py +++ b/studio/backend/tests/test_llama_server_args.py @@ -75,7 +75,7 @@ validate_extra_args = _lsa.validate_extra_args # Reasoning controls ["--reasoning-format", "deepseek"], ["-rea", "auto"], - # Soft-managed: user flags last-wins over Studio's auto-set version. + # Soft-managed: user flags last-wins over Unsloth's auto-set version. # --parallel / -np / --n-parallel are hard-denied (KV-cache + slot # count would desync); use `unsloth studio run --parallel N` instead. ["-c", "131072"], @@ -150,7 +150,7 @@ def test_non_flag_token_passes_through(): "--mmproj", "-mmu", "--mmproj-url", - # Networking (Studio binds + proxies) + # Networking (Unsloth binds + proxies) "--host", "--port", "--path", @@ -176,12 +176,12 @@ def test_non_flag_token_passes_through(): "--models-autoload", "--no-models-autoload", # Server-mode flips: --embedding / --rerank restrict llama-server to - # those endpoints and break Studio's chat hop. + # those endpoints and break Unsloth's chat hop. "--embedding", "--embeddings", "--rerank", "--reranking", - # llama-server's own --tools clashes with Studio's tool policy. + # llama-server's own --tools clashes with Unsloth's tool policy. "--tools", ], ) @@ -194,7 +194,7 @@ def test_denylist_rejects_all_aliases(denied): "args,offending", [ # Pass-through --parallel would last-wins-override the real slot - # count while Studio's KV-cache fit + llama_parallel_slots stay at + # count while Unsloth's KV-cache fit + llama_parallel_slots stay at # the typer value -- plan vs. process disagree. (["--parallel", "8"], "--parallel"), (["--parallel=8"], "--parallel"), @@ -656,7 +656,7 @@ def test_extra_args_disable_mmproj_last_wins(): def test_strip_shadowing_flags_drops_model_draft_with_spec(): - # --model-draft (and aliases) are Studio-managed since the separate + # --model-draft (and aliases) are Unsloth-managed since the separate # MTP drafter support: an inherited copy must not last-wins-override # the auto-detected drafter. out = strip_shadowing_flags( @@ -681,7 +681,7 @@ def test_strip_shadowing_flags_drops_model_draft_with_spec(): ) def test_strip_shadowing_flags_drops_hf_drafter_selectors_with_spec(selector): # HF drafter selectors must reset on inherit like local --model-draft, or a - # stale inherited HF drafter last-wins over Studio's re-derived spec choice. + # stale inherited HF drafter last-wins over Unsloth's re-derived spec choice. out = strip_shadowing_flags( selector + ["--top-k", "20"], strip_context = False, @@ -769,7 +769,7 @@ def test_strip_split_mode_only_preserves_none_and_empty(): def test_strip_shadowing_flags_drops_tensor_split_with_split_mode(): # --tensor-split is coupled to the split mode: stripped together so a stale - # ratio can't override Studio's computed tensor split. Other flags survive. + # ratio can't override Unsloth's computed tensor split. Other flags survive. out = strip_shadowing_flags( ["--split-mode", "row", "--tensor-split", "1,1", "--top-k", "20"], strip_context = False, diff --git a/studio/backend/tests/test_local_llama_cpp_link.py b/studio/backend/tests/test_local_llama_cpp_link.py index c78c029d91..6b44f61972 100644 --- a/studio/backend/tests/test_local_llama_cpp_link.py +++ b/studio/backend/tests/test_local_llama_cpp_link.py @@ -4,7 +4,7 @@ """Behavioral tests for the --with-llama-cpp-dir 'unmanaged local link' contract. When the canonical llama.cpp dir is a symlink (POSIX) / junction (Windows) to a -user's own checkout, Studio must treat it as externally managed: +user's own checkout, Unsloth must treat it as externally managed: - the in-app updater must not offer or apply a prebuilt over the link - orphan cleanup must not kill a llama-server the user launched from that tree @@ -67,7 +67,7 @@ def test_active_install_is_local_link(tmp_path: Path) -> None: binary = str(link / _server_subpath()) assert u._active_install_is_local_link(binary) is True - # A plain (non-link) llama.cpp dir is Studio-managed, not a local link. + # A plain (non-link) llama.cpp dir is Unsloth-managed, not a local link. plain = tmp_path / "plain" / "llama.cpp" plain.mkdir(parents = True) assert u._active_install_is_local_link(str(plain / _server_subpath())) is False diff --git a/studio/backend/tests/test_mcp_servers.py b/studio/backend/tests/test_mcp_servers.py index 6432ffb8e1..c5c37f098f 100644 --- a/studio/backend/tests/test_mcp_servers.py +++ b/studio/backend/tests/test_mcp_servers.py @@ -577,7 +577,7 @@ def test_clear_oauth_tokens_swallows_constructor_errors(tmp_path, monkeypatch): def test_tool_xml_parser_handles_hyphenated_function_names(): """Hyphenated tool names like `mcp__srv__list-issues` must parse, else the - model can call the tool but Studio can't dispatch.""" + model can call the tool but Unsloth can't dispatch.""" from core.inference.tool_call_parser import parse_tool_calls_from_text calls = parse_tool_calls_from_text( diff --git a/studio/backend/tests/test_mcp_stdio_improvements.py b/studio/backend/tests/test_mcp_stdio_improvements.py index b0bfd45135..745c2cc447 100644 --- a/studio/backend/tests/test_mcp_stdio_improvements.py +++ b/studio/backend/tests/test_mcp_stdio_improvements.py @@ -188,7 +188,7 @@ def test_validate_url_allows_url_in_argument(monkeypatch): # ── P6: Data Recipe stdio path obeys the same host gate ───────────── -# build_mcp_providers needs the Studio-only data_designer plugin; skip if absent. +# build_mcp_providers needs the Unsloth-only data_designer plugin; skip if absent. _STDIO_RECIPE = { "mcp_providers": [ diff --git a/studio/backend/tests/test_mlx_inference_backend.py b/studio/backend/tests/test_mlx_inference_backend.py index 7b8aefb722..fafaea0043 100644 --- a/studio/backend/tests/test_mlx_inference_backend.py +++ b/studio/backend/tests/test_mlx_inference_backend.py @@ -236,7 +236,7 @@ def test_mlx_inference_vlm_lora_uses_unsloth_loader_without_native_adapter_rewri _install_fake_fast_mlx(monkeypatch, calls) def _native_vlm_load(*_args, **_kwargs): - raise AssertionError("Studio MLX VLM inference must use FastMLXModel") + raise AssertionError("Unsloth MLX VLM inference must use FastMLXModel") mlx_vlm = types.ModuleType("mlx_vlm") mlx_vlm.load = _native_vlm_load diff --git a/studio/backend/tests/test_mlx_repair.py b/studio/backend/tests/test_mlx_repair.py index 365cc46410..47a695ccbd 100644 --- a/studio/backend/tests/test_mlx_repair.py +++ b/studio/backend/tests/test_mlx_repair.py @@ -103,7 +103,7 @@ def test_repair_install_pins_transformers_and_cleans_up(monkeypatch): assert mr.attempt_mlx_repair() is True cmd = captured["cmd"] # transformers is pinned via a constraint file so the mlx install cannot - # upgrade it underneath Studio, and the temp constraint file is cleaned up. + # upgrade it underneath Unsloth, and the temp constraint file is cleaned up. assert "--constraint" in cmd assert "--upgrade" in cmd reinstall_pairs = set(zip(cmd, cmd[1:])) @@ -123,7 +123,7 @@ def test_install_requires_prebuilt_wheels(monkeypatch): # A source distribution's PEP 517 build backend runs arbitrary code at install # time, before the post-install stack check. The unattended self-heal must # require pre-built wheels so a malicious resolver-selected sdist cannot execute - # during ordinary Studio startup. mlx/mlx-metal ship wheels only and + # during ordinary Unsloth startup. mlx/mlx-metal ship wheels only and # mlx-lm/mlx-vlm publish py3-none-any wheels, so a healthy self-heal still works. pytest.importorskip("transformers") captured = {} @@ -143,7 +143,7 @@ def test_install_requires_prebuilt_wheels(monkeypatch): def test_install_env_drops_secrets_and_source_redirects(monkeypatch): - # The unattended self-heal must not hand resolver/build code the full Studio + # The unattended self-heal must not hand resolver/build code the full Unsloth # environment: secrets and package-source redirects are dropped, while the # variables uv genuinely needs are forwarded. monkeypatch.setenv("HF_TOKEN", "secret-hf") diff --git a/studio/backend/tests/test_mtp_vram_budget.py b/studio/backend/tests/test_mtp_vram_budget.py index 0efbbf596d..694d60cfc6 100644 --- a/studio/backend/tests/test_mtp_vram_budget.py +++ b/studio/backend/tests/test_mtp_vram_budget.py @@ -502,7 +502,7 @@ class TestExtraArgsMtpDetection: assert _extra_args_mtp_draft_path([], env = dict(os.environ)) == "/large.gguf" def test_load_model_gates_env_spec_type_on_off_mode(self): - # LLAMA_ARG_SPEC_TYPE only reaches the child when Studio emits no spec + # LLAMA_ARG_SPEC_TYPE only reaches the child when Unsloth emits no spec # flag (UI mode "off", no user --spec-type); otherwise the emitted # --spec-type/--spec-default overrides the env, so the reserve must not # consult it or a stale MTP env over-reserves (Finding F3). Whitespace- @@ -530,8 +530,8 @@ class TestExtraArgsMtpDetection: def test_load_model_drafter_budget_precedence(self): # The budget sizes the drafter the launch actually loads: CLI extras win, - # then Studio's emitted mtp_draft_path (overrides LLAMA_ARG_SPEC_DRAFT_MODEL), - # then the env drafter -- not the env before Studio's (reviewer.py R3). + # then Unsloth's emitted mtp_draft_path (overrides LLAMA_ARG_SPEC_DRAFT_MODEL), + # then the env drafter -- not the env before Unsloth's (reviewer.py R3). compact = "".join(inspect.getsource(LlamaCppBackend.load_model).split()) assert "_cli_draft_for_budget=_extra_args_mtp_draft_path(extra_args,env={})" in compact assert "_env_draft_for_budget=_extra_args_mtp_draft_path([],env=os.environ)" in compact @@ -732,7 +732,7 @@ class TestExtraArgsMtpDetection: assert _extra_args_n_ubatch([], env = {"LLAMA_ARG_UBATCH": "notint"}) is None def test_env_main_cache_type_for_budget(self): - # The child inherits LLAMA_ARG_CACHE_TYPE_K/_V, but Studio emits no + # The child inherits LLAMA_ARG_CACHE_TYPE_K/_V, but Unsloth emits no # --cache-type when neither param nor extras set it -> a heavier env # main KV (f32) must be adopted so the reserve matches the child. assert _env_main_cache_type_for_budget(env = {}) is None @@ -765,7 +765,7 @@ class TestExtraArgsMtpDetection: assert "cache_type_kv=_env_main_cache_type_for_budget()" in compact def test_env_split_mode_is_tensor(self): - # The child inherits LLAMA_ARG_SPLIT_MODE, but Studio emits --split-mode + # The child inherits LLAMA_ARG_SPLIT_MODE, but Unsloth emits --split-mode # only on its tensor branch -> a tensor env must flip the budget so the # heavier per-device compute buffer is reserved (not layer overhead). assert _env_split_mode_is_tensor(env = {}) is False @@ -918,7 +918,7 @@ class TestExtraArgsMtpDetection: # Cluster A: when the final decision is layer split, an inherited # non-layer LLAMA_ARG_SPLIT_MODE (and paired LLAMA_ARG_TENSOR_SPLIT) must # be popped from the child env so the child cannot run tensor/row/none - # against Studio's layer budget. Whitespace-stripped for formatter. + # against Unsloth's layer budget. Whitespace-stripped for formatter. compact = "".join(inspect.getsource(LlamaCppBackend.load_model).split()) assert 'env.get("LLAMA_ARG_SPLIT_MODE")' in compact assert '_inherited_sm!="layer"' in compact @@ -936,10 +936,10 @@ class TestExtraArgsMtpDetection: assert "env.pop(_ct_var,None)" in compact def test_load_model_clears_tensor_split_env_in_tensor_mode(self): - # review run3 #2: Studio owns the tensor split. When it emits no + # review run3 #2: Unsloth owns the tensor split. When it emits no # --tensor-split (even split), a stale inherited LLAMA_ARG_TENSOR_SPLIT must # be cleared in the TENSOR branch too (not just the layer downgrade), or the - # child runs a split Studio didn't budget. The else (tensor) branch pops it. + # child runs a split Unsloth didn't budget. The else (tensor) branch pops it. src = inspect.getsource(LlamaCppBackend.load_model) compact = "".join(src.split()) # appears in both the layer branch and the tensor branch. @@ -1005,14 +1005,14 @@ def test_qwen36_class_regression_picks_lower_ctx_with_mtp(): def test_mtp_draft_budget_prefers_user_extras_drafter(): # A user --model-draft in extras is appended last and wins at launch, so the - # VRAM budget must size it first; then Studio's emitted mtp_draft_path (which + # VRAM budget must size it first; then Unsloth's emitted mtp_draft_path (which # overrides LLAMA_ARG_SPEC_DRAFT_MODEL), then the env drafter (load_model is too # entangled to drive end-to-end; assert the precedence at the source level). # Whitespace-stripped so the check survives any formatter line-wrapping. compact = "".join(inspect.getsource(LlamaCppBackend.load_model).split()) - # CLI extras sized first (env={} so the env doesn't pre-empt Studio's drafter). + # CLI extras sized first (env={} so the env doesn't pre-empt Unsloth's drafter). assert "_cli_draft_for_budget=_extra_args_mtp_draft_path(extra_args,env={})" in compact - # Order: CLI extras, then Studio's mtp_draft_path, then the env drafter. + # Order: CLI extras, then Unsloth's mtp_draft_path, then the env drafter. assert "_cli_draft_for_budgetor_studio_draft_for_budgetor_env_draft_for_budget" in compact - # The env must not be consulted before Studio's resolved drafter. + # The env must not be consulted before Unsloth's resolved drafter. assert "_extra_args_mtp_draft_path(extra_args)ormtp_draft_path" not in compact diff --git a/studio/backend/tests/test_multimodal_document.py b/studio/backend/tests/test_multimodal_document.py index 5cd7c876cc..b347c4aef8 100644 --- a/studio/backend/tests/test_multimodal_document.py +++ b/studio/backend/tests/test_multimodal_document.py @@ -3,7 +3,7 @@ """Tests for PDF / document attachment translation on external providers. -Studio adds a normalised `input_document` content part on +Unsloth adds a normalised `input_document` content part on ChatCompletionRequest so the frontend needn't know the per-provider attachment shape: diff --git a/studio/backend/tests/test_nudge_tool_calls_wiring.py b/studio/backend/tests/test_nudge_tool_calls_wiring.py index e03fd0c7d7..82a6543aeb 100644 --- a/studio/backend/tests/test_nudge_tool_calls_wiring.py +++ b/studio/backend/tests/test_nudge_tool_calls_wiring.py @@ -3,7 +3,7 @@ """Wiring guard for the plan-without-action ``nudge_tool_calls`` policy. -Decided policy: the re-prompt is ALWAYS ON for the Studio inference paths +Decided policy: the re-prompt is ALWAYS ON for the Unsloth inference paths (safetensors, GGUF/llama_cpp, MLX) and OPT-IN for the API (/v1 OpenAI-compat + Anthropic-compat, controlled by the request's ``nudge_tool_calls``, default off). @@ -16,7 +16,7 @@ Mechanism (verified here without loading a model): opt-in), while the GGUF loop keeps its pre-existing default-on behaviour (``None`` keeps nudging) so an omitted flag never disables GGUF; * the API request models default the flag to ``None`` (opt-in / off); - * the Studio-facing routes forward the request's flag, and the Studio frontend + * the Unsloth-facing routes forward the request's flag, and the Unsloth frontend sends ``nudge_tool_calls: true`` -- exercised behaviourally in ``test_safetensors_tool_loop.py`` and ``test_llama_cpp_tool_loop.py``. """ @@ -87,7 +87,7 @@ def test_api_request_models_default_the_flag_off(): def test_studio_routes_forward_the_request_flag(): - # The Studio chat frontend posts to /v1/chat/completions and /v1/messages + # The Unsloth chat frontend posts to /v1/chat/completions and /v1/messages # with nudge_tool_calls=true; the route handlers forward the request value # (external API clients that omit it fall back to the opt-in default). from routes import inference as routes_inference diff --git a/studio/backend/tests/test_offline_gguf_cache_fallback.py b/studio/backend/tests/test_offline_gguf_cache_fallback.py index 0b9a1e704f..295549c443 100644 --- a/studio/backend/tests/test_offline_gguf_cache_fallback.py +++ b/studio/backend/tests/test_offline_gguf_cache_fallback.py @@ -897,7 +897,7 @@ class TestHfOfflineIfDnsDead: assert "HF_HUB_OFFLINE" not in os.environ def test_user_set_hf_hub_offline_is_preserved(self, dns, clean_offline_env, monkeypatch): - # User explicitly set offline before launching Studio. + # User explicitly set offline before launching Unsloth. monkeypatch.setenv("HF_HUB_OFFLINE", "1") dns.fail() with _hf_offline_if_dns_dead() as did_set: diff --git a/studio/backend/tests/test_offline_inference_parent.py b/studio/backend/tests/test_offline_inference_parent.py index 71331220d6..bd0014ea64 100644 --- a/studio/backend/tests/test_offline_inference_parent.py +++ b/studio/backend/tests/test_offline_inference_parent.py @@ -139,7 +139,7 @@ class TestLoraDetectOffline: monkeypatch.setenv("HF_HUB_OFFLINE", "1") - # Studio catches Exception broadly; pin that the call still happens + # Unsloth catches Exception broadly; pin that the call still happens # (so cached LoRAs aren't missed) and returns fast via the mock. class _OfflineModeIsEnabled(Exception): pass diff --git a/studio/backend/tests/test_openai_auto_switch.py b/studio/backend/tests/test_openai_auto_switch.py index 8742b84ae7..90fbd19297 100644 --- a/studio/backend/tests/test_openai_auto_switch.py +++ b/studio/backend/tests/test_openai_auto_switch.py @@ -689,7 +689,7 @@ def test_v1_models_retrieve_is_case_insensitive(monkeypatch): def test_index_excludes_hidden_models(tmp_path, monkeypatch): # The llama.cpp validation probe and RAG embedding weights are hidden from - # Studio's pickers; they must never become auto-switch targets. + # Unsloth's pickers; they must never become auto-switch targets. from types import SimpleNamespace import routes.models as models_route diff --git a/studio/backend/tests/test_openai_compaction.py b/studio/backend/tests/test_openai_compaction.py index c7de0a9aed..6fad2c5eaf 100644 --- a/studio/backend/tests/test_openai_compaction.py +++ b/studio/backend/tests/test_openai_compaction.py @@ -86,7 +86,7 @@ def test_cloud_openai_sets_compaction_block(monkeypatch): def test_cloud_openai_below_default_threshold_passes_through(monkeypatch): - # Studio doesn't clamp the OpenAI side -- the API accepts whatever the + # Unsloth doesn't clamp the OpenAI side -- the API accepts whatever the # caller sends, so a small probe like 60k still goes through. captured = _capture( monkeypatch, diff --git a/studio/backend/tests/test_openai_image_generation.py b/studio/backend/tests/test_openai_image_generation.py index ace57588d3..c2eef0381f 100644 --- a/studio/backend/tests/test_openai_image_generation.py +++ b/studio/backend/tests/test_openai_image_generation.py @@ -4,7 +4,7 @@ """Unit tests for OpenAI Responses API image_generation tool wiring. The tool is a server-side Responses-API tool (``{type: "image_generation"}``); -the result comes back as an ``image_generation_call`` output item, which Studio +the result comes back as an ``image_generation_call`` output item, which Unsloth translates into ``_toolEvent`` chunks so the chat adapter renders it inline. Tests pin: the tool is added to the body only on a cloud OpenAI base when asked for, the done event produces the expected chunks, and non-cloud bases drop it. diff --git a/studio/backend/tests/test_openai_tool_passthrough.py b/studio/backend/tests/test_openai_tool_passthrough.py index 8725b28ac8..161c8743c4 100644 --- a/studio/backend/tests/test_openai_tool_passthrough.py +++ b/studio/backend/tests/test_openai_tool_passthrough.py @@ -119,7 +119,7 @@ class TestFriendlyUpstreamError: raw = '{"error":{"code":400,"message":"Failed to initialize samplers: failed to parse grammar","type":"invalid_request_error"}}' msg = _friendly_upstream_error(raw) assert "failed to parse grammar" not in msg # raw body is not surfaced verbatim - assert "tool-calling grammar" in msg and "Update Studio" in msg + assert "tool-calling grammar" in msg and "Update Unsloth" in msg def test_failed_to_initialize_samplers_alone_matches(self): assert "tool-calling grammar" in _friendly_upstream_error("Failed to initialize samplers") @@ -262,7 +262,7 @@ class TestChatMessageToolRoles: def test_tool_empty_content_accepted(self): # Empty tool output (mkdir, git add, ...) is routine in agentic loops; - # OpenAI and llama-server both accept it, so Studio must not 400. + # OpenAI and llama-server both accept it, so Unsloth must not 400. msg = ChatMessage(role = "tool", tool_call_id = "call_1", content = "") assert msg.content == "" @@ -400,7 +400,7 @@ class TestChatCompletionRequestToolFields: assert req.session_id == "abc" def test_stream_defaults_false_matching_openai_spec(self): - # OpenAI defaults `stream` to false. Studio used to default true, + # OpenAI defaults `stream` to false. Unsloth used to default true, # breaking naive curl/.NET clients (#5047) that omit it. Pin the fix. req = self._make() assert req.stream is False @@ -664,7 +664,7 @@ class TestChatCompletionRequestToolFields: raise AssertionError("client tools must use passthrough") def generate_chat_completion_with_tools(self, **_kwargs): - raise AssertionError("Studio tool loop must stay disabled") + raise AssertionError("Unsloth tool loop must stay disabled") async def fake_passthrough(llama_backend, payload, model_name, **kwargs): captured["body"] = inference_route._build_openai_passthrough_body( @@ -707,11 +707,11 @@ class TestChatCompletionRequestToolFields: assert monitor.active_count() == 0 def test_permission_mode_does_not_reject_client_tool_passthrough(self, monkeypatch): - # A non-streaming client-tool passthrough (client tools, no Studio tool + # A non-streaming client-tool passthrough (client tools, no Unsloth tool # loop) that also carries permission_mode "ask"/"auto" must reach the # provider passthrough, not the confirm-without-stream guard: the # validator leaves confirm_tool_calls unset for passthrough, and a bare - # permission_mode only gates Studio's own local tool loop. An explicit + # permission_mode only gates Unsloth's own local tool loop. An explicit # confirm_tool_calls=True still forces the local-confirm rejection. # The pre-switch guard only runs when an automatic load may run, so force # that predicate on to exercise it against a resident passthrough backend. @@ -732,7 +732,7 @@ class TestChatCompletionRequestToolFields: raise AssertionError("client tools must use passthrough") def generate_chat_completion_with_tools(self, **_kwargs): - raise AssertionError("Studio tool loop must stay disabled") + raise AssertionError("Unsloth tool loop must stay disabled") async def fake_passthrough(llama_backend, payload, model_name, **kwargs): inference_route.api_monitor.finish(kwargs.get("monitor_id")) @@ -757,7 +757,7 @@ class TestChatCompletionRequestToolFields: return self._v1_client(monkeypatch, _GGUFBackend()) # A process --enable-tools policy must not turn a client-tool passthrough - # into a Studio local loop, so a policy of None or True both keep the + # into an Unsloth local loop, so a policy of None or True both keep the # passthrough (the guard mirrors _explicit_studio_tool_loop_requested). for policy in (None, True): for mode in ("ask", "auto"): @@ -810,7 +810,7 @@ class TestChatCompletionRequestToolFields: assert "requires stream=true" in resp.json()["error"]["message"] def test_permission_mode_policy_forced_local_loop_rejected_before_switch(self, monkeypatch): - # A process --enable-tools policy forces Studio's own tool loop on even + # A process --enable-tools policy forces Unsloth's own tool loop on even # when the request omits enable_tools and carries no client tools. A # non-streaming ask/auto request is then confirm-gated with no stream to # prompt on, so it must 400 at the pre-switch guard -- before @@ -863,7 +863,7 @@ class TestChatCompletionRequestToolFields: def test_enable_tools_on_non_tool_backend_keeps_client_tools_on_passthrough(self, monkeypatch): # DiffusionGemma forces supports_tools off while passthrough stays # available (#6851): enable_tools=True must not steal client tools - # from the passthrough into a Studio tool loop that cannot run. + # from the passthrough into an Unsloth tool loop that cannot run. import routes.inference as inference_route captured = {} @@ -883,7 +883,7 @@ class TestChatCompletionRequestToolFields: raise AssertionError("client tools must use passthrough") def generate_chat_completion_with_tools(self, **_kwargs): - raise AssertionError("Studio tool loop cannot run on a non-tool backend") + raise AssertionError("Unsloth tool loop cannot run on a non-tool backend") async def fake_passthrough(llama_backend, payload, model_name, **kwargs): captured["body"] = inference_route._build_openai_passthrough_body( @@ -2581,7 +2581,7 @@ class TestGgufVisionToolRouting: raise AssertionError("plain GGUF path should not be used") def _tools(**_kwargs): - raise AssertionError("Studio tool loop should not steal response_format") + raise AssertionError("Unsloth tool loop should not steal response_format") backend = SimpleNamespace( is_loaded = True, @@ -2654,7 +2654,7 @@ class TestGgufVisionToolRouting: raise AssertionError("plain GGUF path should not be used") def _tools(**_kwargs): - raise AssertionError("Studio tool loop should not replace client tools") + raise AssertionError("Unsloth tool loop should not replace client tools") backend = SimpleNamespace( is_loaded = True, @@ -2726,7 +2726,7 @@ class TestGgufVisionToolRouting: yield "plain response" def _tools(**_kwargs): - raise AssertionError("tool_choice='none' must not start Studio's tool loop") + raise AssertionError("tool_choice='none' must not start Unsloth's tool loop") backend = SimpleNamespace( is_loaded = True, @@ -2780,7 +2780,7 @@ class TestGgufVisionToolRouting: raise AssertionError("plain GGUF path should not be used") def _tools(**_kwargs): - raise AssertionError("enabled_tools alone must not start Studio's tool loop") + raise AssertionError("enabled_tools alone must not start Unsloth's tool loop") backend = SimpleNamespace( is_loaded = True, @@ -2844,7 +2844,7 @@ class TestGgufVisionToolRouting: raise AssertionError("plain GGUF path should not be used") def _tools(**_kwargs): - raise AssertionError("enabled_tools alone must not start Studio's tool loop") + raise AssertionError("enabled_tools alone must not start Unsloth's tool loop") backend = SimpleNamespace( is_loaded = True, diff --git a/studio/backend/tests/test_password_prompt_backstop.py b/studio/backend/tests/test_password_prompt_backstop.py index 597eac1625..3c2c1956f9 100644 --- a/studio/backend/tests/test_password_prompt_backstop.py +++ b/studio/backend/tests/test_password_prompt_backstop.py @@ -3,7 +3,7 @@ """Pre-tunnel terminal password gate: never publish a public Cloudflare URL while the seeded default admin password is active. Imports run.py directly, -so run under the Studio venv.""" +so run under the Unsloth venv.""" from __future__ import annotations diff --git a/studio/backend/tests/test_permission_mode.py b/studio/backend/tests/test_permission_mode.py index 3b7197fc49..4fc64a6291 100644 --- a/studio/backend/tests/test_permission_mode.py +++ b/studio/backend/tests/test_permission_mode.py @@ -1438,7 +1438,7 @@ def test_unknown_permission_mode_normalizes_to_ask_on_request_models(): def test_ask_auto_self_enable_confirm_on_chat_request(): # "Ask" gates every call, so a direct /chat/completions caller that requests - # ask but omits the legacy confirm flag self-enables it when Studio's own tool + # ask but omits the legacy confirm flag self-enables it when Unsloth's own tool # loop is requested. Only the router's loop-entry signals count (enable_tools / # mcp_enabled); enabled_tools alone never starts the loop. for loop in ({"enable_tools": True}, {"mcp_enabled": True}): @@ -1481,7 +1481,7 @@ def test_ask_auto_self_enable_confirm_on_chat_request(): confirm_tool_calls = False, ) assert req.confirm_tool_calls is False - # A plain client-tool passthrough (client-supplied tools that Studio does not + # A plain client-tool passthrough (client-supplied tools that Unsloth does not # execute) must NOT self-enable confirm, or the route rejects the passthrough. req = ChatCompletionRequest( messages = [{"role": "user", "content": "hi"}], diff --git a/studio/backend/tests/test_providers_api.py b/studio/backend/tests/test_providers_api.py index 5e24ed752d..7cac3a9e99 100644 --- a/studio/backend/tests/test_providers_api.py +++ b/studio/backend/tests/test_providers_api.py @@ -38,11 +38,11 @@ BASE_URL = os.getenv("STUDIO_TEST_URL", "http://localhost:8000") USERNAME = os.getenv("STUDIO_TEST_USER", "unsloth") PASSWORD = os.getenv("STUDIO_TEST_PASSWORD", "") -# Skip the whole module when no live Studio server / bootstrap password is +# Skip the whole module when no live Unsloth server / bootstrap password is # available (e.g. on CI) so pytest discovery does not error out. pytestmark = pytest.mark.skipif( not PASSWORD, - reason = "Integration test requires a running Studio server; set STUDIO_TEST_PASSWORD to enable.", + reason = "Integration test requires a running Unsloth server; set STUDIO_TEST_PASSWORD to enable.", ) # provider_type → (env var name, model for inference test) diff --git a/studio/backend/tests/test_rag_embed_llama_server.py b/studio/backend/tests/test_rag_embed_llama_server.py index 0e1f74cefe..3a332ee19b 100644 --- a/studio/backend/tests/test_rag_embed_llama_server.py +++ b/studio/backend/tests/test_rag_embed_llama_server.py @@ -149,7 +149,7 @@ def test_build_env_gpu_inherits_devices(monkeypatch): monkeypatch.setenv("CUDA_VISIBLE_DEVICES", "0,1") b = LlamaServerBackend() env = b._build_env("/bin/llama-server", use_gpu = True) - assert env.get("CUDA_VISIBLE_DEVICES") == "0,1" # inherit Studio's selection + assert env.get("CUDA_VISIBLE_DEVICES") == "0,1" # inherit Unsloth's selection def test_use_gpu_explicit_modes(monkeypatch): diff --git a/studio/backend/tests/test_recommended_folders_permission.py b/studio/backend/tests/test_recommended_folders_permission.py index 33a457755e..b65695ad93 100644 --- a/studio/backend/tests/test_recommended_folders_permission.py +++ b/studio/backend/tests/test_recommended_folders_permission.py @@ -112,7 +112,7 @@ def test_path_under_unreadable_parent_returns_false_not_raises(tmp_path): ) def test_demonstrates_the_underlying_stdlib_regression(tmp_path): """Documents *why* _safe_is_dir exists: the old bare pattern raises on - the interpreters Studio ships on (3.12+).""" + the interpreters Unsloth ships on (3.12+).""" parent = tmp_path / "ollama" parent.mkdir() os.chmod(parent, 0o000) diff --git a/studio/backend/tests/test_responses_tool_passthrough.py b/studio/backend/tests/test_responses_tool_passthrough.py index 46dd0d42e4..69715649b7 100644 --- a/studio/backend/tests/test_responses_tool_passthrough.py +++ b/studio/backend/tests/test_responses_tool_passthrough.py @@ -120,7 +120,7 @@ class TestResponsesRequestTools: def test_builtin_tool_type_passes_validation(self): """Non-function built-in tools (web_search, file_search, mcp, ...) must not raise at validation so SDKs that default to them don't - fail on Studio; they're filtered out during translation.""" + fail on Unsloth; they're filtered out during translation.""" req = ResponsesRequest( input = "hi", tools = [{"type": "web_search_preview"}], diff --git a/studio/backend/tests/test_rocm_oom_guard.py b/studio/backend/tests/test_rocm_oom_guard.py index 6e70c7cde4..699d0b74f5 100644 --- a/studio/backend/tests/test_rocm_oom_guard.py +++ b/studio/backend/tests/test_rocm_oom_guard.py @@ -36,7 +36,7 @@ class TestIsIntegratedSignal: """hipDeviceProp_t.integrated wins when truthy; 0/absent never downgrades. Same universal gate PR #5988's UMA safetensors fast-load uses -- keeps - Studio's two unified-memory consumers on one signal.""" + Unsloth's two unified-memory consumers on one signal.""" def test_integrated_upgrades_unknown_apu(self) -> None: # gfx1103 Phoenix iGPU: outside the hardcoded arch set, but the diff --git a/studio/backend/tests/test_safetensors_tool_loop.py b/studio/backend/tests/test_safetensors_tool_loop.py index 915f82ac8e..31c728afca 100644 --- a/studio/backend/tests/test_safetensors_tool_loop.py +++ b/studio/backend/tests/test_safetensors_tool_loop.py @@ -2230,8 +2230,8 @@ def _reprompt_loop(*, auto_heal_tool_calls): tools = [{"type": "function", "function": {"name": "search_knowledge_base"}}], execute_tool = exec_fn, auto_heal_tool_calls = auto_heal_tool_calls, - # Studio always nudges (always-on for the Studio inference paths); the - # API opts in per request. Model the Studio caller here. + # Unsloth always nudges (always-on for the Unsloth inference paths); the + # API opts in per request. Model the Unsloth caller here. nudge_tool_calls = True, max_tool_iterations = 3, ) @@ -3203,7 +3203,7 @@ class TestLoopBehaviour: class TestLoopRePrompt: - """Plan-without-action re-prompt parity with GGUF: nudge instead of terminating, up to ``MAX_ACT_REPROMPTS`` extra slots. Studio always nudges, so these drive the loop with ``nudge_tool_calls=True``.""" + """Plan-without-action re-prompt parity with GGUF: nudge instead of terminating, up to ``MAX_ACT_REPROMPTS`` extra slots. Unsloth always nudges, so these drive the loop with ``nudge_tool_calls=True``.""" def test_reasoning_intent_does_not_reprompt_a_visible_answer(self): generations = 0 @@ -4258,7 +4258,7 @@ class TestPlanWithoutActionReprompt: def test_omitted_nudge_flag_is_not_reprompted(self): # The retry is new on this loop: API callers who do not send the flag - # must keep today's behavior. Studio opts in explicitly. + # must keep today's behavior. Unsloth opts in explicitly. loop, exec_fn = _make_loop( turns = [ ["I'll search the web for that."], diff --git a/studio/backend/tests/test_secure_tunnel_gate.py b/studio/backend/tests/test_secure_tunnel_gate.py index 2c13e13bbb..a8c0c2305f 100644 --- a/studio/backend/tests/test_secure_tunnel_gate.py +++ b/studio/backend/tests/test_secure_tunnel_gate.py @@ -2,7 +2,7 @@ # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 """Cloudflare tunnel start gate, incl. --secure on loopback. Imports run.py -directly, so run under the Studio venv.""" +directly, so run under the Unsloth venv.""" from __future__ import annotations diff --git a/studio/backend/tests/test_server_disk_logging.py b/studio/backend/tests/test_server_disk_logging.py index 05d03d869c..ce733c2aaa 100644 --- a/studio/backend/tests/test_server_disk_logging.py +++ b/studio/backend/tests/test_server_disk_logging.py @@ -3,7 +3,7 @@ """Tests for the server session log + native-crash capture in run.py. -Field regression: Studio "terminates without a warning" -- a native crash in +Field regression: Unsloth "terminates without a warning" -- a native crash in the GPU runtime kills the process with no Python traceback, and a desktop- shortcut console closes before anything can be read. The server must tee its console output to disk and aim faulthandler at the same file so even hard diff --git a/studio/backend/tests/test_slot_offload_fit.py b/studio/backend/tests/test_slot_offload_fit.py index ac606e4627..d354c7e113 100644 --- a/studio/backend/tests/test_slot_offload_fit.py +++ b/studio/backend/tests/test_slot_offload_fit.py @@ -3,7 +3,7 @@ """Tests for the offload-avoidance serving-slot reduction (`_slots_that_fit_on_gpu`). -When a pinned context does not fit at the requested `--parallel` slot count, Studio would +When a pinned context does not fit at the requested `--parallel` slot count, Unsloth would flip to `--fit on` and llama-server offloads layers to host RAM, collapsing decode ~3x (oobabooga #6718). Instead the loader retries the on-GPU fit at fewer slots and keeps the largest count that stays fully on GPU (`-ngl -1`). These tests drive the real helper with diff --git a/studio/backend/tests/test_studio_api.py b/studio/backend/tests/test_studio_api.py index 928b636e3e..087c00b648 100644 --- a/studio/backend/tests/test_studio_api.py +++ b/studio/backend/tests/test_studio_api.py @@ -11,7 +11,7 @@ the CLI's ``--help`` output: 1. curl -- basic chat completions (non-streaming) 2. curl -- streaming chat completions 3. Python OpenAI SDK -- streaming completions - 4. curl -- Studio server-side tools (enable_tools=true) + 4. curl -- Unsloth server-side tools (enable_tools=true) 5. curl -- Standard OpenAI function calling (non-streaming) 6. curl -- Standard OpenAI function calling (streaming) 7. curl -- Standard OpenAI function calling (multi-turn tool loop) @@ -31,7 +31,7 @@ Usage: python tests/test_studio_api.py python tests/test_studio_api.py --model unsloth/... --gguf-variant ... - # Pytest mode, external server — start a Studio server yourself, + # Pytest mode, external server — start an Unsloth server yourself, # then point pytest at it. Fastest iteration loop. unsloth studio run --model unsloth/Qwen3-1.7B-GGUF --gguf-variant UD-Q4_K_XL & export UNSLOTH_E2E_BASE_URL=http://127.0.0.1:8080 @@ -341,7 +341,7 @@ def _final_finish_reason(chunks: list[dict]) -> str | None: def test_openai_tools_nonstream(base_url: str, api_key: str): """Standard OpenAI function calling, non-streaming, tool_choice='required'. - Regression: before the fix, Studio stripped `tools` and the model + Regression: before the fix, Unsloth stripped `tools` and the model returned plain text with finish_reason='stop'. After the fix, llama-server's response is forwarded verbatim so the client sees finish_reason='tool_calls' with a structured tool_calls array and diff --git a/studio/backend/tests/test_tensor_parallel.py b/studio/backend/tests/test_tensor_parallel.py index 0d71b89d87..06f72d3b9f 100644 --- a/studio/backend/tests/test_tensor_parallel.py +++ b/studio/backend/tests/test_tensor_parallel.py @@ -420,7 +420,7 @@ def test_runtime_recovery_fires_for_user_env_mtp(monkeypatch): # MTP driven by user extra_args / LLAMA_ARG_SPEC_TYPE leaves _speculative_type # unset, but the launch flag still gates recovery on (pass-through MTP). b = _recovery_backend() - b._speculative_type = None # Studio stepped back; user/env owns the spec + b._speculative_type = None # Unsloth stepped back; user/env owns the spec done = threading.Event() captured = {} diff --git a/studio/backend/tests/test_tool_confirm_stream.py b/studio/backend/tests/test_tool_confirm_stream.py index b8e0472e12..0813f6b68d 100644 --- a/studio/backend/tests/test_tool_confirm_stream.py +++ b/studio/backend/tests/test_tool_confirm_stream.py @@ -3,12 +3,12 @@ """End-to-end handshake test for the tool-confirmation gate, no model. -The real Studio stream wrappers in ``routes/inference.py`` drive the +The real Unsloth stream wrappers in ``routes/inference.py`` drive the synchronous agentic generator with ``await asyncio.to_thread(next, gen, ...)`` so the blocking ``threading.Event`` wait runs off the event loop. This test rebuilds that exact pattern around the real ``state.tool_approvals`` functions, served by a real uvicorn process on -loopback (the same server Studio uses), and proves the load-bearing +loopback (the same server Unsloth uses), and proves the load-bearing property: * ``tool_start`` reaches the client before the gate blocks, and diff --git a/studio/backend/tests/test_tool_message_empty_content.py b/studio/backend/tests/test_tool_message_empty_content.py index d63b16ce80..636a35f5a9 100644 --- a/studio/backend/tests/test_tool_message_empty_content.py +++ b/studio/backend/tests/test_tool_message_empty_content.py @@ -4,7 +4,7 @@ """Empty ``role="tool"`` content must be accepted on the OpenAI-compat surface. Agentic clients send ``content: ""`` when a command produced no output; -OpenAI and llama-server both accept it. Studio used to 400, which standard +OpenAI and llama-server both accept it. Unsloth used to 400, which standard clients treat as non-retryable and kill the session. The validator must normalize empty/missing tool content to ``""`` instead of raising. """ diff --git a/studio/backend/tests/test_tp_vision_regression.py b/studio/backend/tests/test_tp_vision_regression.py index 09af876da6..fb0989b306 100644 --- a/studio/backend/tests/test_tp_vision_regression.py +++ b/studio/backend/tests/test_tp_vision_regression.py @@ -625,7 +625,7 @@ def _fallback_loaded_backend(layer_preserves_tensor_intent: bool) -> LlamaCppBac def test_tensor_off_echo_preserves_multi_gpu_fallback(): - """The Studio UI always sends tensor_parallel and echoes the /load response's + """The Unsloth UI always sends tensor_parallel and echoes the /load response's resolved value, so after a fallback a ctx/settings reload carries tensor_parallel= false even though the user never changed it. That echo must NOT collapse the preserved multi-GPU placement -- it dedupes (Codex #6659).""" diff --git a/studio/backend/tests/test_trained_model_scan.py b/studio/backend/tests/test_trained_model_scan.py index 7bf572e214..5d74bb7d28 100644 --- a/studio/backend/tests/test_trained_model_scan.py +++ b/studio/backend/tests/test_trained_model_scan.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for Studio trained-model discovery used by Chat.""" +"""Tests for Unsloth trained-model discovery used by Chat.""" import json from pathlib import Path diff --git a/studio/backend/tests/test_training_nan_loss_handling.py b/studio/backend/tests/test_training_nan_loss_handling.py index a2dc78bee2..5a477a084d 100644 --- a/studio/backend/tests/test_training_nan_loss_handling.py +++ b/studio/backend/tests/test_training_nan_loss_handling.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -"""Pin Studio's behavior when a training event reports non-finite (NaN/Inf) loss. +"""Pin Unsloth's behavior when a training event reports non-finite (NaN/Inf) loss. The training event handler used to filter NaN/Inf to None silently while leaving the previous finite loss in progress.loss — so the API kept reporting diff --git a/studio/backend/tests/test_transformers_latest.py b/studio/backend/tests/test_transformers_latest.py index 20616dccba..af48d674cc 100644 --- a/studio/backend/tests/test_transformers_latest.py +++ b/studio/backend/tests/test_transformers_latest.py @@ -1036,7 +1036,7 @@ def test_upgrade_check_mixed_pypi_main_reports_dev_only(monkeypatch): def test_install_endpoint_not_mounted_on_v1(): - """The consented pip-install endpoint is a Studio admin action; it must live + """The consented pip-install endpoint is an Unsloth admin action; it must live on studio_router (kept off the OpenAI-compatible /v1 mount), not router.""" from routes import inference as ri diff --git a/studio/backend/utils/_studio_release_build.py b/studio/backend/utils/_studio_release_build.py index 267197a202..07ede36912 100644 --- a/studio/backend/utils/_studio_release_build.py +++ b/studio/backend/utils/_studio_release_build.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Build-stamped Studio release metadata. +"""Build-stamped Unsloth release metadata. Release builds may rewrite this module in the build workspace before creating Python artifacts. Keep the committed value neutral so source checkouts do not diff --git a/studio/backend/utils/api_errors.py b/studio/backend/utils/api_errors.py index cae8daf287..a3686c3a26 100644 --- a/studio/backend/utils/api_errors.py +++ b/studio/backend/utils/api_errors.py @@ -20,7 +20,7 @@ client-error responses on the ``/v1/*`` surface: CRITICAL: the exception handlers installed by :func:`install_api_error_handlers` are global, but they ONLY transform responses for paths that start with ``/v1/``. For every other path (``/api/...``, frontend routes) they reproduce FastAPI's -default behavior byte-for-byte, because the Studio frontend depends on the +default behavior byte-for-byte, because the Unsloth frontend depends on the ``{"detail": ...}`` shape for ``/api/*``. Public contract (other modules depend on these): @@ -107,7 +107,7 @@ def anthropic_error_body( Returns ``{"type": "error", "request_id": None, "error": {"type", "message"}}``. ``request_id`` is a required (nullable) field on the spec's ErrorResponse; - Studio has no request-id system, so it is null. ``err_type`` defaults to + Unsloth has no request-id system, so it is null. ``err_type`` defaults to :data:`ANTHROPIC_TYPE_BY_STATUS` for ``status`` (``"api_error"`` fallback). """ return { @@ -192,7 +192,7 @@ def install_api_error_handlers(app) -> None: Both handlers are global but only transform responses for OpenAI/Anthropic- compatible surfaces (see :func:`wants_api_error_envelope`: the ``/v1/*`` mount and the preview ``/p/.../v1/*`` mount). Every other path reproduces FastAPI's - default ``{"detail": ...}`` behavior exactly so the Studio frontend keeps working. + default ``{"detail": ...}`` behavior exactly so the Unsloth frontend keeps working. """ @app.exception_handler(RequestValidationError) diff --git a/studio/backend/utils/client_ip.py b/studio/backend/utils/client_ip.py index 94acbf1809..cc48a096d2 100644 --- a/studio/backend/utils/client_ip.py +++ b/studio/backend/utils/client_ip.py @@ -4,12 +4,12 @@ """Resolve the caller's IP for rate limiting. Trust model, in order: - 1. If the operator opts in via ``UNSLOTH_STUDIO_TRUST_FORWARDED`` (Studio behind + 1. If the operator opts in via ``UNSLOTH_STUDIO_TRUST_FORWARDED`` (Unsloth behind their own reverse proxy), honor the *rightmost* ``X-Forwarded-For`` hop -- the one the trusted proxy appended. The leftmost entry is client-controlled and spoofable, so this assumes a proxy that appends (or overwrites) the header; only enable the env var behind such a proxy. - 2. If the socket peer is loopback, honor ``CF-Connecting-IP``. Studio's managed + 2. If the socket peer is loopback, honor ``CF-Connecting-IP``. Unsloth's managed Cloudflare tunnel terminates at 127.0.0.1, so every tunneled visitor would otherwise collapse onto the same socket peer (the local cloudflared process) and share one rate-limit bucket. ``CF-Connecting-IP`` is set by Cloudflare's diff --git a/studio/backend/utils/cpu_threads.py b/studio/backend/utils/cpu_threads.py index 4ed0021054..91d577408d 100644 --- a/studio/backend/utils/cpu_threads.py +++ b/studio/backend/utils/cpu_threads.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Early CPU thread-pool configuration for Studio processes.""" +"""Early CPU thread-pool configuration for Unsloth processes.""" import os from typing import MutableMapping, Optional diff --git a/studio/backend/utils/datasets/cache_safe.py b/studio/backend/utils/datasets/cache_safe.py index e629210f33..d2dc7b737a 100644 --- a/studio/backend/utils/datasets/cache_safe.py +++ b/studio/backend/utils/datasets/cache_safe.py @@ -7,7 +7,7 @@ A shared HF datasets cache can contain subtrees owned by another user (for example populated by an earlier root-run job). datasets then raises "[Errno 13] Permission denied: ..._builder.lock" while locking the cached builder, killing the training run even though the dataset itself is fine. -Retry such loads in a Studio-owned cache so the run proceeds; the worst case +Retry such loads in an Unsloth-owned cache so the run proceeds; the worst case is one rebuild of the dataset in the fallback location. """ @@ -26,7 +26,7 @@ def studio_datasets_cache() -> str: def load_dataset_cache_safe(*args, **kwargs): - """datasets.load_dataset, retried in a Studio-owned cache on EACCES.""" + """datasets.load_dataset, retried in an Unsloth-owned cache on EACCES.""" from datasets import load_dataset try: return load_dataset(*args, **kwargs) diff --git a/studio/backend/utils/hardware/VRAM_ESTIMATION.md b/studio/backend/utils/hardware/VRAM_ESTIMATION.md index a6b4de29d2..68ca1d5ffd 100644 --- a/studio/backend/utils/hardware/VRAM_ESTIMATION.md +++ b/studio/backend/utils/hardware/VRAM_ESTIMATION.md @@ -106,7 +106,7 @@ Non_flash_attention = B * num_attention_heads * S^2 * 2 * 12.0 * effective_layer Activations = max(Per_layer_with_gc, Non_flash_attention) ``` -Studio resolves the attention implementation with Unsloth's +Unsloth resolves the attention implementation with Unsloth's `resolve_attention_implementation` helper and uses that result directly. The estimator does not duplicate model-family attention policy. diff --git a/studio/backend/utils/hardware/amd.py b/studio/backend/utils/hardware/amd.py index f5b64c45d0..91a06c9a2a 100644 --- a/studio/backend/utils/hardware/amd.py +++ b/studio/backend/utils/hardware/amd.py @@ -125,7 +125,7 @@ def _run_amd_smi(*args: str, timeout: int = _AMD_SMI_DEFAULT_TIMEOUT) -> Optiona # amd-smi does not exist on Windows (neither Adrenalin nor the HIP SDK # ship a CLI) and can be absent on minimal Linux installs. Disable the # poller in one step instead of burning the 3-strike circuit breaker - # on guaranteed FileNotFoundError spawns. Studio's VRAM display falls + # on guaranteed FileNotFoundError spawns. Unsloth's VRAM display falls # back to torch mem_get_info. if not _amd_smi_disabled: logger.info( diff --git a/studio/backend/utils/hardware/hardware.py b/studio/backend/utils/hardware/hardware.py index 117ad7b780..adc9a54aab 100644 --- a/studio/backend/utils/hardware/hardware.py +++ b/studio/backend/utils/hardware/hardware.py @@ -37,7 +37,7 @@ logger = get_logger(__name__) # ── GPU index ordering ────────────────────────────────────────────────────── # CUDA defaults to CUDA_DEVICE_ORDER=FASTEST_FIRST, numbering GPUs by compute -# performance. nvidia-smi -- and every free-VRAM probe in Studio -- numbers GPUs +# performance. nvidia-smi -- and every free-VRAM probe in Unsloth -- numbers GPUs # by PCI bus id instead. On a mixed-GPU host (e.g. an RTX 5090 alongside an RTX # PRO 6000) the two orderings disagree, so an index picked from nvidia-smi data # ("the emptiest card is GPU 1") gets written into CUDA_VISIBLE_DEVICES and then @@ -49,7 +49,7 @@ logger = get_logger(__name__) # and spawn workers copy os.environ. setdefault so an explicit user override wins. os.environ.setdefault("CUDA_DEVICE_ORDER", "PCI_BUS_ID") -# Studio workers can import MLX without importing unsloth first, so mirror the +# Unsloth workers can import MLX without importing unsloth first, so mirror the # package bootstrap here. Keep an explicit user value authoritative. if platform.system() == "Darwin" and platform.machine() == "arm64": os.environ.setdefault("AGX_RELAX_CDM_CTXSTORE_TIMEOUT", "1") @@ -117,7 +117,7 @@ def _has_mlx() -> bool: def _has_usable_mlx_stack() -> bool: - """True only when the FULL Studio MLX training/export stack is usable + """True only when the FULL Unsloth MLX training/export stack is usable (mlx + mlx-lm + mlx-vlm at the minimum versions unsloth-zoo requires), not just a bare ``import mlx.core``. A backtracked/old mlx-vlm still imports but breaks VLM Train/Export, so the training gate must match the self-heal's own diff --git a/studio/backend/utils/helper_precache_settings.py b/studio/backend/utils/helper_precache_settings.py index db19a2d028..e7d3c0e6dd 100644 --- a/studio/backend/utils/helper_precache_settings.py +++ b/studio/backend/utils/helper_precache_settings.py @@ -32,7 +32,7 @@ def helper_model_disabled_by_env() -> bool: def get_helper_precache_enabled() -> bool: """Read the persisted startup pre-cache preference. - Missing or unreadable settings default to False so Studio startup never + Missing or unreadable settings default to False so Unsloth startup never performs optional network work unless the user explicitly opted in. """ try: @@ -45,7 +45,7 @@ def get_helper_precache_enabled() -> bool: def set_helper_precache_enabled(value: Any) -> bool: - """Persist whether Studio should pre-cache the Helper LLM at startup.""" + """Persist whether Unsloth should pre-cache the Helper LLM at startup.""" parsed = _coerce_bool(value) if parsed is None: raise ValueError("Helper LLM startup pre-cache must be true or false.") diff --git a/studio/backend/utils/hf_xet_fallback.py b/studio/backend/utils/hf_xet_fallback.py index 9bc4a60fad..2628b99a2d 100644 --- a/studio/backend/utils/hf_xet_fallback.py +++ b/studio/backend/utils/hf_xet_fallback.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Studio shim over the shared ``unsloth_zoo.hf_xet_fallback`` Xet -> HTTP stall fallback. +"""Unsloth shim over the shared ``unsloth_zoo.hf_xet_fallback`` Xet -> HTTP stall fallback. -Re-exports the shared API and injects Studio's marker-aware cache purge +Re-exports the shared API and injects Unsloth's marker-aware cache purge (``prepare_cache_for_transport``) so the download manager keeps its ``.transport`` marker semantics on the HTTP retry. @@ -68,7 +68,7 @@ def _load_shared() -> bool: _shared_available = True _shared_import_error = None return True - except Exception as exc2: # noqa: BLE001 - degrade so Studio still boots with plain HF + except Exception as exc2: # noqa: BLE001 - degrade so Unsloth still boots with plain HF _shared_import_error = exc2 _shared_available = False import logging as _logging @@ -263,7 +263,7 @@ __all__ = [ def _studio_prepare_for_http(repo_type: str, repo_id: str) -> None: - """Studio's marker-aware purge before an HTTP resume, keeping the download manager's ``.transport`` + """Unsloth's marker-aware purge before an HTTP resume, keeping the download manager's ``.transport`` accounting consistent (vs unsloth_zoo's generic default). Guarded: a purge failure is logged, not fatal to the retry.""" try: @@ -273,7 +273,7 @@ def _studio_prepare_for_http(repo_type: str, repo_id: str) -> None: try: from loggers import get_logger get_logger(__name__).debug( - "Studio prepare_cache_for_transport failed for %s: %s", repo_id, exc + "Unsloth prepare_cache_for_transport failed for %s: %s", repo_id, exc ) except ModuleNotFoundError as logger_exc: if logger_exc.name != "loggers": @@ -294,8 +294,8 @@ def hf_hub_download_with_xet_fallback( on_status: Optional[Callable[[str], None]] = None, force_download: bool = False, ) -> str: - """Single-file download via the shared fallback with Studio's marker-aware HTTP-retry prep. - ``force_download`` re-fetches a newer blob over a cached one (Studio's model-update path).""" + """Single-file download via the shared fallback with Unsloth's marker-aware HTTP-retry prep. + ``force_download`` re-fetches a newer blob over a cached one (Unsloth's model-update path).""" return _shared_hf_hub_download_with_xet_fallback( repo_id, filename, @@ -313,6 +313,6 @@ def hf_hub_download_with_xet_fallback( def snapshot_download_with_xet_fallback(repo_id: str, **kwargs: Any) -> str: - """Whole-repo download via the shared fallback with Studio's marker-aware HTTP-retry prep.""" + """Whole-repo download via the shared fallback with Unsloth's marker-aware HTTP-retry prep.""" kwargs.setdefault("prepare_for_http_fn", _studio_prepare_for_http) return _shared_snapshot_download_with_xet_fallback(repo_id, **kwargs) diff --git a/studio/backend/utils/host_policy.py b/studio/backend/utils/host_policy.py index f506eadc03..55565bb338 100644 --- a/studio/backend/utils/host_policy.py +++ b/studio/backend/utils/host_policy.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Bind-host trust policy for the Studio backend. +"""Bind-host trust policy for the Unsloth backend. Stdlib only -- safe to import without the rest of the backend. diff --git a/studio/backend/utils/llama_cpp_update.py b/studio/backend/utils/llama_cpp_update.py index f6d3635301..31dbda63ea 100644 --- a/studio/backend/utils/llama_cpp_update.py +++ b/studio/backend/utils/llama_cpp_update.py @@ -82,7 +82,7 @@ def _utcnow() -> str: def _find_binary() -> Optional[str]: """Locate the active llama-server binary via the inference backend's own - resolver, so update targets exactly what Studio runs. Lazy import keeps the + resolver, so update targets exactly what Unsloth runs. Lazy import keeps the heavy inference module off this module's import path.""" try: from core.inference.llama_cpp import LlamaCppBackend @@ -109,7 +109,7 @@ def _installer_script() -> Optional[Path]: """Locate install_llama_prebuilt.py. Honours UNSLOTH_LLAMA_INSTALLER, then searches up from this file for both ``/install_llama_prebuilt.py`` and ``/studio/install_llama_prebuilt.py`` so it works in the dev tree and - in an installed Studio layout.""" + in an installed Unsloth layout.""" env = os.environ.get("UNSLOTH_LLAMA_INSTALLER") if env and Path(env).is_file(): return Path(env) @@ -227,7 +227,7 @@ def _is_under(path: Path, root: Path) -> bool: def _llama_install_root(binary: Optional[str]) -> Optional[Path]: - """The Studio-managed llama.cpp root the active binary lives under, or None + """The Unsloth-managed llama.cpp root the active binary lives under, or None when the binary is unmanaged. Installing anywhere the active binary is not would not replace what _find_llama_server_binary runs (which prefers a pinned LLAMA_SERVER_PATH, then UNSLOTH_LLAMA_CPP_PATH, then a llama.cpp tree), so we @@ -327,7 +327,7 @@ def _source_build_status(binary: str, *, force_refresh: bool) -> Optional[dict]: def _is_external_link(path: Optional[Path]) -> bool: """True when ``path`` is a --with-llama-cpp-dir local link: a POSIX symlink or a Windows directory junction / reparse point. Such a link resolves into - the user's own llama.cpp checkout, so Studio must never auto-update it.""" + the user's own llama.cpp checkout, so Unsloth must never auto-update it.""" if path is None: return False try: @@ -635,7 +635,7 @@ def start_update() -> dict: "reason": "local_link", "message": ( "llama.cpp is a local directory linked with --with-llama-cpp-dir; " - "Studio won't replace it. Update your own llama.cpp checkout instead." + "Unsloth won't replace it. Update your own llama.cpp checkout instead." ), "job": get_update_status()["job"], } diff --git a/studio/backend/utils/mlx_repair.py b/studio/backend/utils/mlx_repair.py index 7e1c9864c9..4ea1ec62f5 100644 --- a/studio/backend/utils/mlx_repair.py +++ b/studio/backend/utils/mlx_repair.py @@ -3,7 +3,7 @@ """Best-effort MLX self-heal for Apple Silicon. -On macOS, Studio enables Train/Export only when the MLX training/export stack is +On macOS, Unsloth enables Train/Export only when the MLX training/export stack is usable (see utils.hardware.hardware.detect_hardware -> CHAT_ONLY). MLX is pulled only transitively via unsloth-zoo, and a resolver backtrack (mlx-vlm -> transformers>=5 vs the single-env transformers pin) can silently drop it, leaving @@ -13,7 +13,7 @@ a background thread, then re-detects so the gate re-opens without a manual The install mirrors the main Apple Silicon installer (install_python_stack.py): it points UV_OVERRIDE at overrides-darwin-arm64.txt so the resolver keeps the -Studio transformers pin AND installs a current mlx-vlm, and it requires the same +Unsloth transformers pin AND installs a current mlx-vlm, and it requires the same minimum versions unsloth-zoo declares so a backtracked old mlx-vlm (which still imports but breaks VLM Train/Export) is never accepted as healthy. @@ -69,11 +69,11 @@ _MLX_REINSTALL_ARGS = tuple( # reject anything. mlx/mlx-metal ship wheels only (no sdist on PyPI) and # mlx-lm/mlx-vlm publish py3-none-any wheels, so requiring wheels does not break a # healthy self-heal; if a wheel is genuinely unavailable the install fails and -# Studio stays chat-only (the existing safe fallback) until `unsloth studio update`. +# Unsloth stays chat-only (the existing safe fallback) until `unsloth studio update`. _ONLY_BINARY_ARG = "--only-binary=:all:" # Allowlist of environment variables forwarded to the install subprocess. The # self-heal runs without confirmation on the default startup path, so it must not -# hand resolver/build code the full Studio environment. Everything outside this +# hand resolver/build code the full Unsloth environment. Everything outside this # set is dropped, which excludes three dangerous classes by construction: # * secrets (HF_TOKEN, AWS_*, WANDB_API_KEY, ...) that a malicious wheel/sdist # build hook would otherwise read straight out of os.environ; @@ -207,13 +207,13 @@ def _mlx_install_env() -> dict[str, str]: The self-heal runs without confirmation on the default startup path, so it forwards only the variables uv genuinely needs (see _MLX_ENV_ALLOWLIST) instead - of the full Studio environment: secrets and package-source redirects in + of the full Unsloth environment: secrets and package-source redirects in os.environ are dropped so a malicious resolver-selected artifact cannot read - Studio secrets or be steered to a hostile index. + Unsloth secrets or be steered to a hostile index. Mirror the main installer (install_python_stack.py) by pointing UV_OVERRIDE at overrides-darwin-arm64.txt, which relaxes mlx-vlm/mlx-lm's transformers>=5 - requirement to >=4.57.6. Without it, uv keeps the Studio transformers pin only + requirement to >=4.57.6. Without it, uv keeps the Unsloth transformers pin only by silently backtracking mlx-vlm to an old, unsupported version (uv honours UV_OVERRIDE; plain pip ignores it, so the transformers constraint below is the pip-path safety net). We set UV_OVERRIDE ourselves, so a poisoned one in the @@ -234,17 +234,17 @@ def _mlx_install_env() -> dict[str, str]: def _transformers_constraint_args() -> tuple[list[str], str | None]: """Pin transformers to the running version for the mlx install. - The install must never upgrade transformers underneath a running Studio + The install must never upgrade transformers underneath a running Unsloth (the single-env install pins transformers==4.57.6). With UV_OVERRIDE set this is belt-and-suspenders; on the plain-pip path (no UV_OVERRIDE support) it is the actual guard -- the resolver either finds an mlx build compatible with the - pin or fails, leaving us chat-only rather than breaking Studio. Returns + pin or fails, leaving us chat-only rather than breaking Unsloth. Returns (pip args, temp file path to clean up). Read the version from installed metadata rather than `import transformers`: transformers can have valid metadata yet fail to import (e.g. an incompatible huggingface_hub), and in that case we still want to pin it so the mlx install - cannot quietly upgrade it out from under Studio.""" + cannot quietly upgrade it out from under Unsloth.""" from importlib.metadata import PackageNotFoundError, version as _dist_version try: @@ -263,10 +263,10 @@ def attempt_mlx_repair(*, timeout: int = _REPAIR_TIMEOUT_S) -> bool: """Install a usable mlx/mlx-lm/mlx-vlm stack by name into the running venv. Best-effort; returns True iff the resulting stack meets unsloth-zoo's minimums (so a backtracked old mlx-vlm is rejected, not accepted). transformers is held - at its pinned version so the install can never upgrade it underneath Studio.""" + at its pinned version so the install can never upgrade it underneath Unsloth.""" # Prepare the constraint inside the try: this runs on a daemon thread, so an # exception here (e.g. tempfile.mkstemp failing on a full disk or bad TMPDIR) - # must leave Studio chat-only, not crash the background self-heal thread. + # must leave Unsloth chat-only, not crash the background self-heal thread. constraint_path = None try: constraint_args, constraint_path = _transformers_constraint_args() @@ -279,7 +279,7 @@ def attempt_mlx_repair(*, timeout: int = _REPAIR_TIMEOUT_S) -> bool: ) if cmd is None: logger.warning( - "MLX self-heal requires uv so Studio can apply dependency overrides; " + "MLX self-heal requires uv so Unsloth can apply dependency overrides; " "staying chat-only. Run `unsloth studio update` to restore uv." ) return False diff --git a/studio/backend/utils/models/checkpoints.py b/studio/backend/utils/models/checkpoints.py index b6b080b1c4..f2125ad034 100644 --- a/studio/backend/utils/models/checkpoints.py +++ b/studio/backend/utils/models/checkpoints.py @@ -37,7 +37,7 @@ def _checkpoint_sort_key(checkpoint_path: Path) -> tuple[int, int, str]: def _infer_base_model_from_history(checkpoint_dir: Path) -> Optional[str]: - """Best-effort base-model lookup using persisted Studio run metadata.""" + """Best-effort base-model lookup using persisted Unsloth run metadata.""" checkpoint_name = checkpoint_dir.name resolved_checkpoint_dir = str(checkpoint_dir.resolve()) diff --git a/studio/backend/utils/models/model_config.py b/studio/backend/utils/models/model_config.py index 284bbb5745..dadf103cea 100644 --- a/studio/backend/utils/models/model_config.py +++ b/studio/backend/utils/models/model_config.py @@ -2083,7 +2083,7 @@ def _has_model_weight_files(model_dir: Path) -> bool: def _detect_training_output_type(model_dir: Path) -> Optional[str]: - """Classify a Studio training output as LoRA or full finetune.""" + """Classify an Unsloth training output as LoRA or full finetune.""" adapter_config = model_dir / "adapter_config.json" adapter_model = model_dir / "adapter_model.safetensors" if adapter_config.exists() or adapter_model.exists(): @@ -2105,7 +2105,7 @@ def _looks_like_lora_adapter(model_dir: Path) -> bool: def scan_trained_models(outputs_dir: str = str(outputs_root())) -> List[Tuple[str, str, str]]: - """Scan outputs folder for trained Studio models. + """Scan outputs folder for trained Unsloth models. Returns: List of (display_name, model_path, model_type), where model_type is diff --git a/studio/backend/utils/paths/storage_roots.py b/studio/backend/utils/paths/storage_roots.py index 759681da3f..1faa2b1281 100644 --- a/studio/backend/utils/paths/storage_roots.py +++ b/studio/backend/utils/paths/storage_roots.py @@ -36,7 +36,7 @@ def _infer_studio_home_from_venv() -> Path | None: def studio_root() -> Path: - """Studio install root. + """Unsloth install root. Priority: UNSLOTH_STUDIO_HOME, then STUDIO_HOME alias, then sys.prefix inference, then legacy ~/.unsloth/studio. UNSLOTH_STUDIO_HOME wins if @@ -62,7 +62,7 @@ def cache_root() -> Path: def studio_bin_root() -> Path: - """Dir for Studio-managed executables (the `unsloth` shim, downloaded tools like cloudflared).""" + """Dir for Unsloth-managed executables (the `unsloth` shim, downloaded tools like cloudflared).""" return studio_root() / "bin" @@ -443,7 +443,7 @@ def resolve_export_write_dir(path_value: str | None = None) -> Path: Unlike :func:`resolve_export_dir`, this function passes absolute paths through as-is so users can target a different drive when - their Studio install lives on a constrained system volume + their Unsloth install lives on a constrained system volume (see :gh-issue:`6082`). Used only by the export write path. """ if not path_value or not str(path_value).strip(): diff --git a/studio/backend/utils/preview_rate_limit.py b/studio/backend/utils/preview_rate_limit.py index dd38cfd5e7..c59a1bf5b3 100644 --- a/studio/backend/utils/preview_rate_limit.py +++ b/studio/backend/utils/preview_rate_limit.py @@ -5,7 +5,7 @@ A signed link stops ref guessing, but anyone with a link can still drive GPU generation. This bounds sustained abuse from a single source. In-process and -single-worker only (like the login limiter in ``routes/auth.py``); Studio runs as +single-worker only (like the login limiter in ``routes/auth.py``); Unsloth runs as one uvicorn process, so a shared store isn't needed. """ diff --git a/studio/backend/utils/process_lifetime.py b/studio/backend/utils/process_lifetime.py index 3ffd54cc26..c63227ae86 100644 --- a/studio/backend/utils/process_lifetime.py +++ b/studio/backend/utils/process_lifetime.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Bind Studio child processes to the parent's lifetime so none survive an +"""Bind Unsloth child processes to the parent's lifetime so none survive an abnormal parent exit (terminal-window close, Task Manager "End Task", SIGKILL, crash) -- the cooperative shutdown path only runs on graceful exits. @@ -139,7 +139,7 @@ def _install_windows_job() -> None: kernel32.CloseHandle(job) return # AssignProcessToJobObject(parent) makes children inherit the job. May - # fail if Studio already runs inside an incompatible host job (pre-Win8); + # fail if Unsloth already runs inside an incompatible host job (pre-Win8); # degrade to the cooperative path rather than blocking startup. if not kernel32.AssignProcessToJobObject(job, kernel32.GetCurrentProcess()): kernel32.CloseHandle(job) diff --git a/studio/backend/utils/studio_version.py b/studio/backend/utils/studio_version.py index 9c18070fbb..82ade74bba 100644 --- a/studio/backend/utils/studio_version.py +++ b/studio/backend/utils/studio_version.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Network-free Studio release version resolution for display-only UI.""" +"""Network-free Unsloth release version resolution for display-only UI.""" from __future__ import annotations @@ -20,7 +20,7 @@ _MAX_VERSION_LENGTH = 64 def is_valid_studio_release_version(value: object) -> bool: - """Return True for Studio release tags such as ``v0.1.39-beta``.""" + """Return True for Unsloth release tags such as ``v0.1.39-beta``.""" if not isinstance(value, str): return False version = value.strip() @@ -102,7 +102,7 @@ def _git_branch(repo_root: Path) -> str | None: def get_studio_version(repo_root: Path | None = None) -> str: - """Return the installed Studio release tag for display, or ``dev``. + """Return the installed Unsloth release tag for display, or ``dev``. Intentionally separate from the PyPI ``unsloth`` package version used by update checks. Never performs network requests. diff --git a/studio/backend/utils/training_runs.py b/studio/backend/utils/training_runs.py index dc2535e570..dcdfa1395d 100644 --- a/studio/backend/utils/training_runs.py +++ b/studio/backend/utils/training_runs.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Helpers for naming and describing Studio training runs.""" +"""Helpers for naming and describing Unsloth training runs.""" from __future__ import annotations diff --git a/studio/backend/utils/transformers_latest.py b/studio/backend/utils/transformers_latest.py index 40c8f729a5..9f1d11be5b 100644 --- a/studio/backend/utils/transformers_latest.py +++ b/studio/backend/utils/transformers_latest.py @@ -5,7 +5,7 @@ When a model's ``model_type`` is absent from every installed transformers overlay (base 4.57.x plus the .venv_t5_530/550/510 sidecars and, if provisioned, .venv_t5_latest), -Studio cannot load it today. This module answers, without authentication, code execution, +Unsloth cannot load it today. This module answers, without authentication, code execution, or trust_remote_code: 1. Does the LATEST transformers release on PyPI ship this ``model_type``? @@ -387,7 +387,7 @@ def check_upgrade_for_model(model_name: str, hf_token: str | None = None) -> dic _SHADOWABLE_DEPS = frozenset({"tokenizers", "safetensors"}) # Provided by the sidecar recipe; checked against its pin, not the base env. _SIDECAR_PROVIDED = {"huggingface-hub": "1.8.0", "hf-xet": "1.4.2"} -# CLI-only; never imported at runtime in Studio's workers. +# CLI-only; never imported at runtime in Unsloth's workers. _IGNORED_DEPS = frozenset({"typer"}) @@ -538,7 +538,7 @@ def _install_latest_transformers_locked(version: str, before_swap = None) -> dic return { "success": False, "version": version, - "message": "Cannot install: Studio is in offline mode.", + "message": "Cannot install: Unsloth is in offline mode.", } # Re-verify against a LIVE snapshot (a release may land inside the cache TTL); # fall back to the cached one on fetch failure. @@ -573,13 +573,13 @@ def _install_latest_transformers_locked(version: str, before_swap = None) -> dic "version": version, "message": "Cannot install transformers " f"{version}: this environment does not satisfy {', '.join(blockers)}. " - "A Studio update is required first.", + "An Unsloth update is required first.", } if not ensure_latest_transformers_venv(version, extra_packages, before_swap = before_swap): return { "success": False, "version": version, - "message": f"Installing transformers {version} failed; see the Studio logs.", + "message": f"Installing transformers {version} failed; see the Unsloth logs.", } _invalidate_capability_caches() return { diff --git a/studio/backend/utils/transformers_version.py b/studio/backend/utils/transformers_version.py index 9f9f8aa3de..1fbcc9f46f 100644 --- a/studio/backend/utils/transformers_version.py +++ b/studio/backend/utils/transformers_version.py @@ -2151,7 +2151,7 @@ def end_sidecar_swap() -> None: def sidecar_swap_in_progress() -> bool: """True while a .venv_t5_latest install or repair holds the reservation, - in this process or any other Studio process (lock file).""" + in this process or any other Unsloth process (lock file).""" return sidecar_swap_kind() is not None diff --git a/studio/backend/utils/upload_limits.py b/studio/backend/utils/upload_limits.py index c21ea69af7..fff0ac423f 100644 --- a/studio/backend/utils/upload_limits.py +++ b/studio/backend/utils/upload_limits.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Shared Studio upload/request size limits.""" +"""Shared Unsloth upload/request size limits.""" from __future__ import annotations diff --git a/studio/frontend/.npmrc b/studio/frontend/.npmrc index 19783b5ff4..414379da6e 100644 --- a/studio/frontend/.npmrc +++ b/studio/frontend/.npmrc @@ -1,4 +1,4 @@ -# Studio frontend npm configuration. +# Unsloth frontend npm configuration. # # Mini Shai-Hulud / Axios-style supply chain defense. # Requires npm >=11.10.0. Refuses tarballs published less than 7 days ago, diff --git a/studio/frontend/src/app/provider.tsx b/studio/frontend/src/app/provider.tsx index c35706e50a..a7e9469cfc 100644 --- a/studio/frontend/src/app/provider.tsx +++ b/studio/frontend/src/app/provider.tsx @@ -412,7 +412,7 @@ function TauriWrapper({ children }: { children: ReactNode }) { {desktopBooting ? (

-
Preparing Studio
+
Preparing Unsloth
The local backend is ready. Signing in to your desktop session before loading chats. diff --git a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx index 8e06181585..ff4bd4bebd 100644 --- a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx +++ b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx @@ -1679,7 +1679,7 @@ export function HubModelPicker({ const deviceType = usePlatformStore((s) => s.deviceType); const isMac = deviceType === "mac"; - // Drop models Studio can't run for chat (diffusion / image / video / etc.) + // Drop models Unsloth can't run for chat (diffusion / image / video / etc.) // using the Hub's classifier on the tags the listing already carries. const isChatSupported = useCallback( (r: HfModelResult) => @@ -1730,7 +1730,7 @@ export function HubModelPicker({ let rows = recommendedSearch.results .filter((r) => !isHiddenModelId(r.id)) .filter((r) => !isMobileVariant(r.id)); - // Drop models Studio can't run for chat (diffusion / image / video / etc.). + // Drop models Unsloth can't run for chat (diffusion / image / video / etc.). rows = rows.filter(isChatSupported); // With no explicit format, show the device-recommended formats (GGUF, plus // MLX on Mac). When the user picks a format, honor it instead so Safetensors @@ -1868,7 +1868,7 @@ export function HubModelPicker({ // eslint-disable-next-line react-hooks/exhaustive-deps [lmStudioModels, downloadedSort, formatFilter, loadTimes, localQuery], ); - // Local ./models entries. Chat-only Studio runs GGUF (any host) and MLX (Mac + // Local ./models entries. Chat-only Unsloth runs GGUF (any host) and MLX (Mac // only), so raw checkpoints there are hidden (mirrors the cached non-GGUF // rule). An MLX build a Mac user dropped in ./models stays selectable. const sortedLocalDir = useMemo( diff --git a/studio/frontend/src/components/assistant-ui/thread.tsx b/studio/frontend/src/components/assistant-ui/thread.tsx index 62b8af6e3a..235dcb3c3d 100644 --- a/studio/frontend/src/components/assistant-ui/thread.tsx +++ b/studio/frontend/src/components/assistant-ui/thread.tsx @@ -1970,7 +1970,7 @@ function isNativeComposing(event: Event) { } // Fallback timeout for stuck IME composition. With Chrome on Windows against -// a WSL-hosted Studio (issue #5546), `compositionend` never fires after the +// a WSL-hosted Unsloth (issue #5546), `compositionend` never fires after the // candidate commits, so `composingRef` stays true and Send stays disabled. // Every compositionupdate / non-composing input resets the timer; only a true // gap-after-commit lets it fire. 2500ms is above a normal candidate-window diff --git a/studio/frontend/src/components/ui/confetti.tsx b/studio/frontend/src/components/ui/confetti.tsx index 892bffdb18..35f5913240 100644 --- a/studio/frontend/src/components/ui/confetti.tsx +++ b/studio/frontend/src/components/ui/confetti.tsx @@ -34,7 +34,7 @@ export type ConfettiRef = Api | null; const ConfettiContext = createContext({} as Api); -// Studio CSP blocks canvas-confetti's default blob: worker, so force +// Unsloth CSP blocks canvas-confetti's default blob: worker, so force // useWorker: false. Module-scoped so the prop default keeps stable // identity across renders (`canvasRef` depends on `globalOptions`). const DEFAULT_GLOBAL_OPTIONS: ConfettiGlobalOptions = { diff --git a/studio/frontend/src/features/auth/components/auth-form.tsx b/studio/frontend/src/features/auth/components/auth-form.tsx index 119471da10..73db10d41b 100644 --- a/studio/frontend/src/features/auth/components/auth-form.tsx +++ b/studio/frontend/src/features/auth/components/auth-form.tsx @@ -298,7 +298,7 @@ export function AuthForm({ mode }: AuthFormProps): ReactElement | null { // reset-password"), which the installer puts on PATH on every platform. // Do NOT rewrite it to a relative Windows path like // ".\unsloth_studio\Scripts\unsloth.exe ..." -- that only resolves inside - // the Studio home dir and fails with CommandNotFoundException elsewhere. + // the Unsloth home dir and fails with CommandNotFoundException elsewhere. // Show the backend message as-is. const msg = err instanceof Error ? err.message : "Auth failed."; setError(msg); diff --git a/studio/frontend/src/features/chat/artifacts/html-frame.tsx b/studio/frontend/src/features/chat/artifacts/html-frame.tsx index b26f2f6685..36e3ed8a5b 100644 --- a/studio/frontend/src/features/chat/artifacts/html-frame.tsx +++ b/studio/frontend/src/features/chat/artifacts/html-frame.tsx @@ -28,7 +28,7 @@ export function buildArtifactSrcDoc(code: string): string { } // Preview iframes intentionally omit allow-downloads: generated canvases can -// offer their own UI, but downloads must go through Studio's explicit +// offer their own UI, but downloads must go through Unsloth's explicit // copy/download controls outside the no-same-origin sandbox. export function ArtifactHtmlFrame({ code, diff --git a/studio/frontend/src/features/chat/hooks/use-chat-sidebar-items.ts b/studio/frontend/src/features/chat/hooks/use-chat-sidebar-items.ts index 0a0df1139b..bfb3eeb14c 100644 --- a/studio/frontend/src/features/chat/hooks/use-chat-sidebar-items.ts +++ b/studio/frontend/src/features/chat/hooks/use-chat-sidebar-items.ts @@ -45,7 +45,7 @@ export function groupThreads( for (const t of threads) { // Coerce archived to a boolean before comparing. Legacy threads (from the - // older browser-only Studio, or any record predating the archived field) + // older browser-only Unsloth, or any record predating the archived field) // can have archived === undefined or null; a raw `!== archived` comparison // would drop those from BOTH the Recents (archived=false) and Archived // (archived=true) lists, hiding existing chats. Treat missing as false. diff --git a/studio/frontend/src/features/chat/lib/friendly-names.ts b/studio/frontend/src/features/chat/lib/friendly-names.ts index 79744b3181..bc9c77b12d 100644 --- a/studio/frontend/src/features/chat/lib/friendly-names.ts +++ b/studio/frontend/src/features/chat/lib/friendly-names.ts @@ -5,7 +5,7 @@ * Friendly default names for auto-created OpenAI shell containers, used by the * chat-adapter's lazy-create path (Code pill on, no thread container, non-default * TTL). Goal: a memorable label like "otter" instead of "chat-abc12345"; users - * can still rename via the Studio alias map. + * can still rename via the Unsloth alias map. * * The list is curated to be unambiguous, non-offensive nouns from natural * categories (animals, plants, geography, materials, weather), avoid diff --git a/studio/frontend/src/features/chat/provider-capabilities.ts b/studio/frontend/src/features/chat/provider-capabilities.ts index 79c9a3205c..ec251cdada 100644 --- a/studio/frontend/src/features/chat/provider-capabilities.ts +++ b/studio/frontend/src/features/chat/provider-capabilities.ts @@ -409,7 +409,7 @@ function isGeminiImageModel(modelId: string): boolean { * Whether the saved Gemini connection points at a custom OpenAI-compat gateway * (any non-Google host). The backend `_is_openai_compatible` routes these * through `/chat/completions` instead of the native translator, so native Gemini - * tool envelopes never reach them. Hide the matching Studio pills here so the + * tool envelopes never reach them. Hide the matching Unsloth pills here so the * request, builder, and UI agree. */ export function isGeminiCustomOpenAICompatBase( diff --git a/studio/frontend/src/features/chat/stores/chat-runtime-store.ts b/studio/frontend/src/features/chat/stores/chat-runtime-store.ts index af99458349..192ce1ec69 100644 --- a/studio/frontend/src/features/chat/stores/chat-runtime-store.ts +++ b/studio/frontend/src/features/chat/stores/chat-runtime-store.ts @@ -671,7 +671,7 @@ type ChatRuntimeStore = { // Describe figures/charts at ingest time (vision model required). ragCaptionFigures: boolean; /** - * When on, local Studio tool calls pause for an explicit allow/deny in the + * When on, local Unsloth tool calls pause for an explicit allow/deny in the * chat before they run. */ confirmToolCalls: boolean; @@ -1824,7 +1824,7 @@ export const useChatRuntimeStore = create((set, get) => ({ setContextUsage: (contextUsage) => set({ contextUsage }), })); -// Mirror token edits made through the shared store (e.g. Studio's field). +// Mirror token edits made through the shared store (e.g. Unsloth's field). const unsubscribeHfTokenMirror = mirrorHfTokenInto(useChatRuntimeStore); if (import.meta.hot) { import.meta.hot.dispose(unsubscribeHfTokenMirror); diff --git a/studio/frontend/src/features/chat/utils/chat-history-storage.ts b/studio/frontend/src/features/chat/utils/chat-history-storage.ts index 00df3657b1..2ed17c26a0 100644 --- a/studio/frontend/src/features/chat/utils/chat-history-storage.ts +++ b/studio/frontend/src/features/chat/utils/chat-history-storage.ts @@ -324,7 +324,7 @@ async function importLegacyChatsIfNeeded(): Promise { if (legacyChatImportPromise) return legacyChatImportPromise; legacyChatImportPromise = (async () => { - // Fast-path: no Dexie DB -- new user, never had browser-only Studio. + // Fast-path: no Dexie DB -- new user, never had browser-only Unsloth. if (await dexieDbAbsent()) { markLegacyChatImportDone(); return; diff --git a/studio/frontend/src/features/hub/download-manager/api.ts b/studio/frontend/src/features/hub/download-manager/api.ts index 3373a47b04..2c55b90edd 100644 --- a/studio/frontend/src/features/hub/download-manager/api.ts +++ b/studio/frontend/src/features/hub/download-manager/api.ts @@ -12,7 +12,7 @@ function parseErrorText(status: number, body: unknown): string { const detail = (body as { detail?: unknown }).detail; const formatted = formatFastApiDetail(detail); if (status === 405) { - return `${formatted || "Method Not Allowed"} - the Studio backend did not accept this API method. Restart Studio so the frontend and backend are on the same build.`; + return `${formatted || "Method Not Allowed"} - the Unsloth backend did not accept this API method. Restart Unsloth so the frontend and backend are on the same build.`; } if (formatted) return formatted; const message = (body as { message?: unknown }).message; diff --git a/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx b/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx index a5ce25dd32..e60786decb 100644 --- a/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx +++ b/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx @@ -113,7 +113,7 @@ export function ModelSelectionStep() { return applyPriorityOrdering(ids); }, [hfResults]); - // Match Studio: only show exception signals (OOM/TIGHT) in training flows. + // Match Unsloth: only show exception signals (OOM/TIGHT) in training flows. const vramMap = useMemo(() => { const fitMap = buildModelVramMap( hfResults, diff --git a/studio/frontend/src/features/settings/components/usage-examples.tsx b/studio/frontend/src/features/settings/components/usage-examples.tsx index 86125585cc..4ccc43d16c 100644 --- a/studio/frontend/src/features/settings/components/usage-examples.tsx +++ b/studio/frontend/src/features/settings/components/usage-examples.tsx @@ -229,12 +229,12 @@ curl.exe ${base}/v1/chat/completions \` } // A second OpenAI call naming a different downloaded GGUF: with auto-switch on, -// Studio loads it before serving, so the model field selects the served model. +// Unsloth loads it before serving, so the model field selects the served model. function pythonSwitchDemo(): string { return ` # "Switch model by request" is on: replace the model below with another GGUF you -# have downloaded and Studio loads it before serving. Unknown names keep serving +# have downloaded and Unsloth loads it before serving. Unknown names keep serving # the current model. response = client.chat.completions.create( model=${j(SWITCH_MODEL)}, @@ -349,7 +349,7 @@ function javascriptSwitchDemo(): string { return ` // "Switch model by request" is on: replace the model below with another GGUF you -// have downloaded and Studio loads it before serving. Unknown names keep serving +// have downloaded and Unsloth loads it before serving. Unknown names keep serving // the current model. const switchResponse = await client.chat.completions.create({ model: ${j(SWITCH_MODEL)}, @@ -512,7 +512,7 @@ export function UsageExamples({ apiKey }: { apiKey?: string | null }) { if (!localAgentDetection) { setDetectedAgents([]); // A previously auto-picked agent was only ever verified against the - // Studio backend's PATH, which is meaningless now that this panel no + // Unsloth backend's PATH, which is meaningless now that this panel no // longer targets a loopback base -- don't leave it selected, but // never touch a choice the user made by hand. if (!agentPickedByUserRef.current) { diff --git a/studio/frontend/src/features/studio/recent-trainings-section.tsx b/studio/frontend/src/features/studio/recent-trainings-section.tsx index 43808a7ca9..ba65d7f736 100644 --- a/studio/frontend/src/features/studio/recent-trainings-section.tsx +++ b/studio/frontend/src/features/studio/recent-trainings-section.tsx @@ -8,7 +8,7 @@ import { HistoryCardGrid } from "./history-card-grid"; /** * Recent training runs surfaced on Data Recipes and Export. Selecting a run - * stores its id and navigates to Studio, which auto-opens its History tab. + * stores its id and navigates to Unsloth, which auto-opens its History tab. * Renders nothing once we know there are no runs. */ export function RecentTrainingsSection() { diff --git a/studio/frontend/src/features/training/stores/training-config-store.ts b/studio/frontend/src/features/training/stores/training-config-store.ts index a927f83fd8..d204e24f47 100644 --- a/studio/frontend/src/features/training/stores/training-config-store.ts +++ b/studio/frontend/src/features/training/stores/training-config-store.ts @@ -1004,7 +1004,7 @@ export const useTrainingConfigStore = create()( } if (version < 12) { // hfToken moved to the shared hf-token-store; seed it once so an - // existing Studio-only token isn't lost. + // existing Unsloth-only token isn't lost. const legacyToken = typeof s.hfToken === "string" ? s.hfToken.trim() : ""; if (legacyToken && !getHfToken()) { useHfTokenStore.getState().setToken(legacyToken); diff --git a/studio/frontend/src/features/transformers-upgrade/components/transformers-upgrade-dialog.tsx b/studio/frontend/src/features/transformers-upgrade/components/transformers-upgrade-dialog.tsx index 98c590e5e4..3e39a8ff1c 100644 --- a/studio/frontend/src/features/transformers-upgrade/components/transformers-upgrade-dialog.tsx +++ b/studio/frontend/src/features/transformers-upgrade/components/transformers-upgrade-dialog.tsx @@ -82,7 +82,7 @@ export function TransformersUpgradeDialog() { <> Even the latest transformers release on PyPI does not support it yet: the architecture is only available on the - transformers development branch (main), and Studio does + transformers development branch (main), and Unsloth does not install development builds. Support arrives with the next transformers release on PyPI. diff --git a/studio/frontend/src/hooks/use-tauri-backend.ts b/studio/frontend/src/hooks/use-tauri-backend.ts index db996768a6..53122864e7 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 Unsloth 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 Unsloth 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."; + ? `An Unsloth 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.` + : "An Unsloth 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."; } async function waitForManagedServerPort( diff --git a/studio/frontend/src/i18n/README.md b/studio/frontend/src/i18n/README.md index ba8cce5d8d..7594e4723f 100644 --- a/studio/frontend/src/i18n/README.md +++ b/studio/frontend/src/i18n/README.md @@ -8,5 +8,5 @@ - Preserve interpolation variables exactly, for example `{count}`, `{model}`, and `{provider}`. - Keep product and technical names unchanged unless there is an established localized name, for example `Unsloth Studio`, `LoRA`, `GGUF`, and `Hugging Face`. - Keep translation changes small and reviewable. Prefer separate commits for runtime changes, UI migration, and locale text. -- When adding user-facing Studio UI text, add the English message key first and add non-English overrides only when the translation is clear. +- When adding user-facing Unsloth UI text, add the English message key first and add non-English overrides only when the translation is clear. - Run `npx tsx src/i18n/check-parity.ts` before committing to ensure there are no shape mismatches or placeholder discrepancies in the non-English overlays. diff --git a/studio/frontend/src/i18n/locales/ar.ts b/studio/frontend/src/i18n/locales/ar.ts index 744a2002c9..4bd4328cce 100644 --- a/studio/frontend/src/i18n/locales/ar.ts +++ b/studio/frontend/src/i18n/locales/ar.ts @@ -113,7 +113,7 @@ export const ar = { showToken: "إظهار التوكن", tokenSaved: "تم حفظ التوكن", password: "كلمة المرور", - passwordDescription: "تغيير كلمة المرور لحساب Studio هذا.", + passwordDescription: "تغيير كلمة المرور لحساب Unsloth هذا.", passwordDialog: { trigger: "تغيير كلمة المرور", title: "تغيير كلمة المرور", @@ -284,7 +284,7 @@ export const ar = { }, resources: { title: "النظام", - description: "مراقبة أجهزة خادم Studio هذا وتخزينه.", + description: "مراقبة أجهزة خادم Unsloth هذا وتخزينه.", liveUpdates: "التحديثات المباشرة", floatingWindow: "نافذة عائمة", disableOverlay: "تعطيل التراكب", diff --git a/studio/frontend/src/i18n/locales/de.ts b/studio/frontend/src/i18n/locales/de.ts index 7d94e7656e..c28d07790f 100644 --- a/studio/frontend/src/i18n/locales/de.ts +++ b/studio/frontend/src/i18n/locales/de.ts @@ -115,7 +115,7 @@ export const de = { tokenSaved: "Token gespeichert", password: "Passwort", passwordDescription: - "Ändern Sie das Passwort für dieses Studio-Konto.", + "Ändern Sie das Passwort für dieses Unsloth-Konto.", passwordDialog: { trigger: "Passwort ändern", title: "Passwort ändern", @@ -295,7 +295,7 @@ export const de = { resources: { title: "System", description: - "Überwachen Sie Hardware und Speicher dieses Studio-Servers.", + "Überwachen Sie Hardware und Speicher dieses Unsloth-Servers.", liveUpdates: "Live-Updates", floatingWindow: "Schwebendes Fenster", disableOverlay: "Overlay deaktivieren", diff --git a/studio/frontend/src/i18n/locales/es.ts b/studio/frontend/src/i18n/locales/es.ts index 988c109a3f..b7dfee10b8 100644 --- a/studio/frontend/src/i18n/locales/es.ts +++ b/studio/frontend/src/i18n/locales/es.ts @@ -115,7 +115,7 @@ export const es = { tokenSaved: "Token guardado", password: "Contraseña", passwordDescription: - "Cambia la contraseña de esta cuenta de Studio.", + "Cambia la contraseña de esta cuenta de Unsloth.", passwordDialog: { trigger: "Cambiar contraseña", title: "Cambiar contraseña", @@ -294,7 +294,7 @@ export const es = { resources: { title: "Sistema", description: - "Monitorea el hardware y el almacenamiento de este servidor de Studio.", + "Monitorea el hardware y el almacenamiento de este servidor de Unsloth.", liveUpdates: "Actualizaciones en vivo", floatingWindow: "Ventana flotante", disableOverlay: "Desactivar superposición", diff --git a/studio/frontend/src/i18n/locales/fr.ts b/studio/frontend/src/i18n/locales/fr.ts index e1f2a0c5ec..6105cd8ccf 100644 --- a/studio/frontend/src/i18n/locales/fr.ts +++ b/studio/frontend/src/i18n/locales/fr.ts @@ -115,7 +115,7 @@ export const fr = { tokenSaved: "Token enregistré", password: "Mot de passe", passwordDescription: - "Changez le mot de passe de ce compte Studio.", + "Changez le mot de passe de ce compte Unsloth.", passwordDialog: { trigger: "Changer le mot de passe", title: "Changer le mot de passe", @@ -291,7 +291,7 @@ export const fr = { resources: { title: "Système", description: - "Surveillez le matériel et le stockage de ce serveur Studio.", + "Surveillez le matériel et le stockage de ce serveur Unsloth.", liveUpdates: "Mises à jour en direct", floatingWindow: "Fenêtre flottante", disableOverlay: "Désactiver la superposition", diff --git a/studio/frontend/src/i18n/locales/hi.ts b/studio/frontend/src/i18n/locales/hi.ts index 77b6265e7b..732ae1d7fa 100644 --- a/studio/frontend/src/i18n/locales/hi.ts +++ b/studio/frontend/src/i18n/locales/hi.ts @@ -113,7 +113,7 @@ export const hi = { showToken: "token दिखाएं", tokenSaved: "Token सहेजा गया", password: "पासवर्ड", - passwordDescription: "इस Studio खाते के लिए पासवर्ड बदलें।", + passwordDescription: "इस Unsloth खाते के लिए पासवर्ड बदलें।", passwordDialog: { trigger: "पासवर्ड बदलें", title: "पासवर्ड बदलें", @@ -283,7 +283,7 @@ export const hi = { }, resources: { title: "सिस्टम", - description: "इस Studio सर्वर के हार्डवेयर और स्टोरेज की निगरानी करें।", + description: "इस Unsloth सर्वर के हार्डवेयर और स्टोरेज की निगरानी करें।", liveUpdates: "लाइव अपडेट", floatingWindow: "फ्लोटिंग विंडो", disableOverlay: "ओवरले अक्षम करें", diff --git a/studio/frontend/src/i18n/locales/ja.ts b/studio/frontend/src/i18n/locales/ja.ts index a261994f03..de5e93c672 100644 --- a/studio/frontend/src/i18n/locales/ja.ts +++ b/studio/frontend/src/i18n/locales/ja.ts @@ -360,7 +360,7 @@ export const ja = { }, resources: { title: "システム", - description: "この Studio サーバーのハードウェアとストレージを監視します。", + description: "この Unsloth サーバーのハードウェアとストレージを監視します。", liveUpdates: "リアルタイム更新", floatingWindow: "フローティングウィンドウ", disableOverlay: "オーバーレイを無効化", diff --git a/studio/frontend/src/i18n/locales/ko.ts b/studio/frontend/src/i18n/locales/ko.ts index 7c5691925e..6ff9cdbb5e 100644 --- a/studio/frontend/src/i18n/locales/ko.ts +++ b/studio/frontend/src/i18n/locales/ko.ts @@ -113,7 +113,7 @@ export const ko = { showToken: "토큰 표시", tokenSaved: "토큰이 저장되었습니다", password: "비밀번호", - passwordDescription: "이 Studio 계정의 비밀번호를 변경합니다.", + passwordDescription: "이 Unsloth 계정의 비밀번호를 변경합니다.", passwordDialog: { trigger: "비밀번호 변경", title: "비밀번호 변경", @@ -282,7 +282,7 @@ export const ko = { }, resources: { title: "시스템", - description: "이 Studio 서버의 하드웨어와 저장소를 모니터링합니다.", + description: "이 Unsloth 서버의 하드웨어와 저장소를 모니터링합니다.", liveUpdates: "실시간 업데이트", floatingWindow: "플로팅 창", disableOverlay: "오버레이 비활성화", diff --git a/studio/frontend/src/i18n/locales/ru.ts b/studio/frontend/src/i18n/locales/ru.ts index c7464a3b44..60e939bb0d 100644 --- a/studio/frontend/src/i18n/locales/ru.ts +++ b/studio/frontend/src/i18n/locales/ru.ts @@ -113,7 +113,7 @@ export const ru = { showToken: "Показать токен", tokenSaved: "Токен сохранён", password: "Пароль", - passwordDescription: "Изменить пароль для этого аккаунта Studio.", + passwordDescription: "Изменить пароль для этого аккаунта Unsloth.", passwordDialog: { trigger: "Изменить пароль", title: "Изменить пароль", @@ -283,7 +283,7 @@ export const ru = { }, resources: { title: "Система", - description: "Мониторинг оборудования и хранилища этого сервера Studio.", + description: "Мониторинг оборудования и хранилища этого сервера Unsloth.", liveUpdates: "Обновления в реальном времени", floatingWindow: "Плавающее окно", disableOverlay: "Отключить оверлей", diff --git a/studio/frontend/src/i18n/locales/zh-CN.ts b/studio/frontend/src/i18n/locales/zh-CN.ts index ff218adad2..37c73086cf 100644 --- a/studio/frontend/src/i18n/locales/zh-CN.ts +++ b/studio/frontend/src/i18n/locales/zh-CN.ts @@ -375,7 +375,7 @@ export const zhCN = { }, resources: { title: "系统", - description: "监控此 Studio 服务器的硬件和存储。", + description: "监控此 Unsloth 服务器的硬件和存储。", liveUpdates: "实时更新", floatingWindow: "悬浮窗口", disableOverlay: "禁用悬浮层", diff --git a/studio/frontend/src/index.css b/studio/frontend/src/index.css index 6d4c21eec8..a43e3d974b 100644 --- a/studio/frontend/src/index.css +++ b/studio/frontend/src/index.css @@ -2599,7 +2599,7 @@ html[data-chat-font] .aui-root { * the documented WCAG outcome (motion is "minimised, not removed"). * * .animate-spin and generated image loading dots are the exceptions: loading - * indicators across Studio (tool execution loaders, sonner toasts, Tauri + * indicators across Unsloth (tool execution loaders, sonner toasts, Tauri * startup / update screens, the primitive, and image generation * cards). Freezing them removes the only visual signal that work is in flight, * so they keep animating. diff --git a/studio/frontend/src/lib/tauri-diagnostics.ts b/studio/frontend/src/lib/tauri-diagnostics.ts index 5c07931a86..2b687478fe 100644 --- a/studio/frontend/src/lib/tauri-diagnostics.ts +++ b/studio/frontend/src/lib/tauri-diagnostics.ts @@ -62,7 +62,7 @@ export function redactDiagnosticsText(text: string): string { "$1$2", ); - // Redact Studio paths before broader home-directory paths. + // Redact Unsloth paths before broader home-directory paths. redacted = redacted.replace( /(?:\/Users|\/home)\/[^\s/]+\/\.unsloth\/studio/gi, "", diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index cca8886777..9bbd0cb8be 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -2897,7 +2897,7 @@ def _pick_rocm_gfx_target(out: str) -> str | None: break if _vis_raw is not None: _vis = _vis_raw.strip() - # Empty or "-1" means "no AMD GPU visible" (matches the rest of Studio). + # Empty or "-1" means "no AMD GPU visible" (matches the rest of Unsloth). if _vis == "" or _vis == "-1": return None _first = _vis.split(",")[0].strip() @@ -4437,7 +4437,7 @@ def ensure_diffusion_visual_server( approved_checksums: ApprovedReleaseChecksums, ) -> None: """Best-effort placement of the DiffusionGemma visual-server binary next to - llama-server in the install tree, so Studio can serve DiffusionGemma GGUFs + llama-server in the install tree, so Unsloth can serve DiffusionGemma GGUFs without any DG_* env. This is an Unsloth artifact (not a ggml-org one), so it is optional: if it is already present we just make it executable, otherwise we try the published release and quietly skip on absence. A source build @@ -4719,7 +4719,7 @@ def runtime_patterns_for_choice(choice: AssetChoice) -> list[str]: # repackage the SO/DLL set (e.g. ggml-org/llama.cpp#23462 split the # per-binary entry code into paired ``lib-impl.so`` shared # libraries between b9279 and b9283) without us re-enumerating - # every new file. Studio invokes llama-server, llama-quantize, and the + # every new file. Unsloth invokes llama-server, llama-quantize, and the # DiffusionGemma visual-server (when the bundle ships it, for native # DiffusionGemma serving); other CLIs upstream ships (llama-cli, # llama-bench, ...) are skipped. @@ -6320,7 +6320,7 @@ def _linux_published_attempts(host: HostInfo, bundle: PublishedReleaseBundle) -> from asset names.""" attempts: list[AssetChoice] = [] if host.has_usable_nvidia: - # Prefer the cudart major Studio loads at runtime (torch's bundled + # Prefer the cudart major Unsloth loads at runtime (torch's bundled # libcudart), not the newest detected on disk. Without this a stray # cuda13 runtime outranks the torch cuda12 the binary links against. torch_preference = detect_torch_cuda_runtime_preference(host) diff --git a/studio/install_node_prebuilt.py b/studio/install_node_prebuilt.py index 4038216eef..fb40634e95 100644 --- a/studio/install_node_prebuilt.py +++ b/studio/install_node_prebuilt.py @@ -6,7 +6,7 @@ Downloads an official Node.js archive from nodejs.org into an isolated ``/node`` and never touches the system Node/npm. Pinning Node 24+ -LTS clears the Studio frontend build floor (Vite 8: Node ^20.19 || >=22.12, +LTS clears the Unsloth frontend build floor (Vite 8: Node ^20.19 || >=22.12, npm >= 11) with the npm it bundles. Archives are verified against sha256 digests pinned in ``node_prebuilt_pins.json`` diff --git a/studio/install_python_stack.py b/studio/install_python_stack.py index 19c492deaa..95c9356d4a 100644 --- a/studio/install_python_stack.py +++ b/studio/install_python_stack.py @@ -1008,7 +1008,7 @@ def _install_bnb_windows_rocm() -> bool: # `hipinfo.exe` at import time to detect the GPU arch and logs a scary # (harmless) ERROR + WARNING on every import when it is missing. The venv # Scripts dir is on PATH only when the venv is activated, which neither - # Studio nor the installer's child processes ever do. + # Unsloth nor the installer's child processes ever do. _scripts_dir = os.path.dirname(sys.executable) if os.path.isfile(os.path.join(_scripts_dir, "hipInfo.exe")) and not shutil.which( "hipinfo.exe" @@ -2318,7 +2318,7 @@ def install_python_stack() -> int: _progress("dependency overrides (skipped, no torch)") elif _rocm_windows_torch_installed or _installed_torch_is_windows_rocm(): # No working Windows ROCm torchao build: it imports an absent c10d backend - # and crashes transformers.quantizers. Studio stubs it at runtime, so + # and crashes transformers.quantizers. Unsloth stubs it at runtime, so # installing it only ships a package that crashes on import -- skip it. _progress("dependency overrides (skipped, Windows ROCm)") _safe_print(" Windows ROCm -- skipping torchao (no working build; stubbed at runtime)") @@ -2371,7 +2371,7 @@ def install_python_stack() -> int: # "https://raw.githubusercontent.com/unslothai/unsloth/refs/heads/main/unsloth/save.py", # ) - # 8. Studio dependencies + # 8. Unsloth dependencies _progress("studio deps") pip_install( "Installing studio dependencies", diff --git a/studio/setup.ps1 b/studio/setup.ps1 index dab1e1e73f..98e801cd3c 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -322,7 +322,7 @@ function Write-CudaDriverToolkitMismatch { $driverMajor = $DriverMaxCuda.Split('.')[0] substep "CUDA Toolkit $ToolkitVersion is a major-version mismatch: toolkit major $toolkitMajor exceeds driver CUDA major $driverMajor ($DriverMaxCuda)." $Color substep "Update the NVIDIA GPU driver to run CUDA Toolkit $ToolkitVersion, or install a CUDA $driverMajor.x toolkit." $Color - substep "Or let Studio use the prebuilt CUDA bundle; it does not need the local toolkit." $Color + substep "Or let Unsloth use the prebuilt CUDA bundle; it does not need the local toolkit." $Color } # Detect CUDA Compute Capability via nvidia-smi. @@ -937,13 +937,13 @@ function Show-NpmRegistryHint { Write-Host "" step "frontend" "registry.npmjs.org looks blocked (corporate firewall/proxy?)" "Yellow" if ($mirror) { - substep "Studio pins the public npm registry; your mirror is being ignored." + substep "Unsloth pins the public npm registry; your mirror is being ignored." substep "Detected a registry in your npm config:" substep " $mirror" - substep "Re-run pointing Studio at it:" + substep "Re-run pointing Unsloth at it:" substep " `$env:UNSLOTH_NPM_REGISTRY='$mirror'; .\install.ps1 --local" } else { - substep "If you use a private mirror/proxy, point Studio at it and re-run:" + substep "If you use a private mirror/proxy, point Unsloth at it and re-run:" substep " `$env:UNSLOTH_NPM_REGISTRY='https://your-mirror.example/api/npm/'; .\install.ps1 --local" } substep "(min-release-age and save-exact stay enforced.)" @@ -1070,7 +1070,7 @@ if (-not $HasNvidiaSmi) { } # ── Helper: run amd-smi without triggering a UAC elevation prompt ── # amd-smi on Windows auto-elevates to read GPU/APU memory, surfacing a confusing -# DiskPart UAC prompt mid-install (Studio backend amd.py hits the same). RunAsInvoker +# DiskPart UAC prompt mid-install (Unsloth backend amd.py hits the same). RunAsInvoker # forces it (and helpers it spawns) to run un-elevated; on failure the WMI name -> # gfx fallback still resolves the arch. function Invoke-AmdSmiNoElevate { @@ -1132,7 +1132,7 @@ if (-not $HasNvidiaSmi) { if ([string]::IsNullOrWhiteSpace($HipinfoPath)) { return $false } # VenvDir/VIRTUAL_ENV can be unset this early (the update flow probes before # VenvDir is set), so also derive the venv from the setup python + default - # Studio home, else the venv hipInfo isn't caught. + # Unsloth home, else the venv hipInfo isn't caught. $venvRoots = @() if ($env:VIRTUAL_ENV) { $venvRoots += $env:VIRTUAL_ENV } $vd = Get-Variable -Name VenvDir -ValueOnly -ErrorAction SilentlyContinue @@ -1141,7 +1141,7 @@ if (-not $HasNvidiaSmi) { try { $venvRoots += (Split-Path -Parent (Split-Path -Parent $env:UNSLOTH_SETUP_PYTHON)) } catch {} } if ($env:USERPROFILE) { $venvRoots += (Join-Path $env:USERPROFILE ".unsloth\studio\unsloth_studio") } - # A custom Studio home (UNSLOTH_STUDIO_HOME / STUDIO_HOME alias) moves the + # A custom Unsloth home (UNSLOTH_STUDIO_HOME / STUDIO_HOME alias) moves the # venv off the default path; seed it too or its hipInfo escapes the filter. $studioHomeEnv = if (-not [string]::IsNullOrWhiteSpace($env:UNSLOTH_STUDIO_HOME)) { $env:UNSLOTH_STUDIO_HOME.Trim() } elseif (-not [string]::IsNullOrWhiteSpace($env:STUDIO_HOME)) { $env:STUDIO_HOME.Trim() } else { $null } if ($studioHomeEnv) { @@ -1860,7 +1860,7 @@ $SysNpmVersion = "" $NodeSource = $null if (-not $IsPipInstall) { - # Put Node beside the Studio root. OXC can still need npm when the + # Put Node beside the Unsloth root. OXC can still need npm when the # frontend build is skipped. if (-not [string]::IsNullOrWhiteSpace($env:UNSLOTH_STUDIO_HOME)) { $NodeOverride = $env:UNSLOTH_STUDIO_HOME.Trim() } elseif (-not [string]::IsNullOrWhiteSpace($env:STUDIO_HOME)) { $NodeOverride = $env:STUDIO_HOME.Trim() } @@ -2137,17 +2137,17 @@ if ($NeedNodeForSetup) { step "frontend" "skipped (no suitable Node; system left untouched)" "Yellow" } $NeedFrontendBuild = $false - substep "found Node='$SysNodeVersion' npm='$SysNpmVersion'; Studio needs Node >=20.19/22.12/23 and npm >= 11" "Yellow" + substep "found Node='$SysNodeVersion' npm='$SysNpmVersion'; Unsloth needs Node >=20.19/22.12/23 and npm >= 11" "Yellow" substep "install a suitable Node + npm, or unset UNSLOTH_SKIP_NODE_INSTALL to let Unsloth manage an isolated Node" "Yellow" } elseif ($NodeSource -eq "bundled") { New-Item -ItemType Directory -Force -Path $NodeParent -ErrorAction SilentlyContinue | Out-Null - # Minimal ownership guard for a custom-home dir (the full Studio-owned + # Minimal ownership guard for a custom-home dir (the full Unsloth-owned # helpers are defined later); never os.replace over a user-owned dir. if ($NodeOverride -and (Test-Path -LiteralPath $NodeDir -PathType Container)) { $nodeOwnedMarker = Join-Path $NodeDir ".unsloth-studio-owned" $nodeMeta = Join-Path $NodeDir "UNSLOTH_NODE_PREBUILT_INFO.json" if (-not (Test-Path -LiteralPath $nodeOwnedMarker) -and -not (Test-Path -LiteralPath $nodeMeta)) { - Write-Host "[ERROR] $NodeDir already exists and is not a Studio-owned Node install." -ForegroundColor Red + Write-Host "[ERROR] $NodeDir already exists and is not an Unsloth-owned Node install." -ForegroundColor Red Write-Host " Move it aside or choose an empty UNSLOTH_STUDIO_HOME before re-running." -ForegroundColor Yellow exit 1 } @@ -2160,7 +2160,7 @@ if ($NeedNodeForSetup) { $nodeExit = $LASTEXITCODE if ($nodeExit -eq 3) { Write-Host $nodeOut -ForegroundColor DarkGray - step "node" "install blocked by another active Studio install" "Red" + step "node" "install blocked by another active Unsloth install" "Red" exit 3 } elseif ($nodeExit -ne 0) { Write-Host $nodeOut -ForegroundColor DarkGray @@ -2486,7 +2486,7 @@ if (Test-Path -LiteralPath $LegacyStudioHome -PathType Container) { $LegacyStudioHome = (Resolve-Path -LiteralPath $LegacyStudioHome).Path } $StudioHomeIsCustom = ($_studioHomeCanon -ne $LegacyStudioHome) -# Directory-local evidence that Studio created $Path, used to adopt a custom-home +# Directory-local evidence that Unsloth created $Path, used to adopt a custom-home # llama.cpp predating the .unsloth-studio-owned marker (see setup.sh). Only the # prebuilt UNSLOTH_PREBUILT_INFO.json counts; source builds are indistinguishable # from a user clone on Windows and stay under the strict guard. @@ -2506,7 +2506,7 @@ function Assert-StudioOwnedOrAbsent { Mark-StudioOwned $Path return } - Write-Host "[ERROR] $Path already exists and is not marked as a Studio-owned $Label." -ForegroundColor Red + Write-Host "[ERROR] $Path already exists and is not marked as an Unsloth-owned $Label." -ForegroundColor Red Write-Host " Move it aside or choose an empty UNSLOTH_STUDIO_HOME before re-running." -ForegroundColor Yellow exit 1 } @@ -2576,7 +2576,7 @@ if ((Test-Path -LiteralPath $VenvDir -PathType Container) -and -not $NoTorchMode $reason = if ($installedTorchTag) { "torch $installedTorchTag != required $expectedTorchTag" } else { "torch could not be imported" } if ($InstallerManagedSetup) { substep "Stale venv detected ($reason)." "Yellow" - Write-Host " [ERROR] The existing Studio environment needs repair." -ForegroundColor Red + Write-Host " [ERROR] The existing Unsloth environment needs repair." -ForegroundColor Red Write-Host " Re-run install.ps1 so it can replace the environment safely with rollback." -ForegroundColor Yellow exit 1 } @@ -2598,7 +2598,7 @@ if ((Test-Path -LiteralPath $VenvDir -PathType Container) -and -not $NoTorchMode Remove-Item -LiteralPath $VenvDir -Recurse -Force -ErrorAction Stop } catch { Write-Host " [ERROR] Could not remove stale venv: $($_.Exception.Message)" -ForegroundColor Red - Write-Host " Close any running Studio/Python processes and re-run setup." -ForegroundColor Red + Write-Host " Close any running Unsloth/Python processes and re-run setup." -ForegroundColor Red exit 1 } } @@ -2764,7 +2764,7 @@ if ($script:UnslothVerbose) { # The CUDA tag is chosen based on the driver's max supported CUDA version. # Triton/inductor filenames are long and can hit Windows MAX_PATH (260). With long -# paths on, cache under Studio home; else use a short drive-root dir for headroom. +# paths on, cache under Unsloth home; else use a short drive-root dir for headroom. if ($LongPathsEnabled) { $TorchCacheDir = Join-Path $StudioHome "TORCHINDUCTOR_CACHE_DIR" } else { @@ -2793,7 +2793,7 @@ $ROCmIndexUrl = $null # Install AMD ROCm PyTorch wheels when ROCm is confirmed OR a gfx arch is known # (name-inferred on Adrenalin-only hosts). The per-arch wheels bundle the runtime # (rocm-sdk-libraries-), so torch.cuda.is_available() is True without a HIP -# SDK -- which flips Studio out of chat-only (CHAT_ONLY) and enables Train/Export. +# SDK -- which flips Unsloth out of chat-only (CHAT_ONLY) and enables Train/Export. # Gating on $HasROCm alone left Strix Halo / Radeon 8060S on CPU torch; a failed # ROCm install still falls back to CPU below, so this is safe. if (($HasROCm -or $ROCmGfxArch) -and $CuTag -eq "cpu") { @@ -3231,7 +3231,7 @@ if ($LocalLlamaCppSrc) { # Reusing a local dir disables both the prebuilt download and the source # build, so a runnable llama-server.exe must already be present. Accept any # layout LlamaCppBackend._layout_candidates() resolves (root-level, build\bin, - # or build\bin\Release) so the flag never rejects a tree Studio could run. + # or build\bin\Release) so the flag never rejects a tree Unsloth could run. $LocalLlamaServerFound = $false foreach ($_cand in @( (Join-Path $ResolvedLocal "llama-server.exe"), @@ -3253,7 +3253,7 @@ if ($LocalLlamaCppSrc) { } } else { # Fail clearly rather than junction an unbuilt or wrong-platform checkout - # and leave Studio with no usable binary. + # and leave Unsloth with no usable binary. if (-not $LocalLlamaServerFound) { step "llama.cpp" "no llama-server.exe under $ResolvedLocal (looked for .\llama-server.exe, .\build\bin and .\build\bin\Release) -- build llama.cpp there first, or drop --with-llama-cpp-dir" "Red" exit 1 @@ -3281,7 +3281,7 @@ if ($LocalLlamaCppSrc) { # prebuilt path's active-process handling and stop with a clear message. if (Test-Path -LiteralPath $LlamaCppDir) { step "llama.cpp" "install blocked by active llama.cpp process" "Yellow" - substep "Close Studio or other llama.cpp users and retry" "Yellow" + substep "Close Unsloth or other llama.cpp users and retry" "Yellow" exit 3 } } @@ -3414,7 +3414,7 @@ if ($LocalLlamaCppLinked) { if (Test-Path -LiteralPath $LlamaCppDir) { substep "Existing install was restored" "Yellow" } - substep "Close Studio or other llama.cpp users and retry" "Yellow" + substep "Close Unsloth or other llama.cpp users and retry" "Yellow" exit 3 } else { step "llama.cpp" "prebuilt install failed (continuing)" "Yellow" @@ -4003,7 +4003,7 @@ if ($LocalLlamaCppLinked) { } # -- Step E: Build the DiffusionGemma visual server (optional, best-effort) -- - # An example target present on llama.cpp PR #24423; lets Studio serve + # An example target present on llama.cpp PR #24423; lets Unsloth serve # DiffusionGemma GGUFs without DG_VISUAL_BIN. No-op when not configured. if ($BuildOk) { $null = cmake --build $BuildDir --config Release --target llama-diffusion-gemma-visual-server -j $NumCpu 2>&1 | Out-String diff --git a/studio/setup.sh b/studio/setup.sh index 3d67db3da7..8d47eecfda 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -127,13 +127,13 @@ _suggest_npm_registry() { printf '\n' >&2 step "frontend" "registry.npmjs.org looks blocked (corporate firewall/proxy?)" "$C_WARN" >&2 if [ -n "$_mirror" ]; then - substep "Studio pins the public npm registry; your mirror is being ignored." >&2 + substep "Unsloth pins the public npm registry; your mirror is being ignored." >&2 substep "Detected a registry in your npm config:" >&2 substep " $_mirror" >&2 - substep "Re-run pointing Studio at it:" >&2 + substep "Re-run pointing Unsloth at it:" >&2 substep " UNSLOTH_NPM_REGISTRY=$_mirror ./install.sh --local" >&2 else - substep "If you use a private mirror/proxy, point Studio at it and re-run:" >&2 + substep "If you use a private mirror/proxy, point Unsloth at it and re-run:" >&2 substep " UNSLOTH_NPM_REGISTRY=https://your-mirror.example/api/npm/ ./install.sh --local" >&2 fi substep "(min-release-age and save-exact stay enforced.)" >&2 @@ -396,7 +396,7 @@ _print_cuda_driver_toolkit_mismatch() { local _driver_major=${_driver_version%%.*} substep "CUDA Toolkit $_toolkit_version is a major-version mismatch: toolkit major $_toolkit_major exceeds driver CUDA major $_driver_major ($_driver_version)." "$C_WARN" substep "Update the NVIDIA GPU driver to run CUDA Toolkit $_toolkit_version, or install a CUDA $_driver_major.x toolkit." "$C_WARN" - substep "Or let Studio use the prebuilt CUDA bundle; it does not need the local toolkit." "$C_WARN" + substep "Or let Unsloth use the prebuilt CUDA bundle; it does not need the local toolkit." "$C_WARN" } print_llama_error_log() { @@ -531,7 +531,7 @@ _STUDIO_HOME_IS_CUSTOM=false if [ "$_studio_home_canon" != "$_LEGACY_STUDIO_HOME" ]; then _STUDIO_HOME_IS_CUSTOM=true fi -# Directory-local evidence Studio created "$1": only prebuilt-installer metadata +# Directory-local evidence Unsloth created "$1": only prebuilt-installer metadata # counts (UNSLOTH_PREBUILT_INFO.json for llama.cpp, UNSLOTH_NODE_PREBUILT_INFO.json # for Node), both written only by our installers. Mirrors the setup.ps1 Node guard. # A markerless source build stays strict since this runs right before an rm -rf. @@ -549,7 +549,7 @@ _assert_studio_owned_or_absent() { : > "$_aso_dir/$_STUDIO_OWNED_MARKER" 2>/dev/null || true return 0 fi - echo "ERROR: $_aso_dir already exists and is not marked as a Studio-owned $_aso_label." >&2 + echo "ERROR: $_aso_dir already exists and is not marked as an Unsloth-owned $_aso_label." >&2 echo " Move it aside or choose an empty UNSLOTH_STUDIO_HOME before re-running." >&2 exit 1 fi @@ -585,7 +585,7 @@ if [ "$_NEED_FRONTEND_BUILD" = false ] && [ ! -d "$_OXC_DIR" ]; then else # ── Node (isolated; never touches the system Node/npm) ── -# Studio's frontend (Vite 8) needs Node ^20.19 || >=22.12 || >=23 and npm >= 11. +# Unsloth's frontend (Vite 8) needs Node ^20.19 || >=22.12 || >=23 and npm >= 11. # Three sources: # system -- system Node + npm already satisfy both; used read-only. # bundled -- install a pinned isolated Node under $UNSLOTH_HOME/node, build-only. @@ -666,9 +666,9 @@ elif [ "$NODE_SOURCE" = bundled ]; then fi set -e if [ "$_NODE_STATUS" -eq 3 ]; then - step "node" "install blocked by another active Studio install" "$C_ERR" + step "node" "install blocked by another active Unsloth install" "$C_ERR" sed 's/^/ | /' "$_NODE_LOG" >&2; rm -f "$_NODE_LOG" - substep "close other Studio installs and retry" + substep "close other Unsloth installs and retry" exit 3 elif [ "$_NODE_STATUS" -ne 0 ]; then step "node" "isolated Node install failed" "$C_ERR" @@ -692,7 +692,7 @@ elif [ "$NODE_SOURCE" = bundled ]; then else _FRONTEND_SKIP=true step "frontend" "skipped (no suitable Node; system left untouched)" "$C_WARN" - substep "found Node='${_SYS_NODE_VER:-none}' npm='${_SYS_NPM_VER:-none}'; Studio needs Node >=20.19/22.12/23 and npm >= 11" + substep "found Node='${_SYS_NODE_VER:-none}' npm='${_SYS_NPM_VER:-none}'; Unsloth needs Node >=20.19/22.12/23 and npm >= 11" substep "install a suitable Node + npm, or unset UNSLOTH_SKIP_NODE_INSTALL to let Unsloth manage an isolated Node" fi verbose_substep "node source: $NODE_SOURCE (sys node=${_SYS_NODE_VER:-none} npm=${_SYS_NPM_VER:-none}) dir=$NODE_DIR" @@ -873,11 +873,11 @@ _remove_agent_instruction_files \ _COLAB_NO_VENV=false if [ ! -x "$VENV_DIR/bin/python" ]; then if [ "$IS_COLAB" = true ]; then - # On Colab there is no Studio venv -- install backend deps into system Python. + # On Colab there is no Unsloth venv -- install backend deps into system Python. # Strip all version constraints so pip keeps Colab's pre-installed # packages (huggingface-hub, datasets, transformers) and only pulls # in genuinely missing ones (structlog, fastapi, etc.). - substep "Colab detected, installing Studio backend dependencies..." + substep "Colab detected, installing Unsloth backend dependencies..." _COLAB_REQS_TMP="$(mktemp)" sed 's/[><=!~;].*//' "$SCRIPT_DIR/backend/requirements/studio.txt" \ | grep -v '^#' | grep -v '^$' > "$_COLAB_REQS_TMP" @@ -1254,7 +1254,7 @@ _link_local_llama_quantize_shim() { } # Accept any layout LlamaCppBackend._layout_candidates() resolves so the flag -# never rejects a tree Studio could actually run: a root-level llama-server (a +# never rejects a tree Unsloth could actually run: a root-level llama-server (a # `make` build or a flat-extracted release) or the CMake build/bin/llama-server. _has_local_llama_server() { [ -x "$1/llama-server" ] || [ -x "$1/build/bin/llama-server" ] @@ -1298,13 +1298,13 @@ if [ -n "${UNSLOTH_LOCAL_LLAMA_CPP_DIR:-}" ]; then # Reusing disables BOTH the prebuilt download and the source build, so the # linked tree must already contain a runnable llama-server in one of the # layouts the backend resolves (root-level or build/bin/). Fail clearly - # rather than link an unbuilt or wrong-platform checkout and leave Studio + # rather than link an unbuilt or wrong-platform checkout and leave Unsloth # with no usable binary. if ! _has_local_llama_server "$_RESOLVED_LOCAL"; then step "llama.cpp" "no llama-server under $_RESOLVED_LOCAL (looked for ./llama-server and ./build/bin/llama-server) -- build llama.cpp there first, or drop --with-llama-cpp-dir" "$C_ERR" exit 1 fi - # A stale link from a previous --with-llama-cpp-dir run isn't Studio-owned + # A stale link from a previous --with-llama-cpp-dir run isn't Unsloth-owned # content; drop it before the ownership check so re-runs stay idempotent # for a custom UNSLOTH_STUDIO_HOME (the assert would otherwise follow the # link into the user's dir and reject it as unowned). @@ -1389,7 +1389,7 @@ else if [ -d "$LLAMA_CPP_DIR" ]; then substep "existing install was restored" fi - substep "close Studio or other llama.cpp users and retry" + substep "close Unsloth or other llama.cpp users and retry" exit 3 else step "llama.cpp" "prebuilt install failed (continuing)" "$C_WARN" @@ -1886,7 +1886,7 @@ else ln -sf build/bin/llama-quantize "$LLAMA_CPP_DIR/llama-quantize" fi # DiffusionGemma visual server, if it was built (PR #24423): link next to - # llama-server so Studio serves DiffusionGemma GGUFs without DG_VISUAL_BIN. + # llama-server so Unsloth serves DiffusionGemma GGUFs without DG_VISUAL_BIN. if [ -f "$LLAMA_CPP_DIR/build/bin/llama-diffusion-gemma-visual-server" ]; then ln -sf build/bin/llama-diffusion-gemma-visual-server "$LLAMA_CPP_DIR/llama-diffusion-gemma-visual-server" fi @@ -1983,7 +1983,7 @@ echo "" # When called from install.sh (SKIP_STUDIO_BASE=1), exit non-zero so the # installer can report the GGUF failure after finishing PATH/shortcut setup. # When called directly via 'unsloth studio update', keep the install -# successful -- the footer above already reports the limitation and Studio +# successful -- the footer above already reports the limitation and Unsloth # is still usable for non-GGUF workflows. if [ "$_LLAMA_CPP_DEGRADED" = true ] && [ "${SKIP_STUDIO_BASE:-0}" = "1" ]; then exit 1 diff --git a/studio/src-tauri/src/commands.rs b/studio/src-tauri/src/commands.rs index 6bc2116786..5d41cb9217 100644 --- a/studio/src-tauri/src/commands.rs +++ b/studio/src-tauri/src/commands.rs @@ -25,12 +25,12 @@ fn should_emit_repair_failed(msg: &str) -> bool { fn external_conflict_message(conflict: &crate::preflight::ExternalBackendConflict) -> String { if conflict.reason == "desktop_owned_backend_active" { return format!( - "A desktop-owned Studio server for this install is already running on port {}. Quit the other desktop app instance, then try again.", + "A desktop-owned Unsloth server for this install is already running on port {}. Quit the other desktop app instance, then try again.", conflict.port ); } 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.", + "An Unsloth 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.", conflict.port ) } @@ -475,7 +475,7 @@ pub async fn start_backend_update( .map_err(|e| format!("Update task panicked: {e}"))? } -/// Repair a stale managed Studio install. +/// Repair a stale managed Unsloth install. #[tauri::command] pub async fn start_managed_repair( app: AppHandle, @@ -522,7 +522,7 @@ pub async fn start_managed_repair( let repair_group_id = install::take_pending_repair_group_for_resume(&install_state) .unwrap_or_else(|| diagnostics::begin_repair_group(&diagnostics_state)); - let _ = app.emit("repair-progress", "Updating existing Studio install..."); + let _ = app.emit("repair-progress", "Updating existing Unsloth install..."); let update_app = app.clone(); let update_state = update_state.inner().clone(); let update_diagnostics = diagnostics_state.clone(); @@ -549,7 +549,7 @@ pub async fn start_managed_repair( warn!("Managed repair update finished, but preflight is still not ready; falling back to installer"); let _ = app.emit( "repair-progress", - "Update finished, but Studio is still not ready. Running bundled installer...", + "Update finished, but Unsloth is still not ready. Running bundled installer...", ); } Err(msg) => { @@ -627,7 +627,7 @@ pub async fn start_managed_repair( return Ok(()); } - let msg = "Repair finished, but Studio install is still not desktop-ready.".to_string(); + let msg = "Repair finished, but Unsloth install is still not desktop-ready.".to_string(); error!("{}", msg); diagnostics::finish_repair_group( &diagnostics_state, diff --git a/studio/src-tauri/src/desktop_auth.rs b/studio/src-tauri/src/desktop_auth.rs index 33605f6c65..db65b8796f 100644 --- a/studio/src-tauri/src/desktop_auth.rs +++ b/studio/src-tauri/src/desktop_auth.rs @@ -201,7 +201,7 @@ async fn exchange_desktop_secret( if response.status() == reqwest::StatusCode::NOT_FOUND { return Err(AuthError::StaleResponder( - "Running Studio backend is too old for this desktop app. Update that backend and restart." + "Running Unsloth backend is too old for this desktop app. Update that backend and restart." .to_string(), )); } @@ -364,7 +364,7 @@ async fn desktop_auth_inner( } Err( - "Desktop auth failed. Update or repair the managed Studio install, then restart Studio." + "Desktop auth failed. Update or repair the managed Unsloth install, then restart Unsloth." .to_string(), ) } @@ -465,7 +465,7 @@ mod tests { .message(); assert_eq!( error, - "Running Studio backend is too old for this desktop app. Update that backend and restart." + "Running Unsloth backend is too old for this desktop app. Update that backend and restart." ); } } diff --git a/studio/src-tauri/src/main.rs b/studio/src-tauri/src/main.rs index 4ed12051ed..4724317601 100644 --- a/studio/src-tauri/src/main.rs +++ b/studio/src-tauri/src/main.rs @@ -107,7 +107,7 @@ fn cleanup_child_processes(app: &tauri::AppHandle) { } fn setup_tray(app: &tauri::App) -> Result<(), Box> { - let open = MenuItemBuilder::with_id("open", "Open Studio").build(app)?; + let open = MenuItemBuilder::with_id("open", "Open Unsloth").build(app)?; let toggle = MenuItemBuilder::with_id("toggle", "Start/Stop Server").build(app)?; let quit = MenuItemBuilder::with_id("quit", "Quit").build(app)?; let menu = MenuBuilder::new(app) diff --git a/studio/src-tauri/src/native_path_policy.rs b/studio/src-tauri/src/native_path_policy.rs index b2ebb34621..b82e516e7a 100644 --- a/studio/src-tauri/src/native_path_policy.rs +++ b/studio/src-tauri/src/native_path_policy.rs @@ -191,7 +191,7 @@ fn reject_sensitive_artifact(path: &Path) -> Result<(), String> { "\\pid", ] { if lowered.contains(needle) { - return Err("Sensitive Studio state cannot be registered as an artifact.".to_string()); + return Err("Sensitive Unsloth state cannot be registered as an artifact.".to_string()); } } if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { diff --git a/tests/python/test_e2e_no_torch_sandbox.py b/tests/python/test_e2e_no_torch_sandbox.py index f412de2063..bb61af462d 100644 --- a/tests/python/test_e2e_no_torch_sandbox.py +++ b/tests/python/test_e2e_no_torch_sandbox.py @@ -30,7 +30,7 @@ VLM_PROCESSING = DATASETS_DIR / "vlm_processing.py" ITERABLE = DATASETS_DIR / "iterable.py" HARDWARE_PY = HARDWARE_DIR / "hardware.py" -# Studio venv for server tests +# Unsloth venv for server tests STUDIO_VENV = Path.home() / ".unsloth" / "studio" / "unsloth_studio" sys.path.insert(0, str(STUDIO_DIR)) @@ -957,20 +957,20 @@ server = pytest.mark.server @server class TestLiveServerStartup: - """Live server startup against the existing Studio venv with torch made unimportable (pytest -m server).""" + """Live server startup against the existing Unsloth venv with torch made unimportable (pytest -m server).""" @pytest.fixture(autouse = True) def _check_studio_venv(self): py = _studio_venv_python() if py is None: - pytest.skip("Studio venv not found at ~/.unsloth/studio/unsloth_studio") + pytest.skip("Unsloth venv not found at ~/.unsloth/studio/unsloth_studio") @pytest.fixture(scope = "class") def server_process(self): """Start the studio backend server without torch, yield (proc, port), then stop.""" py = _studio_venv_python() if py is None: - pytest.skip("Studio venv not found") + pytest.skip("Unsloth venv not found") port = _server_port() backend_dir = BACKEND_DIR diff --git a/tests/python/test_studio_import_no_torch.py b/tests/python/test_studio_import_no_torch.py index c4efbc8cea..f551519de9 100644 --- a/tests/python/test_studio_import_no_torch.py +++ b/tests/python/test_studio_import_no_torch.py @@ -1,4 +1,4 @@ -"""Sandbox tests: Studio dataset modules load/run in isolated no-torch venvs.""" +"""Sandbox tests: Unsloth dataset modules load/run in isolated no-torch venvs.""" from __future__ import annotations diff --git a/tests/saving/test_prewarm_base_model_hub_cache.py b/tests/saving/test_prewarm_base_model_hub_cache.py index cbb52863ba..4269f9d61c 100644 --- a/tests/saving/test_prewarm_base_model_hub_cache.py +++ b/tests/saving/test_prewarm_base_model_hub_cache.py @@ -4,7 +4,7 @@ """Regression tests for #6890: repeated base-model downloads across checkpoint exports. merge_and_overwrite_lora downloads missing 16-bit shards with hf_hub_download(local_dir), -which never populates the persistent HF hub cache; a temporary merge directory (Studio +which never populates the persistent HF hub cache; a temporary merge directory (Unsloth GGUF exports delete it) means every checkpoint export re-downloads the full base model. _prewarm_base_model_hub_cache snapshot-downloads the base into the hub cache first so the zoo's cache-copy fast path is hit on later exports. diff --git a/tests/studio/_playwright_robust.py b/tests/studio/_playwright_robust.py index a4590066d4..b2f6df751e 100644 --- a/tests/studio/_playwright_robust.py +++ b/tests/studio/_playwright_robust.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Shared CI-runner workarounds for the Studio Playwright tests (Chromium flags, +"""Shared CI-runner workarounds for the Unsloth Playwright tests (Chromium flags, view-transition killer, page recovery, post-action response wait). Imported directly by the standalone scripts; does NOT depend on pytest. """ @@ -130,7 +130,7 @@ def wait_for_health( timeout = 3.0, ) last_status, last_body = status, body - # Accept any 200 -- different Studio builds report status differently. + # Accept any 200 -- different Unsloth builds report status differently. if status == 200: if info is not None: info(f"health pre-flight OK: status=200, body keys={list((body or {}).keys())}") diff --git a/tests/studio/install/smoke_test_parallel_studio_home.py b/tests/studio/install/smoke_test_parallel_studio_home.py index 9d840355e1..55b6df5190 100644 --- a/tests/studio/install/smoke_test_parallel_studio_home.py +++ b/tests/studio/install/smoke_test_parallel_studio_home.py @@ -80,7 +80,7 @@ def _launch_backend( env = os.environ.copy() env["HOME"] = str(fake_home) # Pin UNSLOTH_STUDIO_HOME and clear the alias so the child can't inherit a - # Studio root from the caller's shell and resolve to the wrong install. + # Unsloth root from the caller's shell and resolve to the wrong install. env["UNSLOTH_STUDIO_HOME"] = str(studio_home) env.pop("STUDIO_HOME", None) # Popen dups stdout into the child, so closing the parent's handle here is safe. diff --git a/tests/studio/install/test_launch_studio_launcher.py b/tests/studio/install/test_launch_studio_launcher.py index a7396aaf5d..1c12024f6d 100644 --- a/tests/studio/install/test_launch_studio_launcher.py +++ b/tests/studio/install/test_launch_studio_launcher.py @@ -1,4 +1,4 @@ -"""Guard install.ps1's Studio launcher against the AV-heuristic shape (Kaspersky +"""Guard install.ps1's Unsloth launcher against the AV-heuristic shape (Kaspersky HEUR:Trojan.VBS.Agent.gen): a WScript .vbs spawning a hidden ExecutionPolicy-Bypass PowerShell. The shortcut must stay windowless via powershell.exe -WindowStyle Hidden over launch-studio.ps1, never a .vbs/WScript.Shell.Run wrapper, and any pre-existing .vbs must be deleted on upgrade.""" diff --git a/tests/studio/install/test_managed_node_runtime.py b/tests/studio/install/test_managed_node_runtime.py index 0dbc6788ca..17c7e3e60f 100644 --- a/tests/studio/install/test_managed_node_runtime.py +++ b/tests/studio/install/test_managed_node_runtime.py @@ -3,7 +3,7 @@ """Tests for the runtime managed-Node resolver (studio/backend/utils/node_runtime.py). -The Studio frontend installer may provision an isolated Node under +The Unsloth frontend installer may provision an isolated Node under ``/node`` that is never added to the user's PATH. The backend OXC validator must still find a usable Node at runtime: a version-adequate system Node, else the managed isolated one. These tests pin that resolution and the diff --git a/tests/studio/install/test_pr5940_followups.py b/tests/studio/install/test_pr5940_followups.py index ac6a96167a..d6dc8b2f8e 100644 --- a/tests/studio/install/test_pr5940_followups.py +++ b/tests/studio/install/test_pr5940_followups.py @@ -420,7 +420,7 @@ def test_ps_installers_gate_amd_smi_on_windows(): assert ( "UNSLOTH_SETUP_PYTHON" in text ), f"{ps.name} venv-internal check must seed the venv root from UNSLOTH_SETUP_PYTHON" - # A custom Studio home moves the venv off the default path; it must be + # A custom Unsloth home moves the venv off the default path; it must be # seeded too or its hipInfo escapes the filter and reopens the gate. assert ( "UNSLOTH_STUDIO_HOME" in text @@ -429,7 +429,7 @@ def test_ps_installers_gate_amd_smi_on_windows(): @pytest.mark.parametrize("ps", [_INSTALL_PS1, _SETUP_PS1], ids = ["install.ps1", "setup.ps1"]) def test_ps_venv_probe_expands_tilde_for_custom_studio_home(ps): - # The probe seeds the venv root from a custom Studio home; a ~\studio form + # The probe seeds the venv root from a custom Unsloth home; a ~\studio form # must expand to USERPROFILE like the canonical resolver, else GetFullPath # keeps the literal ~ (cwd-relative) and the hipInfo escapes the filter. text = ps.read_text(encoding = "utf-8") @@ -439,7 +439,7 @@ def test_ps_venv_probe_expands_tilde_for_custom_studio_home(ps): block = text[i:j] assert "USERPROFILE" in block and ".Substring(1)" in block, ( f"{ps.name}: the venv-internal probe must expand a leading ~ in the custom " - "Studio home before seeding the venv root (mirroring the canonical resolver)" + "Unsloth home before seeding the venv root (mirroring the canonical resolver)" ) # The ~ expansion must be guarded on a non-empty USERPROFILE; otherwise # Join-Path $env:USERPROFILE throws on a service/SYSTEM account with no profile, diff --git a/tests/studio/install/test_rocm_support.py b/tests/studio/install/test_rocm_support.py index 5cabf41f57..e7ac0ec82d 100644 --- a/tests/studio/install/test_rocm_support.py +++ b/tests/studio/install/test_rocm_support.py @@ -360,7 +360,7 @@ class TestRuntimePatterns: install_kind = "windows-hip", ) patterns = runtime_patterns_for_choice(choice) - # Narrowed from "*.exe" to the two binaries Studio actually invokes. + # Narrowed from "*.exe" to the two binaries Unsloth actually invokes. assert "llama-server.exe" in patterns assert "llama-quantize.exe" in patterns assert "*.dll" in patterns @@ -378,7 +378,7 @@ class TestRuntimePatterns: assert "lib*.dylib" in patterns def test_diffusion_visual_server_kept(self): - # The DiffusionGemma visual-server must survive the prune so Studio can + # The DiffusionGemma visual-server must survive the prune so Unsloth can # serve DiffusionGemma GGUFs natively. for kind, name in ( ("linux-cuda", "llama-diffusion-gemma-visual-server"), diff --git a/tests/studio/install/test_selection_logic.py b/tests/studio/install/test_selection_logic.py index 7575dea581..e372a3bbeb 100644 --- a/tests/studio/install/test_selection_logic.py +++ b/tests/studio/install/test_selection_logic.py @@ -270,13 +270,13 @@ def mock_windows_runtime(monkeypatch, lines): # =========================================================================== -# Studio run.py localhost warning +# Unsloth run.py localhost warning # =========================================================================== class TestStudioLocalhostIpv6Warning: def _prepare_loopback(self, run_module, monkeypatch): - # Studio confirmed answering on the IPv4 loopback. + # Unsloth confirmed answering on the IPv4 loopback. monkeypatch.setattr( run_module, "_working_local_url", @@ -327,7 +327,7 @@ class TestStudioLocalhostIpv6Warning: assert "http://localhost:8888" in captured.out def test_ipv6_listener_does_not_suppress_warning(self, monkeypatch): - # A process on ::1 is NOT Studio (binds 127.0.0.1 only), so the warning must + # A process on ::1 is NOT Unsloth (binds 127.0.0.1 only), so the warning must # still fire -- that is exactly when http://localhost opens the wrong service. run_module = load_studio_run_module(monkeypatch) self._prepare_loopback(run_module, monkeypatch) @@ -356,7 +356,7 @@ class TestStudioLocalhostIpv6Warning: assert run_module._localhost_ipv6_mismatch_url("127.0.0.1", port) is None def test_ipv4_not_answering_suppresses_warning(self, monkeypatch): - # Studio not confirmed on 127.0.0.1 -> no warning. + # Unsloth not confirmed on 127.0.0.1 -> no warning. run_module = load_studio_run_module(monkeypatch) monkeypatch.setattr(run_module, "_working_local_url", lambda port: None) self._set_getaddrinfo(monkeypatch, [self._ipv6()]) @@ -3668,7 +3668,7 @@ class TestCpuFallback: # =========================================================================== -@pytest.mark.skipif(sys.platform == "win32", reason = "bash-only Studio installer tests") +@pytest.mark.skipif(sys.platform == "win32", reason = "bash-only Unsloth installer tests") class TestCudaDriverToolkitMismatchMessage: _SETUP_SH = PACKAGE_ROOT / "studio" / "setup.sh" _SETUP_PS1 = PACKAGE_ROOT / "studio" / "setup.ps1" @@ -3873,7 +3873,7 @@ class TestCudaDriverToolkitMismatchMessage: "or install a CUDA $driverMajor.x toolkit." in source ) assert ( - "Or let Studio use the prebuilt CUDA bundle; it does not need the local toolkit." + "Or let Unsloth use the prebuilt CUDA bundle; it does not need the local toolkit." ) in source assert ( "Write-CudaDriverToolkitMismatch -ToolkitVersion $IncompatibleToolkit " diff --git a/tests/studio/playwright_chat_ime_i18n.py b/tests/studio/playwright_chat_ime_i18n.py index 9c01e95fd4..5bebf0a9e8 100644 --- a/tests/studio/playwright_chat_ime_i18n.py +++ b/tests/studio/playwright_chat_ime_i18n.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Studio chat composer IME + multilingual regression smoke. +"""Unsloth chat composer IME + multilingual regression smoke. Covers: stuck IME composition (#5318 / PR #5327), multilingual paste round-trip, stuck compositionend (#5546), and Mac input-method switch recovery (keydown/blur). diff --git a/tests/studio/playwright_chat_ui.py b/tests/studio/playwright_chat_ui.py index 065ba7a745..35b18756ff 100644 --- a/tests/studio/playwright_chat_ui.py +++ b/tests/studio/playwright_chat_ui.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Comprehensive Studio chat UI test, run locally + in CI.""" +"""Comprehensive Unsloth chat UI test, run locally + in CI.""" import json import os @@ -156,7 +156,7 @@ with sync_playwright() as p: # pointer events and break Playwright's actionability check. reduced_motion = "reduce", ) - # Hard-disable CSS view-transitions: Studio's theme toggle + sidebar + # Hard-disable CSS view-transitions: Unsloth's theme toggle + sidebar # collapse run startViewTransition() which can leave intercepting # pointer events for a beat after each route swap. See _playwright_robust.py. install_view_transition_killer(ctx) @@ -477,7 +477,7 @@ with sync_playwright() as p: fail(f"/api/inference/load returned {load_resp['status']}: {load_resp.get('body')!r}") info(f"loaded model: {(load_resp['body'] or {}).get('display_name')}") - # Studio caches model state in zustand; reload so the composer picks + # Unsloth caches model state in zustand; reload so the composer picks # up the loaded model. page.reload() composer = page.locator('textarea[aria-label="Message input"]') @@ -493,7 +493,7 @@ with sync_playwright() as p: # (app-sidebar.tsx) -- as stable as anything in the codebase. picker_btn = page.locator('[data-tour="chat-model-selector"]').first if picker_btn.count() == 0: - # Fall back to text-based locators for older Studio builds. + # Fall back to text-based locators for older Unsloth builds. picker_btn = page.locator( 'button:has-text("gemma-3-270m"), ' 'button:has-text("Gemma 3"), ' @@ -893,7 +893,7 @@ with sync_playwright() as p: if len(observed) < 3: soft_fail(f"theme toggle ran only {len(observed)} cycle(s), expected 3") # Don't strict-fail on both polarities: the runner's - # prefers-color-scheme + Studio's "system" default can collapse + # prefers-color-scheme + Unsloth's "system" default can collapse # to one polarity even when .dark toggles correctly. The 3-cycle # completion above is the real invariant. if light_seen and dark_seen: diff --git a/tests/studio/playwright_extra_ui.py b/tests/studio/playwright_extra_ui.py index 209a8a06f1..dde6c5d635 100644 --- a/tests/studio/playwright_extra_ui.py +++ b/tests/studio/playwright_extra_ui.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Studio extra-UI Playwright test: Compare tab, Recipes editor, /export, /studio, Settings tabs.""" +"""Unsloth extra-UI Playwright test: Compare tab, Recipes editor, /export, /studio, Settings tabs.""" import json import os @@ -90,11 +90,11 @@ with sync_playwright() as p: ) install_view_transition_killer(ctx) page = ctx.new_page() - # 60s default for slow macos-14 --single-process Chromium (second Studio boot of the job). + # 60s default for slow macos-14 --single-process Chromium (second Unsloth boot of the job). page.set_default_timeout(60_000) page_errors = [] - # Filter out known-benign React errors (timing artefacts on slow CI runners, not Studio bugs); + # Filter out known-benign React errors (timing artefacts on slow CI runners, not Unsloth bugs); # shared base list lives in _playwright_robust.BENIGN_PAGE_ERROR_PATTERNS. def _on_pageerror(e): msg = str(e) @@ -451,9 +451,9 @@ with sync_playwright() as p: ) # ───────────────────────────────────────────────────── - # 4. Studio training route. + # 4. Unsloth training route. # ───────────────────────────────────────────────────── - step(f"Studio route ({'chat-only redirect' if chat_only else 'tabs + sections'})") + step(f"Unsloth route ({'chat-only redirect' if chat_only else 'tabs + sections'})") page.goto(f"{BASE}/studio") page.wait_for_timeout(1500) shoot("08-studio") diff --git a/tests/studio/run_real_mlx_smoke.py b/tests/studio/run_real_mlx_smoke.py index 275fe7ac57..63bc0dbba9 100644 --- a/tests/studio/run_real_mlx_smoke.py +++ b/tests/studio/run_real_mlx_smoke.py @@ -106,7 +106,7 @@ def _compute_loss_and_grad_norm(model, tokenizer, text: str) -> tuple[float, flo import mlx.nn as nn from mlx.utils import tree_flatten - # Match Studio's text dataset path: no EOS appended behind the user's back. + # Match Unsloth's text dataset path: no EOS appended behind the user's back. ids = list(tokenizer.encode(text)) if len(ids) < 2: raise RuntimeError(f"text too short to compute loss: {len(ids)} tokens") diff --git a/tests/studio/studio_api_smoke.py b/tests/studio/studio_api_smoke.py index 845a9ed021..d30bd11dca 100644 --- a/tests/studio/studio_api_smoke.py +++ b/tests/studio/studio_api_smoke.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""End-to-end Studio API & Auth HTTP integration tests against an externally-booted Studio.""" +"""End-to-end Unsloth API & Auth HTTP integration tests against an externally-booted Unsloth.""" import json import os @@ -571,7 +571,7 @@ EXPECTED_AUTH_ENDPOINTS = [ for method, path in EXPECTED_AUTH_ENDPOINTS: if (method, path) in PUBLIC: continue - # Don't actually shut Studio down: an unauthenticated call must 401/403 before the trigger fires. + # Don't actually shut Unsloth down: an unauthenticated call must 401/403 before the trigger fires. if path == "/api/shutdown": code, _ = http(method, path) if code in (401, 403): @@ -610,6 +610,6 @@ if _failed: sys.exit(1) _emit( "", - "PASS all Studio API & Auth assertions" + "PASS all Unsloth API & Auth assertions" + (f" ({len(_warned)} audit findings logged)" if _warned else ""), ) diff --git a/tests/studio/test_auth_form_input_count.py b/tests/studio/test_auth_form_input_count.py index 4d5d72d20b..75e6cfd1fb 100644 --- a/tests/studio/test_auth_form_input_count.py +++ b/tests/studio/test_auth_form_input_count.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -"""Fast source and runtime contracts for Studio's frontend authentication flows. +"""Fast source and runtime contracts for Unsloth's frontend authentication flows. PR #5490 added a third "Current password" input, regressing first-boot UX to three inputs; PR #5545 restores two by rendering it only when BOOTSTRAP is absent. diff --git a/tests/studio/test_chat_title_generation.py b/tests/studio/test_chat_title_generation.py index b568a51400..6a47cfbce4 100644 --- a/tests/studio/test_chat_title_generation.py +++ b/tests/studio/test_chat_title_generation.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. -"""Regression checks for Studio chat title generation context.""" +"""Regression checks for Unsloth chat title generation context.""" from __future__ import annotations diff --git a/tests/studio/test_cli_studio_stop_windows.py b/tests/studio/test_cli_studio_stop_windows.py index 778679c73b..2267d7feda 100644 --- a/tests/studio/test_cli_studio_stop_windows.py +++ b/tests/studio/test_cli_studio_stop_windows.py @@ -5,7 +5,7 @@ `stop` once used `os.kill(pid, 0)`, which raises WinError 87 on Windows before reaching taskkill; the fix adds cross-platform `_pid_alive` (tasklist on Windows, -signal-0 elsewhere). AST + mock-only; no real processes, no Studio deps imported. +signal-0 elsewhere). AST + mock-only; no real processes, no Unsloth deps imported. """ import ast diff --git a/tests/studio/test_hardware_dispatch_matrix.py b/tests/studio/test_hardware_dispatch_matrix.py index bccddac967..19619bc318 100644 --- a/tests/studio/test_hardware_dispatch_matrix.py +++ b/tests/studio/test_hardware_dispatch_matrix.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: AGPL-3.0-only -"""Studio hardware dispatch matrix: spoofs platform/torch/mlx per PROFILES to exercise CUDA/ROCm/XPU/MLX/CPU paths without real hardware.""" +"""Unsloth hardware dispatch matrix: spoofs platform/torch/mlx per PROFILES to exercise CUDA/ROCm/XPU/MLX/CPU paths without real hardware.""" from __future__ import annotations @@ -31,9 +31,9 @@ class HardwareProfile: mps_available: bool # torch.backends.mps.is_available() value expect_is_mlx: bool # unsloth._IS_MLX - expect_device_type: str # Studio DeviceType (uppercased name: "CUDA"/"XPU"/"MLX"/"CPU") - expect_is_rocm: bool # Studio IS_ROCM - expect_apple_silicon: bool # Studio is_apple_silicon() + expect_device_type: str # Unsloth DeviceType (uppercased name: "CUDA"/"XPU"/"MLX"/"CPU") + expect_is_rocm: bool # Unsloth IS_ROCM + expect_apple_silicon: bool # Unsloth is_apple_silicon() extra_notes: str = "" @@ -66,7 +66,7 @@ PROFILES = [ expect_is_rocm = True, expect_apple_silicon = False, extra_notes = "PyTorch ROCm reuses torch.cuda.* over HIP; " - "Studio still uses DeviceType.CUDA but flips IS_ROCM=True.", + "Unsloth still uses DeviceType.CUDA but flips IS_ROCM=True.", ), HardwareProfile( name = "intel_xpu", @@ -154,7 +154,7 @@ def spoof_hardware(monkeypatch): import platform import torch - # platform spoof (used by both the unsloth gate and Studio's helpers) + # platform spoof (used by both the unsloth gate and Unsloth's helpers) monkeypatch.setattr(platform, "system", lambda: profile.system) monkeypatch.setattr(platform, "machine", lambda: profile.machine) @@ -227,7 +227,7 @@ def spoof_hardware(monkeypatch): monkeypatch.setattr(importlib.util, "find_spec", _no_mlx) - # Studio's _has_mlx() does `import mlx.core`, not find_spec; block it + # Unsloth's _has_mlx() does `import mlx.core`, not find_spec; block it # with a meta_path finder that raises ImportError for mlx.*. class _BlockMLXFinder: def find_spec( @@ -266,7 +266,7 @@ def _evaluate_unsloth_is_mlx_gate() -> bool: def _import_studio_hardware_module(): - """Lazy-load Studio's hardware module under the bare-imports layout.""" + """Lazy-load Unsloth's hardware module under the bare-imports layout.""" if str(STUDIO_BACKEND) not in sys.path: sys.path.insert(0, str(STUDIO_BACKEND)) # Fresh import so detect_hardware re-runs under the current spoofs. @@ -290,7 +290,7 @@ def test_unsloth_is_mlx_gate_matches_profile(profile, spoof_hardware): @pytest.mark.parametrize("profile", PROFILES, ids = PROFILE_IDS) def test_studio_detect_hardware_matches_profile(profile, spoof_hardware): - """Studio's detect_hardware() routes to the right DeviceType per profile.""" + """Unsloth's detect_hardware() routes to the right DeviceType per profile.""" spoof_hardware(profile) hw = _import_studio_hardware_module() detected = hw.detect_hardware() @@ -306,7 +306,7 @@ def test_studio_detect_hardware_matches_profile(profile, spoof_hardware): @pytest.mark.parametrize("profile", PROFILES, ids = PROFILE_IDS) def test_studio_is_apple_silicon_matches_profile(profile, spoof_hardware): - """Studio's is_apple_silicon() helper agrees with platform spoof.""" + """Unsloth's is_apple_silicon() helper agrees with platform spoof.""" spoof_hardware(profile) hw = _import_studio_hardware_module() assert hw.is_apple_silicon() is profile.expect_apple_silicon, ( diff --git a/tests/studio/test_is_mlx_dispatch_gate.py b/tests/studio/test_is_mlx_dispatch_gate.py index f31f6d1655..0e5de1b789 100644 --- a/tests/studio/test_is_mlx_dispatch_gate.py +++ b/tests/studio/test_is_mlx_dispatch_gate.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: AGPL-3.0-only -"""Regression tests for the CUDA-vs-MLX dispatch gates Studio relies on. +"""Regression tests for the CUDA-vs-MLX dispatch gates Unsloth relies on. Two gates: (1) ``unsloth._IS_MLX`` (import-time, delegates to the zoo MLX runtime gate behind a local precheck barrier); (2) @@ -147,7 +147,7 @@ def test_is_mlx_gate_false_on_non_apple_silicon(): def _import_studio_hardware(): - """Lazy import of the Studio hardware module (studio/backend on sys.path).""" + """Lazy import of the Unsloth hardware module (studio/backend on sys.path).""" studio_backend = REPO_ROOT / "studio" / "backend" if str(studio_backend) not in sys.path: sys.path.insert(0, str(studio_backend)) diff --git a/tests/studio/test_llama_cpp_wall_clock_cap.py b/tests/studio/test_llama_cpp_wall_clock_cap.py index f173cfbc55..b7b6917092 100644 --- a/tests/studio/test_llama_cpp_wall_clock_cap.py +++ b/tests/studio/test_llama_cpp_wall_clock_cap.py @@ -1,4 +1,4 @@ -"""Timeout policy checks for Studio's local llama-server path.""" +"""Timeout policy checks for Unsloth's local llama-server path.""" from __future__ import annotations diff --git a/tests/studio/test_locale_root_direction_contract.py b/tests/studio/test_locale_root_direction_contract.py index 1baabdbbca..20d10aa805 100644 --- a/tests/studio/test_locale_root_direction_contract.py +++ b/tests/studio/test_locale_root_direction_contract.py @@ -1,4 +1,4 @@ -"""Regression guard for locale changes affecting the entire Studio layout.""" +"""Regression guard for locale changes affecting the entire Unsloth layout.""" from pathlib import Path diff --git a/tests/studio/test_node_decision.ps1 b/tests/studio/test_node_decision.ps1 index bd5d5c8677..44f3ef0e1e 100644 --- a/tests/studio/test_node_decision.ps1 +++ b/tests/studio/test_node_decision.ps1 @@ -60,7 +60,7 @@ $globalBunOffset = $source.IndexOf('npm install -g bun') Check "NodeSource initialized before SKIP_STUDIO_FRONTEND branch" ( $nodeSourceOffset -ge 0 -and $skipFrontendBranchOffset -ge 0 -and $nodeSourceOffset -lt $skipFrontendBranchOffset ) -Check "custom Studio home validated before Node parent creation" ( +Check "custom Unsloth home validated before Node parent creation" ( $customHomeErrorOffset -ge 0 -and $nodeParentMkdirOffset -ge 0 -and $customHomeErrorOffset -lt $nodeParentMkdirOffset ) Check "bundled Node pins npm prefix and clears NODE_PATH" ( diff --git a/tests/studio/test_studio_gguf_export_script_pin.py b/tests/studio/test_studio_gguf_export_script_pin.py index 1f7e7adaa4..defd0d49d4 100644 --- a/tests/studio/test_studio_gguf_export_script_pin.py +++ b/tests/studio/test_studio_gguf_export_script_pin.py @@ -1,4 +1,4 @@ -"""Studio GGUF export pins convert_hf_to_gguf.py via UNSLOTH_LLAMA_CPP_SCRIPTS_DIR, with a once-per-process warning fallback when unsloth_zoo lacks the local-script resolver.""" +"""Unsloth GGUF export pins convert_hf_to_gguf.py via UNSLOTH_LLAMA_CPP_SCRIPTS_DIR, with a once-per-process warning fallback when unsloth_zoo lacks the local-script resolver.""" from __future__ import annotations diff --git a/tests/studio/test_studio_text_descender_clipping.py b/tests/studio/test_studio_text_descender_clipping.py index 73d1244ee3..359ca4873a 100644 --- a/tests/studio/test_studio_text_descender_clipping.py +++ b/tests/studio/test_studio_text_descender_clipping.py @@ -1,4 +1,4 @@ -"""Regression guard: Studio text spans must not pair `leading-none` with +"""Regression guard: Unsloth text spans must not pair `leading-none` with `truncate`, which clips glyph descenders (g, p, q, y, j) in visible labels. """ diff --git a/tests/test_studio_install_workspace_guard.py b/tests/test_studio_install_workspace_guard.py index 89836cfb0d..ea5b34672c 100644 --- a/tests/test_studio_install_workspace_guard.py +++ b/tests/test_studio_install_workspace_guard.py @@ -1,4 +1,4 @@ -"""install.sh/install.ps1 must refuse to rm -rf an existing Studio venv in env-mode without a sentinel.""" +"""install.sh/install.ps1 must refuse to rm -rf an existing Unsloth venv in env-mode without a sentinel.""" from __future__ import annotations @@ -127,7 +127,7 @@ def test_install_ps1_has_matching_env_mode_guard(): ), "install.ps1 must gate Remove-Item $VenvDir on env-mode" assert "share\\studio.conf" in block, "install.ps1 guard must check share\\studio.conf sentinel" assert "bin\\unsloth.exe" in block, "install.ps1 guard must check bin\\unsloth.exe sentinel" - assert "Refusing to delete non-Studio venv" in block + assert "Refusing to delete non-Unsloth venv" in block def test_setup_ps1_has_writability_probe(): @@ -160,7 +160,7 @@ def test_env_mode_blocks_when_bin_unsloth_is_a_directory(tmp_path): capture_output = True, ) assert res.returncode != 0, ( - "directory at bin/unsloth must NOT satisfy the Studio sentinel; " + "directory at bin/unsloth must NOT satisfy the Unsloth sentinel; " f"stdout={res.stdout!r} stderr={res.stderr!r}" ) assert (venv / "important.txt").is_file(), "unrelated workspace data must survive" @@ -205,7 +205,7 @@ def test_install_ps1_sentinel_uses_pathtype_leaf(): def test_setup_ps1_stale_venv_has_env_mode_guard(): - """setup.ps1 stale-venv branch must gate Remove-Item $VenvDir on a custom-root Studio sentinel.""" + """setup.ps1 stale-venv branch must gate Remove-Item $VenvDir on a custom-root Unsloth sentinel.""" src = SETUP_PS1.read_text() idx = src.index("Stale venv detected") block = src[idx : idx + 1500] diff --git a/tests/test_studio_root_resilience.py b/tests/test_studio_root_resilience.py index 0dfb826376..779ff2f3f1 100644 --- a/tests/test_studio_root_resilience.py +++ b/tests/test_studio_root_resilience.py @@ -1,4 +1,4 @@ -"""Studio install-root inference must not crash under hostile filesystem conditions (PermissionError/OSError swallowed; custom root kept when resolve() fails).""" +"""Unsloth install-root inference must not crash under hostile filesystem conditions (PermissionError/OSError swallowed; custom root kept when resolve() fails).""" from __future__ import annotations diff --git a/tests/test_studio_shutdown_thread_wait.py b/tests/test_studio_shutdown_thread_wait.py index 4ec2afc0f0..8299116d9a 100644 --- a/tests/test_studio_shutdown_thread_wait.py +++ b/tests/test_studio_shutdown_thread_wait.py @@ -130,4 +130,4 @@ def test_cli_entrypoints_wait_before_returning_to_shell(): assert ( _calls_shutdown_wait_getattr(tree) >= 3 - ), "Studio CLI terminal paths must wait for the backend thread after requesting shutdown" + ), "Unsloth CLI terminal paths must wait for the backend thread after requesting shutdown" diff --git a/unsloth/chat_templates.py b/unsloth/chat_templates.py index bd612db1d5..f47c78ba80 100644 --- a/unsloth/chat_templates.py +++ b/unsloth/chat_templates.py @@ -2047,7 +2047,7 @@ def get_chat_template( .replace("'assistant'", "'" + mapping["assistant"] + "'") if use_zoo_tokenizer_patch: - # Studio MLX avoids the model-utils tokenizer wrapper because that + # Unsloth MLX avoids the model-utils tokenizer wrapper because that # import path pulls in Torch/GPU-specific modules before MLX training. from unsloth_zoo.tokenizer_utils import patch_tokenizer else: diff --git a/unsloth/import_fixes.py b/unsloth/import_fixes.py index 09de248c7b..5d54815705 100644 --- a/unsloth/import_fixes.py +++ b/unsloth/import_fixes.py @@ -3008,7 +3008,7 @@ def maybe_set_windows_rocm_bnb_version(): No-op unless ALL of: Windows, a real HIP torch build (env hints like HIP_PATH do not count), a ROCm DLL installed, and no explicit user value. - Linux is untouched. Values seeded by Studio's venv sitecustomize.py + Linux is untouched. Values seeded by Unsloth's venv sitecustomize.py (marked ``UNSLOTH_BNB_ROCM_VERSION_SOURCE=sitecustomize``) are redetectable defaults, not overrides; ``UNSLOTH_SKIP_BNB_ROCM_VERSION=1`` opts out and drops a seeded default. Returns the value set, else None. diff --git a/unsloth/models/loader_utils.py b/unsloth/models/loader_utils.py index 1e9cc641e9..7661b0d714 100644 --- a/unsloth/models/loader_utils.py +++ b/unsloth/models/loader_utils.py @@ -821,7 +821,7 @@ def _exclude_rope_inv_freq_from_ddp(model): # ============================================================================= # Offline loading - single source of truth (shared by vision.py, loader.py and -# the Studio exporter). Decide offline ONCE at the load boundary and force it +# the Unsloth exporter). Decide offline ONCE at the load boundary and force it # ONCE around the whole load, so every nested HF call inherits it. # ============================================================================= diff --git a/unsloth/save.py b/unsloth/save.py index 7d5774aa97..0e2650b174 100644 --- a/unsloth/save.py +++ b/unsloth/save.py @@ -228,7 +228,7 @@ def _loaded_via_remote_code(obj): Transformers loads auto_map code into the ``transformers_modules`` package, so a ``transformers_modules`` class proves the original load actually ran that remote code - (which the caller's / Studio's consent gate scans at load time). Export paths derive their + (which the caller's / Unsloth's consent gate scans at load time). Export paths derive their reload trust_remote_code from this - the already approved load decision - instead of from a checkpoint's static ``auto_map``: a model that loads with built-in classes must not have its unvetted remote code run when it is re-read during quantization export. Walks PEFT / wrapper @@ -3858,7 +3858,7 @@ def _prewarm_base_model_hub_cache( from huggingface_hub import HfFileSystem, hf_hub_download, snapshot_download # Resolve the cache from the live env like the merge, not huggingface_hub's frozen - # constants: a runtime cache redirect (read-only default, Studio) would else miss (#6890). + # constants: a runtime cache redirect (read-only default, Unsloth) would else miss (#6890). try: from unsloth_zoo.hf_cache import _active_caches _hub_cache = _active_caches()[1] diff --git a/unsloth/tokenizer_utils.py b/unsloth/tokenizer_utils.py index d6e8247ec4..c7f61288d5 100644 --- a/unsloth/tokenizer_utils.py +++ b/unsloth/tokenizer_utils.py @@ -1476,7 +1476,7 @@ def get_tokenizer_info(tokenizer) -> dict: """Return a concise diagnostic summary of a tokenizer instance. Collects key properties into a JSON-safe dict for logging, debugging, or the - Studio UI. Missing attributes fall back to ``None`` rather than raising. + Unsloth UI. Missing attributes fall back to ``None`` rather than raising. Example output:: diff --git a/unsloth_cli/__init__.py b/unsloth_cli/__init__.py index b3831f5314..121b26f03f 100644 --- a/unsloth_cli/__init__.py +++ b/unsloth_cli/__init__.py @@ -81,7 +81,7 @@ app.add_typer(studio_app, name = "studio", help = "Unsloth Studio commands.") app.add_typer( start_app, name = "start", - help = "Start a coding agent (Claude, Codex, OpenClaw, OpenCode, Hermes, Pi) against Studio.", + help = "Start a coding agent (Claude, Codex, OpenClaw, OpenCode, Hermes, Pi) against Unsloth.", ) # Backwards-compatible hidden alias: `unsloth connect` routes to `unsloth start`. app.add_typer( diff --git a/unsloth_cli/_inference.py b/unsloth_cli/_inference.py index c3b188710e..551bef4787 100644 --- a/unsloth_cli/_inference.py +++ b/unsloth_cli/_inference.py @@ -18,7 +18,7 @@ _THINK_OPEN = "" _THINK_BLOCK = re.compile(rf"{re.escape(_THINK_OPEN)}.*?", re.DOTALL) _STREAMED_ERROR_PREFIX = "Error: " -# Cloudflare (in front of remote Studio proxies like RunPod) 403s the default +# Cloudflare (in front of remote Unsloth proxies like RunPod) 403s the default # "Python-urllib/X.Y" User-Agent as a bot; send a real one on every request. _USER_AGENT = "unsloth-cli" _MPI_ENV_PAIRS = ( @@ -36,7 +36,7 @@ _no_redirect_opener = None def urlopen_no_redirect(request, timeout): """urlopen that errors on any redirect: following a 3xx would send a bearer token (or accept an identity proof) to a base we never vetted, letting a port - squatter relay a real Studio's response.""" + squatter relay a real Unsloth's response.""" global _no_redirect_opener if _no_redirect_opener is None: import urllib.error @@ -540,7 +540,7 @@ def find_studio_server(timeout: float = 3.0) -> Optional[str]: def is_loopback_url(base: str) -> bool: """True only when *base* resolves to loopback. find_studio_server() trusts a base after only a health probe, so credentials are auto-sent only to loopback - (a local Studio or an SSH tunnel on 127.0.0.1), the targets the auto flows mean.""" + (a local Unsloth or an SSH tunnel on 127.0.0.1), the targets the auto flows mean.""" from urllib.parse import urlparse host = (urlparse(base).hostname or "").lower() @@ -554,7 +554,7 @@ def is_loopback_url(base: str) -> bool: def verify_studio_identity(base: str, timeout: float = 3.0) -> bool: - """Confirm `base` is really this machine's Studio before sending a secret. + """Confirm `base` is really this machine's Unsloth before sending a secret. Send a random nonce to /api/auth/identity and check the returned HMAC against the one computed from the local same-user secret; an endpoint without that @@ -578,7 +578,7 @@ def verify_studio_identity(base: str, timeout: float = 3.0) -> bool: port = parsed.port or (443 if parsed.scheme == "https" else 80) # Resolve to one concrete address and talk to *that* address, then bind the # proof to (address, port). A name like localhost can resolve to a squatter on - # ::1 while the real Studio is on 127.0.0.1; connecting to the resolved IP and + # ::1 while the real Unsloth is on 127.0.0.1; connecting to the resolved IP and # binding to it means a proof relayed from a different address/port won't match. try: ip = socket.getaddrinfo(host, port, type = socket.SOCK_STREAM)[0][4][0] @@ -592,7 +592,7 @@ def verify_studio_identity(base: str, timeout: float = 3.0) -> bool: headers = {"User-Agent": _USER_AGENT, "Host": parsed.netloc}, ) try: - # No redirects: a 302 could relay a real Studio's proof (see urlopen_no_redirect). + # No redirects: a 302 could relay a real Unsloth's proof (see urlopen_no_redirect). # Cap the read: the server is still unverified, so don't trust its length. with urlopen_no_redirect(request, timeout = timeout) as response: proof = json.loads(response.read(65536).decode() or "{}").get("proof") @@ -623,7 +623,7 @@ def _studio_token() -> Optional[str]: class HttpChatBackend: - """Chat against a running Studio server over its OpenAI-compatible API. + """Chat against a running Unsloth server over its OpenAI-compatible API. close() leaves the model loaded on purpose — the next session (or the UI) starts instantly. @@ -666,7 +666,7 @@ class HttpChatBackend: tensor_parallel: bool = False, llama_extra_args: Optional[List[str]] = None, ) -> None: - typer.echo(f"Loading {model} on the Studio server", err = True) + typer.echo(f"Loading {model} on the Unsloth server", err = True) payload = { "model_path": model, "hf_token": hf_token, @@ -769,7 +769,7 @@ def connect_studio_server( tensor_parallel: bool = False, llama_extra_args: Optional[List[str]] = None, ): - """Backend on a running Studio server, or None (caller loads locally).""" + """Backend on a running Unsloth server, or None (caller loads locally).""" base_url = find_studio_server() if not base_url: return None @@ -782,20 +782,20 @@ def connect_studio_server( if not explicit: return None typer.echo( - f"Can't attach to the Studio server at {base_url}: {reason} Run Studio " + f"Can't attach to the Unsloth server at {base_url}: {reason} Run Unsloth " "on this machine, or unset UNSLOTH_STUDIO_URL to load the model locally.", err = True, ) raise typer.Exit(code = 1) # Only hand the self-issued JWT (signed with the local secret) to loopback: a - # remote URL is unverified and a real remote Studio would reject it anyway. + # remote URL is unverified and a real remote Unsloth would reject it anyway. if not is_loopback_url(base_url): return _refuse( - "it isn't a local Studio, so a self-issued token can't " + "it isn't a local Unsloth, so a self-issued token can't " "authenticate to it and must not be sent to it." ) - # Confirm the loopback responder is really our Studio (not a port squatter). + # Confirm the loopback responder is really our Unsloth (not a port squatter). if not verify_studio_identity(base_url): return _refuse( "its identity couldn't be verified (it may be running as a " @@ -803,7 +803,7 @@ def connect_studio_server( ) token = _studio_token() if not token: - return _refuse("couldn't self-issue a Studio token (is Studio set up here?).") + return _refuse("couldn't self-issue an Unsloth token (is Unsloth set up here?).") backend = HttpChatBackend(base_url, token) backend.ensure_loaded( model, diff --git a/unsloth_cli/commands/chat.py b/unsloth_cli/commands/chat.py index bc4a72f36c..bba5fab08e 100644 --- a/unsloth_cli/commands/chat.py +++ b/unsloth_cli/commands/chat.py @@ -206,7 +206,7 @@ def chat( no_server: bool = typer.Option( False, "--no-server", - help = "Load the model in-process even if a Studio server is running.", + help = "Load the model in-process even if an Unsloth server is running.", ), ): """Start an interactive chat with a model (loads once, stays warm).""" @@ -262,14 +262,14 @@ def chat( llama_extra_args = llama_extra_args, ) - # Prefer a running Studio server: instant starts, model shared with the UI. + # Prefer a running Unsloth server: instant starts, model shared with the UI. chat_backend = ( None if (no_server or is_mlx_distributed) else connect_studio_server(model, **load_opts) ) server_mode = chat_backend is not None if server_mode and should_print: console.print( - "(Studio server connected — model stays warm after /exit)", + "(Unsloth server connected — model stays warm after /exit)", style = "bright_black", ) else: diff --git a/unsloth_cli/commands/inference.py b/unsloth_cli/commands/inference.py index 84a126163e..524d8fd015 100644 --- a/unsloth_cli/commands/inference.py +++ b/unsloth_cli/commands/inference.py @@ -67,7 +67,7 @@ def inference( no_server: bool = typer.Option( False, "--no-server", - help = "Load the model in-process even if a Studio server is running.", + help = "Load the model in-process even if an Unsloth server is running.", ), ): """Run a single inference using the specified model.""" @@ -85,7 +85,7 @@ def inference( ) raise typer.Exit(code = 1) - # A running Studio server keeps the model warm between runs. Under + # A running Unsloth server keeps the model warm between runs. Under # mlx.launch, every rank must enter the local MLX path instead of rank 0 # alone talking to a server. load_opts = dict( diff --git a/unsloth_cli/commands/start.py b/unsloth_cli/commands/start.py index fa39fdf761..9257a7fbcb 100644 --- a/unsloth_cli/commands/start.py +++ b/unsloth_cli/commands/start.py @@ -1,7 +1,7 @@ # 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 start` — launch a coding agent against a running Studio server.""" +"""`unsloth start` — launch a coding agent against a running Unsloth server.""" import atexit import contextlib @@ -35,7 +35,7 @@ from unsloth_cli._inference import ( ) start_app = typer.Typer( - help = "Start a coding agent against a running Studio server.", + help = "Start a coding agent against a running Unsloth server.", no_args_is_help = True, context_settings = {"help_option_names": ["-h", "--help"]}, ) @@ -75,14 +75,14 @@ _CLAUDE_ENV_UNSET = ("ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN") # Shared by every agent command; only the config/env/command differ. _MODEL_OPTION = typer.Option( - None, "--model", "-m", help = "Model for the agent; defaults to the one loaded in Studio." + None, "--model", "-m", help = "Model for the agent; defaults to the one loaded in Unsloth." ) _KEY_OPTION = typer.Option( None, "--api-key", envvar = "UNSLOTH_API_KEY", help = ( - "Studio API key. For a local Studio it is minted automatically and " + "Unsloth API key. For a local Unsloth it is minted automatically and " "remembered per server. For a remote server, pass one with --api-key " "(or UNSLOTH_API_KEY); it is remembered for next time." ), @@ -96,7 +96,7 @@ _SERVE_OPTION = typer.Option( True, "--serve/--no-serve", help = ( - "If no Studio server is running, auto-start one for --model and stop it when the " + "If no Unsloth server is running, auto-start one for --model and stop it when the " "agent exits. --no-serve keeps the old behavior of erroring out." ), ) @@ -347,7 +347,7 @@ def _shutdown_auto_served() -> None: global _auto_served_server server, _auto_served_server = _auto_served_server, None if server is not None and server.poll() is None: - typer.echo("Stopping the auto-started Studio server…") + typer.echo("Stopping the auto-started Unsloth server…") _shutdown_server(server) @@ -381,7 +381,7 @@ def _start_studio_server(base: str, model: str, load: LoadOptions) -> subprocess log_path = Path(tempfile.gettempdir()) / f"unsloth-start-server-{os.getpid()}.log" typer.echo( - f"No Studio server at {base}. Starting one for {model} (loading the model can take a while)…" + f"No Unsloth server at {base}. Starting one for {model} (loading the model can take a while)…" ) typer.echo(f"Server log: {log_path}") # 0600: the `unsloth run` banner in this log carries the minted sk-unsloth- key, and @@ -408,16 +408,16 @@ def _start_studio_server(base: str, model: str, load: LoadOptions) -> subprocess if server.poll() is not None: tail = _log_tail(log_path) _shutdown_auto_served() - _fail(f"The Studio server stopped before it was ready. Last log lines:\n{tail}") + _fail(f"The Unsloth server stopped before it was ready. Last log lines:\n{tail}") # `unsloth run` prints the minted key only after the server is up AND the model is # loaded, so it is the fully-ready signal (same contract serve-unsloth-run.sh uses). if _studio_healthy(base) and "sk-unsloth-" in _log_tail(log_path, lines = 400): - typer.echo(f"Studio server ready at {base}.") + typer.echo(f"Unsloth server ready at {base}.") return server time.sleep(2.0) _shutdown_auto_served() _fail( - f"The Studio server didn't become ready within {_SERVER_START_TIMEOUT_S}s. See {log_path}." + f"The Unsloth server didn't become ready within {_SERVER_START_TIMEOUT_S}s. See {log_path}." ) @@ -463,7 +463,7 @@ def _require_studio( return expected, _start_studio_server(expected, model, load or LoadOptions()) model_hint = "" if model else " Pass --model to have it start one for you, or" _fail( - f"No running Studio server found at {expected}.{model_hint} start one with " + f"No running Unsloth server found at {expected}.{model_hint} start one with " "`unsloth studio`, or point UNSLOTH_STUDIO_URL at a remote server." ) @@ -567,12 +567,12 @@ def _key_accepted(base: str, key: str) -> bool: if exc.code in (401, 403): return False _fail( - f"Studio server error while checking an API key ({exc.code}). " + f"Unsloth server error while checking an API key ({exc.code}). " "The server may be starting up or unhealthy; try again shortly." ) except (urllib.error.URLError, TimeoutError) as exc: _fail( - "Couldn't reach the Studio server while checking an API key: " + "Couldn't reach the Unsloth server while checking an API key: " f"{getattr(exc, 'reason', None) or exc}" ) @@ -592,10 +592,10 @@ def _agent_api_key( # UNSLOTH_API_KEY meant for some other server must not fail the # launch: the loopback mint path below is guaranteed to work. # (An explicit key that the fresh server accepts, e.g. one persisted - # in this Studio home's auth db, is still honored above.) + # in this Unsloth home's auth db, is still honored above.) # Replay a key the user saved for *this exact* server first (scoped per base, - # so it only goes back there -- including a remote/SSH-tunnelled Studio whose + # so it only goes back there -- including a remote/SSH-tunnelled Unsloth whose # secret the local handshake can't match). Skip ones the server rejects. for key in _cached_keys(cache, base, "saved"): if _key_accepted(base, key): @@ -608,15 +608,15 @@ def _agent_api_key( if not is_loopback_url(base): _fail( f"No saved API key for {base} and automatic minting only runs against " - "a local Studio. Create an API key in Studio → Settings → API and " + "a local Unsloth. Create an API key in Unsloth → Settings → API and " "pass it with --api-key (it is remembered per server), or set " "UNSLOTH_API_KEY." ) if not verify_studio_identity(base): _fail( - f"Couldn't verify that {base} is your Studio (it may be running as a " + f"Couldn't verify that {base} is your Unsloth (it may be running as a " "different OS user, or another process took the port). Create an API " - "key in Studio → Settings → API and pass it with --api-key, or set " + "key in Unsloth → Settings → API and pass it with --api-key, or set " "UNSLOTH_API_KEY." ) @@ -630,8 +630,8 @@ def _agent_api_key( token = _studio_token() if token is None: _fail( - "Couldn't authenticate with the Studio server automatically. Create " - "an API key in Studio → Settings → API and pass it with --api-key, " + "Couldn't authenticate with the Unsloth server automatically. Create " + "an API key in Unsloth → Settings → API and pass it with --api-key, " "or set UNSLOTH_API_KEY." ) key = _http_json( @@ -664,7 +664,7 @@ def _is_hub_model_id(value: object) -> bool: return False # A hub id is exactly "namespace/name" over a restricted charset. Anything with # extra path segments (e.g. a server-side relative path such as - # models/Llama/Foo.gguf on a remote Studio) is not a hub id and must not be + # models/Llama/Foo.gguf on a remote Unsloth) is not a hub id and must not be # casefold-matched against a differently cased path on a case-sensitive # filesystem. This is host independent, unlike the existence probe below which # cannot see a path that only exists on the server. @@ -690,8 +690,8 @@ def _model_id_matches( if actual == requested: return True # Case-insensitive matching is only safe when the local existence probe in - # _is_hub_model_id is authoritative, i.e. against a loopback Studio on this host. - # Against a remote Studio a two-segment string is indistinguishable from a + # _is_hub_model_id is authoritative, i.e. against a loopback Unsloth on this host. + # Against a remote Unsloth a two-segment string is indistinguishable from a # server-side relative path (e.g. Models/Foo vs models/foo), so casefolding it # could attach to the wrong model on a case-sensitive server; defer to an exact # match there and let the load endpoint resolve the requested path. @@ -709,7 +709,7 @@ def _resolve_model( load: LoadOptions = LoadOptions(), ) -> dict: models = _loaded_models(base, key) - # Only casefold-match ids against a loopback Studio, where _is_hub_model_id's + # Only casefold-match ids against a loopback Unsloth, where _is_hub_model_id's # local existence probe can actually reject a server-side path; see the note there. allow_casefold = is_loopback_url(base) # /v1/models reports the model id but not the active GGUF variant or runtime load @@ -741,7 +741,7 @@ def _resolve_model( typer.echo( f"Ensuring {requested} is loaded with the requested settings…" if load_has_overrides - else f"Loading {requested} on the Studio server (this can take a while)…" + else f"Loading {requested} on the Unsloth server (this can take a while)…" ) # Mirror `unsloth run`'s load knobs; keep the default payload as just # model_path so a bare `--model` load is unchanged. @@ -762,7 +762,7 @@ def _resolve_model( timeout = 3600, error = "Model load failed", ) - # Studio registers the model under a canonical id (resolved identifier, + # Unsloth registers the model under a canonical id (resolved identifier, # casing) that /v1/models echoes but which may differ from the path we # passed; match on the id the load reports so we don't silently fall # through to models[0] and connect to a different loaded model. @@ -783,22 +783,22 @@ def _resolve_model( if match is not None: return match if requested: - # We asked Studio to load it and it didn't surface in /v1/models; don't + # We asked Unsloth to load it and it didn't surface in /v1/models; don't # silently hand back an unrelated loaded model. _fail( - f"Studio didn't report '{requested}' as loaded. Double-check the model " + f"Unsloth didn't report '{requested}' as loaded. Double-check the model " "id, or load it from the model dropdown in the UI." ) if not models: _fail( - "No model is loaded in Studio. Load one from the model dropdown in " + "No model is loaded in Unsloth. Load one from the model dropdown in " "the UI, or pass --model to load it from here." ) return models[0] def _require_gguf_for_codex(base: str, key: str, model_id: str) -> None: - # Codex always streams, and Studio only streams /v1/responses from llama-server. + # Codex always streams, and Unsloth only streams /v1/responses from llama-server. try: status = _http_json("GET", f"{base}/api/inference/status", key) except urllib.error.HTTPError as exc: @@ -901,7 +901,7 @@ def _codex_supports_model_catalog() -> bool: def _codex_model_catalog(model: dict) -> dict: - """Return conservative metadata for a Studio model unknown to Codex's built-in catalog.""" + """Return conservative metadata for an Unsloth model unknown to Codex's built-in catalog.""" model_id = model["id"] window = model.get("context_length") or model.get("max_context_length") entry = { @@ -1202,7 +1202,7 @@ def _connect( # `--model org/name:QUANT` is shorthand for `--model org/name --gguf-variant QUANT`. # Split it before we match/serve so the attach path resolves against the already-loaded # `org/name` (listed without the suffix) instead of reloading a `:`-suffixed repo id -- - # which Studio rejects and which would evict a model another session is using. + # which Unsloth rejects and which would evict a model another session is using. if model: repo, variant = _split_repo_variant(model) if variant: @@ -1240,7 +1240,7 @@ def _run( # --no-launch recipes stay intact. if launch and clear_screen: click.clear() - typer.echo(f"Studio {base} · model {entry['id']}") + typer.echo(f"Unsloth {base} · model {entry['id']}") wsl_env_bridge = _wsl_bridge_names(env, unset_env) if _wsl_windows_executable(command) else () if not launch: _print_env(env, command, unset_env = unset_env, wsl_env_bridge = wsl_env_bridge) @@ -1306,7 +1306,7 @@ def write_openclaw_config( ) return before = json.dumps(config, sort_keys = True) - # Studio is a generic OpenAI-compatible /v1 endpoint (the vLLM/LM Studio path). + # Unsloth is a generic OpenAI-compatible /v1 endpoint (the vLLM/LM Studio path). provider_model = {"id": model["id"], "name": model["id"]} window = model.get("context_length") or model.get("max_context_length") if window: @@ -1570,14 +1570,14 @@ def write_pi_config(base: str, key: str, model: dict, path: Path) -> None: return before = json.dumps(config, sort_keys = True) # Pi reads custom providers from ~/.pi/agent/models.json (HOME-relocated for the - # session). Studio is a generic OpenAI-compatible /v1 endpoint, and the key lives + # session). Unsloth is a generic OpenAI-compatible /v1 endpoint, and the key lives # in the config rather than the env (matching openclaw/opencode). provider_model = {"id": model["id"]} window = model.get("context_length") or model.get("max_context_length") if window: window = int(window) # An unspecified model defaults to contextWindow 128000 / maxTokens 16384, - # far larger than a small Studio context, so Pi compacts too late and overflows + # far larger than a small Unsloth context, so Pi compacts too late and overflows # the server. Pin the real window and a sane output cap (mirrors OpenCode). provider_model["contextWindow"] = window provider_model["maxTokens"] = min(window // 4, 8192) @@ -1606,7 +1606,7 @@ def claude( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point Claude Code at the running Studio server and start it.""" + """Point Claude Code at the running Unsloth server and start it.""" base, key, entry = _connect( api_key, model, @@ -1690,7 +1690,7 @@ def codex( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point OpenAI Codex at the running Studio server and start it.""" + """Point OpenAI Codex at the running Unsloth server and start it.""" base, key, entry = _connect( api_key, model, @@ -1734,7 +1734,7 @@ def openclaw( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point OpenClaw at the running Studio server and start it.""" + """Point OpenClaw at the running Unsloth server and start it.""" base, key, entry = _connect( api_key, model, @@ -1791,7 +1791,7 @@ def opencode( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point OpenCode at the running Studio server and start it.""" + """Point OpenCode at the running Unsloth server and start it.""" base, key, entry = _connect( api_key, model, @@ -1835,7 +1835,7 @@ def opencode( # setting them in the highest-priority inline overlay neutralizes any user allowlist # or denylist for the launch. It is session-only: it lives in OPENCODE_CONFIG_CONTENT # for this invocation and never touches the user's config files, so their normal - # `opencode` is unchanged; only this session is limited to the Studio provider. + # `opencode` is unchanged; only this session is limited to the Unsloth provider. # small_model is opencode's separate model for lightweight tasks; pin it to the # session model too, or a user/project small_model on another (now filtered) # provider would resolve a not-found error mid-session. The session serves one @@ -1869,7 +1869,7 @@ def hermes( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point Hermes (Nous Research) at the running Studio server and start it.""" + """Point Hermes (Nous Research) at the running Unsloth server and start it.""" native_args = [*_yolo_command_flags("hermes", yolo), *ctx.args] command = ["hermes", *_hermes_resume_oneshot_args(native_args)] base, key, entry = _connect( @@ -1902,7 +1902,7 @@ def pi( yolo: bool = _YOLO_OPTION, persist: bool = _PERSIST_OPTION, ): - """Point Pi (coding agent) at the running Studio server and start it.""" + """Point Pi (coding agent) at the running Unsloth server and start it.""" base, key, entry = _connect( api_key, model, diff --git a/unsloth_cli/commands/studio.py b/unsloth_cli/commands/studio.py index 09355bd454..f2f41fc583 100644 --- a/unsloth_cli/commands/studio.py +++ b/unsloth_cli/commands/studio.py @@ -40,7 +40,7 @@ def _enable_verbose_access_logs() -> None: # UNSLOTH_STUDIO_HOME wins when both env vars are set. def _looks_like_installer_managed_studio_home(candidate: Path) -> bool: """Sentinel check (studio.conf or bin shim) so a dev venv named - unsloth_studio is not misidentified as a custom Studio root. + unsloth_studio is not misidentified as a custom Unsloth root. """ shim_name = "unsloth.exe" if platform.system() == "Windows" else "unsloth" return (candidate / "share" / "studio.conf").is_file() or ( @@ -212,7 +212,7 @@ def _find_run_py() -> Optional[Path]: run_py = _PACKAGE_ROOT / "studio" / "backend" / "run.py" if run_py.is_file(): return run_py - # 2. Studio venv's site-packages (Linux + Windows layouts) + # 2. Unsloth venv's site-packages (Linux + Windows layouts) for pattern in ( "lib/python*/site-packages/studio/backend/run.py", "Lib/site-packages/studio/backend/run.py", @@ -273,7 +273,7 @@ def _find_setup_script() -> Optional[Path]: s = _PACKAGE_ROOT / "studio" / name if s.is_file(): return s - # 2. Studio venv's site-packages + # 2. Unsloth venv's site-packages for pattern in ( f"lib/python*/site-packages/studio/{name}", f"Lib/site-packages/studio/{name}", @@ -641,7 +641,7 @@ def _create_desktop_secret_in_cli() -> str: def _should_prompt_password_change( *, cloudflare: Optional[bool], host: str, secure: bool, api_only: bool ) -> bool: - """Whether this launch will expose Studio through the Cloudflare tunnel. + """Whether this launch will expose Unsloth through the Cloudflare tunnel. CLI mirror of run.py's _cloudflare_tunnel_should_start, minus the Colab case (Colab launches never come through this CLI path). --secure implies @@ -747,7 +747,7 @@ def _apply_supplied_password_before_launch(supplied_password: "str | None") -> N conn = _connect_auth_db() except (OSError, sqlite3.Error) as exc: typer.echo( - f"Error: --password could not open the Studio auth database ({exc}); not starting.", + f"Error: --password could not open the Unsloth auth database ({exc}); not starting.", err = True, ) raise typer.Exit(1) @@ -767,7 +767,7 @@ def _apply_supplied_password_before_launch(supplied_password: "str | None") -> N raise typer.Exit(1) if not row[2]: typer.echo( - "Error: a Studio admin password is already set; --password only sets " + "Error: an Unsloth admin password is already set; --password only sets " "the initial password. Run `unsloth studio reset-password` first " "(or change it in the UI).", err = True, @@ -790,7 +790,7 @@ def _apply_supplied_password_before_launch(supplied_password: "str | None") -> N # Any DB failure fails closed (typer.Exit is not caught here, so the # deliberate Exit(1) branches above propagate unchanged). typer.echo( - f"Error: --password could not update the Studio auth database ({exc}); not starting.", + f"Error: --password could not update the Unsloth auth database ({exc}); not starting.", err = True, ) raise typer.Exit(1) @@ -813,9 +813,9 @@ def _strip_seeded_bootstrap_password_or_exit(*, context: str) -> None: bootstrap_file.unlink(missing_ok = True) except OSError as exc: typer.echo( - "Error: refusing to publish Studio on a public Cloudflare URL: " + "Error: refusing to publish Unsloth on a public Cloudflare URL: " f"could not remove the seeded bootstrap password file ({exc}), so an " - f"older Studio child could still serve the default credential ({context}). " + f"older Unsloth child could still serve the default credential ({context}). " "Delete it manually or change the admin password (run `unsloth studio` " "locally with a terminal attached, or `unsloth studio reset-password`), " "then retry.", @@ -851,7 +851,7 @@ def _require_servable_frontend_or_exit( return frontend typer.echo( "Error: --frontend points at a directory with no index.html, so a " - "public Studio launch would have no login page to change the seeded " + "public Unsloth launch would have no login page to change the seeded " "admin password. Point --frontend at a built dist, rebuild it (re-run " "install.sh), or use --api-only.", err = True, @@ -862,7 +862,7 @@ def _require_servable_frontend_or_exit( if resolved is not None: return resolved typer.echo( - "Error: the Studio frontend is not built, so a public launch would have " + "Error: the Unsloth frontend is not built, so a public launch would have " "no login page to change the seeded admin password. Build it (re-run " "install.sh), pass --frontend PATH to a built dist, or use --api-only.", err = True, @@ -892,8 +892,8 @@ def _validate_inproc_backend_before_strip( _load_run_module() except Exception as exc: typer.echo( - f"Error: the Studio backend could not be loaded ({exc}); refusing to " - "expose Studio publicly before it is confirmed runnable. Re-run: " + f"Error: the Unsloth backend could not be loaded ({exc}); refusing to " + "expose Unsloth publicly before it is confirmed runnable. Re-run: " "unsloth studio setup", err = True, ) @@ -902,7 +902,7 @@ def _validate_inproc_backend_before_strip( def _tunnel_binary_confirmed_unavailable() -> bool: """True only if cloudflared is provably unavailable (found nowhere on PATH or - in the Studio cache AND the download failed), so the tunnel cannot start. + in the Unsloth cache AND the download failed), so the tunnel cannot start. Used on the --secure path (loopback bind, so the tunnel is the ONLY public exposure) to skip stripping the seeded recovery password before a public URL @@ -921,7 +921,7 @@ def _tunnel_binary_confirmed_unavailable() -> bool: if not tunnel_py.is_file(): return False # ensure_cloudflared() lazily imports utils.paths.storage_roots to resolve the - # Studio bin cache. The outer CLI hasn't added studio/backend to sys.path yet, + # Unsloth bin cache. The outer CLI hasn't added studio/backend to sys.path yet, # so that import would fail and return None (a false "unavailable" that wrongly # refuses --secure). Add the backend dir so the cache path resolves as in the child. added_backend_path = False @@ -946,7 +946,7 @@ def _tunnel_binary_confirmed_unavailable() -> bool: def _child_self_suppresses(*, in_studio_venv: bool, child_run_py: Optional[Path]) -> bool: - """True when the child that will serve Studio is provably THIS install's + """True when the child that will serve Unsloth is provably THIS install's backend, whose pre-bind gate sets app.state.suppress_bootstrap_injection and so never serves the seeded credential publicly -- even with .bootstrap_password on disk. The parent-side strip is then unnecessary and can be skipped to avoid @@ -1002,8 +1002,8 @@ def _enforce_password_change_before_exposure( # Refuse rather than risk a child serving the default login; a transient # lock clears on retry. typer.echo( - "Error: refusing to publish Studio on a public Cloudflare URL: could " - f"not open the Studio auth database ({exc}) to confirm the admin " + "Error: refusing to publish Unsloth on a public Cloudflare URL: could " + f"not open the Unsloth auth database ({exc}) to confirm the admin " "password was changed. Retry (a transient database lock clears), or " "change the password first (run `unsloth studio` locally with a " "terminal attached, or `unsloth studio reset-password`).", @@ -1028,8 +1028,8 @@ def _enforce_password_change_before_exposure( except OSError: pass typer.echo( - "Error: refusing to publish Studio on a public Cloudflare URL: could " - f"not initialize the admin account ({exc}), so a re-exec'd Studio " + "Error: refusing to publish Unsloth on a public Cloudflare URL: could " + f"not initialize the admin account ({exc}), so a re-exec'd Unsloth " "child could regenerate and serve a default credential. Retry (a " "transient database lock clears), or change the password first (run " "`unsloth studio` locally with a terminal attached, or `unsloth " @@ -1053,7 +1053,7 @@ def _enforce_password_change_before_exposure( # regenerate; we just couldn't read must_change back. Strip the seeded # file so nothing serves it, failing closed if the strip itself fails. typer.echo( - f"Warning: could not read the Studio admin state back ({exc}); " + f"Warning: could not read the Unsloth admin state back ({exc}); " "removing the seeded bootstrap password before public exposure.", err = True, ) @@ -1066,7 +1066,7 @@ def _enforce_password_change_before_exposure( # the launch: it never arms for api-only, and TIMEOUT=0 disables it. if api_only or not _bootstrap_deadline_active(): typer.echo( - "Error: refusing to publish Studio on a public Cloudflare " + "Error: refusing to publish Unsloth on a public Cloudflare " "URL: the default admin password was never changed, no " "terminal is attached to change it here, and the bootstrap " "shutdown deadline does not apply to this launch (api-only, " @@ -1085,12 +1085,12 @@ def _enforce_password_change_before_exposure( # fails). Keep the file for LOCAL recovery; must_change stays set # and the deadline arms. typer.echo( - "Warning: Studio is being exposed publicly while the admin " + "Warning: Unsloth is being exposed publicly while the admin " "account still uses its auto-generated bootstrap password. The " "login page forces a change and the credential is never served " "on the public page. Set a new password by running `unsloth " "studio` locally with a terminal attached, or `unsloth studio " - "reset-password`; Studio shuts down after ~1h if the password " + "reset-password`; Unsloth shuts down after ~1h if the password " "stays unchanged (UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT).", err = True, ) @@ -1104,7 +1104,7 @@ def _enforce_password_change_before_exposure( # uncertainty.) if secure and _tunnel_binary_confirmed_unavailable(): typer.echo( - "Error: refusing to expose Studio: the Cloudflare tunnel binary " + "Error: refusing to expose Unsloth: the Cloudflare tunnel binary " "(cloudflared) is unavailable and could not be downloaded, so no " "public URL can start. The seeded bootstrap password is preserved " "for recovery; fix connectivity and retry, or change the password " @@ -1121,11 +1121,11 @@ def _enforce_password_change_before_exposure( # forces a change and the timer still arms; only the on-disk copy goes. _strip_seeded_bootstrap_password_or_exit(context = "no terminal to change it") typer.echo( - "Warning: Studio is being exposed publicly while the admin account " + "Warning: Unsloth is being exposed publicly while the admin account " "still uses its auto-generated bootstrap password. The seeded password " "file has been removed so it is not served on the public page. Set a new " "password by running `unsloth studio` locally with a terminal attached, " - "or `unsloth studio reset-password`; Studio shuts down after ~1h if the " + "or `unsloth studio reset-password`; Unsloth shuts down after ~1h if the " "password stays unchanged (UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT).", err = True, ) @@ -1146,7 +1146,7 @@ def _enforce_password_change_before_exposure( new_password = _password_prompt.prompt_new_password(_is_current_password) except (KeyboardInterrupt, EOFError): typer.echo( - "\nError: password change aborted; refusing to expose Studio " + "\nError: password change aborted; refusing to expose Unsloth " "with the default admin password. Re-run and set a password, " "or launch without --secure/--cloudflare.", err = True, @@ -1247,7 +1247,7 @@ def studio_default( cloudflare: Optional[bool] = typer.Option( None, "--cloudflare/--no-cloudflare", - help = "Expose Studio on a PUBLIC internet URL via a free Cloudflare HTTPS " + help = "Expose Unsloth on a PUBLIC internet URL via a free Cloudflare HTTPS " "tunnel, for non-api-only wildcard binds (0.0.0.0 or ::). Off by default; " "pass --cloudflare to enable it (--secure implies it). --no-cloudflare forces " "it off but does not change a raw wildcard bind.", @@ -1404,7 +1404,7 @@ def studio_default( studio_python = _studio_venv_python() run_py = _find_run_py() if not (studio_python and run_py): - typer.echo("Studio not set up. Run install.sh first.") + typer.echo("Unsloth Studio not set up. Run install.sh first.") raise typer.Exit(1) # A public UI launch must have a servable login page BEFORE the gate can # strip the seeded .bootstrap_password, or the child has no way to change @@ -1510,7 +1510,7 @@ def studio_default( rc = proc.wait() if rc != 0: typer.echo( - f"\nError: Studio server exited unexpectedly (code {rc}).", + f"\nError: Unsloth server exited unexpectedly (code {rc}).", err = True, ) typer.echo( @@ -1522,7 +1522,7 @@ def studio_default( else: os.execvp(str(studio_python), args) else: - typer.echo("Studio not set up. Run install.sh first.") + typer.echo("Unsloth Studio not set up. Run install.sh first.") raise typer.Exit(1) run_mod = _load_run_module() @@ -1733,7 +1733,7 @@ def run( cloudflare: Optional[bool] = typer.Option( None, "--cloudflare/--no-cloudflare", - help = "Expose Studio on a PUBLIC internet URL via a free Cloudflare HTTPS " + help = "Expose Unsloth on a PUBLIC internet URL via a free Cloudflare HTTPS " "tunnel, for non-api-only wildcard binds (0.0.0.0 or ::). Off by default; " "pass --cloudflare to enable it (--secure implies it). --no-cloudflare forces " "it off but does not change a raw wildcard bind.", @@ -1769,16 +1769,16 @@ def run( "process list and shell history. Rotate later with `unsloth studio reset-password`.", ), ): - """Start Studio, load a model, print an API key -- one-liner server. + """Start Unsloth, load a model, print an API key -- one-liner server. - Unknown flags pass through to llama-server (GGUF only). Studio + Unknown flags pass through to llama-server (GGUF only). Unsloth rejects managed flags with HTTP 400: model identity, network (--host/--port/--path/--api-prefix/--reuse-port), auth/TLS (--api-key/--ssl-*), single-model UI (--ui/--models-*/--webui), and parallel slots (use --parallel above). Full denylist in studio/backend/core/inference/llama_server_args.py. Other knobs (-c, -ngl, --jinja, --flash-attn, -t, ...) pass through and - last-wins-override Studio's auto-set value. + last-wins-override Unsloth's auto-set value. Example: unsloth studio run --model unsloth/Qwen3-1.7B-GGUF --gguf-variant UD-Q4_K_XL @@ -1792,7 +1792,7 @@ def run( # Set before any re-exec so the in-venv server inherits it via the env. # `run --verbose` used to pass through to llama-server (its own -v); keep - # that by forwarding --log-verbose so we add Studio logs without dropping it. + # that by forwarding --log-verbose so we add Unsloth logs without dropping it. if verbose: _enable_verbose_access_logs() if not any(a in ("--verbose", "-v", "--log-verbose") for a in extra_llama_args): @@ -1878,14 +1878,14 @@ def run( if not in_studio_venv: studio_python = _studio_venv_python() if not studio_python: - typer.echo("Studio not set up. Run install.sh first.") + typer.echo("Unsloth Studio not set up. Run install.sh first.") raise typer.Exit(1) # Re-exec via the studio venv's `unsloth` console-script. studio_bin = studio_python.parent / "unsloth" if not studio_bin.is_file(): - typer.echo("Studio venv missing 'unsloth' entry point. Re-run: unsloth studio setup") + typer.echo("Unsloth venv missing 'unsloth' entry point. Re-run: unsloth studio setup") raise typer.Exit(1) - # `run` serves the same Studio UI (unless --api-only); a public launch must + # `run` serves the same Unsloth UI (unless --api-only); a public launch must # have a servable login page BEFORE the gate strips the seeded password, or # the child has no way to change it. Validate here and forward the resolved # dist so a shadowed child that can't self-resolve one still serves it. @@ -2216,7 +2216,7 @@ def stop(): import signal as _signal if not _PID_FILE.is_file(): - typer.echo("No running Studio server found (no PID file).") + typer.echo("No running Unsloth server found (no PID file).") raise typer.Exit(0) pid_text = _PID_FILE.read_text().strip() @@ -2229,7 +2229,7 @@ def stop(): # Check if still alive (os.kill(pid, 0) is invalid on Windows -- see _pid_alive). if not _pid_alive(pid): - typer.echo(f"Studio server (PID {pid}) is not running. Cleaning up stale PID file.") + typer.echo(f"Unsloth server (PID {pid}) is not running. Cleaning up stale PID file.") _PID_FILE.unlink(missing_ok = True) raise typer.Exit(0) @@ -2239,13 +2239,13 @@ def stop(): subprocess.run(["taskkill", "/PID", str(pid), "/F"], check = True) else: os.kill(pid, _signal.SIGTERM) - typer.echo(f"Sent shutdown signal to Studio server (PID {pid}).") + typer.echo(f"Sent shutdown signal to Unsloth server (PID {pid}).") except ProcessLookupError: - typer.echo(f"Studio server (PID {pid}) already exited.") + typer.echo(f"Unsloth server (PID {pid}) already exited.") _PID_FILE.unlink(missing_ok = True) raise typer.Exit(0) except Exception as e: - typer.echo(f"Failed to stop Studio server (PID {pid}): {e}", err = True) + typer.echo(f"Failed to stop Unsloth server (PID {pid}): {e}", err = True) raise typer.Exit(1) # Wait briefly for the process to exit and clean up. @@ -2253,10 +2253,10 @@ def stop(): time.sleep(0.5) if not _pid_alive(pid): _PID_FILE.unlink(missing_ok = True) - typer.echo("Studio server stopped.") + typer.echo("Unsloth server stopped.") raise typer.Exit(0) - typer.echo("Studio server is shutting down (may take a few seconds).") + typer.echo("Unsloth server is shutting down (may take a few seconds).") # ── unsloth studio setup / update ───────────────────────────────────── @@ -2471,7 +2471,7 @@ def setup( help = "Full pip/build output during setup for troubleshooting.", ), ): - """Run Studio setup (called by install.ps1 / install.sh).""" + """Run Unsloth setup (called by install.ps1 / install.sh).""" _run_setup_script(verbose = verbose) @@ -2632,10 +2632,10 @@ def provision_desktop_auth(): @studio_app.command("reset-password") def reset_password(): - """Reset the Studio admin password. + """Reset the Unsloth admin password. Deletes the auth database so that a fresh admin account with a new - random password is created on the next server start. The Studio + random password is created on the next server start. The Unsloth server must be restarted after running this command. """ auth_dir = STUDIO_HOME / "auth" @@ -2647,7 +2647,7 @@ def reset_password(): had_db = db_file.exists() # Delete auth.db FIRST and prove it is gone before touching the seeded - # credential files. If it cannot be removed (a running Studio or Windows + # credential files. If it cannot be removed (a running Unsloth or Windows # holds it open, or a read-only auth dir), abort with the credential files # untouched: deleting them while an un-resettable DB (must_change_password=1) # survives would lock a forgotten-password reset out of any recovery @@ -2657,7 +2657,7 @@ def reset_password(): except OSError as exc: typer.echo( f"Error: could not delete the auth database ({exc}). Stop any running " - "Studio and retry; no credential files were changed.", + "Unsloth and retry; no credential files were changed.", err = True, ) raise typer.Exit(1) @@ -2679,7 +2679,7 @@ def reset_password(): except OSError as exc: typer.echo( f"Error: could not remove or clear {path.name} ({exc}); delete " - "it manually before restarting Studio or the old password may " + "it manually before restarting Unsloth or the old password may " "be reused.", err = True, ) diff --git a/unsloth_cli/tests/test_inference_chat.py b/unsloth_cli/tests/test_inference_chat.py index 56633408fb..ae6f8dcfd4 100644 --- a/unsloth_cli/tests/test_inference_chat.py +++ b/unsloth_cli/tests/test_inference_chat.py @@ -372,7 +372,7 @@ def test_find_studio_server_none_when_not_running(monkeypatch): def test_find_studio_server_prefers_ipv4_loopback_for_localhost(monkeypatch): - # localhost resolving ::1-first must not hide a Studio bound to 127.0.0.1: + # localhost resolving ::1-first must not hide an Unsloth bound to 127.0.0.1: # discovery tries each loopback address and returns the one that answers. import socket import urllib.request diff --git a/unsloth_cli/tests/test_start.py b/unsloth_cli/tests/test_start.py index 7e76465144..2405ba0480 100644 --- a/unsloth_cli/tests/test_start.py +++ b/unsloth_cli/tests/test_start.py @@ -648,7 +648,7 @@ def test_resolve_model_attaches_to_loaded_catalog_hit_without_reload(monkeypatch def test_resolve_model_remote_studio_does_not_casefold_attach(monkeypatch): - # Against a remote Studio the local existence probe cannot see server-side paths, + # Against a remote Unsloth the local existence probe cannot see server-side paths, # so a case-variant loaded id must NOT attach without a load: it could be a distinct # server-side path on a case-sensitive host. The load endpoint resolves the request. calls = [] @@ -738,7 +738,7 @@ def test_no_launch_output_is_parseable(fake_studio): result = CliRunner().invoke(start.start_app, ["codex", "--no-launch"]) assert result.exit_code == 0, result.output lines = [ln for ln in result.output.splitlines() if ln.strip()] - skip = ("export ", "unset ", "Studio ", "Updated ", "Disabled ", "Warning", "Loading") + skip = ("export ", "unset ", "Unsloth ", "Updated ", "Disabled ", "Warning", "Loading") body = [ln for ln in lines if not ln.startswith(skip)] assert "codex --oss --profile unsloth_api" in body[-1] assert any(ln.startswith("export CODEX_HOME=") for ln in lines) @@ -767,7 +767,7 @@ def test_no_launch_last_line_is_self_contained(fake_studio, tmp_path): def test_no_launch_claude_last_line_blanks_conflicting_auth(fake_studio): # The unset vars must be neutralized inline too, or a partial copy would send the - # user's own ANTHROPIC_API_KEY to the Studio base. + # user's own ANTHROPIC_API_KEY to the Unsloth base. result = CliRunner().invoke(start.start_app, ["claude", "--no-launch"]) assert result.exit_code == 0, result.output last = [ln for ln in result.output.splitlines() if ln.strip()][-1] @@ -814,7 +814,7 @@ def test_https_loopback_never_auto_serves(fake_studio, monkeypatch): ) result = CliRunner().invoke(start.start_app, ["claude", "--model", "unsloth/Qwen3-1.7B-GGUF"]) assert result.exit_code == 1 - assert "No running Studio server" in result.output + assert "No running Unsloth server" in result.output assert started["called"] is False @@ -967,7 +967,7 @@ def test_connect_model_flag_forwards_load_options(fake_studio): def test_connect_model_flag_matches_canonical_id(fake_studio, monkeypatch): - # Studio registers a loaded model under a canonical id (resolved identifier + # Unsloth registers a loaded model under a canonical id (resolved identifier # / casing) that can differ from the path we passed. The agent must connect # to that model, not silently fall through to the first loaded one. requested = "Unsloth/Qwen3.5-35B-A3B" @@ -1024,7 +1024,7 @@ def test_connect_model_bare_id_matches_loaded_without_reload(fake_studio): def test_connect_model_variant_suffix_defers_to_server_dedup(fake_studio): # `--model repo:QUANT` splits into a VALID load payload (bare repo + gguf_variant), - # never the `:`-suffixed repo id Studio rejects. The variant knob defers to + # never the `:`-suffixed repo id Unsloth rejects. The variant knob defers to # /api/inference/load, whose already-loaded dedup answers without reloading when the # active variant+settings match -- so a second session running the same command # attaches without evicting the first, while a genuinely different quant reloads. @@ -1116,7 +1116,7 @@ def test_connect_no_model_loaded_errors(fake_studio, monkeypatch): def test_connect_requested_model_not_loaded_fails(fake_studio, monkeypatch): - # Studio never surfaces the requested model; fail loudly rather than + # Unsloth never surfaces the requested model; fail loudly rather than # silently connecting to whatever else happens to be loaded. inner = start._http_json @@ -1187,7 +1187,7 @@ def test_connect_nonloopback_explicit_key_is_allowed(fake_studio, monkeypatch): def test_connect_nonloopback_replays_saved_key(fake_studio, tmp_path, monkeypatch): - # A key saved for a remote (non-loopback) Studio is replayed on keyless runs; + # A key saved for a remote (non-loopback) Unsloth is replayed on keyless runs; # auto-minting stays blocked for non-loopback. remote = "http://studio.example:8888" monkeypatch.setattr(start, "find_studio_server", lambda: remote) @@ -1201,7 +1201,7 @@ def test_connect_nonloopback_replays_saved_key(fake_studio, tmp_path, monkeypatc def test_connect_studio_server_errors_on_explicit_remote(monkeypatch): - # A user who pointed UNSLOTH_STUDIO_URL at a remote Studio should get an + # A user who pointed UNSLOTH_STUDIO_URL at a remote Unsloth should get an # error, not a silent local model load (which they did not ask for). import typer @@ -1242,7 +1242,7 @@ def test_connect_unverified_loopback_without_cached_key_refuses_to_mint( def test_connect_replays_saved_key_without_identity_check(fake_studio, tmp_path, monkeypatch): - # A "saved" key (e.g. for an SSH-tunnelled Studio the handshake can't match) + # A "saved" key (e.g. for an SSH-tunnelled Unsloth the handshake can't match) # replays on keyless runs without the handshake, scoped to its own base. cache = tmp_path / "agent_api_key.json" cache.write_text(json.dumps({"servers": {BASE: {"saved": ["sk-unsloth-deadbeefdeadbeef"]}}})) @@ -1358,7 +1358,7 @@ def _serve_redirect(target): def test_verify_studio_identity_rejects_redirect(tmp_path, monkeypatch): - # A squatter could 302 /api/auth/identity to the real Studio and relay its + # A squatter could 302 /api/auth/identity to the real Unsloth and relay its # proof; redirects must be refused so the squatter's base isn't accepted. import unsloth_cli._inference as inference @@ -1384,7 +1384,7 @@ def test_verify_studio_identity_rejects_redirect(tmp_path, monkeypatch): def test_verify_studio_identity_rejects_relayed_proof(tmp_path, monkeypatch): - # A squatter that proxies the nonce to the real Studio on another port gets a + # A squatter that proxies the nonce to the real Unsloth on another port gets a # proof bound to *that* port; the client expects one bound to the port it # connected to, so the relayed proof is rejected. import unsloth_cli._inference as inference @@ -1435,7 +1435,7 @@ def test_connect_no_studio_errors(fake_studio, monkeypatch): monkeypatch.setattr(start, "find_studio_server", lambda: None) result = CliRunner().invoke(start.start_app, ["claude", "--no-launch"]) assert result.exit_code == 1 - assert "No running Studio server" in result.output + assert "No running Unsloth server" in result.output @pytest.fixture(autouse = True) @@ -1564,7 +1564,7 @@ def test_no_serve_preserves_error(fake_studio, monkeypatch): start.start_app, ["claude", "--model", "unsloth/Qwen3-1.7B-GGUF", "--no-serve"] ) assert result.exit_code == 1 - assert "No running Studio server" in result.output + assert "No running Unsloth server" in result.output assert started["called"] is False @@ -1578,7 +1578,7 @@ def test_no_launch_never_serves(fake_studio, monkeypatch): start.start_app, ["claude", "--model", "unsloth/Qwen3-1.7B-GGUF", "--no-launch"] ) assert result.exit_code == 1 - assert "No running Studio server" in result.output + assert "No running Unsloth server" in result.output assert started["called"] is False @@ -1871,7 +1871,7 @@ def _opencode_inline_config(output: str) -> dict: def test_opencode_inline_scopes_session_to_studio_provider(fake_studio): # opencode filters even config-defined providers through enabled/disabled_providers, # and a model pin does not bypass that gate. The inline overlay (session-only, highest - # layer, arrays replace) allowlists our provider and clears the denylist so the Studio + # layer, arrays replace) allowlists our provider and clears the denylist so the Unsloth # model always loads regardless of the user's config, without reading or editing it. result = CliRunner().invoke(start.start_app, ["opencode", "--no-launch"]) assert result.exit_code == 0, result.output @@ -2657,7 +2657,7 @@ def test_agent_api_key_auto_started_rejected_env_key_falls_back(fake_studio, tmp def test_agent_api_key_auto_started_accepted_key_is_honored(fake_studio, tmp_path): - # An explicit key the fresh server accepts (e.g. persisted in this Studio + # An explicit key the fresh server accepts (e.g. persisted in this Unsloth # home's auth db across restarts) keeps working exactly as before. key = start._agent_api_key(BASE, "sk-unsloth-deadbeefdeadbeef", auto_started = True) assert key == "sk-unsloth-deadbeefdeadbeef" @@ -2937,11 +2937,11 @@ def test_hermes_resume_oneshot_rejects_usage_file(monkeypatch, usage_arg): def test_native_resume_flag_passes_through_unchanged(fake_studio, monkeypatch): # The persistence flag is --persist, NOT --resume, so an agent's own # `--resume ` (e.g. `unsloth start claude --resume `) still flows - # through to the agent verbatim and is not swallowed as a Studio option. + # through to the agent verbatim and is not swallowed as an Unsloth option. monkeypatch.setattr(start.shutil, "which", lambda _: "/usr/local/bin/claude") monkeypatch.setattr(start, "_claude_flags", lambda: []) captured = _capture_launch(monkeypatch, ["claude", "--resume", "some-session-guid"]) assert captured["command"][-2:] == ["--resume", "some-session-guid"] - # Studio never auto-appends its own resume token when the user drives resume. + # Unsloth never auto-appends its own resume token when the user drives resume. assert captured["command"].count("--resume") == 1 assert "--continue" not in captured["command"] diff --git a/unsloth_cli/tests/test_studio_cloudflare_flag.py b/unsloth_cli/tests/test_studio_cloudflare_flag.py index fb57d7aaf4..7287737f75 100644 --- a/unsloth_cli/tests/test_studio_cloudflare_flag.py +++ b/unsloth_cli/tests/test_studio_cloudflare_flag.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for the `--cloudflare/--no-cloudflare` Studio flag. +"""Tests for the `--cloudflare/--no-cloudflare` Unsloth flag. Pins the typer Option (tri-state, default off / None) on both `unsloth studio` and `unsloth studio run`, and that the chosen polarity reaches the re-exec'd diff --git a/unsloth_cli/tests/test_studio_password_prompt.py b/unsloth_cli/tests/test_studio_password_prompt.py index 7bbdfe3703..6e9a2c1d52 100644 --- a/unsloth_cli/tests/test_studio_password_prompt.py +++ b/unsloth_cli/tests/test_studio_password_prompt.py @@ -885,7 +885,7 @@ def test_run_non_tty_deletes_bootstrap_password_file(monkeypatch, tmp_path): def test_run_missing_frontend_exits_before_stripping_bootstrap(monkeypatch, tmp_path): # Regression (item B / reviewer finding 4): `unsloth studio run` serves the - # same Studio UI and strips the seeded password on a headless public launch, + # same Unsloth UI and strips the seeded password on a headless public launch, # so a missing frontend dist must abort BEFORE the strip -- the same lockout # guard as `unsloth studio`, not just `studio run`'s model-load residual. import typer as _typer @@ -1104,7 +1104,7 @@ def test_cli_update_password_truncates_locked_bootstrap_after_change(monkeypatch def test_reset_password_fails_closed_when_db_cannot_be_deleted(monkeypatch, tmp_path): - # If auth.db cannot be removed (running Studio / Windows lock, read-only dir), + # If auth.db cannot be removed (running Unsloth / Windows lock, read-only dir), # reset must abort BEFORE touching the credential files -- deleting them while # an un-resettable must_change_password=1 DB survives would lock a # forgotten-password reset out with no recovery credential. diff --git a/unsloth_cli/tests/test_studio_secure_flag.py b/unsloth_cli/tests/test_studio_secure_flag.py index 5e5895309c..2a67aad95a 100644 --- a/unsloth_cli/tests/test_studio_secure_flag.py +++ b/unsloth_cli/tests/test_studio_secure_flag.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for the `--secure/--no-secure` Studio flag: option registration, +"""Tests for the `--secure/--no-secure` Unsloth flag: option registration, re-exec/run_server forwarding, the forced 127.0.0.1 bind, and rejection alongside --no-cloudflare or before a subcommand. Modeled on test_studio_cloudflare_flag.py.""" diff --git a/unsloth_cli/tests/test_studio_verbose_flag.py b/unsloth_cli/tests/test_studio_verbose_flag.py index 4af32fd4a2..20468b5f02 100644 --- a/unsloth_cli/tests/test_studio_verbose_flag.py +++ b/unsloth_cli/tests/test_studio_verbose_flag.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -"""Tests for the `--verbose/-v` Studio flag: option registration on both the +"""Tests for the `--verbose/-v` Unsloth flag: option registration on both the plain callback and the `run` subcommand, re-exec forwarding, the access-log env override, and rejection before a subcommand. Modeled on test_studio_secure_flag.py.""" @@ -123,7 +123,7 @@ def test_run_without_verbose_leaves_env_unset(monkeypatch): def test_run_verbose_preserves_llama_server_verbosity(monkeypatch): - # Studio consumes --verbose but still forwards llama-server's own verbosity. + # Unsloth consumes --verbose but still forwards llama-server's own verbosity. monkeypatch.delenv(_DEDUP, raising = False) monkeypatch.delenv(_POLL, raising = False) captured = _invoke_run(monkeypatch, _BASE + ["--verbose"])