unsloth/studio/backend/tests/test_llama_cpp_update.py
Daniel Han 898d3dd0b5
Studio: offer the in-app llama.cpp update for source-build (markerless) installs (#6188)
* Studio: offer the in-app llama.cpp update for source-build (markerless) installs

Source-build installs have no UNSLOTH_PREBUILT_INFO.json marker, so freshness
reported supported=False and the Update button never showed (notably on macOS,
where the fork shipped no prebuilt before b9585 and setup fell back to a source
build). When an install has no marker but an official prebuilt now exists for
the host, surface the update and let one click swap it in place.

- install_llama_prebuilt.py: published_repo_for_host() (the setup.sh host->repo
  rule in Python) and a --resolve-prebuilt mode that reports whether a prebuilt
  exists for this host without downloading.
- llama_cpp_update.py: markerless branch in get_update_status/start_update,
  version-suppressed so source builds already newer than latest are not nagged;
  fail-open throughout.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Studio: run llama update detection off the event loop, expose source_build

The markerless source-build check probes the host and reads GitHub, so run
get_update_status and start_update in a worker thread to keep the API
responsive. Expose source_build in the status response so the banner can label
the source-build switch.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Keep the llama-route auth stub out of sys.modules for the rest of the suite

test_llama_route.py replaced sys.modules['auth.authentication'] with a
bare stub at collection time and never restored it, so every later test
importing create_access_token got the stub: 17 failures across
test_desktop_auth, test_middleware, test_openai_tool_passthrough and
test_rag_preview on all four Backend CI Python versions. Import the
real module when its deps are available and only stub in minimal envs,
popping the stubs after the standalone route load either way.

* Studio: address review on the source-build update path

- published_repo_for_host: route CPU-only Windows to ggml-org too (mirrors
  setup.ps1; the fork ships no win-cpu bundle), macOS always the fork.
- markerless detection compares/display the upstream llama_tag, not a possible
  fork wrapper release_tag, so a source build is not wrongly judged newer.
- do not offer when there is no resolvable install root (a pinned
  LLAMA_SERVER_PATH outside a managed dir): an apply would not take effect.

* Ignore version probes in the update tests' subprocess capture

The status polls in these tests trigger the new source-build detection,
which shells out to llama-server --version through the same patched
subprocess.run. On slow runners that probe lands after the installer
call and clobbers the single captured argv, failing the flag
assertions (seen on the 3.10/3.11 Backend CI jobs). Skip probe calls
in all three fakes so only the installer invocation is captured.

* Skip markerless re-detection while the update job is swapping the tree

On a source-build install the frontend polls update-status every 3s
during an apply, and each poll ran _source_build_status, which execs
the very llama-server binary the job is concurrently replacing. On
Windows that exec can hold the exe long enough to fail the installer's
os.replace; everywhere it is a per-poll subprocess spawn for a status
the poller does not read (it only consumes job progress). Gate the
markerless branch on the job not running; the marked path is probe-free
and still returns the live job state.

* Studio: tighten source-build update root, repo routing, and downgrade guard

Only manage a markerless install when the active binary lives under a
resolvable llama.cpp root (marker dir, UNSLOTH_LLAMA_CPP_PATH it sits in,
or a llama.cpp ancestor); a pinned LLAMA_SERVER_PATH or a PATH/system
binary is left alone so an apply cannot install where it would not take
effect. Gate start_update on the same suppression as detection so a
direct POST cannot downgrade a source build newer than the latest
prebuilt. Route Linux hosts with AMD tooling (rocminfo/amd-smi/hipconfig/
hipinfo) to the fork in --resolve-prebuilt, matching setup.sh, so a HIP
source build is not offered an upstream CPU prebuilt.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Studio: cover inactive env root and pinned llama.cpp checkout in update root tests

---------

Co-authored-by: danielhanchen <michaelhan2050@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-11 02:45:12 -07:00

761 lines
28 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
"""Hermetic tests for the in-app llama.cpp update orchestration.
No network, no real install: the GitHub release lookup and the installer
subprocess are both monkeypatched. Verifies detection (update_available) and
the apply flow (job lifecycle, installer invocation, post-swap re-read).
"""
from __future__ import annotations
import json
import sys
import time
from pathlib import Path
from types import ModuleType
import pytest
_BACKEND = Path(__file__).resolve().parents[1]
if str(_BACKEND) not in sys.path:
sys.path.insert(0, str(_BACKEND))
import utils.llama_cpp_freshness as freshness # noqa: E402
import utils.llama_cpp_update as upd # noqa: E402
MARKER = "UNSLOTH_PREBUILT_INFO.json"
def _write_install(
dir_: Path,
tag: str,
repo: str = "unslothai/llama.cpp",
asset: str | None = None,
) -> str:
"""Create a fake prebuilt install tree and return the llama-server path.
``asset`` is the bundle filename recorded in the marker; omit it to model an
older marker that predates asset-based ROCm forwarding (backward compat)."""
bin_dir = dir_ / "build" / "bin"
bin_dir.mkdir(parents = True, exist_ok = True)
binary = bin_dir / "llama-server"
binary.write_text("#!/bin/sh\necho stub\n")
marker = {
"tag": tag,
"release_tag": tag,
"published_repo": repo,
"installed_at_utc": "2020-01-01T00:00:00Z",
"bundle_profile": "cuda13-newer",
"runtime_line": "cuda13",
}
if asset is not None:
marker["asset"] = asset
(dir_ / MARKER).write_text(json.dumps(marker))
return str(binary)
@pytest.fixture(autouse = True)
def _clean_state(monkeypatch):
freshness.reset_caches()
upd._reset_job_for_tests()
upd._resolve_memo.clear()
# Deterministic markerless paths: no host-pinned binary, no custom dir.
monkeypatch.delenv("LLAMA_SERVER_PATH", raising = False)
monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False)
# Never hit the network in these tests.
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: None)
yield
freshness.reset_caches()
upd._reset_job_for_tests()
upd._resolve_memo.clear()
def _no_prebuilt(monkeypatch):
"""Stub the host prebuilt probe to 'none available' (no source-build offer)."""
monkeypatch.setattr(upd, "_resolve_prebuilt_for_host", lambda *, force_refresh = False: None)
def _prebuilt(
monkeypatch,
*,
repo = "unslothai/llama.cpp",
release_tag = "b9585",
llama_tag = None,
asset = None,
):
"""Stub the host prebuilt probe to report an available prebuilt."""
payload = {
"prebuilt_available": True,
"repo": repo,
"release_tag": release_tag,
"llama_tag": llama_tag or release_tag,
"asset": asset or f"llama-{release_tag}-bin-macos-arm64.tar.gz",
"install_kind": "macos-arm64",
}
monkeypatch.setattr(upd, "_resolve_prebuilt_for_host", lambda *, force_refresh = False: payload)
def test_status_no_marker_no_prebuilt(monkeypatch, tmp_path):
# No marker AND no prebuilt available for the host -> unsupported (the genuine
# source-build-with-nothing-to-offer case).
binary = tmp_path / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub") # no marker file alongside
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
_no_prebuilt(monkeypatch)
st = upd.get_update_status()
assert st["supported"] is False
assert st["update_available"] is False
assert st["installed_tag"] is None
def test_status_source_build_offers_prebuilt(monkeypatch, tmp_path):
# Markerless source build with a prebuilt now available for the host: surface
# the update. Unknown installed version (source build) is treated as behind.
binary = tmp_path / "llama.cpp" / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
_prebuilt(monkeypatch, release_tag = "b9585")
monkeypatch.setattr(upd, "_installed_build_number", lambda b: None)
st = upd.get_update_status()
assert st["supported"] is True
assert st["update_available"] is True
assert st["source_build"] is True
assert st["latest_tag"] == "b9585"
assert st["published_repo"] == "unslothai/llama.cpp"
def test_status_source_build_compares_llama_tag(monkeypatch, tmp_path):
# release_tag may be a fork wrapper (v1.0); compare/display the upstream
# llama_tag (b9457) so a source build is not wrongly judged newer.
binary = tmp_path / "llama.cpp" / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
_prebuilt(monkeypatch, release_tag = "v1.0", llama_tag = "b9457")
monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9000)
st = upd.get_update_status()
assert st["latest_tag"] == "b9457" # not the wrapper tag
assert st["update_available"] is True # 9000 < 9457
def test_status_source_build_pinned_binary_not_offered(monkeypatch, tmp_path):
# LLAMA_SERVER_PATH pins a custom binary outside any llama.cpp dir; an apply
# could not take effect, so the button must not surface.
binary = tmp_path / "custom" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setenv("LLAMA_SERVER_PATH", str(binary))
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
_prebuilt(monkeypatch)
st = upd.get_update_status()
assert st["supported"] is False
assert st["update_available"] is False
def test_llama_install_root_pinned_returns_none(monkeypatch, tmp_path):
binary = tmp_path / "custom" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setenv("LLAMA_SERVER_PATH", str(binary))
assert upd._llama_install_root(str(binary)) is None
def test_status_source_build_suppressed_when_newer(monkeypatch, tmp_path):
# A source build already newer than the latest prebuilt is not nagged.
binary = tmp_path / "llama.cpp" / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
_prebuilt(monkeypatch, release_tag = "b9518")
monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9600)
st = upd.get_update_status()
assert st["supported"] is True
assert st["update_available"] is False
assert st["installed_tag"] == "b9600"
def test_status_source_build_skips_probe_while_job_runs(monkeypatch, tmp_path):
# While the updater swaps the tree, status polls must not exec the binary
# being replaced (on Windows that exec can fail the installer's os.replace);
# the 3s poller only consumes job progress.
binary = tmp_path / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
probes = {"resolve": 0, "version": 0}
def _count_resolve(*, force_refresh = False):
probes["resolve"] += 1
return None
def _count_version(b):
probes["version"] += 1
return None
monkeypatch.setattr(upd, "_resolve_prebuilt_for_host", _count_resolve)
monkeypatch.setattr(upd, "_installed_build_number", _count_version)
with upd._job_lock:
upd._job["state"] = upd._JOB_RUNNING
st = upd.get_update_status()
assert st["job"]["state"] == "running"
assert probes == {"resolve": 0, "version": 0}
def test_status_update_available(monkeypatch, tmp_path):
binary = _write_install(tmp_path, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: "b9518")
st = upd.get_update_status(force_refresh = True)
assert st["supported"] is True
assert st["installed_tag"] == "b9493"
assert st["latest_tag"] == "b9518"
assert st["update_available"] is True
def test_status_up_to_date(monkeypatch, tmp_path):
binary = _write_install(tmp_path, "b9518")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: "b9518")
st = upd.get_update_status(force_refresh = True)
assert st["installed_tag"] == "b9518"
assert st["latest_tag"] == "b9518"
assert st["update_available"] is False
def test_start_update_no_marker_no_prebuilt_refuses(monkeypatch, tmp_path):
binary = tmp_path / "llama-server"
binary.write_text("stub") # no marker
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
_no_prebuilt(monkeypatch)
res = upd.start_update()
assert res["started"] is False
assert res["reason"] == "no_prebuilt_available"
def test_start_update_source_build_installs_prebuilt(monkeypatch, tmp_path):
# Markerless install + available prebuilt: install in place into the resolved
# root, with the asset-derived ROCm forwarding and the resolved repo.
install_dir = tmp_path / "llama.cpp"
binary = install_dir / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub") # no marker
monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False)
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
_prebuilt(
monkeypatch, repo = "unslothai/llama.cpp", asset = "app-b9585-linux-x64-rocm-gfx110X.tar.gz"
)
captured = {}
class _Proc:
returncode = 0
stdout = "installed"
stderr = ""
def _fake_run(cmd, **kwargs):
cmd = list(cmd)
# Status polls probe `llama-server --version`; keep the installer argv.
if "--version" in cmd:
return _Proc()
captured["cmd"] = cmd
_write_install(install_dir, "b9585") # installer writes the marker
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd.start_update()
assert res["started"] is True, res
deadline = time.time() + 10
while time.time() < deadline:
if upd.get_update_status()["job"]["state"] in ("success", "error"):
break
time.sleep(0.05)
cmd = captured["cmd"]
assert "--install-dir" in cmd and str(install_dir) in cmd
assert "--published-repo" in cmd and "unslothai/llama.cpp" in cmd
assert "--llama-tag" in cmd and "latest" in cmd
assert cmd[cmd.index("--rocm-gfx") + 1] == "gfx110x"
assert "--simple-policy" not in cmd and "--cpu-fallback" not in cmd
def test_start_update_happy_path(monkeypatch, tmp_path):
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: "b9518")
captured = {}
class _Proc:
returncode = 0
stdout = "installed"
stderr = ""
def _fake_run(cmd, **kwargs):
cmd = list(cmd)
# Status polls probe `llama-server --version`; keep the installer argv.
if "--version" in cmd:
return _Proc()
captured["cmd"] = cmd
# Simulate the installer writing a new marker with the latest tag.
_write_install(install_dir, "b9518")
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd.start_update()
assert res["started"] is True
assert res["job"]["from_tag"] == "b9493"
# Wait for the background worker.
deadline = time.time() + 10
while time.time() < deadline:
job = upd.get_update_status()["job"]
if job["state"] in ("success", "error"):
break
time.sleep(0.05)
assert job["state"] == "success", job
assert job["to_tag"] == "b9518"
# Installer was invoked with the resolved install dir + latest + repo.
assert "--install-dir" in captured["cmd"]
assert str(install_dir) in captured["cmd"]
assert "--llama-tag" in captured["cmd"] and "latest" in captured["cmd"]
assert "unslothai/llama.cpp" in captured["cmd"]
def test_start_update_installer_failure_reports_error(monkeypatch, tmp_path):
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
class _Proc:
returncode = 2
stdout = ""
stderr = "boom: network error"
monkeypatch.setattr(upd.subprocess, "run", lambda cmd, **kw: _Proc())
res = upd.start_update()
assert res["started"] is True
deadline = time.time() + 10
while time.time() < deadline:
job = upd.get_update_status()["job"]
if job["state"] in ("success", "error"):
break
time.sleep(0.05)
assert job["state"] == "error"
assert "boom" in (job["error"] or "")
# --- installer-argument construction (mirrors the post-#5963 setup scripts) ---
def test_rocm_install_args_lemonade_gfx():
# Lemonade HIP app bundle: gfx family lives in the asset name.
assert upd._rocm_install_args("app-b9585-linux-x64-rocm-gfx110X.tar.gz") == [
"--rocm-gfx",
"gfx110x",
]
assert upd._rocm_install_args("app-b9585-windows-x64-rocm-gfx1150.zip") == [
"--rocm-gfx",
"gfx1150",
]
def test_rocm_install_args_fork_version_bundle():
# Fork ROCm bundles encode a ROCm version, not a gfx -> forward --has-rocm.
assert upd._rocm_install_args("llama-b9334-bin-ubuntu-rocm-6.4-x64.tar.gz") == ["--has-rocm"]
def test_rocm_install_args_windows_hip():
assert upd._rocm_install_args("llama-b9334-bin-win-hip-radeon-x64.zip") == ["--has-rocm"]
def test_rocm_install_args_non_rocm_and_missing():
assert upd._rocm_install_args("llama-b9334-bin-ubuntu-x64.tar.gz") == []
assert upd._rocm_install_args("app-b9585-linux-x64-cuda13.tar.gz") == []
assert upd._rocm_install_args(None) == []
def _capture_install_cmd(
monkeypatch,
tmp_path,
*,
tag = "b9493",
repo = "unslothai/llama.cpp",
asset = None,
latest = "b9518",
) -> list:
"""Run start_update() with the installer subprocess stubbed; return the argv."""
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, tag, repo = repo, asset = asset)
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: latest)
captured = {}
class _Proc:
returncode = 0
stdout = "installed"
stderr = ""
def _fake_run(cmd, **kwargs):
cmd = list(cmd)
# Status polls probe `llama-server --version`; keep the installer argv.
if "--version" in cmd:
return _Proc()
captured["cmd"] = cmd
_write_install(install_dir, latest, repo = repo, asset = asset)
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd.start_update()
assert res["started"] is True, res
deadline = time.time() + 10
while time.time() < deadline:
if upd.get_update_status()["job"]["state"] in ("success", "error"):
break
time.sleep(0.05)
return captured.get("cmd", [])
def test_install_cmd_rocm_marker_forwards_gfx(monkeypatch, tmp_path):
cmd = _capture_install_cmd(
monkeypatch, tmp_path, asset = "app-b9585-linux-x64-rocm-gfx110X.tar.gz"
)
assert "--rocm-gfx" in cmd
assert cmd[cmd.index("--rocm-gfx") + 1] == "gfx110x"
assert "--has-rocm" not in cmd
assert "--cpu-fallback" not in cmd
assert "--simple-policy" not in cmd
assert "--published-repo" in cmd and "unslothai/llama.cpp" in cmd
def test_install_cmd_fork_rocm_marker_forwards_has_rocm(monkeypatch, tmp_path):
cmd = _capture_install_cmd(
monkeypatch, tmp_path, asset = "llama-b9334-bin-ubuntu-rocm-6.4-x64.tar.gz"
)
assert "--has-rocm" in cmd
assert "--rocm-gfx" not in cmd
def test_install_cmd_ggml_cpu_marker_has_no_cpu_fallback(monkeypatch, tmp_path):
# CPU installs come from ggml-org. Re-running into the same install-dir/repo
# reproduces the same CPU bundle; --cpu-fallback (which force-drops GPU
# detection) is reserved for setup.sh's arm64 rescue and must not appear here.
cmd = _capture_install_cmd(
monkeypatch,
tmp_path,
repo = "ggml-org/llama.cpp",
asset = "llama-b9334-bin-ubuntu-x64.tar.gz",
)
assert "--cpu-fallback" not in cmd
assert "--rocm-gfx" not in cmd
assert "--has-rocm" not in cmd
assert "--simple-policy" not in cmd
assert "--published-repo" in cmd and "ggml-org/llama.cpp" in cmd
def test_install_cmd_cuda_marker_minimal_and_backward_compatible(monkeypatch, tmp_path):
# Marker without an asset field (older install): no ROCm flags, no crash, and
# never the obsolete --simple-policy that #5963 removed from setup.
cmd = _capture_install_cmd(monkeypatch, tmp_path, asset = None)
assert "--simple-policy" not in cmd
assert "--rocm-gfx" not in cmd
assert "--has-rocm" not in cmd
assert "--cpu-fallback" not in cmd
# --- refusal + maintenance-state coordination ---
def test_start_update_already_running_refuses(monkeypatch, tmp_path):
binary = _write_install(tmp_path / "llama.cpp", "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
with upd._job_lock:
upd._job.update(state = upd._JOB_RUNNING)
res = upd.start_update()
assert res["started"] is False
assert res["reason"] == "already_running"
def test_start_update_installer_missing_refuses(monkeypatch, tmp_path):
binary = _write_install(tmp_path / "llama.cpp", "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: None)
res = upd.start_update()
assert res["started"] is False
assert res["reason"] == "installer_missing"
class _FakeBackend:
"""Minimal stand-in for LlamaCppBackend's update-coordination surface."""
def __init__(self):
import threading
self._serial_load_lock = threading.Lock()
self._llama_update_in_progress = False
self.is_active = True
self.unloaded = False
def unload_model(self):
self.unloaded = True
def _inject_backend(monkeypatch, backend):
routes_pkg = ModuleType("routes")
routes_pkg.__path__ = []
inference_mod = ModuleType("routes.inference")
inference_mod.get_llama_cpp_backend = lambda: backend
monkeypatch.setitem(sys.modules, "routes", routes_pkg)
monkeypatch.setitem(sys.modules, "routes.inference", inference_mod)
def test_update_sets_maintenance_flag_and_unloads(monkeypatch, tmp_path):
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: "b9518")
backend = _FakeBackend()
_inject_backend(monkeypatch, backend)
seen = {}
class _Proc:
returncode = 0
stdout = "ok"
stderr = ""
def _fake_run(cmd, **kwargs):
# The maintenance flag must be set while the installer runs.
seen["flag_during_install"] = backend._llama_update_in_progress
_write_install(install_dir, "b9518")
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd.start_update()
assert res["started"] is True
deadline = time.time() + 10
while time.time() < deadline:
if upd.get_update_status()["job"]["state"] in ("success", "error"):
break
time.sleep(0.05)
assert backend.unloaded is True
assert seen.get("flag_during_install") is True
# Cleared in the finally so model loads work again after the swap.
assert backend._llama_update_in_progress is False
def test_update_clears_maintenance_flag_on_installer_failure(monkeypatch, tmp_path):
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
backend = _FakeBackend()
_inject_backend(monkeypatch, backend)
class _Proc:
returncode = 1
stdout = ""
stderr = "boom"
monkeypatch.setattr(upd.subprocess, "run", lambda cmd, **kw: _Proc())
res = upd.start_update()
assert res["started"] is True
deadline = time.time() + 10
while time.time() < deadline:
if upd.get_update_status()["job"]["state"] in ("success", "error"):
break
time.sleep(0.05)
assert upd.get_update_status()["job"]["state"] == "error"
assert backend._llama_update_in_progress is False
def test_update_fails_open_when_backend_unavailable(monkeypatch, tmp_path):
install_dir = tmp_path / "llama.cpp"
binary = _write_install(install_dir, "b9493")
monkeypatch.setattr(upd, "_find_binary", lambda: binary)
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
monkeypatch.setattr(freshness, "_fetch_latest_release_tag", lambda repo, timeout = 5.0: "b9518")
def _raise():
raise RuntimeError("no backend")
inference_mod = ModuleType("routes.inference")
inference_mod.get_llama_cpp_backend = lambda: _raise()
routes_pkg = ModuleType("routes")
routes_pkg.__path__ = []
monkeypatch.setitem(sys.modules, "routes", routes_pkg)
monkeypatch.setitem(sys.modules, "routes.inference", inference_mod)
class _Proc:
returncode = 0
stdout = "ok"
stderr = ""
def _fake_run(cmd, **kwargs):
_write_install(install_dir, "b9518")
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd.start_update()
assert res["started"] is True
deadline = time.time() + 10
while time.time() < deadline:
job = upd.get_update_status()["job"]
if job["state"] in ("success", "error"):
break
time.sleep(0.05)
assert job["state"] == "success", job
# --- markerless helper units ---
def test_resolve_prebuilt_parses_and_caches(monkeypatch, tmp_path):
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
calls = {"n": 0}
class _Proc:
returncode = 0
# stderr noise plus the JSON line on stdout (installer logs to stderr).
stdout = (
'{"prebuilt_available": true, "repo": "unslothai/llama.cpp", "release_tag": "b9585"}'
)
stderr = "[llama-prebuilt] some log\n"
def _fake_run(cmd, **kwargs):
calls["n"] += 1
assert "--resolve-prebuilt" in cmd
return _Proc()
monkeypatch.setattr(upd.subprocess, "run", _fake_run)
res = upd._resolve_prebuilt_for_host()
assert res["prebuilt_available"] is True and res["release_tag"] == "b9585"
# Second call is memoized (no second subprocess).
upd._resolve_prebuilt_for_host()
assert calls["n"] == 1
def test_resolve_prebuilt_fails_open(monkeypatch, tmp_path):
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
def _boom(cmd, **kwargs):
raise OSError("subprocess failed")
monkeypatch.setattr(upd.subprocess, "run", _boom)
assert upd._resolve_prebuilt_for_host() is None
# Failures are not cached: a later success is observed.
class _Proc:
returncode = 0
stdout = '{"prebuilt_available": false}'
stderr = ""
monkeypatch.setattr(upd.subprocess, "run", lambda cmd, **kw: _Proc())
assert upd._resolve_prebuilt_for_host() == {"prebuilt_available": False}
def test_installed_build_number(monkeypatch):
def _ver(text):
class _Proc:
returncode = 0
stdout = ""
stderr = text
monkeypatch.setattr(upd.subprocess, "run", lambda cmd, **kw: _Proc())
return upd._installed_build_number("/bin/llama-server")
assert _ver("version: 9585 (abc1234)\nbuilt with clang\n") == 9585
assert _ver("version: 1 (deadbee)\n") is None # source build without tags
assert _ver("no version here") is None
assert upd._installed_build_number(None) is None
def test_llama_install_root_finds_llama_cpp_ancestor(monkeypatch, tmp_path):
root = tmp_path / "llama.cpp"
binary = root / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False)
assert upd._llama_install_root(str(binary)) == root
def test_llama_install_root_unmanaged_path_returns_none(monkeypatch, tmp_path):
# A binary on PATH (no marker, no env pin, no llama.cpp ancestor) is foreign:
# installing elsewhere would not replace it, so report no manageable root.
binary = tmp_path / "usr" / "local" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False)
assert upd._llama_install_root(str(binary)) is None
def test_llama_install_root_unsloth_env_dir(monkeypatch, tmp_path):
# UNSLOTH_LLAMA_CPP_PATH dir holding the active binary is the managed root.
root = tmp_path / "vendor" / "llama"
binary = root / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setenv("UNSLOTH_LLAMA_CPP_PATH", str(root))
assert upd._llama_install_root(str(binary)) == root
def test_llama_install_root_ignores_inactive_env_root(monkeypatch, tmp_path):
# UNSLOTH_LLAMA_CPP_PATH set but the active binary is not under it: do not
# target the stale env root, resolve from the binary's own llama.cpp tree.
inactive = tmp_path / "custom-empty"
inactive.mkdir()
active = tmp_path / "llama.cpp"
binary = active / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setenv("UNSLOTH_LLAMA_CPP_PATH", str(inactive))
assert upd._llama_install_root(str(binary)) == active
def test_llama_install_root_refuses_pinned_checkout_under_llama_cpp(monkeypatch, tmp_path):
# The LLAMA_SERVER_PATH pin guard must run before the ancestor scan, or a
# user's own llama.cpp checkout could be handed to the installer.
root = tmp_path / "my-project" / "llama.cpp"
binary = root / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub")
monkeypatch.setenv("LLAMA_SERVER_PATH", str(binary))
monkeypatch.delenv("UNSLOTH_LLAMA_CPP_PATH", raising = False)
assert upd._llama_install_root(str(binary)) is None
def test_start_update_source_build_refuses_when_newer(monkeypatch, tmp_path):
# A direct POST on a source build already newer than the prebuilt must not
# downgrade it; start_update mirrors the detection suppression.
install_dir = tmp_path / "llama.cpp"
binary = install_dir / "build" / "bin" / "llama-server"
binary.parent.mkdir(parents = True)
binary.write_text("stub") # no marker
monkeypatch.setattr(upd, "_find_binary", lambda: str(binary))
monkeypatch.setattr(upd, "_installer_script", lambda: tmp_path / "install_llama_prebuilt.py")
_prebuilt(monkeypatch, release_tag = "b9518")
monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9600)
res = upd.start_update()
assert res["started"] is False
assert res["reason"] == "up_to_date"