#!/usr/bin/env bash # SPDX-License-Identifier: AGPL-3.0-only # Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" RULE=$(printf '\342\224\200%.0s' {1..52}) # ── Maintainer-editable defaults ────────────────────────────────────────── # Change these in the GitHub-hosted script so all users get updated defaults. # User environment variables always override these baked-in values. # # _DEFAULT_LLAMA_PR_FORCE : PR number to build by default ("" = normal path) # _DEFAULT_LLAMA_SOURCE : git clone URL for source builds # _DEFAULT_LLAMA_TAG : llama.cpp ref to build ("latest" = newest release, # "master" = bleeding-edge, "bNNNN" = specific tag) # Prefer "latest" over "master" -- "master" bypasses # the prebuilt resolver (no matching GitHub release), # forces a source build, and causes HTTP 422 errors. # Only use "master" temporarily when the latest release # is missing support for a new model architecture. # ────────────────────────────────────────────────────────────────────────── _DEFAULT_LLAMA_PR_FORCE="" _DEFAULT_LLAMA_SOURCE="https://github.com/ggml-org/llama.cpp" _DEFAULT_LLAMA_TAG="latest" _DEFAULT_LLAMA_FORCE_COMPILE_REF="master" # ── Fetch remote manifest ──────────────────────────────────────────── # UNSLOTH_UPDATE_DETAILS.json controls update behavior centrally. # Fields are parsed into MANIFEST_* shell variables; all default to # empty when the fetch fails (graceful degradation). _MANIFEST_URL="https://raw.githubusercontent.com/unslothai/unsloth/main/UNSLOTH_UPDATE_DETAILS.json" MANIFEST_UNSLOTH_SOURCE="" MANIFEST_UNSLOTH_GITHUB_REF="" MANIFEST_LLAMA_CPP_SOURCE="" MANIFEST_LLAMA_CPP_TAG="" MANIFEST_CRITICAL_TIME="" MANIFEST_ANNOUNCEMENT_MESSAGE="" MANIFEST_ANNOUNCEMENT_BADGE="" MANIFEST_ANNOUNCEMENT_URL="" _fetch_manifest() { local _raw _raw=$(curl -fsSL --max-time 8 "$_MANIFEST_URL" 2>/dev/null) || return 0 # Parse all manifest fields in a single Python invocation to avoid # spawning 8 separate processes (each adds ~50ms startup overhead). eval "$(printf '%s' "$_raw" | python -c " import sys, json, shlex try: m = json.load(sys.stdin) except Exception: sys.exit(0) def s(v, default=''): '''Convert to string, treating None/null as empty.''' return str(v) if v is not None else default a = m.get('announcement') or {} for k, v in [ ('MANIFEST_UNSLOTH_SOURCE', s(m.get('unsloth_source'))), ('MANIFEST_UNSLOTH_GITHUB_REF', s(m.get('unsloth_github_ref'), 'main')), ('MANIFEST_LLAMA_CPP_SOURCE', s(m.get('llama_cpp_source'))), ('MANIFEST_LLAMA_CPP_TAG', s(m.get('llama_cpp_tag'))), ('MANIFEST_CRITICAL_TIME', s(m.get('CRITICAL_TIME'))), ('MANIFEST_ANNOUNCEMENT_MESSAGE', s(a.get('message'))), ('MANIFEST_ANNOUNCEMENT_BADGE', s(a.get('badge'))), ('MANIFEST_ANNOUNCEMENT_URL', s(a.get('url'))), ]: print(f'{k}={shlex.quote(v)}') " 2>/dev/null)" || true substep "manifest: source=${MANIFEST_UNSLOTH_SOURCE:-pypi} llama=${MANIFEST_LLAMA_CPP_SOURCE:-default}@${MANIFEST_LLAMA_CPP_TAG:-default}" } # ── Colors (same palette as startup_banner / install_python_stack) ── if [ -n "${NO_COLOR:-}" ]; then C_TITLE= C_DIM= C_OK= C_WARN= C_ERR= C_RST= elif [ -t 1 ] || [ -n "${FORCE_COLOR:-}" ]; then C_TITLE=$'\033[38;5;150m' C_DIM=$'\033[38;5;245m' C_OK=$'\033[38;5;108m' C_WARN=$'\033[38;5;136m' C_ERR=$'\033[91m' C_RST=$'\033[0m' else C_TITLE= C_DIM= C_OK= C_WARN= C_ERR= C_RST= fi # ── Output helpers ── # Consistent column layout: 2-space indent, 15-char label (fits llama-quantize), then value. # Usage: step