-
-
Notifications
You must be signed in to change notification settings - Fork 7.1k
Studio: resolve llama.cpp prebuilts via the release-assets CDN to avoid GitHub API rate limits #7086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Studio: resolve llama.cpp prebuilts via the release-assets CDN to avoid GitHub API rate limits #7086
Changes from all commits
ccd72b7
759e1db
323fa6f
1d01768
5b93b90
9a62284
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2355,10 +2355,9 @@ def pinned_published_release_bundle( | |||||||||||||||||||||||
| return bundle | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def validated_checksums_for_bundle( | ||||||||||||||||||||||||
| repo: str, bundle: PublishedReleaseBundle | ||||||||||||||||||||||||
| def _validate_checksums_against_bundle( | ||||||||||||||||||||||||
| repo: str, bundle: PublishedReleaseBundle, checksums: ApprovedReleaseChecksums | ||||||||||||||||||||||||
| ) -> ApprovedReleaseChecksums: | ||||||||||||||||||||||||
| checksums = load_approved_release_checksums(repo, bundle.release_tag) | ||||||||||||||||||||||||
| manifest_hash = checksums.artifacts.get(bundle.manifest_asset_name) | ||||||||||||||||||||||||
| if manifest_hash is not None and bundle.manifest_sha256 is not None: | ||||||||||||||||||||||||
| if manifest_hash.sha256 != bundle.manifest_sha256: | ||||||||||||||||||||||||
|
|
@@ -2382,6 +2381,129 @@ def validated_checksums_for_bundle( | |||||||||||||||||||||||
| return checksums | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def validated_checksums_for_bundle( | ||||||||||||||||||||||||
| repo: str, bundle: PublishedReleaseBundle | ||||||||||||||||||||||||
| ) -> ApprovedReleaseChecksums: | ||||||||||||||||||||||||
| checksums = load_approved_release_checksums(repo, bundle.release_tag) | ||||||||||||||||||||||||
| return _validate_checksums_against_bundle(repo, bundle, checksums) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _download_host_resolve_enabled() -> bool: | ||||||||||||||||||||||||
| """Escape hatch to force the legacy GitHub API path instead of the | ||||||||||||||||||||||||
| download-host fast path (which avoids the api.github.com rate limit).""" | ||||||||||||||||||||||||
| return os.environ.get( | ||||||||||||||||||||||||
| "UNSLOTH_LLAMA_DISABLE_DOWNLOAD_HOST_RESOLVE", "" | ||||||||||||||||||||||||
| ).strip().lower() not in {"1", "true", "yes", "on"} | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _release_asset_download_url(repo: str, tag: str, asset_name: str) -> str: | ||||||||||||||||||||||||
| """Tag-pinned asset URL on the release-assets CDN (not api.github.com, so no | ||||||||||||||||||||||||
| rate limit).""" | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| f"https://github.com/{urllib.parse.quote(repo, safe = '/')}/releases/download/" | ||||||||||||||||||||||||
| f"{urllib.parse.quote(tag, safe = '')}/" | ||||||||||||||||||||||||
| f"{urllib.parse.quote(asset_name, safe = '')}" | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _download_host_latest_release_tag(repo: str) -> str | None: | ||||||||||||||||||||||||
| """Authoritative latest tag from GitHub's /releases/latest redirect target | ||||||||||||||||||||||||
| (github.com, no api.github.com rate limit); the fast path pins URLs to it rather | ||||||||||||||||||||||||
| than the checksum asset's self-reported release_tag. /releases/latest resolves by | ||||||||||||||||||||||||
| created_at/make_latest, which can lag the published_at newest the freshness | ||||||||||||||||||||||||
| detection uses. None on 404 so the caller falls back to the API.""" | ||||||||||||||||||||||||
| url = f"https://github.com/{urllib.parse.quote(repo, safe = '/')}/releases/latest" | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When GitHub's Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For default Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A |
||||||||||||||||||||||||
| request = urllib.request.Request( | ||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||
| method = "HEAD", | ||||||||||||||||||||||||
| headers = {"User-Agent": "unsloth-studio-llama-prebuilt"}, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| with _URL_OPENER.open(request, timeout = 30) as response: | ||||||||||||||||||||||||
| final_url = response.geturl() | ||||||||||||||||||||||||
| except urllib.error.HTTPError as exc: | ||||||||||||||||||||||||
| if exc.code == 404: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||
| marker = "/releases/tag/" | ||||||||||||||||||||||||
| index = final_url.find(marker) | ||||||||||||||||||||||||
| if index == -1: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| tag = urllib.parse.unquote(final_url[index + len(marker) :]).strip("/") | ||||||||||||||||||||||||
| return tag or None | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _fetch_download_host_json(url: str) -> Any: | ||||||||||||||||||||||||
| # Public CDN asset: plain unauthenticated GET, not the rate-limited API. | ||||||||||||||||||||||||
| data = download_bytes( | ||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||
| timeout = 30, | ||||||||||||||||||||||||
| headers = {"User-Agent": "unsloth-studio-llama-prebuilt"}, | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| return json.loads(data.decode("utf-8")) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _download_host_resolved_release(repo: str) -> ResolvedPublishedRelease | None: | ||||||||||||||||||||||||
| """Resolve the latest fork release from the download host with zero | ||||||||||||||||||||||||
| api.github.com calls, reusing the API path's parsing and validation. The latest | ||||||||||||||||||||||||
| tag is the authoritative /releases/latest redirect tag, and the checksum asset's | ||||||||||||||||||||||||
| self-reported release_tag is cross-checked against it. Returns None (caller falls | ||||||||||||||||||||||||
| back to the API) on a missing JSON asset or a tag mismatch.""" | ||||||||||||||||||||||||
| release_tag = _download_host_latest_release_tag(repo) | ||||||||||||||||||||||||
| if not release_tag: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| sha_url = _release_asset_download_url(repo, release_tag, DEFAULT_PUBLISHED_SHA256_ASSET) | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| sha_payload = _fetch_download_host_json(sha_url) | ||||||||||||||||||||||||
| except urllib.error.HTTPError as exc: | ||||||||||||||||||||||||
| if exc.code == 404: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||
| if not isinstance(sha_payload, dict): | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| # Cross-check the asset's self-reported release_tag against the authoritative | ||||||||||||||||||||||||
| # redirect tag: parse_approved_release_checksums raises on a mismatch. | ||||||||||||||||||||||||
| checksums = parse_approved_release_checksums(repo, release_tag, sha_payload) | ||||||||||||||||||||||||
| # Synthesize the API release payload with tag-pinned CDN URLs for every named | ||||||||||||||||||||||||
| # asset; parse_published_release_bundle then reads the manifest, still no API. | ||||||||||||||||||||||||
| asset_names = set(checksums.artifacts) | { | ||||||||||||||||||||||||
| DEFAULT_PUBLISHED_MANIFEST_ASSET, | ||||||||||||||||||||||||
| DEFAULT_PUBLISHED_SHA256_ASSET, | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| synthetic_release: dict[str, Any] = { | ||||||||||||||||||||||||
| "tag_name": release_tag, | ||||||||||||||||||||||||
| "draft": False, | ||||||||||||||||||||||||
| "prerelease": False, | ||||||||||||||||||||||||
| "assets": [ | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "name": name, | ||||||||||||||||||||||||
| "browser_download_url": _release_asset_download_url(repo, release_tag, name), | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+2477
to
+2481
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the latest manifest/checksum JSON advertises an asset whose upload failed or was deleted, this synthetic release map still invents a Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only triggers on a malformed or incomplete release (the manifest or checksum JSON names an asset that was never uploaded or was deleted). It cannot install an unverified binary: every download is sha256-checked ( |
||||||||||||||||||||||||
| for name in sorted(asset_names) | ||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| bundle = parse_published_release_bundle(repo, synthetic_release) | ||||||||||||||||||||||||
| except urllib.error.HTTPError as exc: | ||||||||||||||||||||||||
| # In-progress release: the checksum asset can land before the manifest; | ||||||||||||||||||||||||
| # treat a manifest 404 like the sha256 404 above and fall back to the API. | ||||||||||||||||||||||||
| if exc.code == 404: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||
| if bundle is None: | ||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||
| # A manifest artifact can be keyed in the checksum JSON under an upstream-tag | ||||||||||||||||||||||||
| # alias, so add a tag-pinned URL for any manifest artifact missing above (the | ||||||||||||||||||||||||
| # API path gets these from the real asset list); sha256 is still verified. | ||||||||||||||||||||||||
| for artifact in bundle.artifacts: | ||||||||||||||||||||||||
| bundle.assets.setdefault( | ||||||||||||||||||||||||
| artifact.asset_name, | ||||||||||||||||||||||||
| _release_asset_download_url(repo, release_tag, artifact.asset_name), | ||||||||||||||||||||||||
|
Comment on lines
+2498
to
+2501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the latest release is partially published or the manifest contains a stale/typoed artifact, this adds a download URL for every manifest artifact even if that asset is not actually present on the GitHub release. The existing API path only exposes names from the release asset list, so selectors skip missing artifacts via Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I verified the sharper form of this (a partial-publish best-fit falling through to another in-release asset on the API path but not the fast path), and it is a real behavioral difference in theory, but it is unreachable on the fork's real release input, so it stays below the fix bar. Why it cannot be observed in practice:
Defense in depth if a future release tool ever changed that order:
Reproduced and bounded in an isolated sandbox: the API path skips the mid-upload best-fit and selects the second compatible in-release bundle while the fast path commits to the fabricated best-fit, and for a fully published release (the only state
Comment on lines
+2498
to
+2501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Here the fast path turns every artifact listed in the manifest into a presumed release asset, but the selection code treats Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I verified the sharper form of this (a partial-publish best-fit falling through to another in-release asset on the API path but not the fast path), and it is a real behavioral difference in theory, but it is unreachable on the fork's real release input, so it stays below the fix bar. Why it cannot be observed in practice:
Defense in depth if a future release tool ever changed that order:
Reproduced and bounded in an isolated sandbox: the API path skips the mid-upload best-fit and selects the second compatible in-release bundle while the fast path commits to the fabricated best-fit, and for a fully published release (the only state |
||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
Comment on lines
+2498
to
+2502
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the manifest is available before one of the binary assets is uploaded, or the manifest is stale, this unconditionally adds a tag-pinned URL for each manifest artifact even though the fast path never checked the release's real asset list. That bypasses the existing Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a real difference from the API path, but it is bounded to a transient in-progress-publish window and degrades to the same outcome, so it stays below the fix bar.
Verified in an isolated sandbox: the API path rejects the missing asset during planning while the fast path fabricates its URL (divergence reproduced), the fabricated URL 404s as a catchable The |
||||||||||||||||||||||||
| _validate_checksums_against_bundle(repo, bundle, checksums) | ||||||||||||||||||||||||
| return ResolvedPublishedRelease(bundle = bundle, checksums = checksums) | ||||||||||||||||||||||||
|
Comment on lines
+2494
to
+2504
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defensively wrap the call to
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 1d01768. The call to |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def published_release_matches_request(bundle: PublishedReleaseBundle, requested_ref: str) -> bool: | ||||||||||||||||||||||||
| if requested_ref == "latest": | ||||||||||||||||||||||||
| return True | ||||||||||||||||||||||||
|
|
@@ -2448,6 +2570,8 @@ def iter_resolved_published_releases( | |||||||||||||||||||||||
| requested_tag: str | None, | ||||||||||||||||||||||||
| published_repo: str, | ||||||||||||||||||||||||
| published_release_tag: str = "", | ||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||
| allow_download_host_fast_path: bool = True, | ||||||||||||||||||||||||
| ) -> Iterable[ResolvedPublishedRelease]: | ||||||||||||||||||||||||
| repo = published_repo or DEFAULT_PUBLISHED_REPO | ||||||||||||||||||||||||
| normalized_requested = normalized_requested_llama_tag(requested_tag) | ||||||||||||||||||||||||
|
|
@@ -2466,6 +2590,29 @@ def iter_resolved_published_releases( | |||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| return | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Fast path: resolve the fork's latest release from the download host (no | ||||||||||||||||||||||||
| # api.github.com rate limit). It surfaces only the single latest release, so the | ||||||||||||||||||||||||
| # caller disables it when the multi-release walk-back is needed (macOS skipping | ||||||||||||||||||||||||
| # too-new prebuilts); a broken latest then drops to source build, not an older | ||||||||||||||||||||||||
| # release. Any rejection/network error is non-fatal and falls through to the API. | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| allow_download_host_fast_path | ||||||||||||||||||||||||
| and repo == DEFAULT_PUBLISHED_REPO | ||||||||||||||||||||||||
| and normalized_requested == "latest" | ||||||||||||||||||||||||
| and _download_host_resolve_enabled() | ||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| resolved = _download_host_resolved_release(repo) | ||||||||||||||||||||||||
| except PrebuiltFallback as exc: | ||||||||||||||||||||||||
| log(f"download-host latest release rejected for {repo} ({exc}); trying GitHub API") | ||||||||||||||||||||||||
| resolved = None | ||||||||||||||||||||||||
| except Exception as exc: | ||||||||||||||||||||||||
| log(f"download-host latest resolve unavailable for {repo} ({exc}); trying GitHub API") | ||||||||||||||||||||||||
| resolved = None | ||||||||||||||||||||||||
| if resolved is not None: | ||||||||||||||||||||||||
| yield resolved | ||||||||||||||||||||||||
| return | ||||||||||||||||||||||||
|
Comment on lines
+2613
to
+2614
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. macOS is already excluded from the fast path (the caller passes
Comment on lines
+2612
to
+2614
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the CDN-resolved latest release exists but has no usable asset for the current non-macOS host (for example a Linux CUDA host whose SM/runtime is not covered, or a release with no fork bundle), this early Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the intended, documented tradeoff (see the comments on
Comment on lines
+2612
to
+2614
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the CDN fast path successfully resolves the latest release but that release is not installable for the host (for example a Linux/Windows Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the intended, documented tradeoff (see the comments on |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| matched_any = False | ||||||||||||||||||||||||
| skipped_invalid = 0 | ||||||||||||||||||||||||
| yielded_valid = False | ||||||||||||||||||||||||
|
|
@@ -6190,6 +6337,9 @@ def _fork_manifest_release_plans( | |||||||||||||||||||||||
| llama_tag, | ||||||||||||||||||||||||
| published_repo, | ||||||||||||||||||||||||
| published_release_tag, | ||||||||||||||||||||||||
| # macOS relies on the multi-release walk-back to skip too-new prebuilts, | ||||||||||||||||||||||||
| # which the single-latest download-host path cannot provide. | ||||||||||||||||||||||||
| allow_download_host_fast_path = not host.is_macos, | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For non-macOS Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the intended, documented tradeoff (see the comments on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the intended, documented tradeoff (see the comments on |
||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||
| bundle = resolved_release.bundle | ||||||||||||||||||||||||
| checksums = resolved_release.checksums | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On default-repo
latestinstalls this pins the fast path to GitHub's/releases/latestredirect, butstudio/backend/utils/llama_cpp_freshness.pystill resolves latest as the non-draft/non-prerelease release with the maximumpublished_atand explicitly avoids that pointer. When GitHub's latest pointer lags or is manually set,--resolve-prebuilt/install can install the older redirected release while the update checker reports the newer published release, recreating the sticky update/downgrade behavior the freshness code is designed to avoid.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A
published_at-correct resolution only exists in the api.github.com response, so it cannot be reproduced from the CDN in a zero-API path. The fast path pins to the verified/releases/latestredirect tag; the residualcreated_at/make_latestvspublished_atdivergence is called out in the_download_host_latest_release_tagdocstring and is mitigated: today/releases/latestforunslothai/llama.cppequals thepublished_atnewest (GitHub's semver tiebreak), andis_behind()'s base-build guard prevents a divergence from surfacing as the downgrade / sticky "update available" banner the freshness code guards against.UNSLOTH_LLAMA_DISABLE_DOWNLOAD_HOST_RESOLVE=1forces thepublished_atAPI path if a future release stream ever makes it matter.