Studio: gate ROCm hosts out of CPU prebuilt and require offload signal in smoke-test

- direct_upstream_release_plan: Linux x86_64 and arm64 CPU branches now also
  require not host.has_rocm, so a ROCm host whose HIP prebuilt is missing or
  rejected never silently takes a CPU upstream tarball as a 'success'.
- setup.ps1: a Windows ROCm host with no usable HIP prebuilt and no HIP source
  build path is now marked LlamaCppDegraded so the CPU last-resort installs a
  clearly labelled CPU build instead of a 'built' install that runs on CPU.
- validate_server gains require_gpu_signal; smoke_test_server_binary sets it for
  GPU install kinds so the '0 = offload confirmed' CLI contract treats a
  no-GPU-signal log as inconclusive (EXIT_ERROR) rather than a silent pass.
- run_smoke_spoof + unit test cover the new no-signal-is-inconclusive contract.
This commit is contained in:
danielhanchen 2026-06-01 16:30:50 +00:00
commit 889e889d9c
4 changed files with 58 additions and 8 deletions

View file

@ -1611,7 +1611,12 @@ def direct_upstream_release_plan(
install_kind = "macos-x64",
)
)
elif host.is_linux and host.is_x86_64 and not host.has_usable_nvidia:
elif (
host.is_linux
and host.is_x86_64
and not host.has_usable_nvidia
and not host.has_rocm
):
asset_name = f"llama-{release_tag}-bin-ubuntu-x64.tar.gz"
asset_url = assets.get(asset_name)
if asset_url:
@ -1625,7 +1630,12 @@ def direct_upstream_release_plan(
install_kind = "linux-cpu",
)
)
elif host.is_linux and host.is_arm64 and not host.has_usable_nvidia:
elif (
host.is_linux
and host.is_arm64
and not host.has_usable_nvidia
and not host.has_rocm
):
# Upstream ggml-org/llama.cpp ships llama-bNNNN-bin-ubuntu-arm64.tar.gz
# (visible in the b9334 release manifest). Without this branch the
# selector returned 0 attempts and the installer fell back to a
@ -5766,6 +5776,7 @@ def validate_server(
*,
runtime_line: str | None = None,
install_kind: str | None = None,
require_gpu_signal: bool = False,
) -> None:
last_failure: PrebuiltFallback | None = None
for port_attempt in range(1, SERVER_PORT_BIND_ATTEMPTS + 1):
@ -5908,10 +5919,10 @@ def validate_server(
# the next bundle / source build instead of stopping here.
if _enable_gpu_layers:
log_handle.flush()
if (
server_log_shows_gpu_offload(read_full_log(log_path))
is False
):
offload = server_log_shows_gpu_offload(
read_full_log(log_path)
)
if offload is False:
raise GpuOffloadFailure(
"llama-server served a completion but loaded the "
"model entirely on CPU despite GPU offload being "
@ -5924,6 +5935,17 @@ def validate_server(
"(unslothai/unsloth#5807, #5106):\n"
+ read_log_excerpt(log_path)
)
# No GPU signal: install validation stays conservative
# (never reject on no evidence), but --smoke-test sets
# require_gpu_signal so its "0 = offload confirmed"
# contract does not pass an unproven log.
if offload is None and require_gpu_signal:
raise PrebuiltFallback(
"llama-server served a completion but its startup "
"log carried no GPU-offload signal; smoke-test "
"result is inconclusive:\n"
+ read_log_excerpt(log_path)
)
return
finally:
if process is not None and process.poll() is None:
@ -6925,6 +6947,9 @@ def smoke_test_server_binary(
Path(install_dir).expanduser().resolve() if install_dir else server_path.parent
)
resolved_kind = install_kind or resolve_smoke_test_install_kind(host)
# For a GPU kind, the CLI contract is "0 = offload confirmed", so an
# inconclusive (no-signal) log must not pass -- require a positive signal.
require_signal = resolved_kind in _GPU_INSTALL_KINDS
if probe:
probe_path = Path(probe).expanduser().resolve()
if not probe_path.exists():
@ -6935,6 +6960,7 @@ def smoke_test_server_binary(
host,
resolved_install_dir,
install_kind = resolved_kind,
require_gpu_signal = require_signal,
)
else:
with tempfile.TemporaryDirectory(prefix = "unsloth-llama-smoke-") as tmp:
@ -6948,6 +6974,7 @@ def smoke_test_server_binary(
host,
resolved_install_dir,
install_kind = resolved_kind,
require_gpu_signal = require_signal,
)
return resolved_kind

View file

@ -2667,6 +2667,14 @@ if (-not $NeedLlamaSourceBuild) {
substep "GGUF inference and export will not be available." "Yellow"
substep "Install CMake from https://cmake.org/download/ and re-run setup." "Yellow"
$script:LlamaCppDegraded = $true
} elseif ($HasROCm -and -not $HasNvidiaSmi) {
# Windows has no HIP source-build path, so a ROCm host whose HIP prebuilt was
# missing or rejected must not silently CPU-source-build. Mark degraded so
# the CPU-prebuilt last resort below installs a clearly labelled CPU build
# instead of a "built" install that runs on CPU (#5807).
Write-Host ""
step "llama.cpp" "no usable HIP prebuilt and no Windows HIP source build; using CPU prebuilt" "Yellow"
$script:LlamaCppDegraded = $true
} else {
# A source build is committed here. The CUDA toolkit is only needed now, so
# resolve (and winget-install if needed) it lazily, failing fast if no

View file

@ -84,7 +84,7 @@ def main() -> int:
("cuda", GPU_KIND, 0, "GPU binary tagged GPU is accepted"),
("cuda_buffer", GPU_KIND, 0, "GPU buffer-format binary is accepted"),
("cpu", CPU_KIND, 0, "CPU binary tagged CPU is not gated"),
("no_signal", GPU_KIND, 0, "no-signal log is not rejected"),
("no_signal", GPU_KIND, 1, "no-signal GPU log is inconclusive (exit 1)"),
]
for mode, kind, expected, label in cases:
rc = run_smoke(wrapper, probe, kind, mode)

View file

@ -408,11 +408,26 @@ def test_cpu_kind_not_gpu_gated(patched_server, tmp_path):
def test_gpu_intent_no_signal_accepted(patched_server, tmp_path):
# No buffer/device signal -> conservative: do not reject on no evidence.
# No buffer/device signal -> conservative: do not reject on no evidence
# (plain install validation; require_gpu_signal defaults False).
patched_server(NO_SIGNAL_LOG)
_run_validate(tmp_path, nvidia_host(), "linux-cuda") # no raise
def test_smoke_test_no_signal_gpu_is_inconclusive(patched_server, tmp_path):
# The smoke-test CLI sets require_gpu_signal, so a no-signal GPU log is
# inconclusive (PrebuiltFallback -> EXIT_ERROR), not a silent pass.
patched_server(NO_SIGNAL_LOG)
server = tmp_path / "llama-server"
server.write_text("#!/bin/sh\n")
probe = tmp_path / "probe.gguf"
probe.write_bytes(b"GGUF")
with pytest.raises(PrebuiltFallback):
smoke_test_server_binary(
str(server), nvidia_host(), install_dir = str(tmp_path), probe = str(probe)
)
def test_rocm_cpu_only_rejected(patched_server, tmp_path):
patched_server(CPU_ONLY_DEVICE_INFO_LOG)
with pytest.raises(PrebuiltFallback):