Studio: offer llama.cpp update for same-base mix builds on source installs (#6280)
The update banner is driven by update_available from /api/llama/update-status. Prebuilt (marker) installs decide this via freshness.is_behind, which is mix-aware: a release at the same upstream base build but with a new -mix-<sha> suffix counts as behind. Source-build installs went through _source_build_status, which compared only the numeric base build (installed_build < latest_build). When a new prebuilt shared the installed upstream base (our usual mix re-tag at the same base), it returned update_available=False and the banner never showed. This is the common macOS case, where a failed prebuilt fetch falls back to a source build that lacks the mix patches. Make the source-build path mix-aware to match the marker path: same base plus a -mix-<sha> tag now offers the update (and displays the mix tag), a bare same-base rebuild does not, and the downgrade guard and unknown -version fallback are unchanged.
This commit is contained in:
parent
368b19b237
commit
bd9324b634
2 changed files with 63 additions and 10 deletions
|
|
@ -233,6 +233,39 @@ def test_status_source_build_suppressed_when_newer(monkeypatch, tmp_path):
|
|||
assert st["installed_tag"] == "b9600"
|
||||
|
||||
|
||||
def test_status_source_build_offers_same_base_mix(monkeypatch, tmp_path):
|
||||
# The reported banner bug: a source build at the same upstream base as a new
|
||||
# Unsloth prebuilt that adds a mix-<sha> suffix. The base build numbers match
|
||||
# (9596 == 9596) but the mix carries extra patches the source build lacks, so
|
||||
# the update must still surface -- mirroring the marker path's is_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 = "b9596-mix-e6f2453", llama_tag = "b9596")
|
||||
monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9596)
|
||||
st = upd.get_update_status()
|
||||
assert st["supported"] is True
|
||||
assert st["update_available"] is True
|
||||
assert st["source_build"] is True
|
||||
assert st["installed_tag"] == "b9596"
|
||||
assert st["latest_tag"] == "b9596-mix-e6f2453"
|
||||
|
||||
|
||||
def test_status_source_build_same_base_bare_not_offered(monkeypatch, tmp_path):
|
||||
# Same base, but the prebuilt is a bare rebuild (no mix suffix): nothing extra
|
||||
# to gain, so do not nag.
|
||||
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 = "b9596", llama_tag = "b9596")
|
||||
monkeypatch.setattr(upd, "_installed_build_number", lambda b: 9596)
|
||||
st = upd.get_update_status()
|
||||
assert st["update_available"] is False
|
||||
assert st["latest_tag"] == "b9596"
|
||||
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ from utils.llama_cpp_freshness import (
|
|||
_INSTALL_MARKER_NAME,
|
||||
check_prebuilt_freshness,
|
||||
latest_published_release,
|
||||
parse_base_build,
|
||||
read_install_marker,
|
||||
reset_caches,
|
||||
)
|
||||
|
|
@ -220,23 +221,42 @@ def _source_build_status(binary: str, *, force_refresh: bool) -> Optional[dict]:
|
|||
res = _resolve_prebuilt_for_host(force_refresh = force_refresh)
|
||||
if not res or not res.get("prebuilt_available"):
|
||||
return None
|
||||
# llama_tag is the upstream build (bNNNN, what --version reports); release_tag
|
||||
# can be a fork wrapper tag, so compare/display against llama_tag.
|
||||
latest = res.get("llama_tag") or res.get("release_tag")
|
||||
if not latest:
|
||||
# llama_tag is the upstream base (bNNNN, what --version reports); release_tag
|
||||
# is the full tag, either a same-base mix (bNNNN-mix-<sha>) or a fork wrapper
|
||||
# (e.g. v1.0). Compare the numeric base against llama_tag.
|
||||
base_tag = res.get("llama_tag") or res.get("release_tag")
|
||||
release_tag = res.get("release_tag")
|
||||
if not base_tag:
|
||||
return None
|
||||
# No resolvable install root (e.g. a pinned LLAMA_SERVER_PATH we cannot
|
||||
# manage) means an apply would not take effect, so do not offer.
|
||||
if _llama_install_root(binary) is None:
|
||||
return None
|
||||
installed_build = _installed_build_number(binary)
|
||||
m = re.search(r"(\d+)", latest)
|
||||
latest_build = int(m.group(1)) if m else None
|
||||
# Suppress only when the source build is reliably newer/equal; unknown
|
||||
# version (the involuntary source-build case) is treated as behind.
|
||||
update_available = (
|
||||
installed_build is None or latest_build is None or installed_build < latest_build
|
||||
latest_build = parse_base_build(base_tag)
|
||||
# A same-base mix adds patches the bare base lacks, so it is newer even at an
|
||||
# unchanged build number (the marker path's is_behind already does this). The
|
||||
# bNNNN anchor keeps a fork wrapper tag from being read as a mix.
|
||||
latest_is_mix = (
|
||||
isinstance(release_tag, str)
|
||||
and latest_build is not None
|
||||
and parse_base_build(release_tag) == latest_build
|
||||
and release_tag.strip() != f"b{latest_build}"
|
||||
)
|
||||
if installed_build is None or latest_build is None:
|
||||
# Unknown installed/latest version (the involuntary source-build case):
|
||||
# treat as behind so we still offer the prebuilt.
|
||||
update_available = True
|
||||
elif installed_build < latest_build:
|
||||
update_available = True
|
||||
elif installed_build == latest_build:
|
||||
# Same upstream base: offer the extra-patch mix, never a bare rebuild.
|
||||
update_available = latest_is_mix
|
||||
else:
|
||||
# Source build newer than the latest prebuilt: downgrade guard.
|
||||
update_available = False
|
||||
# Display the mix tag when that's what makes it newer; otherwise the base.
|
||||
latest = release_tag if latest_is_mix else base_tag
|
||||
with _job_lock:
|
||||
job = dict(_job)
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue