GGUF: read multi-shard checkpoints - #154
Open
vcruz305 wants to merge 1 commit into
Open
Conversation
Large GGUF models ship split (-00001-of-000NN), and is_gguf_path() accepted a single file,
so every one of them was unopenable regardless of architecture or quant support.
reader.py now resolves any shard, or a directory containing a split set, to the ordered
list; reads metadata, architecture and tokenizer from shard 1; and aggregates the tensor
tables across shards in order. Single-file paths take the same code path as before.
Three properties of llama.cpp's convention, verified against real shard headers from
Hy3-GGUF, Qwen3-235B-A22B-GGUF and Kimi-K2-Instruct-GGUF:
* split.no is 0-based while the filenames are 1-based, so -00002-of-00003 carries
split.no = 1
* split.tensors.count is the TOTAL across every shard, not this shard's count
* only shard 1 carries general.architecture and the tokenizer; later shards hold exactly
three split.* keys
An incomplete set raises with the missing indices named rather than loading a partial
model, since a truncated download is the common failure and silently serving most of a
model is the worst outcome.
Validated end to end against a genuine 3-shard split of a 16GB checkpoint produced by
llama-gguf-split: 573 weights reconciled against the module, then generation matching the
unsplit file. Tests write GGUF bytes directly rather than depending on gguf.GGUFWriter's
API, and include a fixture guard asserting shard 2 really lacks general.architecture so the
shard-1-resolution test cannot pass vacuously.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Splitting this out of #131 so it can be reviewed on its own. One file plus tests, no architecture work attached.
is_gguf_path()accepts a single.gguf. Every large model ships split (-00001-of-000NN), so none of them can be opened today regardless of architecture or quant support. That makes this the availability blocker rather than a convenience: quant types being reachable does not help if the files those quants live in cannot be opened.--modelnow accepts any shard of a set, or the directory holding one.What it does
general.architectureand the tokenizer from shard 1, whichever shard you hand itThree properties of the convention worth stating
Verified by reading real shard headers from
vcruz305/Hy3-GGUF,unsloth/Qwen3-235B-A22B-GGUFandunsloth/Kimi-K2-Instruct-GGUF, because each is easy to get subtly wrong:split.nois 0-based while the filenames are 1-based. Shard-00002-of-00003carriessplit.no = 1.split.tensors.countis the total across every shard, not this shard's count. Hy3 IQ1_M: 694 + 604 = 1298.general.architectureand the tokenizer. Later shards hold exactly threesplit.*keys and nothing else, so anything reading arch from an arbitrary shard gets nothing.An incomplete set raises with the missing indices named. A truncated download is the common failure here, and silently serving most of a model is the worst available outcome.
Some producers use
--no-tensor-first-split, so shard 1 can legitimately hold zero tensors (unsloth/DeepSeek-V4-Flash-0731-GGUFdoes this). That falls out of the design rather than needing a special case, since shard 1 is only consulted for metadata.Testing
End to end against a genuine 3-shard split of a 16GB checkpoint produced by
llama-gguf-split(753 tensors; shard 1 kv=50, shards 2 and 3 kv=3): shard discovery from shard 1, from shard 2 and from the directory; architecture and metadata resolved correctly when handed shard 2; 753 tensors aggregated; 573 weights reconciled against the constructed module; then generation matching the unsplit file, 6/6 on a short factual set at 44 to 46 tok/s on an RTX 4060 Laptop.Unit tests write GGUF bytes directly rather than going through
gguf.GGUFWriter, whose signature has moved between releases. They include a fixture guard asserting shard 2 genuinely lacksgeneral.architecture, since otherwise the shard-1-resolution test would pass vacuously.tests/models/test_gemma4_gguf_rope.pystill passes, and the single-file path is unchanged.Note
Built while working on #131 (GGUF quant types and the Qwen architectures), but it depends on nothing from that branch: the only new imports are
globandre. Happy to rebase either way round depending on which you would rather take first.