e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
118 lines
4.7 KiB
Python
118 lines
4.7 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""GGML_CUDA_ENABLE_UNIFIED_MEMORY must be set only for AMD unified-memory APUs
|
|
(gfx1150/gfx1151), never for discrete AMD, NVIDIA, CPU or macOS."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
|
|
import pytest
|
|
|
|
from core.inference.llama_cpp import LlamaCppBackend
|
|
|
|
|
|
def _fake_torch(
|
|
hip,
|
|
archs,
|
|
*,
|
|
cuda_ok = True,
|
|
):
|
|
t = types.ModuleType("torch")
|
|
t.version = types.SimpleNamespace(hip = hip)
|
|
t.cuda = types.SimpleNamespace(
|
|
is_available = lambda: cuda_ok,
|
|
device_count = lambda: len(archs),
|
|
get_device_properties = lambda i: types.SimpleNamespace(gcnArchName = archs[i]),
|
|
)
|
|
return t
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"hip,archs,expected",
|
|
[
|
|
("6.2.0", ["gfx1151:xnack-"], True), # Strix Halo APU (suffix stripped)
|
|
("6.2.0", ["gfx1150"], True), # Strix Point APU
|
|
("6.2.0", ["gfx1100"], False), # discrete RDNA3
|
|
("6.2.0", ["gfx1201"], False), # discrete RDNA4
|
|
("6.2.0", ["gfx942"], False), # MI300X (data center)
|
|
(None, ["sm_90"], False), # NVIDIA (no torch.version.hip)
|
|
("6.2.0", ["gfx1100", "gfx1151"], True), # mixed dGPU + APU
|
|
],
|
|
)
|
|
def test_apu_unified_memory_gating(monkeypatch, hip, archs, expected):
|
|
monkeypatch.setitem(sys.modules, "torch", _fake_torch(hip, archs))
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory() is expected
|
|
|
|
|
|
def test_apu_guard_scopes_to_selected_gpu(monkeypatch):
|
|
# Mixed host: physical id 0 = discrete gfx1100, 1 = gfx1151 APU.
|
|
for _m in ("HIP_VISIBLE_DEVICES", "ROCR_VISIBLE_DEVICES", "CUDA_VISIBLE_DEVICES"):
|
|
monkeypatch.delenv(_m, raising = False)
|
|
monkeypatch.setitem(sys.modules, "torch", _fake_torch("6.2.0", ["gfx1100", "gfx1151"]))
|
|
# Selecting only the dGPU, or an empty selection, must not be unified-memory.
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory([0]) is False
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory([]) is False
|
|
# Selecting the APU, or no selection, does.
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory([1]) is True
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory() is True
|
|
|
|
|
|
def test_apu_guard_honors_hip_visible_devices_mask(monkeypatch):
|
|
# ROCm resolves ids via HIP first: the mask exposes only the APU as ordinal 0
|
|
# but physical id 1, so the selection [1] must still match.
|
|
monkeypatch.delenv("CUDA_VISIBLE_DEVICES", raising = False)
|
|
monkeypatch.delenv("ROCR_VISIBLE_DEVICES", raising = False)
|
|
monkeypatch.setenv("HIP_VISIBLE_DEVICES", "1")
|
|
monkeypatch.setitem(sys.modules, "torch", _fake_torch("6.2.0", ["gfx1151"]))
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory([1]) is True
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory([0]) is False
|
|
|
|
|
|
def test_cpu_no_cuda_returns_false(monkeypatch):
|
|
monkeypatch.setitem(sys.modules, "torch", _fake_torch("6.2.0", [], cuda_ok = False))
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory() is False
|
|
|
|
|
|
def test_missing_torch_returns_false(monkeypatch):
|
|
monkeypatch.setitem(sys.modules, "torch", None)
|
|
assert LlamaCppBackend._amd_apu_wants_unified_memory() is False
|
|
|
|
|
|
_GB = 1024**3
|
|
_MIB_PER_GB = 1024
|
|
# Module-level (not a class attr) so it stays a plain function, not a bound method.
|
|
_shortfall = LlamaCppBackend._apu_ram_shortfall_message
|
|
|
|
|
|
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."""
|
|
|
|
def test_field_case_wsl_cap_refuses(self):
|
|
# 64.6 GB weights, ~46 GB available (WSL VM): refuse with guidance.
|
|
msg = _shortfall(int(64.6 * _GB), 46 * _MIB_PER_GB)
|
|
assert msg is not None
|
|
assert "65 GB" in msg and "46 GB" in msg
|
|
assert ".wslconfig" in msg
|
|
|
|
def test_bare_metal_fits_allows(self):
|
|
# Same model, ~92 GB available (no WSL cap): allow.
|
|
assert _shortfall(int(64.6 * _GB), 92 * _MIB_PER_GB) is None
|
|
|
|
def test_unknown_available_never_refuses(self):
|
|
assert _shortfall(int(64.6 * _GB), None) is None
|
|
|
|
def test_boundary_at_headroom(self):
|
|
# 20 GB weights, headroom 2 GB. avail 23 GB -> fits; 21 GB -> refuse.
|
|
assert _shortfall(20 * _GB, 23 * _MIB_PER_GB) is None
|
|
assert _shortfall(20 * _GB, 21 * _MIB_PER_GB) is not None
|
|
|
|
def test_available_system_memory_is_int_or_none(self):
|
|
v = LlamaCppBackend._available_system_memory_mib()
|
|
assert v is None or (isinstance(v, int) and v > 0)
|