Repository navigation
feat(ruvllm): write GGUF F16 and Q8_0 from SafeTensors checkpoints - #1201
Open
ohdearquant wants to merge 3 commits into
Open
ohdearquant wants to merge 3 commits into
ohdearquant wants to merge 3 commits into
Conversation
`ruvllm quantize` now converts a SafeTensors checkpoint (a single file or a sharded directory, F32/F16/BF16) of the llama and qwen2 architectures to GGUF F16 or Q8_0, with model and tokenizer metadata. NFC normalizers and llama3 rope scaling are accepted; the latter adds the rope_freqs.weight tensor llama.cpp expects. Added tokens that look special are marked CONTROL, and Q8_0 output quantizes the token embeddings by default (--keep-embed-fp16 keeps them in F16). The output is written to a temporary file and published atomically, so a failed run leaves no output. Unsupported architectures, tokenizers, config features and formats are refused before any output exists; GGUF input and the K-quant formats still fail closed. Refs ruvnet#968
Accept the pre-tokenizer sequence SmolLM2 uses (Digits with individual_digits, then ByteLevel) and write it as the smollm pre-tokenizer. Any other Digits arrangement is still refused before output exists. Numeric BOS/EOS/PAD/UNK token ids no longer overwrite the token type taken from the vocabulary, matching llama.cpp's converter. This keeps an added special unknown token as CONTROL; Qwen2.5 and Llama-3.2 outputs are unchanged. Refs ruvnet#968
This was referenced Oct 12, 2026
Owner
|
Held back for review, not merged. It combines cleanly with the other ruvllm PRs and the suite passes (1656 lib tests including the 1380 lines of writer tests), but it adds ~8k lines of new GGUF writer code and has no linked issue, so I did not merge it unreviewed. Note clippy on rustc 1.98 flags |
This branch has not been deployed
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.
Summary
ruvllm quantizewrites real GGUF files from SafeTensors checkpoints instead of failing closed for every input. This covers the SafeTensors half of #968. GGUF input (gate 2) is not part of this change and still fails closed.model.safetensors.index.json), with F32, F16 or BF16 tensors, for thellamaandqwen2architectures, plusconfig.jsonandtokenizer.json.--keep-embed-fp16keeps them in F16.NFCnormalizer is accepted and writes the same output as no normalizer, as llama.cpp's converter does.rope_scaling(rope_type: "llama3") is accepted and emits therope_freqs.weighttensor that llama.cpp reads.<|...|>and similar) are typed CONTROL, using the converter's rule. Numeric BOS/EOS/PAD/UNK ids are written without changing a token's type, as the converter does.Digitswithindividual_digits, thenByteLevel) is accepted and written assmollm. OtherDigitsarrangements are refused with no output..<name>.part.*file. Unsupported architectures, tokenizer features, config fields and rope types are refused before any output exists. Q4_K_M and Q5_K_M still fail closed.Real checkpoints against llama.cpp
Each model was converted as downloaded, by this change and by llama.cpp's
convert_hf_to_gguf.py(llama.cpp f2cc7282). Both files were read with llama.cpp'sgguf-pyand run with llama.cpp on CPU (same commit).rope_freqs.weightrope_freqs.weight"Identical" means the same name, type, shape and bytes for every tensor. PPL is
llama-perplexityon a fixed 51,851-byte text (llama.cpp's README plus docs/build.md), with-c 512 --chunks 6. Greedy text is 24 tokens fromllama-simplefor "The three primary colors are".Metadata differs only in optional keys; every key both files write has the same value. Keys only one side writes:
general.alignment,qwen2.rope.dimension_count,qwen2.vocab_sizeandtokenizer.ggml.add_space_prefix; the converter also writesgeneral.name,general.size_label,general.typeandgeneral.sampling.*.general.alignmentandtokenizer.ggml.add_space_prefix; the converter also writesgeneral.name,general.size_label,general.type,general.sampling.*,llama.attention.key_length,llama.attention.value_lengthandtokenizer.ggml.add_sep_token.general.alignmentandtokenizer.ggml.add_eos_token; the converter also writesgeneral.name,general.size_label,general.type,llama.attention.key_lengthandllama.attention.value_length.None of these changed the results above.
Gate 4 bindings at this commit
Linux x86_64, 16 CPUs, rustc 1.98.0, release build of
ruvllm-cliat this commit (3d51ab3),/usr/bin/time -v:model.safetensors(sha256)fdf756fa7fcbe7404d5c60e26bff1a0c8b8aa1f72ced49e7dd0210fe288fb7fe0a46b1687ee045642403d5cdb1cc30cc8e746ada05ad3d5cabdd5d20b3ddce4c82d260e58a3e37dabd1c4d33e3a7f6dee0e0c5bca38b012f92a9ab8643146d921ff795ff6a07e6a68085d206fb84417da2f083f68391c2843cd2b8ac6df8538f8b3ec19869adfb6d1515041b928720764ef894e287396b3e269935d0add8433448d614953a561000d040c053f474732a57840f7ab64eaebac935c5adb6c187d75af571cbf074e6d21a03528d2330792e532ca608f24ac70a143f6b369968ab8ce83fd8cb757228fe3dd7222015ba1d3a15479a706faa106b470c38c8e7320951b565cdb1046ed2bc9959f6a09f33cf4d9174df519c560a6b0c395fbca59905aeThe Qwen2.5 and Llama-3.2 files are byte-identical to the ones measured in the table above (and to the previous commit's output), and the SmolLM2 rows of that table were measured on these two SmolLM2 files, so all of those results apply to this commit. Other jobs may have been running on the machine, so the wall times are indicative only.
Tests
At this commit, on the same machine:
cargo fmt --all -- --checkcargo test --locked -p ruvllm --lib gguf::writer::cargo test --locked -p ruvllm-cli --bin ruvllm commands::quantize::cargo clippy --locked -p ruvllm -p ruvllm-cli --all-targets -- -W warningsThe writer is a new
ruvllm::gguf::writermodule. Its unit tests cover:rope_freqs.weightmatching llama.cpp's values bit for bit, legacytype/ default fields, and unsupported rope types or unknown keys being refused with no output;Digitsarrangements being refused with no output;For the normalizer, rope, token-type, embedding and SmolLM2 rules, ten of these tests were checked against sixteen individual code changes they cover: with each change reverted its test failed, and with it restored the test passed.
CI
The red jobs on recent pull requests (
Tests (core-and-rest),Tests (core-platform),Tests (core-and-rest-heavy)) also fail onmainand are tracked in #1178.cargo check -p ruvllm --no-default-featuresfails onmainwith the same Tokio error before and after this change; #1200 fixes that.Refs #968