fix: deduplicate lemonade ROCm prebuilt selection log (#6021)

* fix: deduplicate lemonade ROCm prebuilt selection log

resolve_lemonade_rocm_choice() is called twice per install (direct
planner + resolve_upstream_asset_choice). The API fetch is already
memoised via _fetch_lemonade_release_cached but the selection log
lines were still emitted on both calls, printing the 'trying
lemonade-sdk ROCm prebuilt' banner and hash-manifest NOTE twice.

Add _lemonade_selection_logged set keyed on (gfx_target, asset_name)
and guard the two log() calls behind a membership check so they print
exactly once per process regardless of call count.

Also extend the _clear_lemonade_release_cache test fixture to clear
the new set between tests to prevent cross-test state bleed.

Fixes #6020

* fix: write log() output to stdout to avoid PowerShell NativeCommandError

On Windows, PowerShell treats any stderr output from a native process as
an error record and prefixes it with 'python.exe :' and sets the
ErrorId to NativeCommandError. Since log() wrote to sys.stderr, every
[llama-prebuilt] status line triggered this, making normal progress
output look like errors in the installer console.

Switch log() to sys.stdout. The download progress bar (DownloadProgress)
retains its stderr/tty logic unchanged -- that path is for interactive
terminal rendering, not status logging.

* fix: remove redundant 'or ""' in lemonade log_key

host.rocm_gfx_target is already guaranteed truthy by the early
return at the top of resolve_lemonade_rocm_choice. The fallback
was dead code.

* Keep resolver stdout machine-readable, route install logs to stdout

log() sending everything to stdout breaks the resolver modes: setup.sh
json.load()s the whole stdout, so one helper log line (network retry,
release-tag scan) corrupts the parse and silently drops back to building
"latest". Default log() to stderr and flip to stdout only on the install
path, where PowerShell otherwise renders stderr as NativeCommandError
noise. Also tighten the lemonade dedup comments.

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
Leo Borcherding 2026-06-11 22:14:22 -05:00 committed by GitHub
commit 84b42c9283
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 15 deletions

View file

@ -32,14 +32,19 @@ if resolve_lemonade_rocm_choice is None or _LEMONADE_GFX_FAMILIES is None:
@pytest.fixture(autouse = True)
def _clear_lemonade_release_cache():
"""Prevent cross-test pollution of the lemonade release lru_cache when
future tests vary the fetch_json mock return value."""
"""Prevent cross-test pollution of the lemonade release lru_cache and
selection-log dedup set when tests vary the fetch_json mock return value."""
_cache = getattr(_mod, "_fetch_lemonade_release_cached", None)
_logged: set | None = getattr(_mod, "_lemonade_selection_logged", None)
if _cache is not None and hasattr(_cache, "cache_clear"):
_cache.cache_clear()
if _logged is not None:
_logged.clear()
yield
if _cache is not None and hasattr(_cache, "cache_clear"):
_cache.cache_clear()
if _logged is not None:
_logged.clear()
_STUB_TAG = "b1262"

View file

@ -460,8 +460,14 @@ def is_busy_lock_error(exc: BaseException) -> bool:
return False
# Status logs default to stderr so resolver modes keep stdout machine-readable
# (setup.sh json.load()s the whole stdout). main() flips this for the install
# path, where PowerShell otherwise renders stderr as NativeCommandError noise.
_LOG_TO_STDOUT = False
def log(message: str) -> None:
print(f"[llama-prebuilt] {message}", file = sys.stderr)
print(f"[llama-prebuilt] {message}", file = sys.stdout if _LOG_TO_STDOUT else sys.stderr)
def log_lines(lines: Iterable[str]) -> None:
@ -3928,6 +3934,12 @@ def _is_trusted_github_release_url(url: str, expected_repo: str) -> bool:
return False
# (gfx_target, asset_name) pairs already logged. resolve_lemonade_rocm_choice()
# runs twice per install (direct planner + resolve_upstream_asset_choice), so
# this stops its selection banner and hash-manifest NOTE printing twice.
_lemonade_selection_logged: "set[tuple[str, str]]" = set()
@functools.lru_cache(maxsize = 8)
def _fetch_lemonade_release_cached(api_url: str, llama_tag: str) -> "dict | None":
"""Cached wrapper around fetch_json for lemonade release lookups.
@ -4034,18 +4046,22 @@ def resolve_lemonade_rocm_choice(
# Note: lemonade tags Linux assets with "ubuntu" but the binary is a
# generic glibc build that runs on any distro (Arch, Fedora, ...), so
# this attempt is selected for all Linux ROCm hosts, not just Ubuntu.
log(
f"AMD GPU {host.rocm_gfx_target!r} ({gfx_family}) -- "
f"trying lemonade-sdk ROCm prebuilt {asset_name} "
f"(works on any glibc Linux, not just Ubuntu)"
)
log(
f"NOTE: lemonade-sdk/llamacpp-rocm releases are not covered by the "
f"Unsloth approved-hash manifest; download integrity relies on "
f"functional validation (llama-bench / llama-server smoke tests) "
f"after extraction. Set UNSLOTH_DISABLE_LEMONADE_ROCM=1 to skip "
f"lemonade and fall back to the upstream HIP build path."
)
# Log once per (gfx_target, asset); see _lemonade_selection_logged.
log_key = (host.rocm_gfx_target, asset_name)
if log_key not in _lemonade_selection_logged:
_lemonade_selection_logged.add(log_key)
log(
f"AMD GPU {host.rocm_gfx_target!r} ({gfx_family}) -- "
f"trying lemonade-sdk ROCm prebuilt {asset_name} "
f"(works on any glibc Linux, not just Ubuntu)"
)
log(
f"NOTE: lemonade-sdk/llamacpp-rocm releases are not covered by the "
f"Unsloth approved-hash manifest; download integrity relies on "
f"functional validation (llama-bench / llama-server smoke tests) "
f"after extraction. Set UNSLOTH_DISABLE_LEMONADE_ROCM=1 to skip "
f"lemonade and fall back to the upstream HIP build path."
)
return AssetChoice(
repo = LEMONADE_ROCM_REPO,
tag = release_tag,
@ -6963,6 +6979,9 @@ def main() -> int:
raise SystemExit(
"install_llama_prebuilt.py: --install-dir is required unless --resolve-llama-tag, --resolve-install-tag, or --resolve-source-build is used"
)
# Install path only: route status logs to stdout (see _LOG_TO_STDOUT note).
global _LOG_TO_STDOUT
_LOG_TO_STDOUT = True
install_prebuilt(
install_dir = Path(args.install_dir).expanduser().resolve(),
llama_tag = args.llama_tag,