Skip to content

Support Nemotron 3.5 Lightning Q8 GGUF conversion - #497

Open
justinchuby wants to merge 1 commit into
justinchuby-add-nemotron-35-lightningfrom
justinchuby-support-nemotron-gguf
Open

Support Nemotron 3.5 Lightning Q8 GGUF conversion#497
justinchuby wants to merge 1 commit into
justinchuby-add-nemotron-35-lightningfrom
justinchuby-support-nemotron-gguf

Conversation

@justinchuby

@justinchuby justinchuby commented Aug 15, 2026

Copy link
Copy Markdown
Member

Summary

  • add a reusable GGUF architecture-adapter registry so generic import code owns dispatch and completeness while nemotron_h_moe owns its schedule, tensor names, transforms, qtype policy, tokenizer contract, and MTP semantics
  • convert the pinned Nemotron 3.5 Lightning Q8_0 artifact to an executable ONNX package while preserving Q8 blocks as MatMulNBits(bits=8, block_size=32), including leading-axis routed-expert expansion without dequantization
  • reconstruct the exact GPT-2/Pixtral tokenizer engine, keep tokenizer and model-runtime special-token contracts explicit, and add a reproducible fresh-process direct-ORT acceptance runner

Stack and lineage

This PR is de-stacked on exact final #487 head a8cd77570bca980861eeadab9e6fa077464f0138. The review-fixed PR head is 4e9ab2e51a7ba3d39fa2d54d241be1857f976c0b; a8cd775...HEAD contains exactly one commit, Support Nemotron 3.5 Q8 GGUF conversion. The final #487 skill split removed seven generic skill changes to #500 without changing code/model behavior. No #496 commits or #487 skill changes are duplicated.

Exact architecture and mapping contract

The adapter validates the complete pinned tensor table before graph construction:

  • 417 source tensors
  • 401 mapped backbone sources and 16 explicit block-52 MTP exclusions
  • 52 backbone layers: 23 Mamba + 23 MoE + 6 attention
  • 6,243 logical targets, including 6,006 exact Q8 targets
  • 18,255 mapped weight initializers; missing, unexpected, duplicate, or unmapped targets fail the build
  • ssm_a -> log(-A), A/D/grouped-norm squeeze/flatten, conv-axis expansion, inverse llama.cpp Q/K row permutation, and direct expert-axis slicing are shape-validated

All Nemotron constants and error prose live in src/mobius/integrations/gguf/_nemotron_h_moe.py; the model/component factory changes are source-format agnostic and the existing float/Safetensors graph remains unchanged. The adapter contract requires every adapter to implement source validation, and generic validation dispatch is explicitly typed.

Pinned real-artifact acceptance

Source: unsloth/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF@f2d3fe3694501008786e81e5f20360cbf715496a

  • file: NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q8_0.gguf
  • size: 35,004,643,392 bytes
  • SHA-256: dc5276dd0619c04e277504d2358a793e31ccbe39e894d767d0d14f2a221e2ca4
  • inventory: 32,904,044,544 Q8_0 parameters, 8,877,632 F32 parameters, 344,064 BF16 MTP parameters

The full build/save/reload/prefill/cached-decode acceptance was rerun after the final review fixes on exact head 4e9ab2e51a7b in a clean process:

  • build: 205.574 s; save: 131.723 s
  • package: 36,920,438,736 bytes
  • peak build working set: 54,732,890,112 bytes
  • 6,005 MatMulNBits + 1 GatherBlockQuantized
  • 6,006 Q8 weight initializers
  • no QuantizeLinear or DequantizeLinear

A fresh process loaded the saved external-data package in ONNX Runtime 1.28.0 (45de2a8b06) on CPUExecutionProvider, ran nonzero unpadded prefill, independently ran right-padded prefill with an explicit mask, and performed every cached decode step:

  • session load: 19.543 s
  • five-token prefill: 96.826 s
  • seven cached decode calls: 50.644, 50.603, 50.563, 50.331, 50.519, 50.234, 50.569 s
  • cached decode throughput: 0.01980 steps/s
  • peak runtime working set: 38,144,159,744 bytes
  • right-padded real-token logits max absolute difference: 0.0 (atol=1e-5)

Independent llama.cpp reference: commit 9d57ce456c94d241dde672b2db9cf18879766568, MSVC 19.44.35228.0, CPU only:

llama-server -m NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q8_0.gguf -c 128 -t 12 -tb 12 -b 64 -ub 64 -ngl 0 --host 127.0.0.1 --port 18081 --no-warmup
POST /completion
{"prompt":"The capital of France is","n_predict":8,"temperature":0,"seed":1,"cache_prompt":false,"n_probs":1}
  • prompt IDs: [1784, 8961, 1307, 5498, 1395]
  • generated IDs: [6993, 1046, 1256, 1010, 1784, 8961, 1307, 10787]
  • generated text: Paris. \nThe capital of Germany
  • llama.cpp throughput: 5.58 prompt tok/s, 5.95 generated tok/s
  • separate llama.cpp CLI peak working set: 16.81 GiB

The review-fixed exact-head direct-ORT run matched every generated ID and the decoded text exactly.

Tokenizer and special-token contract

The GGUF tokenizer is selected only for exact model=gpt2, pre=pixtral metadata and matches the pinned official tokenizer at nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16@d468880b6ad3c6e0d21377ce7242adaea4cc884d across vocabulary, added-token flags, pre-tokenizer, decoder, post-processor, encode IDs, and decode text.

  • 131,072-token hash: 4999709474e3c967358c1f1199b6be65fb9055d3eb59e0cd387f9e7077fc40ed
  • 269,443-merge hash: b1b0165185b1925118c2f7b1e978439b02010c3a420ebfec5c19a093a0d9b4cb
  • official tokenizer asset SHA-256: 623c34567aebb18582765289fbe23d901c62704d6518d71866e0e58db892b5b7
  • GGUF BOS/EOS/PAD metadata: 1/11/999
  • model-runtime BOS/EOS/PAD: 1/2/0
  • generation stop IDs: [2, 11]
  • tokenizer asset EOS/PAD role: <|im_end|> ID 11

PAD 999 is rejected because it names <SPECIAL_999>. The GGUF-embedded chat template differs from the pinned official asset, so the recipe verifies immutable official sidecar hashes rather than silently treating it as authoritative. Final #487 sentinel filtering and EOS/EOT/EOM union semantics remain intact for the generic path.

Explicit limits

  • Q4_K_M remains rejected: its large sources are Q5_0/Q5_1, not a preservable Q4-only payload.
  • MXFP4_MOE remains rejected: it contains 12.772B Q5_1 parameters without a validated preserved runtime mapping.
  • split BF16 GGUF remains rejected by the generic shard-assembly guard.
  • dequantize/requantize is not described as preservation.

Validation on exact head

  • focused final-lineage suite: 1,580 passed, 44 skipped, 176 deselected, 39 xfailed
  • pinned real-header + official-tokenizer parity: 2 passed
  • canonical non-integration suite: 3,965 passed, 64 skipped
  • exact-PR formatter and lint: clean
  • git diff --check: clean
  • final independent specialist review of a8cd775...4e9ab2e: no significant findings
  • all automated review findings fixed, replied to, and resolved

Waivers

  • ORT GenAI: generic metadata cannot bind arbitrary non-KV convolution/SSM recurrent-state inputs. This is a graph-structural runtime limitation, so direct ORT cached generation is the acceptance path.
  • CUDA Q8 runtime: the 36.9 GB package cannot fit the available 8 GB GPU. CPU acceptance uses the available hardware and records full semantic/memory evidence.
  • Foundry Local / Q8 Olive: not run for this 36.9 GB GGUF follow-up. Add NVIDIA Nemotron 3.5 Lightning export support #487 validates the official model's CPU/CUDA cached generation and fresh Olive Q4 package; this PR specifically validates the preserved source-Q8 ONNX package through direct ORT.

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing a8cd7754e9ab2e

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 105 105 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 60 60 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 56 56 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 94 94 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 58 58 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 54 54 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 60 60 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 56 56 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 264 264 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 126 126 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 428 428 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing a8cd7754e9ab2e

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 0
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Comment thread src/mobius/integrations/gguf/_architecture.py
@justinchuby
justinchuby requested a review from a team August 15, 2026 05:38
@justinchuby
justinchuby force-pushed the justinchuby-support-nemotron-gguf branch 2 times, most recently from 9e6ffbc to 2e31b58 Compare August 15, 2026 06:55
Copilot AI lite review requested due to automatic review settings August 15, 2026 06:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a GGUF architecture-adapter framework and a strict Nemotron 3.5 Lightning (nemotron_h_moe) adapter to enable exact Q8_0 preservation during GGUF → ONNX conversion, along with tokenizer reconstruction and direct-ORT validation support.

Changes:

  • Introduces a reusable GGUF architecture adapter interface/registry and implements a strict Nemotron adapter that validates the pinned tensor table, schedules, qtypes, and mapping completeness.
  • Extends NemotronH model construction to support quantized projections/embeddings (MatMulNBits + GatherBlockQuantized) and adds repacking support for stacked expert tensors.
  • Adds strict GPT-2/Pixtral tokenizer reconstruction, broader test coverage for the adapter/tokenizer/repacker paths, and updates docs + an acceptance runner script.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/mobius/models/nemotron_h.py Adds quantized linear/embedding wiring for NemotronH graphs.
src/mobius/models/nemotron_h_test.py Adds unit coverage for float vs quantized NemotronH graph contracts and initializer coverage validation.
src/mobius/integrations/gguf/_tokenizer.py Adds strict GPT-2/Pixtral tokenizer reconstruction for Nemotron GGUF metadata.
src/mobius/integrations/gguf/_tokenizer_test.py Adds tests for strict Pixtral/GPT-2 reconstruction and pinned-artifact parity hooks.
src/mobius/integrations/gguf/_repacker.py Adds repacking for leading-axis stacked 3D quantized tensors (expert stacks).
src/mobius/integrations/gguf/_repacker_test.py Adds execution-level test validating stacked-expert repack output matches dequantized matmul.
src/mobius/integrations/gguf/_nemotron_h_moe.py New strict Nemotron 3.5 Lightning adapter: validation, config extraction, mapping, and transforms.
src/mobius/integrations/gguf/_mmproj_test.py Updates multimodal preflight expectation to reflect adapter validation errors.
src/mobius/integrations/gguf/_config_mapping.py Allows config extraction via adapter (validated) before generic mapping.
src/mobius/integrations/gguf/_builder.py Routes GGUF build flow through adapters (validation, quant config, mapping-based quant detection/state_dict load).
src/mobius/integrations/gguf/_builder_test.py Updates preflight guard tests now that Nemotron adapter is supported.
src/mobius/integrations/gguf/_architecture.py New adapter registry + mapping audit + package state-dict coverage checker.
src/mobius/integrations/gguf/_architecture_test.py Adds synthetic + optional real-artifact integration validation for the Nemotron adapter.
src/mobius/components/_mamba_block.py Adds linear_class injection point so Mamba blocks can use quantized linears.
examples/olive/nemotron-3_5-lightning-30b/validate_gguf_q8.py Adds a reproducible build/save/reload/direct-ORT acceptance runner for pinned Q8_0.
docs/cli_reference.md Updates CLI docs to reflect supported pinned Nemotron Q8_0 GGUF import.
docs/api/build_from_gguf.md Replaces the prior waiver section with pinned Q8_0 support + reproducibility details.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mobius/integrations/gguf/_builder.py Outdated
Comment thread src/mobius/integrations/gguf/_architecture.py
Add a reusable GGUF architecture-adapter seam and a strict nemotron_h_moe adapter that validates the pinned 52-layer backbone, excludes the auxiliary MTP block, and enforces complete source-to-initializer mapping.

Preserve Q8_0 weights in MatMulNBits, including stacked routed experts, reconstruct the pinned Pixtral tokenizer contract, and add a fresh-process direct-ORT acceptance runner with exact llama.cpp generation evidence.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@justinchuby
justinchuby force-pushed the justinchuby-support-nemotron-gguf branch from 2e31b58 to 4e9ab2e Compare August 15, 2026 11:11
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.

2 participants