Fix Windows AMD lemonade tag resolution for PR #5963

The fork release scan passes each scanned release's upstream tag
(b9518, ...) to the lemonade lookup, but lemonade publishes its own tag
series (b1292, ...) that never contains upstream tag numbers. On a
Windows AMD host every scanned release therefore 404s the lemonade
fetch twice, the upstream HIP zip is dropped by the approved-hash gate,
and the scan walks the whole release history until it dies on the
unauthenticated GitHub rate limit or falls to a HIP source build. The
Linux path already passes the requested tag ("latest") and works.

Thread the requested tag through resolve_release_asset_choice ->
resolve_asset_choice -> resolve_upstream_asset_choice as lemonade_tag,
used only by the lemonade lookups. Upstream asset names keep the
concrete per-release tag and all new parameters default to the old
behavior.

Verified on a gfx1151 box: before, the native Windows install scanned
b9518..b8811 and aborted on rate limit; after, it selects
llama-b1292-windows-rocm-gfx1151-x64.zip (lemonade) from fork release
b9518, passes staged validation, and the installed llama-server
enumerates ROCm0. WSL keeps selecting the matching ubuntu bundle.
Adds a regression test pinning that the Windows fork path resolves
lemonade via /releases/latest, never /releases/tags/<fork-tag>.
This commit is contained in:
Daniel Han 2026-06-10 00:04:49 -07:00
commit 72f3236449
2 changed files with 79 additions and 6 deletions

View file

@ -423,3 +423,62 @@ def test_pick_rocm_gfx_target_same_arch_multi_gpu(monkeypatch):
monkeypatch.delenv("CUDA_VISIBLE_DEVICES", raising = False)
monkeypatch.setenv("HIP_VISIBLE_DEVICES", "2")
assert _pick_rocm_gfx_target(probe_out) == "gfx1151"
# ---------------------------------------------------------------------------
# Fork release scan: Windows ROCm resolves lemonade by the requested tag
# ---------------------------------------------------------------------------
_resolve_release_asset_choice = getattr(_mod, "resolve_release_asset_choice", None)
_ApprovedReleaseChecksums = getattr(_mod, "ApprovedReleaseChecksums", None)
@pytest.mark.skipif(
_resolve_release_asset_choice is None or _ApprovedReleaseChecksums is None,
reason = "fork release planner not present on this branch",
)
def test_fork_scan_windows_rocm_resolves_lemonade_by_requested_tag():
"""The fork release scan pins llama_tag to per-release upstream tags
(b9457, ...) that lemonade's own tag series never contains, so the
lemonade lookup must use the requested tag ("latest") instead. Pinning
lemonade to the per-release tag 404s on every scanned release and a
Windows ROCm host ends in a rate-limited fatal instead of the lemonade
prebuilt."""
host = _make_rocm_host("gfx1151", windows = True)
# No windows-rocm artifact in the bundle, matching current fork releases.
bundle = _rocm_bundle("gfx1151", ["gfx1151"])
checksums = _ApprovedReleaseChecksums(
repo = "unslothai/llama.cpp",
release_tag = "v1.0",
upstream_tag = "b9457",
artifacts = {},
)
seen_urls: list[str] = []
def _fake_fetch(api_url, *args, **kwargs):
seen_urls.append(api_url)
if "lemonade-sdk" in api_url:
if api_url.endswith("/releases/latest"):
return _stub_lemonade_release()
raise RuntimeError(f"unexpected pinned lemonade fetch: {api_url}")
# ggml-org asset listing for the upstream HIP/CPU filename fallbacks.
return {"tag_name": "b9457", "assets": []}
with patch.object(_mod, "fetch_json", side_effect = _fake_fetch):
attempts = _resolve_release_asset_choice(
host,
"b9457", # concrete per-release upstream tag from the scan loop
bundle,
checksums,
requested_tag = "latest",
)
lemonade = [a for a in attempts if a.source_label == "lemonade"]
assert lemonade, f"lemonade attempt missing for Windows ROCm host; got {attempts}"
assert "gfx1151" in lemonade[0].name
assert any(
u.endswith("/releases/latest") for u in seen_urls
), f"lemonade was never resolved via /releases/latest; fetches: {seen_urls}"
assert not any(
"lemonade-sdk" in u and "/releases/tags/" in u for u in seen_urls
), f"lemonade lookup was pinned to the fork release tag: {seen_urls}"

View file

@ -3702,7 +3702,14 @@ def resolve_lemonade_rocm_choice(
)
def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice:
def resolve_upstream_asset_choice(
host: HostInfo, llama_tag: str, lemonade_tag: "str | None" = None
) -> AssetChoice:
# lemonade_tag: tag for the lemonade lookup only. The release scan pins
# llama_tag to per-release upstream tags (b9518, ...) that lemonade's own
# tag series (b1292, ...) never contains, so pinning lemonade to them 404s
# on every scanned release. Scan callers pass the original request
# (normally "latest") here; upstream asset names keep the pinned tag.
upstream_assets = github_release_assets(UPSTREAM_REPO, llama_tag)
if host.is_linux and host.is_x86_64:
# AMD ROCm: try upstream ROCm prebuilt first, then fall back to source build.
@ -3713,7 +3720,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
# Try lemonade-sdk per-GPU prebuilt first: these are built against
# specific gfx targets and bundle all required ROCm runtime libs.
lemonade_choice = resolve_lemonade_rocm_choice(
host, "ubuntu", "linux-rocm", llama_tag = llama_tag
host, "ubuntu", "linux-rocm", llama_tag = lemonade_tag or llama_tag
)
if lemonade_choice is not None:
return lemonade_choice
@ -3799,7 +3806,7 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
# AMD ROCm on Windows: try lemonade per-GPU prebuilt first, then upstream HIP
if host.has_rocm:
lemonade_choice = resolve_lemonade_rocm_choice(
host, "windows", "windows-hip", llama_tag = llama_tag
host, "windows", "windows-hip", llama_tag = lemonade_tag or llama_tag
)
if lemonade_choice is not None:
return lemonade_choice
@ -3858,12 +3865,14 @@ def resolve_upstream_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice
raise PrebuiltFallback(f"no prebuilt policy exists for {host.system} {host.machine}")
def resolve_asset_choice(host: HostInfo, llama_tag: str) -> AssetChoice:
def resolve_asset_choice(
host: HostInfo, llama_tag: str, lemonade_tag: "str | None" = None
) -> AssetChoice:
if host.is_linux and host.is_x86_64 and host.has_usable_nvidia:
raise PrebuiltFallback(
"Linux CUDA installs require a compatible published bundle; upstream fallback is not available"
)
return resolve_upstream_asset_choice(host, llama_tag)
return resolve_upstream_asset_choice(host, llama_tag, lemonade_tag = lemonade_tag)
def resolve_release_asset_choice(
@ -3871,6 +3880,7 @@ def resolve_release_asset_choice(
llama_tag: str,
release: PublishedReleaseBundle,
checksums: ApprovedReleaseChecksums,
requested_tag: "str | None" = None,
) -> list[AssetChoice]:
if host.is_windows and host.is_x86_64 and host.has_usable_nvidia:
torch_preference = detect_torch_cuda_runtime_preference(host)
@ -3926,7 +3936,10 @@ def resolve_release_asset_choice(
f"{release.repo}@{release.release_tag} {published_choice.name} ({exc})"
)
return apply_approved_hashes([resolve_asset_choice(host, llama_tag)], checksums)
return apply_approved_hashes(
[resolve_asset_choice(host, llama_tag, lemonade_tag = requested_tag)],
checksums,
)
def extract_archive(archive_path: Path, destination: Path) -> None:
@ -5726,6 +5739,7 @@ def _fork_manifest_release_plans(
resolved_tag,
bundle,
checksums,
requested_tag = requested_tag,
)
if not attempts:
raise PrebuiltFallback("no compatible prebuilt asset was found")