Merge remote-tracking branch 'origin/main' into studio-tokenless-llama-prebuilt

This commit is contained in:
Daniel Han 2026-05-31 10:03:02 +00:00
commit b1e20dc893
15 changed files with 1629 additions and 113 deletions

57
.github/scripts/assert-llama-loads.sh vendored Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env bash
# 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
# 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.
set -uo pipefail
UNSLOTH_HOME="${STUDIO_HOME:-$HOME/.unsloth}"
LLAMA_DIR="${LLAMA_CPP_DIR:-$UNSLOTH_HOME/llama.cpp}"
BIN_DIR="$LLAMA_DIR/build/bin"
fail() {
echo "::error::$*"
if [ -f logs/install.log ]; then
echo "---- install.log (llama.cpp lines) ----"
grep -E "llama-prebuilt|llama\.cpp|macos prebuilt|falling back" logs/install.log | tail -80 || true
fi
exit 1
}
SERVER="$(find "$LLAMA_DIR" -type f -name 'llama-server' 2>/dev/null | head -1)"
QUANT="$(find "$LLAMA_DIR" -type f -name 'llama-quantize' 2>/dev/null | head -1)"
[ -n "$SERVER" ] || fail "llama-server not found under $LLAMA_DIR after install"
[ -n "$QUANT" ] || fail "llama-quantize not found under $LLAMA_DIR after install"
HOST_VER="$(sw_vers -productVersion 2>/dev/null || echo '0')"
HOST_MAJOR="${HOST_VER%%.*}"
# Static minimum-OS check on every Mach-O we ship. vtool ships with the Xcode
# command line tools, which GitHub macOS runners always have; if it is somehow
# missing we skip the static check and rely on the runtime launch below.
if command -v vtool >/dev/null 2>&1; then
while IFS= read -r macho; do
[ -n "$macho" ] || continue
minos="$(vtool -show-build "$macho" 2>/dev/null | awk '/minos/{print $2; exit}')"
[ -n "$minos" ] || continue
min_major="${minos%%.*}"
if [ "$min_major" -gt "$HOST_MAJOR" ] 2>/dev/null; then
fail "$(basename "$macho") is built for macOS $minos but this runner is macOS $HOST_VER (prebuilt is newer than the host)"
fi
done < <(find "$BIN_DIR" -type f \( -name '*.dylib' -o -name 'llama-server' -o -name 'llama-quantize' \) 2>/dev/null)
fi
# Runtime launch: --version forces dyld to load every linked dylib (including
# libggml-metal.dylib). A missing Metal symbol or too-new binary fails here.
if ! "$SERVER" --version >/tmp/llama-server-version.txt 2>&1; then
echo "---- llama-server --version output ----"
cat /tmp/llama-server-version.txt || true
fail "llama-server failed to launch on macOS $HOST_VER (dyld load / symbol error)"
fi
echo "llama.cpp load validation passed on macOS $HOST_VER"
echo " server: $SERVER"
sed -n '1,4p' /tmp/llama-server-version.txt 2>/dev/null || true

View file

@ -89,13 +89,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: Install pyjwt for the JWT-expiry forge test
run: pip install 'pyjwt>=2.6'

View file

@ -114,13 +114,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: Install OpenAI + Anthropic Python SDKs
run: pip install 'openai>=1.50' 'anthropic>=0.40'
@ -369,13 +364,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
- 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)
# We deliberately use the API-only mode rather than
@ -760,13 +750,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: Install OpenAI + Anthropic Python SDKs
run: pip install 'openai>=1.50' 'anthropic>=0.40'

View file

@ -0,0 +1,80 @@
# 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
# 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.
name: Mac Studio Install Matrix CI
on:
pull_request:
paths:
- 'studio/install_llama_prebuilt.py'
- 'studio/setup.sh'
- 'install.sh'
- '.github/scripts/assert-llama-loads.sh'
- '.github/workflows/studio-mac-install-matrix.yml'
push:
branches: [main, pip]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
install-load:
name: Install + load (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 25
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-14 # Apple Silicon, macOS 14 Sonoma
experimental: false
- os: macos-15 # Apple Silicon, macOS 15 Sequoia
experimental: false
- os: macos-26 # Apple Silicon, macOS 26 Tahoe
experimental: false
- os: macos-15-intel # Intel x86_64, macOS 15 (informational)
experimental: true
- os: macos-26-intel # Intel x86_64, macOS 26 (last Intel macOS)
experimental: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Install Studio (--local, --no-torch)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p logs
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: Upload install log
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: mac-install-matrix-${{ matrix.os }}-log
path: logs/install.log
retention-days: 7

View file

@ -89,13 +89,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: Install Playwright + Chromium
# No --with-deps on Mac: that flag installs Linux apt packages.

View file

@ -67,21 +67,8 @@ jobs:
set -o pipefail
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
- name: Assert install.sh used the Mac llama.cpp prebuilt
run: |
# Mac install must take the prebuilt path. Source-build
# fallback here is an Unsloth bug.
if grep -q "falling back to source build" logs/install.log; then
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
if ! grep -qE "prebuilt installed and validated|prebuilt up to date and validated|bin-macos-arm64" logs/install.log; then
echo "::error::no Mac prebuilt llama.cpp marker in install.log."
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
exit 1
fi
echo "install.sh installed the Mac prebuilt llama.cpp"
- name: Assert llama.cpp loads on this macOS
run: bash .github/scripts/assert-llama-loads.sh
- name: First update should be a no-op (prebuilt already validated)
env:

View file

@ -0,0 +1,102 @@
# 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 GGUF routing in detect_gguf_model.
Regression test for the bug where a .gguf file temporarily appears
inaccessible on Windows during llama-server process teardown, causing
is_file() to return False and the model to be routed to the transformers
backend instead of llama-server.
"""
import sys
import os
import types
from pathlib import Path
from unittest.mock import patch
# Stub structlog before importing backend modules (mirrors other tests in this suite)
if "structlog" not in sys.modules:
class _DummyLogger:
def __getattr__(self, _):
return lambda *a, **k: None
sys.modules["structlog"] = types.SimpleNamespace(
get_logger = lambda *a, **k: _DummyLogger(),
BoundLogger = _DummyLogger,
)
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from utils.models.model_config import detect_gguf_model
def test_detects_gguf_file_normally(tmp_path):
"""Normal case: .gguf file exists and is accessible."""
gguf = tmp_path / "gpt-oss-20b-MXFP4.gguf"
gguf.write_bytes(b"")
result = detect_gguf_model(str(gguf))
assert result is not None
assert result.endswith("gpt-oss-20b-MXFP4.gguf")
def test_detects_gguf_when_stat_raises_oserror(tmp_path):
"""
Regression: on Windows, both is_file() and exists() call stat() internally.
During the brief lock window after llama-server is killed, stat() raises
OSError, causing both to return False. detect_gguf_model must still route
to llama-server based on the file extension alone.
"""
gguf = tmp_path / "gpt-oss-20b-MXFP4.gguf"
gguf.write_bytes(b"")
original_stat = Path.stat
def flaky_stat(self, **kwargs):
if self.suffix.lower() == ".gguf":
raise OSError("file temporarily inaccessible (Windows lock window)")
return original_stat(self, **kwargs)
with patch.object(Path, "stat", flaky_stat):
result = detect_gguf_model(str(gguf))
assert result is not None, (
"detect_gguf_model returned None when stat() raised OSError. "
"This causes the model to fall through to the transformers backend."
)
def test_does_not_detect_mmproj_as_main_model(tmp_path):
"""mmproj files must never be returned as the primary model."""
mmproj = tmp_path / "mmproj-model-f16.gguf"
mmproj.write_bytes(b"")
result = detect_gguf_model(str(mmproj))
assert result is None
def test_detects_gguf_in_directory(tmp_path):
"""Directory containing a .gguf file is resolved to that file."""
gguf = tmp_path / "model-Q4_K_M.gguf"
gguf.write_bytes(b"")
result = detect_gguf_model(str(tmp_path))
assert result is not None
assert result.endswith("model-Q4_K_M.gguf")
def test_directory_named_like_gguf_scans_inside(tmp_path):
"""A directory named *.gguf resolves the real .gguf inside, not itself."""
gguf_dir = tmp_path / "mymodel.gguf"
gguf_dir.mkdir()
inner = gguf_dir / "model-Q4_K_M.gguf"
inner.write_bytes(b"")
result = detect_gguf_model(str(gguf_dir))
assert result is not None
assert result.endswith("model-Q4_K_M.gguf")
def test_returns_none_for_non_gguf_path(tmp_path):
"""Non-.gguf paths with no .gguf files inside return None."""
result = detect_gguf_model(str(tmp_path))
assert result is None

View file

@ -1156,13 +1156,18 @@ def detect_gguf_model(path: str) -> Optional[str]:
p = Path(path)
# Case 1: direct .gguf file
if p.suffix.lower() == ".gguf" and p.is_file():
if p.suffix.lower() == ".gguf":
if _is_mmproj(p.name):
return None
# Use absolute (not resolve) to preserve symlink names -- e.g.
# Ollama .studio_links/model.gguf -> blobs/sha256-... should
# keep the readable symlink name, not the opaque blob hash.
return str(p.absolute())
# Extension is authoritative: don't gate on is_file()/exists(), which
# can fail in the Windows lock window after llama-server is killed.
try:
is_dir = p.is_dir()
except OSError:
is_dir = False # stat() unavailable in the lock window
if not is_dir:
return str(p.absolute()) # absolute() keeps symlink names readable
# Directory named "*.gguf": fall through to the dir scan below.
# Case 2: directory containing .gguf files (skip mmproj)
if p.is_dir():

View file

@ -49,7 +49,7 @@ import {
Edit03Icon,
Globe02Icon,
HelpCircleIcon,
Logout01Icon,
Logout05Icon,
Search01Icon,
PowerIcon,
PencilEdit02Icon,
@ -796,7 +796,7 @@ export function AppSidebar() {
void navigate({ to: "/login" });
}}
>
<HugeiconsIcon icon={Logout01Icon} strokeWidth={1.75} className="size-icon" />
<HugeiconsIcon icon={Logout05Icon} strokeWidth={1.75} className="size-icon" />
<span>{t("shell.navigation.logOut")}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setShutdownOpen(true)}>

View file

@ -19,6 +19,7 @@ import re
import shutil
import site
import socket
import struct
import subprocess
import sys
import tarfile
@ -160,6 +161,14 @@ DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS = env_int(
2,
minimum = 1,
)
# Deeper macOS-only walk-back: upstream can ship a run of prebuilts built for a
# newer macOS than the host, only caught at validate time, so an older host must
# skip the whole run. Free on new hosts (first plan validates, extras unused).
DEFAULT_MAX_MACOS_RELEASE_FALLBACKS = env_int(
"UNSLOTH_LLAMA_MAX_MACOS_RELEASE_FALLBACKS",
16,
minimum = 1,
)
FORCE_COMPILE_DEFAULT_REF = os.environ.get("UNSLOTH_LLAMA_FORCE_COMPILE_REF", "master")
DIRECT_LINUX_BUNDLE_PROFILES: dict[str, dict[str, Any]] = {
@ -213,6 +222,71 @@ DIRECT_LINUX_BUNDLE_PROFILES: dict[str, dict[str, Any]] = {
},
}
# Lowest CUDA major we ship prebuilts for, and the highest major we probe for
# installed runtime libraries. Detection and runtime-line derivation are
# generated per major so a new toolkit (cuda14, ...) needs no code change while
# llama.cpp keeps the cudart64_<major>.dll / libcudart.so.<major> naming.
_MIN_CUDA_MAJOR = 12
_MAX_PROBE_CUDA_MAJOR = 19
# Last ggml-org release whose Windows win-cuda-13 build is still sub-13.3
# (cuda-13.1, b9360, 2026-05-27). Upstream bumped win-cuda-13 to 13.3 at b9365
# and now ships only cuda-12.4 + cuda-13.3. cuda-12.4 predates Blackwell (ggml
# compiles sm_120 only at toolkit >= 12.8), so a Blackwell host on a 13.1/13.2
# driver is gated off 13.3 and would drop to a CPU-only 12.4 build. b9360 is
# immutable, so we pin its cuda-13.1 build (plus paired cudart) as a GPU
# fallback for exactly those hosts. See unslothai/unsloth#5887.
_PINNED_BLACKWELL_FALLBACK_TAG = "b9360"
_PINNED_BLACKWELL_FALLBACK_RUNTIME = "13.1"
_PINNED_BLACKWELL_DRIVER_FLOOR = (13, 1)
_BLACKWELL_MIN_SM = 120
# ggml compiles Blackwell sm_120 only at toolkit >= 12.8, so an in-release
# windows-cuda build at or above this already covers Blackwell and makes the
# older pinned 13.1 fallback unnecessary (cuda-12.4 is below it).
_BLACKWELL_MIN_TOOLKIT = (12, 8)
_PINNED_BLACKWELL_LLAMA_SHA256 = (
"31ddb8b42d7ab4a47cab8c48c397519f580ca502df7e73f3ab396eacc16c8e8d"
)
_PINNED_BLACKWELL_CUDART_SHA256 = (
"f96935e7e385e3b2d0189239077c10fe8fd7e95690fea4afec455b1b6c7e3f18"
)
def _cuda_runtime_lines_for_major(major: int) -> list[str]:
"""Runtime lines a driver of this CUDA major can use, newest major first
down to the minimum we ship. A driver runs its own major and any older one
(backward compatibility)."""
return [f"cuda{m}" for m in range(major, _MIN_CUDA_MAJOR - 1, -1)]
def _resolve_linux_bundle_profile(bundle_profile: str) -> "dict[str, Any] | None":
"""Profile (runtime line + sm coverage) for a linux-x64-cuda<major>-<class>
bundle. Known majors use their published coverage; an unknown future major
reuses the newest known major's coverage for the same class as a forward
default, with the post-build GPU smoke test as the backstop."""
known = DIRECT_LINUX_BUNDLE_PROFILES.get(bundle_profile)
if known is not None:
return known
m = re.fullmatch(
r"cuda(?P<major>\d+)-(?P<klass>older|newer|portable)", bundle_profile
)
if not m:
return None
base_key = max(
(
k
for k, v in DIRECT_LINUX_BUNDLE_PROFILES.items()
if v["coverage_class"] == m.group("klass")
),
key = lambda k: int(re.match(r"cuda(\d+)-", k).group(1)),
default = None,
)
if base_key is None:
return None
profile = dict(DIRECT_LINUX_BUNDLE_PROFILES[base_key])
profile["runtime_line"] = f"cuda{m.group('major')}"
return profile
@dataclass
class HostInfo:
@ -231,6 +305,9 @@ class HostInfo:
has_usable_nvidia: bool
has_rocm: bool = False
rocm_gfx_target: str | None = None
# (major, minor) from platform.mac_ver(); None off macOS or if unparseable.
# Skips a macos prebuilt whose minimum-OS exceeds this host.
macos_version: tuple[int, int] | None = None
@dataclass
@ -757,6 +834,26 @@ def windows_cuda_asset_aliases(
return aliases
def _published_windows_cuda_runtime(
upstream_assets: dict[str, str], major: int, driver: tuple[int, int] | None
) -> str | None:
"""Highest cuda-<major>.<minor> published upstream that `driver` can run by
default CUDA compatibility, i.e. (major, minor) <= driver. None if nothing
qualifies. Gating on the driver (not just the major) keeps a 13.3 build off
a driver that only advertises 13.1, where it would otherwise rely on the
unguaranteed minor-version-compatibility path."""
if driver is None:
return None
best: int | None = None
for name in upstream_assets:
m = re.search(r"-bin-win-cuda-(\d+)\.(\d+)-x64\.zip$", name)
if m and int(m.group(1)) == major:
minor = int(m.group(2))
if (major, minor) <= driver and (best is None or minor > best):
best = minor
return f"{major}.{best}" if best is not None else None
def format_byte_count(num_bytes: float) -> str:
units = ["B", "KiB", "MiB", "GiB", "TiB"]
value = float(num_bytes)
@ -1341,7 +1438,7 @@ def parse_direct_linux_release_bundle(
inferred_labels: list[str] = []
linux_asset_re = re.compile(
r"^app-(?P<label>.+)-(?P<target>linux-x64(?:-cpu)?|linux-x64-(?:cuda12|cuda13)-(?:older|newer|portable))\.tar\.gz$"
r"^app-(?P<label>.+)-(?P<target>linux-x64(?:-cpu)?|linux-x64-cuda\d+-(?:older|newer|portable))\.tar\.gz$"
)
for asset_name in sorted(assets):
match = linux_asset_re.fullmatch(asset_name)
@ -1366,7 +1463,7 @@ def parse_direct_linux_release_bundle(
continue
bundle_profile = target.removeprefix("linux-x64-")
profile = DIRECT_LINUX_BUNDLE_PROFILES.get(bundle_profile)
profile = _resolve_linux_bundle_profile(bundle_profile)
if profile is None:
continue
artifacts.append(
@ -1526,6 +1623,11 @@ def direct_upstream_release_plan(
torch_preference.selection_log,
)
)
# Blackwell on a 13.1/13.2 driver: prefer the pinned cuda-13.1 GPU
# build over the CPU-only cuda-12.4 the in-release gating leaves.
pinned = _pinned_windows_cuda_fallback(host, attempts)
if pinned is not None:
attempts.insert(0, pinned)
elif host.has_rocm:
lemonade_choice = resolve_lemonade_rocm_choice(
host, "windows", "windows-hip", llama_tag = requested_tag
@ -1666,6 +1768,14 @@ def resolve_simple_install_release_plans(
requested_tag == "latest" and not published_release_tag
)
release_limit = max(1, max_release_fallbacks)
# macOS may need to walk past a run of too-new prebuilts. Only when the host
# version is known; otherwise keep the default (cannot tell up front).
if (
host.is_macos
and allow_older_release_fallback
and host.macos_version is not None
):
release_limit = max(release_limit, DEFAULT_MAX_MACOS_RELEASE_FALLBACKS)
plans: list[InstallReleasePlan] = []
last_error: PrebuiltFallback | None = None
@ -1885,8 +1995,8 @@ def linux_runtime_dirs_for_required_libraries(
def detected_linux_runtime_lines() -> tuple[list[str], dict[str, list[str]]]:
line_requirements = {
"cuda13": ["libcudart.so.13", "libcublas.so.13"],
"cuda12": ["libcudart.so.12", "libcublas.so.12"],
f"cuda{m}": [f"libcudart.so.{m}", f"libcublas.so.{m}"]
for m in range(_MAX_PROBE_CUDA_MAJOR, _MIN_CUDA_MAJOR - 1, -1)
}
detected: list[str] = []
runtime_dirs: dict[str, list[str]] = {}
@ -2888,6 +2998,8 @@ def detect_host() -> HostInfo:
is_x86_64 = machine in {"x86_64", "amd64"}
is_arm64 = machine in {"arm64", "aarch64"}
macos_version = parse_macos_version(platform.mac_ver()[0]) if is_macos else None
nvidia_smi = shutil.which("nvidia-smi")
driver_cuda_version = None
compute_caps: list[str] = []
@ -3063,6 +3175,7 @@ def detect_host() -> HostInfo:
has_usable_nvidia = has_usable_nvidia,
has_rocm = has_rocm,
rocm_gfx_target = rocm_gfx_target,
macos_version = macos_version,
)
@ -3081,17 +3194,21 @@ def compatible_linux_runtime_lines(host: HostInfo) -> list[str]:
if not host.driver_cuda_version:
return []
major, _minor = host.driver_cuda_version
if major >= 13:
return ["cuda13", "cuda12"]
if major >= 12:
return ["cuda12"]
return []
if major < _MIN_CUDA_MAJOR:
return []
return _cuda_runtime_lines_for_major(major)
def windows_runtime_line_info() -> dict[str, tuple[str, ...]]:
# Generated per CUDA major (newest first) so a new toolkit is detected
# without a code change while the cudart64_<major>.dll naming holds.
return {
"cuda13": ("cudart64_13*.dll", "cublas64_13*.dll", "cublasLt64_13*.dll"),
"cuda12": ("cudart64_12*.dll", "cublas64_12*.dll", "cublasLt64_12*.dll"),
f"cuda{m}": (
f"cudart64_{m}*.dll",
f"cublas64_{m}*.dll",
f"cublasLt64_{m}*.dll",
)
for m in range(_MAX_PROBE_CUDA_MAJOR, _MIN_CUDA_MAJOR - 1, -1)
}
@ -3108,12 +3225,13 @@ def detected_windows_runtime_lines() -> tuple[list[str], dict[str, list[str]]]:
def compatible_windows_runtime_lines(host: HostInfo) -> list[str]:
driver_runtime = pick_windows_cuda_runtime(host)
if driver_runtime == "13.1":
return ["cuda13", "cuda12"]
if driver_runtime == "12.4":
return ["cuda12"]
return []
if not host.driver_cuda_version:
return []
major, minor = host.driver_cuda_version
# cuda12 prebuilts need a 12.4+ driver; cuda13+ any minor of the major.
if major < _MIN_CUDA_MAJOR or (major == _MIN_CUDA_MAJOR and minor < 4):
return []
return _cuda_runtime_lines_for_major(major)
def runtime_line_from_cuda_version(cuda_version: str | None) -> str | None:
@ -3190,7 +3308,6 @@ def windows_cuda_attempts(
selection_preamble: Iterable[str] = (),
) -> list[AssetChoice]:
selection_log = list(selection_preamble)
runtime_by_line = {"cuda12": "12.4", "cuda13": "13.1"}
driver_runtime = pick_windows_cuda_runtime(host)
detected_runtime_lines, runtime_dirs = detected_windows_runtime_lines()
compatible_runtime_lines = compatible_windows_runtime_lines(host)
@ -3233,12 +3350,7 @@ def windows_cuda_attempts(
selection_log.append(
"windows_cuda_selection: detected CUDA runtime DLLs were incompatible with the reported driver"
)
fallback_runtime_lines = (
["cuda13", "cuda12"]
if driver_runtime == "13.1"
else (["cuda12"] if driver_runtime == "12.4" else [])
)
normal_runtime_lines = fallback_runtime_lines
normal_runtime_lines = compatible_runtime_lines
runtime_order: list[str] = []
if preferred_runtime_line and preferred_runtime_line in normal_runtime_lines:
@ -3262,6 +3374,13 @@ def windows_cuda_attempts(
for runtime_line in normal_runtime_lines
if runtime_line not in runtime_order
)
# Keep every driver-compatible line reachable as a fallback, so a line gated
# out by the driver version still drops to an older major (cuda13 -> cuda12).
runtime_order.extend(
runtime_line
for runtime_line in compatible_runtime_lines
if runtime_line not in runtime_order
)
selection_log.append(
"windows_cuda_selection: normal_runtime_order="
+ (",".join(normal_runtime_lines) if normal_runtime_lines else "none")
@ -3273,7 +3392,18 @@ def windows_cuda_attempts(
attempts: list[AssetChoice] = []
for runtime_line in runtime_order:
runtime = runtime_by_line[runtime_line]
major = int(runtime_line.removeprefix("cuda"))
# Track whatever minor llama.cpp actually ships for this major
# (cuda13 -> 13.1, 13.3, ...). Skip the line when the release has no
# matching asset instead of guessing a now-missing name.
runtime = _published_windows_cuda_runtime(
upstream_assets, major, host.driver_cuda_version
)
if runtime is None:
selection_log.append(
f"windows_cuda_selection: no driver-supported asset for {runtime_line}"
)
continue
selected_name = None
asset_url = None
for candidate_name in windows_cuda_upstream_asset_names(llama_tag, runtime):
@ -3328,6 +3458,110 @@ def windows_cuda_attempts(
return attempts
def _windows_cuda_attempt_covers_blackwell(attempt: AssetChoice) -> bool:
"""True if an in-release windows-cuda attempt is built with a toolkit that
covers Blackwell sm_120 (>= 12.8), read from its asset name's CUDA minor."""
if attempt.install_kind != "windows-cuda":
return False
m = re.search(r"-bin-win-cuda-(\d+)\.(\d+)-x64\.zip$", attempt.name)
return (
m is not None and (int(m.group(1)), int(m.group(2))) >= _BLACKWELL_MIN_TOOLKIT
)
def _pinned_windows_cuda_fallback(
host: HostInfo, existing_cuda_attempts: list[AssetChoice]
) -> AssetChoice | None:
"""Pinned GPU fallback for a Blackwell host the in-release build gates off.
Upstream stopped publishing a sub-13.3 Windows cuda13 build after b9360, and
cuda-12.4 cannot offload sm_120, so a 13.1/13.2 driver would land on CPU.
b9360's cuda-13.1 build is immutable and runs on those drivers. Returns None
(dormant) whenever the in-release selection already offers a Blackwell-capable
build (toolkit >= 12.8, e.g. a runnable cuda13/cuda14), so it self-disables
once upstream ships a driver-runnable build again.
The b9360 binary reuses the current release's source tree and convert scripts
and is recorded via binary_release_tag, the same binary/source split used for
the lemonade prebuilt."""
if not (host.is_windows and host.is_x86_64 and host.has_usable_nvidia):
return None
driver = host.driver_cuda_version
if driver is None or driver < _PINNED_BLACKWELL_DRIVER_FLOOR:
return None
caps = normalize_compute_caps(host.compute_caps)
if not caps or int(caps[-1]) < _BLACKWELL_MIN_SM:
return None
if any(
_windows_cuda_attempt_covers_blackwell(attempt)
for attempt in existing_cuda_attempts
):
return None
tag = _PINNED_BLACKWELL_FALLBACK_TAG
runtime = _PINNED_BLACKWELL_FALLBACK_RUNTIME
base = (
f"https://github.com/{UPSTREAM_REPO}/releases/download/"
f"{urllib.parse.quote(tag, safe = '')}"
)
name = f"llama-{tag}-bin-win-cuda-{runtime}-x64.zip"
cudart_name = f"cudart-llama-bin-win-cuda-{runtime}-x64.zip"
return AssetChoice(
repo = UPSTREAM_REPO,
tag = tag,
name = name,
url = f"{base}/{name}",
source_label = "upstream",
install_kind = "windows-cuda",
runtime_line = "cuda13",
runtime_name = cudart_name,
runtime_url = f"{base}/{cudart_name}",
expected_sha256 = _PINNED_BLACKWELL_LLAMA_SHA256,
runtime_sha256 = _PINNED_BLACKWELL_CUDART_SHA256,
selection_log = [
f"windows_cuda_selection: pinned {tag} cuda-{runtime} Blackwell GPU "
f"fallback (in-release cuda13 gated off by driver "
f"{driver[0]}.{driver[1]})"
],
)
def _augment_checksums_with_pin(
checksums: ApprovedReleaseChecksums, pin: AssetChoice
) -> ApprovedReleaseChecksums:
"""Add the pin's own verified hashes to a copy of the approved checksums so
apply_approved_hashes keeps it on the published path (b9360 is not in the
release manifest)."""
artifacts = dict(checksums.artifacts)
if pin.expected_sha256:
artifacts[pin.name] = ApprovedArtifactHash(
asset_name = pin.name,
sha256 = pin.expected_sha256,
repo = pin.repo,
kind = "prebuilt",
)
if pin.runtime_name and pin.runtime_sha256:
artifacts[pin.runtime_name] = ApprovedArtifactHash(
asset_name = pin.runtime_name,
sha256 = pin.runtime_sha256,
repo = pin.repo,
kind = "prebuilt",
)
return dataclasses_replace(checksums, artifacts = artifacts)
def _with_pinned_windows_cuda_fallback(
host: HostInfo,
attempts: list[AssetChoice],
checksums: ApprovedReleaseChecksums,
) -> tuple[list[AssetChoice], ApprovedReleaseChecksums]:
"""Insert the Blackwell pin ahead of the Windows CUDA attempts and keep it
through apply_approved_hashes, or return the inputs unchanged when dormant.
Gives the published install path the same GPU fallback as the simple path."""
pin = _pinned_windows_cuda_fallback(host, attempts)
if pin is None:
return attempts, checksums
return [pin, *attempts], _augment_checksums_with_pin(checksums, pin)
def published_windows_cuda_attempts(
host: HostInfo,
release: PublishedReleaseBundle,
@ -3335,13 +3569,26 @@ def published_windows_cuda_attempts(
selection_preamble: Iterable[str] = (),
) -> list[AssetChoice]:
selection_log = list(release.selection_log) + list(selection_preamble)
runtime_by_line = {"cuda12": "12.4", "cuda13": "13.1"}
# Seed the runtime-line ordering from the real published windows-cuda minors
# (their names encode the minor), so a future CUDA major published here is
# ordered too instead of a hardcoded cuda12/cuda13 pair. Keys mirror the
# upstream naming so windows_cuda_attempts can match them; fall back to the
# long-standing default when the release lists no windows-cuda asset.
published_minors: list[str] = []
for artifact in release.artifacts:
if artifact.install_kind != "windows-cuda":
continue
m = re.search(r"-bin-win-cuda-(\d+\.\d+)-x64\.zip$", artifact.asset_name)
if m:
published_minors.append(m.group(1))
if not published_minors:
published_minors = ["12.4", "13.1"]
runtime_order = windows_cuda_attempts(
host,
release.upstream_tag,
{
f"llama-{release.upstream_tag}-bin-win-cuda-{runtime}-x64.zip": "published"
for runtime in runtime_by_line.values()
f"llama-{release.upstream_tag}-bin-win-cuda-{minor}-x64.zip": "published"
for minor in published_minors
},
preferred_runtime_line,
selection_log,
@ -3370,11 +3617,20 @@ def published_windows_cuda_attempts(
asset_url = release.assets.get(artifact.asset_name)
if not asset_url:
continue
# See windows_cuda_attempts: pair the cudart bundle.
am = re.search(r"-bin-win-cuda-(\d+)\.(\d+)-x64\.zip$", artifact.asset_name)
# Gate the real published minor against the driver, so a published
# windows-cuda artifact can never bypass the driver-version gate.
if (
am is not None
and host.driver_cuda_version is not None
and (int(am.group(1)), int(am.group(2))) > host.driver_cuda_version
):
continue
# See windows_cuda_attempts: pair the cudart bundle for the real minor.
runtime_archive_name: str | None = None
runtime_archive_url: str | None = None
if artifact.asset_name.startswith("llama-"):
runtime = runtime_by_line[runtime_line]
if am is not None and artifact.asset_name.startswith("llama-"):
runtime = f"{am.group(1)}.{am.group(2)}"
cudart_name = f"cudart-llama-bin-win-cuda-{runtime}-x64.zip"
cudart_url = release.assets.get(cudart_name)
if cudart_url and cudart_url != asset_url:
@ -3927,18 +4183,23 @@ def resolve_release_asset_choice(
torch_preference.selection_log,
)
if published_attempts:
pin_attempts, pin_checksums = _with_pinned_windows_cuda_fallback(
host, published_attempts, checksums
)
try:
return apply_approved_hashes(published_attempts, checksums)
return apply_approved_hashes(pin_attempts, pin_checksums)
except PrebuiltFallback as exc:
log(
"published Windows CUDA assets ignored for install planning: "
f"{release.repo}@{release.release_tag} ({exc})"
)
upstream_assets = github_release_assets(UPSTREAM_REPO, llama_tag)
return apply_approved_hashes(
upstream_attempts, upstream_checksums = _with_pinned_windows_cuda_fallback(
host,
resolve_windows_cuda_choices(host, llama_tag, upstream_assets),
checksums,
)
return apply_approved_hashes(upstream_attempts, upstream_checksums)
published_choice: AssetChoice | None = None
if host.is_windows and host.is_x86_64:
@ -5010,6 +5271,189 @@ def linux_runtime_dirs(binary_path: Path) -> list[str]:
return linux_runtime_dirs_for_required_libraries(missing)
# macOS prebuilt compatibility. Upstream macos prebuilts built on a newer macOS
# (e.g. minos=26, referencing Metal-4 symbols) fail dyld load on macOS 14/15. We
# read the host macOS version and each binary's minimum-OS so selection can skip
# a too-new prebuilt and walk back to the newest release that runs on this host.
# Mach-O constants (Apple mach-o/fat.h, mach-o/loader.h, mach/machine.h).
_MACHO_FAT_MAGICS = {0xCAFEBABE, 0xCAFEBABF} # universal binary (32/64-bit fat)
_LC_VERSION_MIN_MACOSX = 0x24 # legacy min-macOS load command
_LC_BUILD_VERSION = 0x32 # modern platform+minos+sdk load command
_MACHO_PLATFORM_MACOS = 1 # LC_BUILD_VERSION platform id for macOS (iOS=2, ...)
# CPU types (base | ABI64); used to pick the host slice in a fat binary.
_CPU_TYPE_X86_64 = 0x01000007
_CPU_TYPE_ARM64 = 0x0100000C
def parse_macos_version(value: str | None) -> tuple[int, int] | None:
"""Parse a macOS product version string into (major, minor).
Handles "14.7.1", "15.5", "26.0" and bare "26". Returns None when the
value is empty or cannot be parsed (callers then defer to runtime
validation rather than rejecting every prebuilt)."""
if not value:
return None
match = re.match(r"\s*(\d+)(?:\.(\d+))?", str(value))
if not match:
return None
return int(match.group(1)), int(match.group(2) or 0)
def host_supports_macos_minos(host: HostInfo, minos: tuple[int, int] | None) -> bool:
"""True if a prebuilt requiring `minos` can load on this host. Unknown host
version or unknown minos -> True: let runtime validation decide instead of
rejecting a binary we cannot reason about."""
if minos is None or host.macos_version is None:
return True
return host.macos_version >= minos
def _macho_slice_minos(data: bytes, offset: int) -> tuple[int, int] | None:
"""Minimum macOS for a single thin Mach-O at `offset`, via LC_BUILD_VERSION
(platform macOS) or the legacy LC_VERSION_MIN_MACOSX. None if absent."""
if offset + 4 > len(data):
return None
magic = struct.unpack_from(">I", data, offset)[0]
if magic in (0xFEEDFACE, 0xFEEDFACF):
endian, is64 = ">", magic == 0xFEEDFACF
elif magic in (0xCEFAEDFE, 0xCFFAEDFE):
endian, is64 = "<", magic == 0xCFFAEDFE
else:
return None
header_size = 32 if is64 else 28
if offset + header_size > len(data):
return None
ncmds = struct.unpack_from(endian + "I", data, offset + 16)[0]
cursor = offset + header_size
for _ in range(ncmds):
if cursor + 8 > len(data):
break
cmd, cmdsize = struct.unpack_from(endian + "II", data, cursor)
if cmdsize < 8:
break
if cmd == _LC_BUILD_VERSION and cursor + 16 <= len(data):
platform_id, minos = struct.unpack_from(endian + "II", data, cursor + 8)
if platform_id == _MACHO_PLATFORM_MACOS:
return (minos >> 16) & 0xFFFF, (minos >> 8) & 0xFF
elif cmd == _LC_VERSION_MIN_MACOSX and cursor + 12 <= len(data):
version = struct.unpack_from(endian + "I", data, cursor + 8)[0]
return (version >> 16) & 0xFFFF, (version >> 8) & 0xFF
cursor += cmdsize
return None
def macho_minimum_macos(
path: Path, host: HostInfo | None = None
) -> tuple[int, int] | None:
"""Minimum macOS (major, minor) a Mach-O binary or dylib requires.
Pure-Python so it works on consumer Macs without the Xcode command line
tools (otool/vtool). For universal binaries it prefers the host-arch slice,
else the highest minos found. Returns None for non-Mach-O files or when no
version load command is present."""
try:
data = path.read_bytes()
except Exception:
return None
if len(data) < 8:
return None
magic = struct.unpack_from(">I", data, 0)[0]
if magic in _MACHO_FAT_MAGICS:
is64 = magic == 0xCAFEBABF
nfat = struct.unpack_from(">I", data, 4)[0]
entry = 8
slices: list[tuple[int, tuple[int, int]]] = []
for _ in range(nfat):
if is64:
if entry + 32 > len(data):
break
cputype = struct.unpack_from(">I", data, entry)[0]
slice_offset = struct.unpack_from(">Q", data, entry + 8)[0]
entry += 32
else:
if entry + 20 > len(data):
break
cputype = struct.unpack_from(">I", data, entry)[0]
slice_offset = struct.unpack_from(">I", data, entry + 8)[0]
entry += 20
minos = _macho_slice_minos(data, slice_offset)
if minos is not None:
slices.append((cputype, minos))
if not slices:
return None
if host is not None:
want = (
_CPU_TYPE_ARM64
if host.is_arm64
else (_CPU_TYPE_X86_64 if host.is_x86_64 else None)
)
for cputype, minos in slices:
if cputype == want:
return minos
return max(minos for _cputype, minos in slices)
return _macho_slice_minos(data, 0)
def looks_like_macos_incompatibility(text: str) -> bool:
"""True when dyld output means a prebuilt needs a newer macOS than the host
(the runtime backstop for cases the static minos scan cannot read)."""
if not text:
return False
if "built for macOS" in text and "newer than running OS" in text:
return True
return "Symbol not found" in text and "MTLResidency" in text
def macos_binary_minos_issues(
binaries: Iterable[Path],
install_dir: Path,
host: HostInfo,
) -> list[str]:
"""Issue strings for every installed Mach-O whose minimum macOS exceeds the
host. Scans the given executables plus every bundled .dylib next to them --
the dyld failure originates in libggml-metal.dylib, not the executable."""
candidates: list[Path] = list(binaries)
bin_dir = install_dir / "build" / "bin"
if bin_dir.is_dir():
candidates.extend(sorted(bin_dir.rglob("*.dylib")))
issues: list[str] = []
seen: set[Path] = set()
for path in candidates:
try:
resolved = path.resolve()
except Exception:
resolved = path
if resolved in seen or not path.exists():
continue
seen.add(resolved)
minos = macho_minimum_macos(path, host)
if minos is not None and not host_supports_macos_minos(host, minos):
issues.append(
f"{path.name}: built for macOS {minos[0]}.{minos[1]} > "
f"host macOS {host.macos_version[0]}.{host.macos_version[1]}"
)
return issues
def preflight_macos_installed_binaries(
binaries: Iterable[Path],
install_dir: Path,
host: HostInfo,
) -> None:
"""Reject a macos prebuilt whose minimum-OS is newer than the host so the
release walk-back advances to the newest compatible release. No-op when the
host macOS version is unknown (runtime validation remains the backstop)."""
if not host.is_macos or host.macos_version is None:
return
issues = macos_binary_minos_issues(binaries, install_dir, host)
if issues:
raise PrebuiltFallback(
"macos prebuilt requires a newer macOS than this host:\n"
+ "\n".join(issues)
)
def preflight_linux_installed_binaries(
binaries: Iterable[Path],
install_dir: Path,
@ -5168,10 +5612,17 @@ def validate_quantize(
or not quantized_path.exists()
or quantized_path.stat().st_size == 0
):
combined = result.stdout + ("\n" + result.stderr if result.stderr else "")
# Backstop for prebuilts the static minos scan could not read: a dyld
# "built for macOS N" / missing Metal symbol failure means this binary
# needs a newer macOS than the host, so fall back to an older release.
prefix = (
"macos prebuilt requires a newer macOS than this host: "
if looks_like_macos_incompatibility(combined)
else ""
)
raise PrebuiltFallback(
"llama-quantize validation failed:\n"
+ result.stdout
+ ("\n" + result.stderr if result.stderr else "")
prefix + "llama-quantize validation failed:\n" + combined
)
@ -5589,6 +6040,14 @@ def resolve_install_release_plans(
requested_tag == "latest" and not published_release_tag
)
release_limit = max(1, max_release_fallbacks)
# macOS may need to walk past a run of too-new prebuilts. Only when the host
# version is known; otherwise keep the default (cannot tell up front).
if (
host.is_macos
and allow_older_release_fallback
and host.macos_version is not None
):
release_limit = max(release_limit, DEFAULT_MAX_MACOS_RELEASE_FALLBACKS)
plans: list[InstallReleasePlan] = []
last_error: PrebuiltFallback | None = None
@ -5973,6 +6432,7 @@ def validate_prebuilt_choice(
choice, host, install_dir, work_dir
)
preflight_linux_installed_binaries((server_path, quantize_path), install_dir, host)
preflight_macos_installed_binaries((server_path, quantize_path), install_dir, host)
ensure_repo_shape(install_dir)
write_prebuilt_metadata(
install_dir,

View file

@ -1084,6 +1084,15 @@ else
_IS_MACOS_ARM64=true
fi
# macOS: pin a low deployment target so the source build loads on
# older macOS too (else a macOS 26 host stamps minos=26). Set before
# CPU_FALLBACK_CMAKE_ARGS copies CMAKE_ARGS so both paths inherit it.
if [ "$_HOST_SYSTEM" = "Darwin" ]; then
_MACOS_DEPLOYMENT_TARGET="${UNSLOTH_MACOS_DEPLOYMENT_TARGET:-13.3}"
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET=${_MACOS_DEPLOYMENT_TARGET}"
export MACOSX_DEPLOYMENT_TARGET="${_MACOS_DEPLOYMENT_TARGET}"
fi
if command -v ccache &>/dev/null; then
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache"
fi

View file

@ -2731,15 +2731,14 @@ dependencies = [
[[package]]
name = "openssl"
version = "0.10.76"
version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf"
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
@ -2763,9 +2762,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-sys"
version = "0.9.112"
version = "0.9.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb"
checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
dependencies = [
"cc",
"libc",
@ -2978,7 +2977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"rand 0.8.5",
"rand 0.8.6",
]
[[package]]
@ -2988,7 +2987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand 0.8.5",
"rand 0.8.6",
]
[[package]]
@ -3361,9 +3360,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha 0.3.1",
@ -3382,9 +3381,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.2",
@ -3765,9 +3764,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.10"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
@ -4450,9 +4449,9 @@ dependencies = [
[[package]]
name = "tar"
version = "0.4.45"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
@ -5380,7 +5379,7 @@ dependencies = [
"log",
"open",
"process-wrap",
"rand 0.10.0",
"rand 0.10.1",
"regex",
"reqwest 0.12.28",
"serde",

View file

@ -0,0 +1,322 @@
"""Tests for the host-macOS-version-aware llama.cpp prebuilt selection added
for the Mac "Failing CI" fix.
Covers: parse_macos_version, host_supports_macos_minos, the pure-Python Mach-O
minimum-OS parser (macho_minimum_macos), the dyld-incompatibility classifier,
the install preflight that rejects a too-new prebuilt, and the deeper macOS
release walk-back in resolve_simple_install_release_plans.
No GPU, no network, no torch, no real Mach-O toolchain required -- the Mach-O
samples are synthesized in-process and all I/O is monkeypatched.
"""
import importlib.util
import struct
import sys
from pathlib import Path
import pytest
PACKAGE_ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = PACKAGE_ROOT / "studio" / "install_llama_prebuilt.py"
SPEC = importlib.util.spec_from_file_location(
"studio_install_llama_prebuilt_macos", MODULE_PATH
)
assert SPEC is not None and SPEC.loader is not None
ILP = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = ILP
SPEC.loader.exec_module(ILP)
HostInfo = ILP.HostInfo
PrebuiltFallback = ILP.PrebuiltFallback
_CPU_TYPE_ARM64 = 0x0100000C
_CPU_TYPE_X86_64 = 0x01000007
def make_macos_host(macos_version, *, arm64 = True):
return HostInfo(
system = "Darwin",
machine = "arm64" if arm64 else "x86_64",
is_windows = False,
is_linux = False,
is_macos = True,
is_x86_64 = not arm64,
is_arm64 = arm64,
nvidia_smi = None,
driver_cuda_version = None,
compute_caps = [],
visible_cuda_devices = None,
has_physical_nvidia = False,
has_usable_nvidia = False,
macos_version = macos_version,
)
def thin_macho(minos = (14, 0), *, cputype = _CPU_TYPE_ARM64, build_version = True):
"""Synthesize a minimal little-endian 64-bit Mach-O carrying a macOS
minimum-version load command."""
encoded = (minos[0] << 16) | (minos[1] << 8)
if build_version:
# LC_BUILD_VERSION: cmd, cmdsize, platform(=1 macOS), minos, sdk, ntools
load_command = struct.pack("<6I", 0x32, 24, 1, encoded, encoded, 0)
else:
# LC_VERSION_MIN_MACOSX: cmd, cmdsize, version, sdk
load_command = struct.pack("<4I", 0x24, 16, encoded, encoded)
header = struct.pack("<8I", 0xFEEDFACF, cputype, 0, 0x2, 1, len(load_command), 0, 0)
return header + load_command
def fat_macho(slices):
"""Synthesize a big-endian universal binary from (cputype, thin_bytes)."""
header = struct.pack(">2I", 0xCAFEBABE, len(slices))
data_offset = 8 + 20 * len(slices)
arch_entries = b""
body = b""
for cputype, thin in slices:
offset = data_offset + len(body)
arch_entries += struct.pack(">5I", cputype, 0, offset, len(thin), 0)
body += thin
return header + arch_entries + body
class TestParseMacosVersion:
@pytest.mark.parametrize(
"value, expected",
[
("14.7.1", (14, 7)),
("15.5", (15, 5)),
("26.0", (26, 0)),
("26", (26, 0)),
("13", (13, 0)),
("", None),
(None, None),
("not-a-version", None),
],
)
def test_parse(self, value, expected):
assert ILP.parse_macos_version(value) == expected
class TestHostSupportsMacosMinos:
def test_older_host_rejects_newer_prebuilt(self):
assert not ILP.host_supports_macos_minos(make_macos_host((14, 0)), (26, 0))
def test_same_version_supported(self):
assert ILP.host_supports_macos_minos(make_macos_host((26, 0)), (26, 0))
def test_newer_host_supports_older_prebuilt(self):
assert ILP.host_supports_macos_minos(make_macos_host((15, 5)), (14, 0))
def test_unknown_host_defers_to_runtime(self):
assert ILP.host_supports_macos_minos(make_macos_host(None), (26, 0))
def test_unknown_minos_defers_to_runtime(self):
assert ILP.host_supports_macos_minos(make_macos_host((14, 0)), None)
class TestMachoMinimumMacos:
def test_build_version_thin(self, tmp_path):
path = tmp_path / "lib.dylib"
path.write_bytes(thin_macho((26, 0)))
assert ILP.macho_minimum_macos(path) == (26, 0)
def test_legacy_version_min_thin(self, tmp_path):
path = tmp_path / "lib.dylib"
path.write_bytes(thin_macho((14, 0), build_version = False))
assert ILP.macho_minimum_macos(path) == (14, 0)
def test_universal_prefers_host_arch_slice(self, tmp_path):
# arm64 slice needs macOS 14, x86_64 slice needs macOS 26.
path = tmp_path / "fat"
path.write_bytes(
fat_macho(
[
(_CPU_TYPE_ARM64, thin_macho((14, 0), cputype = _CPU_TYPE_ARM64)),
(_CPU_TYPE_X86_64, thin_macho((26, 0), cputype = _CPU_TYPE_X86_64)),
]
)
)
assert ILP.macho_minimum_macos(path, make_macos_host((14, 0))) == (14, 0)
assert ILP.macho_minimum_macos(path, make_macos_host((26, 0), arm64 = False)) == (
26,
0,
)
def test_non_macho_returns_none(self, tmp_path):
path = tmp_path / "script.sh"
path.write_bytes(b'#!/bin/sh\nexec real "$@"\n')
assert ILP.macho_minimum_macos(path) is None
def test_missing_file_returns_none(self, tmp_path):
assert ILP.macho_minimum_macos(tmp_path / "nope") is None
class TestLooksLikeMacosIncompatibility:
def test_built_for_newer_os(self):
assert ILP.looks_like_macos_incompatibility(
"dyld: ... (built for macOS 26.0 which is newer than running OS)"
)
def test_metal_residency_symbol(self):
assert ILP.looks_like_macos_incompatibility(
"Symbol not found: _OBJC_CLASS_$_MTLResidencySetDescriptor"
)
def test_benign_error(self):
assert not ILP.looks_like_macos_incompatibility("some unrelated failure")
def test_empty(self):
assert not ILP.looks_like_macos_incompatibility("")
class TestPreflightMacosInstalledBinaries:
def _install_dir(self, tmp_path, dylib_minos):
bin_dir = tmp_path / "build" / "bin"
bin_dir.mkdir(parents = True)
(bin_dir / "libggml-metal.dylib").write_bytes(thin_macho(dylib_minos))
server = tmp_path / "llama-server"
server.write_bytes(thin_macho(dylib_minos))
quantize = tmp_path / "llama-quantize"
quantize.write_bytes(thin_macho(dylib_minos))
return tmp_path, (server, quantize)
def test_rejects_too_new_dylib(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
with pytest.raises(PrebuiltFallback, match = "newer macOS"):
ILP.preflight_macos_installed_binaries(
binaries, install_dir, make_macos_host((14, 0))
)
def test_accepts_compatible_prebuilt(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (14, 0))
# Must not raise on a macOS 15 host.
ILP.preflight_macos_installed_binaries(
binaries, install_dir, make_macos_host((15, 5))
)
def test_skips_when_host_version_unknown(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
# Unknown host version -> defer to runtime validation, do not raise.
ILP.preflight_macos_installed_binaries(
binaries, install_dir, make_macos_host(None)
)
def test_noop_on_non_macos_host(self, tmp_path):
install_dir, binaries = self._install_dir(tmp_path, (26, 0))
linux_host = HostInfo(
system = "Linux",
machine = "x86_64",
is_windows = False,
is_linux = True,
is_macos = False,
is_x86_64 = True,
is_arm64 = False,
nvidia_smi = None,
driver_cuda_version = None,
compute_caps = [],
visible_cuda_devices = None,
has_physical_nvidia = False,
has_usable_nvidia = False,
)
ILP.preflight_macos_installed_binaries(binaries, install_dir, linux_host)
def _fake_macos_releases(tags):
return [
{
"tag_name": tag,
"assets": [
{
"name": f"llama-{tag}-bin-macos-arm64.tar.gz",
"browser_download_url": f"https://example.com/{tag}.tar.gz",
}
],
}
for tag in tags
]
class TestMacosReleaseWalkback:
"""A known-version macOS host must generate enough older-release plans to
walk back past a run of too-new prebuilts; unknown-version and non-macOS
hosts keep the conservative 2-release default."""
TAGS = [f"b{n}" for n in range(9437, 9400, -1)] # 37 newest-first releases
def _patch_releases(self, monkeypatch):
monkeypatch.setattr(
ILP,
"iter_release_payloads_by_time",
lambda repo, published_release_tag, requested_tag, host = None: (
_fake_macos_releases(self.TAGS)
),
)
def test_known_macos_host_walks_back_deeper(self, monkeypatch):
self._patch_releases(monkeypatch)
_tag, plans = ILP.resolve_simple_install_release_plans(
"latest",
make_macos_host((14, 0)),
"ggml-org/llama.cpp",
"",
)
assert len(plans) == ILP.DEFAULT_MAX_MACOS_RELEASE_FALLBACKS
assert len(plans) > ILP.DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS
def test_unknown_macos_host_uses_default(self, monkeypatch):
self._patch_releases(monkeypatch)
_tag, plans = ILP.resolve_simple_install_release_plans(
"latest",
make_macos_host(None),
"ggml-org/llama.cpp",
"",
)
assert len(plans) == ILP.DEFAULT_MAX_PREBUILT_RELEASE_FALLBACKS
class TestForwardsBackwardsCompat:
"""The gate is host >= prebuilt minos with no hardcoded version, so it holds
for older and future macOS alike. Emulate the walk-back over a release set
spanning several minos tiers and assert each host takes the newest release
it can load."""
# Newest first: future 27 builds, current 26 builds, an old 14 tier, a 13.
RELEASES = [
("b9600", (27, 0)),
("b9450", (26, 0)),
("b9415", (14, 0)),
("b8300", (13, 0)),
]
def _select(self, tmp_path, host_version):
for tag, minos in self.RELEASES:
bin_dir = tmp_path / tag / "build" / "bin"
bin_dir.mkdir(parents = True)
(bin_dir / "libggml-metal.dylib").write_bytes(thin_macho(minos))
try:
ILP.preflight_macos_installed_binaries(
(), tmp_path / tag, make_macos_host(host_version)
)
return tag
except PrebuiltFallback:
continue
return None
@pytest.mark.parametrize(
"host_version, expected",
[
((13, 0), "b8300"), # older host takes the older prebuilt
((14, 7), "b9415"), # backwards: skip 26/27, take newest that loads
((15, 5), "b9415"),
((26, 0), "b9450"), # unchanged: newest <= host
((27, 1), "b9600"), # forwards: future host takes the future build
],
)
def test_selects_newest_loadable(self, tmp_path, host_version, expected):
assert self._select(tmp_path, host_version) == expected
def test_host_below_prebuilt_floor_falls_through(self, tmp_path):
# macOS 12 is below every prebuilt -> nothing matches -> source build.
assert self._select(tmp_path, (12, 0)) is None

View file

@ -52,6 +52,9 @@ compatible_windows_runtime_lines = (
runtime_line_from_cuda_version = INSTALL_LLAMA_PREBUILT.runtime_line_from_cuda_version
apply_approved_hashes = INSTALL_LLAMA_PREBUILT.apply_approved_hashes
linux_cuda_choice_from_release = INSTALL_LLAMA_PREBUILT.linux_cuda_choice_from_release
parse_direct_linux_release_bundle = (
INSTALL_LLAMA_PREBUILT.parse_direct_linux_release_bundle
)
windows_cuda_attempts = INSTALL_LLAMA_PREBUILT.windows_cuda_attempts
resolve_upstream_asset_choice = INSTALL_LLAMA_PREBUILT.resolve_upstream_asset_choice
resolve_requested_install_tag = INSTALL_LLAMA_PREBUILT.resolve_requested_install_tag
@ -74,6 +77,14 @@ windows_cuda_upstream_asset_names = (
INSTALL_LLAMA_PREBUILT.windows_cuda_upstream_asset_names
)
env_int = INSTALL_LLAMA_PREBUILT.env_int
direct_upstream_release_plan = INSTALL_LLAMA_PREBUILT.direct_upstream_release_plan
_pinned_windows_cuda_fallback = INSTALL_LLAMA_PREBUILT._pinned_windows_cuda_fallback
CudaRuntimePreference = INSTALL_LLAMA_PREBUILT.CudaRuntimePreference
published_windows_cuda_attempts = INSTALL_LLAMA_PREBUILT.published_windows_cuda_attempts
_windows_cuda_attempt_covers_blackwell = (
INSTALL_LLAMA_PREBUILT._windows_cuda_attempt_covers_blackwell
)
resolve_release_asset_choice = INSTALL_LLAMA_PREBUILT.resolve_release_asset_choice
# ---------------------------------------------------------------------------
@ -397,6 +408,44 @@ class TestCompatibleLinuxRuntimeLines:
host = make_host(driver_cuda_version = (13, 0))
assert compatible_linux_runtime_lines(host) == ["cuda13", "cuda12"]
def test_future_major_derives_lines(self):
# A future major (14.x) offers cuda14 first, then older majors.
host = make_host(driver_cuda_version = (14, 0))
assert compatible_linux_runtime_lines(host) == ["cuda14", "cuda13", "cuda12"]
class TestParseDirectLinuxReleaseBundle:
def _release(self, *targets):
names = [f"app-bTEST-linux-x64-{t}.tar.gz" for t in targets]
return {
"tag_name": "bTEST",
"assets": [
{"name": n, "browser_download_url": "https://x/" + n} for n in names
],
}
def _cuda_artifact(self, bundle):
return [a for a in bundle.artifacts if a.install_kind == "linux-cuda"][0]
def test_parses_known_cuda13_bundle(self):
bundle = parse_direct_linux_release_bundle(
"unslothai/llama.cpp", self._release("cuda13-newer")
)
assert bundle is not None
assert self._cuda_artifact(bundle).runtime_line == "cuda13"
def test_parses_future_cuda_major_with_forward_profile(self):
# A future major name parses and inherits the newest known major's
# coverage for the same class as a forward default.
bundle = parse_direct_linux_release_bundle(
"unslothai/llama.cpp", self._release("cuda14-newer")
)
assert bundle is not None
art = self._cuda_artifact(bundle)
assert art.runtime_line == "cuda14"
assert art.coverage_class == "newer"
assert art.max_sm == 120 # inherited from cuda13-newer
# ===========================================================================
# G. pick_windows_cuda_runtime + compatible_windows_runtime_lines
@ -442,6 +491,10 @@ class TestCompatibleWindowsRuntimeLines:
host = make_host(driver_cuda_version = (13, 0))
assert compatible_windows_runtime_lines(host) == ["cuda13", "cuda12"]
def test_future_major_derives_lines(self):
host = make_host(driver_cuda_version = (14, 0))
assert compatible_windows_runtime_lines(host) == ["cuda14", "cuda13", "cuda12"]
# ===========================================================================
# H. runtime_line_from_cuda_version
@ -1777,12 +1830,23 @@ class TestWindowsCudaAttempts:
assert result[0].runtime_line == "cuda13"
assert result[1].runtime_line == "cuda12"
def test_driver_13_0_cuda13_dlls_selects_cuda13_asset(self, monkeypatch):
def test_driver_below_published_minor_is_gated_to_cuda12(self, monkeypatch):
# A 13.0 driver cannot run a 13.1 build (forward minor), so it is gated
# out of cuda13 and falls back to the cuda12 build it can run, even when
# only the cuda13 runtime libs are detected.
mock_windows_runtime(monkeypatch, ["cuda13"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 0))
assets = self._upstream("13.1", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert len(result) == 1
assert result[0].runtime_line == "cuda12"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip"
def test_driver_at_published_minor_selects_cuda13(self, monkeypatch):
# A 13.1 driver matches the published 13.1 build exactly.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 1))
assets = self._upstream("13.1", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].runtime_line == "cuda13"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.1-x64.zip"
@ -1885,6 +1949,464 @@ class TestWindowsCudaAttempts:
assert attempt.runtime_url is None
assert attempt.runtime_name is None
def test_tracks_upstream_cuda13_minor_bump(self, monkeypatch):
# ggml-org bumped the published Windows cuda13 build 13.1 -> 13.3; the
# selector must follow it instead of the old hardcoded 13.1 (#5861).
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 3))
assets = self._upstream("13.3", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].runtime_line == "cuda13"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
def test_cuda13_minor_bump_pairs_matching_cudart(self, monkeypatch):
# The paired cudart bundle must track the same bumped minor.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 3))
assets = {
f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip": "https://example.com/llama-13.3",
"cudart-llama-bin-win-cuda-13.3-x64.zip": "https://example.com/cudart-13.3",
f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip": "https://example.com/llama-12.4",
"cudart-llama-bin-win-cuda-12.4-x64.zip": "https://example.com/cudart-12.4",
}
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
assert result[0].runtime_name == "cudart-llama-bin-win-cuda-13.3-x64.zip"
def test_driver_below_published_minor_does_not_get_newer_build(self, monkeypatch):
# ggml-org ships only cuda-13.3; a 13.1 driver cannot run it (forward
# minor), so it is gated to the cuda-12.4 build instead of an
# unguaranteed 13.3. A 13.3 driver still gets 13.3 (see other tests).
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 1))
assets = self._upstream("13.3", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].runtime_line == "cuda12"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip"
def test_tracks_future_cuda13_minor(self, monkeypatch):
# A later within-major bump (13.4) is tracked the same as 13.3.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (13, 4))
assets = self._upstream("13.4", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.4-x64.zip"
def test_new_cuda_major_selected_when_published(self, monkeypatch):
# A new CUDA major (14.x) driver picks the published cuda14 build.
mock_windows_runtime(monkeypatch, ["cuda14", "cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (14, 0))
assets = self._upstream("14.0", "13.3", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].runtime_line == "cuda14"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-14.0-x64.zip"
def test_new_cuda_major_degrades_to_published_cuda13(self, monkeypatch):
# A 14.x driver with no cuda14 build runs the newest published cuda13
# build via backward compatibility.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = make_host(system = "Windows", machine = "AMD64", driver_cuda_version = (14, 0))
assets = self._upstream("13.3", "12.4")
result = windows_cuda_attempts(host, self.TAG, assets, None)
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
# ===========================================================================
# N.1b. _pinned_windows_cuda_fallback -- pinned b9360 cuda-13.1 Blackwell fallback
# ===========================================================================
class TestPinnedBlackwellCudaFallback:
"""A Blackwell host on a 13.1/13.2 driver, gated off the in-release 13.3
build, gets the pinned immutable b9360 cuda-13.1 GPU build instead of the
CPU-only cuda-12.4 drop. The pin is dormant for everyone else."""
TAG = "b8508"
def _win_host(self, driver, caps):
return make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = driver,
compute_caps = caps,
)
def test_pin_offered_for_driver_13_1_blackwell(self):
pin = _pinned_windows_cuda_fallback(self._win_host((13, 1), ["120"]), [])
assert pin is not None
assert pin.tag == "b9360"
assert pin.runtime_line == "cuda13"
assert pin.name == "llama-b9360-bin-win-cuda-13.1-x64.zip"
assert pin.runtime_name == "cudart-llama-bin-win-cuda-13.1-x64.zip"
assert pin.url.endswith("/b9360/llama-b9360-bin-win-cuda-13.1-x64.zip")
assert pin.runtime_url.endswith("/b9360/cudart-llama-bin-win-cuda-13.1-x64.zip")
assert pin.install_kind == "windows-cuda"
assert pin.expected_sha256 and len(pin.expected_sha256) == 64
assert pin.runtime_sha256 and len(pin.runtime_sha256) == 64
def test_pin_offered_for_driver_13_2(self):
assert (
_pinned_windows_cuda_fallback(self._win_host((13, 2), ["120"]), [])
is not None
)
def test_pin_offered_for_sm121_variant(self):
# sm_121 is Blackwell-family and also needs toolkit >= 12.8.
assert (
_pinned_windows_cuda_fallback(self._win_host((13, 1), ["121"]), [])
is not None
)
def test_pin_uses_max_of_multi_gpu_caps(self):
assert (
_pinned_windows_cuda_fallback(self._win_host((13, 1), ["86", "120"]), [])
is not None
)
@pytest.mark.parametrize("sm", ["89", "90", "100"])
def test_pin_not_offered_to_non_blackwell(self, sm):
# Ada/Hopper run the cuda-12.4 build fine; the pin must not fire.
assert _pinned_windows_cuda_fallback(self._win_host((13, 1), [sm]), []) is None
def test_pin_not_offered_to_driver_13_0(self):
# 13.0 cannot run the 13.1 build (forward minor); residual CPU gap.
assert (
_pinned_windows_cuda_fallback(self._win_host((13, 0), ["120"]), []) is None
)
def test_pin_not_offered_below_floor(self):
assert (
_pinned_windows_cuda_fallback(self._win_host((12, 8), ["120"]), []) is None
)
def test_pin_not_offered_without_driver(self):
assert _pinned_windows_cuda_fallback(self._win_host(None, ["120"]), []) is None
def test_pin_not_offered_on_linux(self):
host = make_host(
system = "Linux",
machine = "x86_64",
driver_cuda_version = (13, 1),
compute_caps = ["120"],
)
assert _pinned_windows_cuda_fallback(host, []) is None
def test_pin_dormant_when_cuda13_attempt_present(self, monkeypatch):
# A runnable in-release cuda13 build makes the pin unnecessary.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
host = self._win_host((13, 1), ["120"])
assets = {
f"llama-{self.TAG}-bin-win-cuda-13.1-x64.zip": "https://example.com/13.1",
f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip": "https://example.com/12.4",
}
existing = windows_cuda_attempts(host, self.TAG, assets, None)
assert any(a.runtime_line == "cuda13" for a in existing)
assert _pinned_windows_cuda_fallback(host, existing) is None
def _win_cuda_attempt(self, minor):
major = minor.split(".")[0]
return AssetChoice(
repo = UPSTREAM_REPO,
tag = self.TAG,
name = f"llama-{self.TAG}-bin-win-cuda-{minor}-x64.zip",
url = "https://example.com/x",
source_label = "upstream",
install_kind = "windows-cuda",
runtime_line = f"cuda{major}",
)
def test_pin_dormant_when_runnable_cuda14_present(self, monkeypatch):
# A future Blackwell host with an in-release cuda14 build (no cuda13)
# must not get the older b9360 13.1 pin ahead of the runnable cuda14.
mock_windows_runtime(monkeypatch, ["cuda14", "cuda12"])
host = self._win_host((14, 0), ["120"])
assets = {
f"llama-{self.TAG}-bin-win-cuda-14.0-x64.zip": "https://example.com/14.0",
f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip": "https://example.com/12.4",
}
existing = windows_cuda_attempts(host, self.TAG, assets, None)
assert any(a.runtime_line == "cuda14" for a in existing)
assert _pinned_windows_cuda_fallback(host, existing) is None
def test_pin_dormant_when_runnable_cuda12_8_present(self):
# A cuda-12.8 build also covers Blackwell, so the pin defers to it.
host = self._win_host((13, 1), ["120"])
existing = [self._win_cuda_attempt("12.8")]
assert _pinned_windows_cuda_fallback(host, existing) is None
def test_pin_fires_when_only_cuda12_4_present(self):
# cuda-12.4 does not cover Blackwell, so the pin still fires.
host = self._win_host((13, 1), ["120"])
existing = [self._win_cuda_attempt("12.4")]
assert _pinned_windows_cuda_fallback(host, existing) is not None
@pytest.mark.parametrize(
"minor, covers",
[
("12.4", False),
("12.8", True),
("13.1", True),
("13.3", True),
("14.0", True),
],
)
def test_attempt_covers_blackwell(self, minor, covers):
assert (
_windows_cuda_attempt_covers_blackwell(self._win_cuda_attempt(minor))
is covers
)
def test_attempt_covers_blackwell_ignores_non_cuda_kind(self):
cpu = AssetChoice(
repo = UPSTREAM_REPO,
tag = self.TAG,
name = f"llama-{self.TAG}-bin-win-cpu-x64.zip",
url = "https://example.com/x",
source_label = "upstream",
install_kind = "windows-cpu",
)
assert _windows_cuda_attempt_covers_blackwell(cpu) is False
# ===========================================================================
# N.1c. direct_upstream_release_plan -- pinned Blackwell fallback ordering
# ===========================================================================
class TestDirectUpstreamBlackwellPin:
"""End to end: the pin lands ahead of cuda-12.4 on the simple/upstream path
a Blackwell Windows host actually uses, and stays absent once a runnable
in-release cuda13 build exists."""
TAG = "b9365"
def _release(self):
names = [
f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip",
"cudart-llama-bin-win-cuda-13.3-x64.zip",
f"llama-{self.TAG}-bin-win-cuda-12.4-x64.zip",
"cudart-llama-bin-win-cuda-12.4-x64.zip",
f"llama-{self.TAG}-bin-win-cpu-x64.zip",
]
return {
"tag_name": self.TAG,
"assets": [
{"name": n, "browser_download_url": f"https://example.com/{n}"}
for n in names
],
}
def _no_torch(self, monkeypatch):
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"detect_torch_cuda_runtime_preference",
lambda host: CudaRuntimePreference(runtime_line = None, selection_log = []),
)
def test_blackwell_13_1_prepends_pin(self, monkeypatch):
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
self._no_torch(monkeypatch)
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 1),
compute_caps = ["120"],
)
plan = direct_upstream_release_plan(
self._release(), host, UPSTREAM_REPO, "latest"
)
order = [(a.tag, a.runtime_line or a.install_kind) for a in plan.attempts]
assert order == [
("b9360", "cuda13"),
(self.TAG, "cuda12"),
(self.TAG, "windows-cpu"),
]
assert plan.attempts[0].name == "llama-b9360-bin-win-cuda-13.1-x64.zip"
# Direct/upstream path stays unverified-by-manifest (no approved hashes).
assert plan.approved_checksums.artifacts == {}
def test_blackwell_13_3_no_pin(self, monkeypatch):
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
self._no_torch(monkeypatch)
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 3),
compute_caps = ["120"],
)
plan = direct_upstream_release_plan(
self._release(), host, UPSTREAM_REPO, "latest"
)
assert "b9360" not in [a.tag for a in plan.attempts]
assert plan.attempts[0].tag == self.TAG
assert plan.attempts[0].runtime_line == "cuda13"
assert plan.attempts[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
# ===========================================================================
# N.1d. published_windows_cuda_attempts -- version-dynamic ordering seed
# ===========================================================================
class TestPublishedWindowsCudaAttemptsDynamicMajor:
"""The published-path ordering seed is derived from the release's real
published minors, so a future CUDA major published here is selectable
instead of being hidden by a hardcoded cuda12/cuda13 seed."""
TAG = "b8508"
def _win_cuda_artifact(self, minor, runtime_line):
return make_artifact(
f"llama-{self.TAG}-bin-win-cuda-{minor}-x64.zip",
install_kind = "windows-cuda",
runtime_line = runtime_line,
max_sm = 120,
)
def _release(self, minors_lines):
artifacts = [self._win_cuda_artifact(m, line) for m, line in minors_lines]
return make_release(artifacts, upstream_tag = self.TAG)
def test_future_cuda14_published_is_selected(self, monkeypatch):
# With the dynamic seed a 14.x driver reaches a published cuda14 build;
# the old hardcoded cuda12/cuda13 seed would never order it (the cuda14
# line would be skipped for want of a 14.x asset in the seed).
mock_windows_runtime(monkeypatch, ["cuda14", "cuda13", "cuda12"])
release = self._release(
[("14.0", "cuda14"), ("13.3", "cuda13"), ("12.4", "cuda12")]
)
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (14, 0),
compute_caps = ["120"],
)
result = published_windows_cuda_attempts(host, release, None)
assert result[0].runtime_line == "cuda14"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-14.0-x64.zip"
def test_cuda13_minor_selected_for_13_3_driver(self, monkeypatch):
# Existing behavior unchanged: a 13.3 driver gets the real 13.3 build.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
release = self._release([("13.3", "cuda13"), ("12.4", "cuda12")])
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 3),
compute_caps = ["120"],
)
result = published_windows_cuda_attempts(host, release, None)
assert result[0].runtime_line == "cuda13"
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
def test_below_minor_driver_gated_to_cuda12(self, monkeypatch):
# A 13.1 driver is gated off a published 13.3 and falls to cuda12.
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
release = self._release([("13.3", "cuda13"), ("12.4", "cuda12")])
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 1),
compute_caps = ["120"],
)
result = published_windows_cuda_attempts(host, release, None)
assert result[0].runtime_line == "cuda12"
# ===========================================================================
# N.1e. resolve_release_asset_choice -- pin on the published install path
# ===========================================================================
class TestResolveReleaseAssetChoicePin:
"""The published (non --simple-policy) install path reaches the same b9360
Blackwell pin as the simple path, with its verified hash threaded."""
TAG = "b8508"
def _release(self, minors_lines):
artifacts = [
make_artifact(
f"llama-{self.TAG}-bin-win-cuda-{minor}-x64.zip",
install_kind = "windows-cuda",
runtime_line = line,
max_sm = 120,
)
for minor, line in minors_lines
]
assets = {}
for minor, _line in minors_lines:
assets[f"llama-{self.TAG}-bin-win-cuda-{minor}-x64.zip"] = (
f"https://example.com/llama-{minor}"
)
assets[f"cudart-llama-bin-win-cuda-{minor}-x64.zip"] = (
f"https://example.com/cudart-{minor}"
)
return make_release(artifacts, upstream_tag = self.TAG, assets = assets)
def _checksums(self, minors):
names = []
for minor in minors:
names.append(f"llama-{self.TAG}-bin-win-cuda-{minor}-x64.zip")
names.append(f"cudart-llama-bin-win-cuda-{minor}-x64.zip")
return make_checksums(names)
def _no_torch(self, monkeypatch):
monkeypatch.setattr(
INSTALL_LLAMA_PREBUILT,
"detect_torch_cuda_runtime_preference",
lambda host: CudaRuntimePreference(runtime_line = None, selection_log = []),
)
def test_pin_applied_on_published_path_for_13_1(self, monkeypatch):
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
self._no_torch(monkeypatch)
release = self._release([("13.3", "cuda13"), ("12.4", "cuda12")])
checksums = self._checksums(["12.4"]) # 13.3 gated off for a 13.1 driver
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 1),
compute_caps = ["120"],
)
result = resolve_release_asset_choice(host, self.TAG, release, checksums)
assert result[0].tag == "b9360"
assert result[0].name == "llama-b9360-bin-win-cuda-13.1-x64.zip"
# apply_approved_hashes threaded the pin's verified hash from the
# augmented checksums (the pin survives the approved-hash gate).
assert result[0].expected_sha256 and len(result[0].expected_sha256) == 64
assert result[0].runtime_sha256 and len(result[0].runtime_sha256) == 64
assert any(a.runtime_line == "cuda12" for a in result)
def test_pin_dormant_on_published_path_for_13_3(self, monkeypatch):
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
self._no_torch(monkeypatch)
release = self._release([("13.3", "cuda13"), ("12.4", "cuda12")])
checksums = self._checksums(["13.3", "12.4"])
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 3),
compute_caps = ["120"],
)
result = resolve_release_asset_choice(host, self.TAG, release, checksums)
assert "b9360" not in [a.tag for a in result]
assert result[0].name == f"llama-{self.TAG}-bin-win-cuda-13.3-x64.zip"
def test_pin_not_applied_for_non_blackwell(self, monkeypatch):
mock_windows_runtime(monkeypatch, ["cuda13", "cuda12"])
self._no_torch(monkeypatch)
release = self._release([("13.3", "cuda13"), ("12.4", "cuda12")])
checksums = self._checksums(["12.4"])
host = make_host(
system = "Windows",
machine = "AMD64",
driver_cuda_version = (13, 1),
compute_caps = ["89"],
)
result = resolve_release_asset_choice(host, self.TAG, release, checksums)
assert "b9360" not in [a.tag for a in result]
# ===========================================================================
# N.1. apply_approved_hashes -- runtime archive checksum threading

View file

@ -1216,8 +1216,6 @@ if is_openai_available():
# =============================================
# Get Flash Attention v2 if Ampere (RTX 30xx, A100)
import bitsandbytes as bnb
from transformers import AutoTokenizer
from transformers.utils.import_utils import _is_package_available