diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index 22fab82923..6c3be0e09f 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -1095,6 +1095,20 @@ def commit_source_archive_urls(repo: str, source_commit: str) -> list[str]: ] +def release_asset_download_url( + repo: str | None, release_tag: str | None, asset_name: str | None +) -> str | None: + """Direct download URL for a release asset, or None if any part is missing. + A mix build's merged commit is never pushed, so its source tree is only + reachable as this asset (codeload would 404 on the merge commit).""" + if not repo or not release_tag or not asset_name: + return None + return ( + f"https://github.com/{repo}/releases/download/" + f"{urllib.parse.quote(release_tag, safe = '')}/{urllib.parse.quote(asset_name, safe = '')}" + ) + + def github_release_assets(repo: str, tag: str) -> dict[str, str]: payload = fetch_json( f"https://api.github.com/repos/{repo}/releases/tags/{urllib.parse.quote(tag, safe = '')}" @@ -4495,13 +4509,17 @@ def hydrate_source_tree( expected_sha256: str | None, source_label: str | None = None, exact_source: bool = False, + asset_url: str | None = None, ) -> None: archive_path = work_dir / f"llama.cpp-source-{source_ref}.tar.gz" - source_urls = ( + repo_urls = ( commit_source_archive_urls(source_repo, source_ref) if exact_source else upstream_source_archive_urls(source_ref) ) + # Prefer the published release asset (the only copy of a mix build's merged + # tree); fall back to codeload/archive for vanilla builds whose commit is real. + source_urls = ([asset_url] if asset_url else []) + repo_urls label = source_label or f"llama.cpp source tree for {source_ref}" extract_dir = Path(tempfile.mkdtemp(prefix = "source-extract-", dir = work_dir)) @@ -6404,6 +6422,15 @@ def validate_prebuilt_choice( source_repo, source_ref, source_archive, exact_source = preferred_source_archive( approved_checksums, llama_tag ) + # For an exact (mix) source the merge commit lives only in the release asset, + # not in any repo, so fetch the asset directly; codeload stays the fallback. + asset_url = ( + release_asset_download_url( + approved_checksums.repo, approved_checksums.release_tag, source_archive.asset_name + ) + if exact_source and source_archive is not None + else None + ) if exact_source: log(f"hydrating exact llama.cpp source for {source_repo}@{source_ref} into {install_dir}") else: @@ -6420,6 +6447,7 @@ def validate_prebuilt_choice( else f"llama.cpp source tree for {llama_tag}" ), exact_source = exact_source, + asset_url = asset_url, ) log(f"overlaying prebuilt bundle {choice.name} into {install_dir}") server_path, quantize_path = install_from_archives(choice, host, install_dir, work_dir) diff --git a/tests/studio/install/test_install_llama_prebuilt_logic.py b/tests/studio/install/test_install_llama_prebuilt_logic.py index 057612d27b..7ca87aa8c4 100644 --- a/tests/studio/install/test_install_llama_prebuilt_logic.py +++ b/tests/studio/install/test_install_llama_prebuilt_logic.py @@ -206,6 +206,110 @@ def test_hydrate_source_tree_extracts_upstream_archive_contents( assert not (install_dir / f"llama.cpp-{upstream_tag}").exists() +def test_release_asset_download_url(): + fn = INSTALL_LLAMA_PREBUILT.release_asset_download_url + assert fn( + "unslothai/llama.cpp", "b9000-mix-abc1234", "llama.cpp-source-commit-deadbeef.tar.gz" + ) == ( + "https://github.com/unslothai/llama.cpp/releases/download/" + "b9000-mix-abc1234/llama.cpp-source-commit-deadbeef.tar.gz" + ) + # Any missing component -> None (no asset url, caller falls back to codeload). + assert fn(None, "b9000", "x.tar.gz") is None + assert fn("unslothai/llama.cpp", None, "x.tar.gz") is None + assert fn("unslothai/llama.cpp", "b9000", None) is None + + +def _mk_source_tarball(path: Path, tag: str) -> None: + with tarfile.open(path, "w:gz") as archive: + add_bytes_to_tar( + archive, f"llama.cpp-{tag}/CMakeLists.txt", b"cmake_minimum_required(VERSION 3.14)\n" + ) + add_bytes_to_tar( + archive, + f"llama.cpp-{tag}/convert_hf_to_gguf.py", + b"#!/usr/bin/env python3\nimport gguf\n", + ) + add_bytes_to_tar(archive, f"llama.cpp-{tag}/gguf-py/gguf/__init__.py", b"__all__ = []\n") + + +def test_hydrate_source_tree_prefers_release_asset_for_mix( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + # A mix build's merge commit is in no repo, so the codeload/archive URLs 404. + # hydrate must fetch the release asset and never touch codeload. + commit = "a" * 40 + archive_path = tmp_path / "merged-source.tar.gz" + _mk_source_tarball(archive_path, f"b9000-mix-{commit[:7]}") + asset_url = INSTALL_LLAMA_PREBUILT.release_asset_download_url( + "unslothai/llama.cpp", "b9000-mix-abc1234", f"llama.cpp-source-commit-{commit}.tar.gz" + ) + codeload_urls = set( + INSTALL_LLAMA_PREBUILT.commit_source_archive_urls("unslothai/llama.cpp", commit) + ) + seen = [] + + def fake_download_file(url: str, destination: Path) -> None: + seen.append(url) + if url in codeload_urls: + raise AssertionError("codeload was hit even though the release asset was available") + assert url == asset_url + destination.write_bytes(archive_path.read_bytes()) + + monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "download_file", fake_download_file) + + install_dir = tmp_path / "install" + work_dir = tmp_path / "work" + work_dir.mkdir() + hydrate_source_tree( + commit, + install_dir, + work_dir, + source_repo = "unslothai/llama.cpp", + expected_sha256 = sha256_file(archive_path), + exact_source = True, + asset_url = asset_url, + ) + assert seen == [asset_url] + assert (install_dir / "CMakeLists.txt").exists() + assert (install_dir / "convert_hf_to_gguf.py").exists() + + +def test_hydrate_source_tree_falls_back_to_codeload_when_asset_missing( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + # If the release asset 404s, fall back to codeload/archive (vanilla path). + commit = "b" * 40 + archive_path = tmp_path / "vanilla-source.tar.gz" + _mk_source_tarball(archive_path, f"commit-{commit[:7]}") + asset_url = INSTALL_LLAMA_PREBUILT.release_asset_download_url( + "unslothai/llama.cpp", "b9000", f"llama.cpp-source-commit-{commit}.tar.gz" + ) + codeload_urls = INSTALL_LLAMA_PREBUILT.commit_source_archive_urls("unslothai/llama.cpp", commit) + + def fake_download_file(url: str, destination: Path) -> None: + if url == asset_url: + raise RuntimeError("404 Not Found") + assert url in codeload_urls + destination.write_bytes(archive_path.read_bytes()) + + monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "download_file", fake_download_file) + + install_dir = tmp_path / "install" + work_dir = tmp_path / "work" + work_dir.mkdir() + hydrate_source_tree( + commit, + install_dir, + work_dir, + source_repo = "unslothai/llama.cpp", + expected_sha256 = sha256_file(archive_path), + exact_source = True, + asset_url = asset_url, + ) + assert (install_dir / "CMakeLists.txt").exists() + + def test_validate_prebuilt_choice_creates_repo_shaped_linux_install( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ):