feat(local-models): pin Flash Next's downloads to the revisions the catalog describes
Downloads resolved files from main, so a re-upload on the publisher's side would change the bytes behind the catalog's exact sizes and the pricing measured from them. AssetFile carries a revision and the download plan resolves each file at it. Flash Next pins unsloth's weights and projector to 38bb39ee and ggml-org's head to 052beea; entries without a revision still follow main. Co-authored-by: Ryan Hill <38674843+RyanUnderhill@users.noreply.github.com>
This commit is contained in:
@@ -50,15 +50,18 @@
|
||||
"files": [
|
||||
{
|
||||
"path": "UD-IQ4_XS/Qwen3.8-Flash-Next-UD-IQ4_XS-00001-of-00003.gguf",
|
||||
"size_bytes": 10946624
|
||||
"size_bytes": 10946624,
|
||||
"revision": "38bb39ee97821de2c9009abb7e93950eec396e66"
|
||||
},
|
||||
{
|
||||
"path": "UD-IQ4_XS/Qwen3.8-Flash-Next-UD-IQ4_XS-00002-of-00003.gguf",
|
||||
"size_bytes": 49835229856
|
||||
"size_bytes": 49835229856,
|
||||
"revision": "38bb39ee97821de2c9009abb7e93950eec396e66"
|
||||
},
|
||||
{
|
||||
"path": "UD-IQ4_XS/Qwen3.8-Flash-Next-UD-IQ4_XS-00003-of-00003.gguf",
|
||||
"size_bytes": 43836407744
|
||||
"size_bytes": 43836407744,
|
||||
"revision": "38bb39ee97821de2c9009abb7e93950eec396e66"
|
||||
}
|
||||
],
|
||||
"lazy_bytes": 28800138240
|
||||
@@ -75,12 +78,14 @@
|
||||
"mmproj": {
|
||||
"path": "mmproj-BF16.gguf",
|
||||
"size_bytes": 907542944,
|
||||
"local": "mmproj-Qwen3.8-Flash-Next-BF16.gguf"
|
||||
"local": "mmproj-Qwen3.8-Flash-Next-BF16.gguf",
|
||||
"revision": "38bb39ee97821de2c9009abb7e93950eec396e66"
|
||||
},
|
||||
"mtp_head": {
|
||||
"repo": "ggml-org/Qwen3.8-Flash-Next-GGUF",
|
||||
"path": "mtp-Qwen3.8-Flash-Next-Q8_0.gguf",
|
||||
"size_bytes": 4137429280
|
||||
"size_bytes": 4137429280,
|
||||
"revision": "052beeaca7bec4a303e59cc7bc630c4f3a1b845d"
|
||||
},
|
||||
"min_engine": "b11370",
|
||||
"quality": 95,
|
||||
|
||||
@@ -39,6 +39,7 @@ class AssetFile:
|
||||
size_bytes: int
|
||||
local: str | None = None
|
||||
repo: str | None = None # HF repo when it is not the entry's own (a companion published elsewhere)
|
||||
revision: str = "main" # commit whose bytes size_bytes and the entry's pricing describe
|
||||
|
||||
@property
|
||||
def local_name(self) -> str:
|
||||
@@ -301,7 +302,7 @@ def _asset_from(d: "dict | None") -> "AssetFile | None":
|
||||
if not d:
|
||||
return None
|
||||
return AssetFile(path=d["path"], size_bytes=int(d["size_bytes"]), local=d.get("local"),
|
||||
repo=d.get("repo"))
|
||||
repo=d.get("repo"), revision=d.get("revision", "main"))
|
||||
|
||||
|
||||
# Scalar CatalogEntry fields parsed from JSON: key -> (coerce, default); None default = required.
|
||||
|
||||
@@ -397,8 +397,8 @@ def _assign_default(job: Dict[str, Any], model_id: str) -> None:
|
||||
|
||||
|
||||
# ── downloads: ranged parallel streams ───────────────────────
|
||||
def _hf_url(repo: str, path: str) -> str:
|
||||
return f"https://huggingface.co/{repo}/resolve/main/{path}"
|
||||
def _hf_url(repo: str, path: str, revision: str = "main") -> str:
|
||||
return f"https://huggingface.co/{repo}/resolve/{revision}/{path}"
|
||||
|
||||
|
||||
def _model_id_for(gguf: Path) -> str:
|
||||
@@ -418,8 +418,9 @@ def _variant_files_on_disk(model_id: str) -> "list[Path]":
|
||||
|
||||
def _download_plan(entry, variant) -> list:
|
||||
"""Everything a variant needs: split parts + mmproj/draft/MTP head assets, as (url, dest, bytes) tuples."""
|
||||
plan = [(_hf_url(entry.repo, a.path), bootstrap.models_dir() / a.local_name, a.size_bytes) for a in variant.files]
|
||||
plan += [(_hf_url(a.repo or entry.repo, a.path), bootstrap.assets_dir() / a.local_name, a.size_bytes)
|
||||
plan = [(_hf_url(entry.repo, a.path, a.revision), bootstrap.models_dir() / a.local_name, a.size_bytes)
|
||||
for a in variant.files]
|
||||
plan += [(_hf_url(a.repo or entry.repo, a.path, a.revision), bootstrap.assets_dir() / a.local_name, a.size_bytes)
|
||||
for a in (entry.mmproj, entry.draft, entry.mtp_head) if a is not None]
|
||||
return plan
|
||||
|
||||
|
||||
@@ -27,15 +27,15 @@ ENTRY = CatalogEntry(
|
||||
id="head-model", display_name="Head Model", description="", repo="org/Head-Model-GGUF",
|
||||
variants=(BUILD,), n_ctx_train=262144, full_layers=12, recurrent_layers=36, per_layer_f16=2048,
|
||||
moe=True, n_vocab=248320, mtp_draft_depth=3,
|
||||
mtp_head=AssetFile("mtp-Head-Model-Q8_0.gguf", 4 * GIB, repo="other/Head-Model-GGUF"))
|
||||
mtp_head=AssetFile("mtp-Head-Model-Q8_0.gguf", 4 * GIB, repo="other/Head-Model-GGUF", revision="0123abc"))
|
||||
|
||||
|
||||
def test_the_head_downloads_with_the_model_from_its_own_repo_and_is_priced_before_download():
|
||||
without_head = replace(ENTRY, mtp_head=None)
|
||||
|
||||
urls = [url for url, _, _ in local_models._download_plan(ENTRY, BUILD)]
|
||||
assert "https://huggingface.co/other/Head-Model-GGUF/resolve/main/mtp-Head-Model-Q8_0.gguf" in urls
|
||||
assert all("/org/Head-Model-GGUF/" in url for url in urls[:2])
|
||||
assert "https://huggingface.co/other/Head-Model-GGUF/resolve/0123abc/mtp-Head-Model-Q8_0.gguf" in urls
|
||||
assert all("/org/Head-Model-GGUF/resolve/main/" in url for url in urls[:2])
|
||||
assert ENTRY.mtp_capable and not without_head.mtp_capable
|
||||
with_plan = ENTRY.launch_plan(BUILD, UNIFIED_128)
|
||||
without_plan = without_head.launch_plan(BUILD, UNIFIED_128)
|
||||
|
||||
Reference in New Issue
Block a user