Skip to content

GGUF: read multi-shard checkpoints - #154

Open
vcruz305 wants to merge 1 commit into
FlashML-org:mainfrom
vcruz305:feat/gguf-multishard
Open

GGUF: read multi-shard checkpoints#154
vcruz305 wants to merge 1 commit into
FlashML-org:mainfrom
vcruz305:feat/gguf-multishard

Conversation

@vcruz305

Copy link
Copy Markdown

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.

--model now accepts any shard of a set, or the directory holding one.

What it does

  • Resolves any shard (or a directory containing exactly one split set) to the ordered shard list
  • Reads metadata, general.architecture and the tokenizer from shard 1, whichever shard you hand it
  • Aggregates the tensor tables across shards, in shard order
  • Single-file paths take exactly the same code path as before

Three properties of the convention worth stating

Verified by reading real shard headers from vcruz305/Hy3-GGUF, unsloth/Qwen3-235B-A22B-GGUF and unsloth/Kimi-K2-Instruct-GGUF, because each is easy to get subtly wrong:

  • split.no is 0-based while the filenames are 1-based. Shard -00002-of-00003 carries split.no = 1.
  • split.tensors.count is the total across every shard, not this shard's count. Hy3 IQ1_M: 694 + 604 = 1298.
  • Only shard 1 carries general.architecture and the tokenizer. Later shards hold exactly three split.* 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-GGUF does 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 lacks general.architecture, since otherwise the shard-1-resolution test would pass vacuously.

tests/models/test_gemma4_gguf_rope.py still 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 glob and re. Happy to rebase either way round depending on which you would rather take first.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant