diff --git a/studio/backend/tests/test_lemonade_llamacpp_rocm_bins_mock.py b/studio/backend/tests/test_lemonade_llamacpp_rocm_bins_mock.py index 57bbc84fb0..7c6c514b8f 100644 --- a/studio/backend/tests/test_lemonade_llamacpp_rocm_bins_mock.py +++ b/studio/backend/tests/test_lemonade_llamacpp_rocm_bins_mock.py @@ -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" diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index bd8c90dc9c..bed7a27a63 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -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,