miketung commited on
Commit
a6c9122
·
verified ·
1 Parent(s): c2f2783

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. LICENSE +61 -0
  2. LICENSE.Qwen +16 -0
  3. NOTICE +13 -0
  4. NVIDIA_MODEL_CARD.md +414 -0
  5. README.md +161 -0
  6. chat_template.jinja +170 -0
  7. config.json +2402 -0
  8. generation_config.json +12 -0
  9. merges.txt +0 -0
  10. preprocessor_config.json +21 -0
  11. recipe/LICENSE +21 -0
  12. recipe/README.md +75 -0
  13. recipe/THIRD_PARTY_NOTICES.md +50 -0
  14. recipe/bench/bench-sbs.py +39 -0
  15. recipe/bench/fetch-evaldata.sh +20 -0
  16. recipe/bench/needle-test.py +16 -0
  17. recipe/bench/quality-probe.py +91 -0
  18. recipe/bench/test-toolcall.py +21 -0
  19. recipe/kl/atomic_kld.py +136 -0
  20. recipe/patches/b12x/loader/_ple_reader.c +614 -0
  21. recipe/patches/b12x/loader/_row_plan.h +314 -0
  22. recipe/patches/b12x/loader/_storage.c +107 -0
  23. recipe/patches/qwen4_exp/nvidia/b12x_ple.py +1064 -0
  24. recipe/patches/qwen4_exp/nvidia/expert_stats.py +68 -0
  25. recipe/patches/qwen4_exp/nvidia/gdn_defer.py +402 -0
  26. recipe/patches/qwen4_exp/nvidia/hyperconnection.py +782 -0
  27. recipe/patches/qwen4_exp/nvidia/logit_dump.py +123 -0
  28. recipe/patches/qwen4_exp/nvidia/model.py +1310 -0
  29. recipe/patches/qwen4_exp/nvidia/mtp.py +968 -0
  30. recipe/patches/qwen4_exp/nvidia/ops/gdn_deferred.py +330 -0
  31. recipe/patches/qwen4_exp/nvidia/ops/gemma_norm.py +71 -0
  32. recipe/patches/qwen4_exp/nvidia/ops/hc_fp8_gemv.py +453 -0
  33. recipe/patches/qwen4_exp/nvidia/ops/mxfp8_gemv.py +249 -0
  34. recipe/patches/qwen4_exp/nvidia/ops/nvfp4_gemv.py +187 -0
  35. recipe/patches/qwen4_exp/nvidia/ops/small_gemv.py +127 -0
  36. recipe/patches/vllm_core/v1/core/boundary_checkpoint.py +438 -0
  37. recipe/patches/vllm_kernels/linear/mxfp8/b12x.py +202 -0
  38. recipe/results/bench-sbs.txt +4 -0
  39. recipe/results/kl-neutral-a16-as-served.json +15 -0
  40. recipe/results/kl-neutral-a4-prefill.json +15 -0
  41. recipe/results/needle.txt +6 -0
  42. recipe/results/quality-probe.jsonl +364 -0
  43. recipe/results/quality-probe.txt +3 -0
  44. recipe/results/toolcall.txt +4 -0
  45. recipe/serve/draft_vocab_64k.json +0 -0
  46. recipe/serve/seccomp.json +1229 -0
  47. recipe/serve/serve.sh +290 -0
  48. tokenizer_config.json +305 -0
  49. video_preprocessor_config.json +21 -0
  50. vocab.json +0 -0
LICENSE ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Source: https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-agreement/ (retrieved 2026-10-20)
2
+
3
+ NVIDIA Open Model Agreement
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ Release Date: April 2, 2026
8
+
9
+ NVIDIA Works released under this License are intended to be used permissively and enable the further development of AI technologies. Subject to the terms of this License, NVIDIA confirms that:
10
+
11
+ - Works are commercially usable.
12
+
13
+ - You are free to create and distribute Derivative Works.
14
+
15
+ - NVIDIA does not claim ownership to any outputs generated using the Works or Derivative Works.
16
+
17
+ By using, reproducing, modifying, distributing, performing or displaying any portion or element of the Works or Derivative Works, or otherwise accepting the terms of this License, you agree to be bound by this License.
18
+
19
+ 1. Definitions.
20
+
21
+ “License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document.
22
+
23
+ “Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ “You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
26
+
27
+ “Work” shall mean the work of authorship, including machine learning model, software, checkpoints, learnt weights, algorithms, parameters, configuration files and documentation, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
28
+
29
+ “Derivative Works” shall mean any work, whether in source or object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
30
+
31
+ 2. Grant of License. Subject to the terms and conditions of this License, NVIDIA hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in source or object form.
32
+
33
+ If You institute patent or copyright litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or an output from the Work constitutes direct or contributory patent or copyright infringement, then any licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
34
+
35
+ 3. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in source or object form, provided that You meet the following conditions:
36
+
37
+ a. You must give any other recipients of the Work a copy of this License; and
38
+
39
+ b. You must retain, in the source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40
+
41
+ c. If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the following attribution notice within a “Notice” text file with such copies and the following statement: “Licensed by NVIDIA Corporation under the NVIDIA Open Model Agreement.”
42
+
43
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
44
+
45
+ 4. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of NVIDIA, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
46
+
47
+ 5. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, NVIDIA provides the Work on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
48
+
49
+ 6. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall NVIDIA be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work, a Derivative Work or an output from the Work or Derivative Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if NVIDIA has been advised of the possibility of such damages.
50
+
51
+ 7. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of NVIDIA, and only if You agree to indemnify, defend, and hold NVIDIA harmless for any liability incurred by, or claims asserted against, NVIDIA by reason of your accepting any such warranty or additional liability. You will indemnify and hold harmless NVIDIA from and against any claim by any third party arising out of or related to your use or distribution of the Works, Derivative Works thereof, or output from the Works or Derivative Works.
52
+
53
+ 8. Feedback. NVIDIA appreciates your feedback, and You agree that NVIDIA may use it without restriction or compensation to You.
54
+
55
+ 9. Governing Law. This Agreement will be governed in all respects by the laws of the United States and the laws of the State of Delaware, without regard to conflict of laws principles or the United Nations Convention on Contracts for the International Sale of Goods. The state and federal courts residing in Santa Clara County, California will have exclusive jurisdiction over any dispute or claim arising out of or related to this Agreement, and the parties irrevocably consent to personal jurisdiction and venue in those courts; except that, either party may apply for injunctive remedies or an equivalent type of urgent legal relief in any jurisdiction.
56
+
57
+ 10. Trade and Compliance. You agree to comply with all applicable export, import, trade and economic sanctions laws and regulations, as amended, including without limitation U.S. Export Administration Regulations and Office of Foreign Assets Control regulations. These laws include restrictions on destinations, end-users and end-use.
58
+
59
+ (v. March 9, 2026)
60
+
61
+ End of terms and conditions
LICENSE.Qwen ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Qwen Community License 1.0
2
+
3
+ Copyright (c) 2026 Qwen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software, including the model weights, parameters, configuration files, inference code and associated documentation files (collectively, the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, sell, deploy, host, fine-tune, and create derivative works from (collectively, "Use" or "Using") copies of the Software; and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ 1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. If the Software (or any derivative works thereof) is Used for any of the licensee's commercial products or services that have more than 100,000,000 monthly active users or US$ 20,000,000 (or equivalent in other currencies) monthly revenue, respective model name must be prominently displayed on the user interface of such product or service; and,
8
+
9
+ 2. If the licensee or any of its affiliates conducts a Model as a Service or AI Work Assistant business, the licensee shall obtain a separate license from Qwen before Using the Software or its derivative works for any commercial purpose. The foregoing requirement shall not apply to the licensee's internal Use of the Software, provided that such Use does not make the Software, its outputs, or its underlying model capabilities available to any third party.
10
+
11
+ "Model as a Service" means giving a third party access to language model inference or fine-tuning (e.g., via API or a hosted endpoint) in a manner that allows such third parties to exercise meaningful control over the inputs, parameters, or training data. This does not include the mere relaying of requests to models hosted by other third parties.
12
+ “AI Work Assistant” means an independent AI-powered product primarily designed for AI-assisted coding or office productivity (e.g., Qoder and QwenWork). It does not include: (a) a single-purpose AI tool (such as an AI translation tool); (b) an AI assistant primarily designed for a domain other than coding or office productivity (such as Taobao AI Shopping Assistant or AMap AI Chat); or (c) an AI assistant that is a feature of a product whose primary purpose is not AI-assisted coding or office productivity.
13
+
14
+ THE SOFTWARE AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL QWEN, ITS AFFILIATES OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE USE OF THE SOFTWARE MUST COMPLY WITH APPLICABLE LAWS AND REGULATIONS, AND MUST NOT INFRINGE THE INTELLECTUAL PROPERTY RIGHTS OF ANY THIRD PARTY.
15
+
16
+ For any questions regarding this license, please contact model-business@notice.qwencloud.com.
NOTICE ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Qwen3.8-Flash-Next FP4-Local for one DGX Spark (diffbot)
2
+
3
+ Model weights: Licensed by NVIDIA Corporation under the NVIDIA Open Model Agreement.
4
+ The files config.json, generation_config.json, the tokenizer and processor files, chat_template.jinja and
5
+ tensors/ are NVIDIA's nvidia/Qwen3.8-Flash-Next-FP4-Local release, redistributed unmodified; NVIDIA's model
6
+ card is kept as NVIDIA_MODEL_CARD.md. A copy of the NVIDIA Open Model Agreement is in LICENSE.
7
+
8
+ The underlying base model, Qwen/Qwen3.8-Flash-Next, is Copyright (c) 2026 Qwen and governed by the Qwen
9
+ Community License 1.0 (LICENSE.Qwen).
10
+
11
+ Additions by Diffbot (recipe/): serving scripts, benchmarks and the KL client are MIT (recipe/LICENSE);
12
+ the files in recipe/patches/ are modified or new files for vLLM and FlashInfer's b12x, Apache-2.0
13
+ (recipe/THIRD_PARTY_NOTICES.md). recipe/serve/draft_vocab_64k.json is Diffbot's token ranking.
NVIDIA_MODEL_CARD.md ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ pipeline_tag: image-text-to-text
3
+ base_model:
4
+ - Qwen/Qwen3.8-Flash-Next
5
+ license: other
6
+ license_name: nvidia-open-model-agreement
7
+ library_name: Model Optimizer
8
+ tags:
9
+ - nvidia
10
+ - ModelOpt
11
+ - Qwen3.8
12
+ - quantized
13
+ - FP4
14
+ - fp4
15
+ ---
16
+
17
+
18
+ # Model Overview
19
+
20
+ **IMPORTANT**: the NVFP4 scale factors in this checkpoint are in a new lossless compression format, but upstream vLLM support for this format has not yet landed. **You must use one of the containers provided below.**
21
+
22
+ ## Description:
23
+ The NVIDIA Qwen3.8-Flash-Next-FP4-Local model is the quantized version of Alibaba's Qwen3.8-Flash-Next model, which is an auto-regressive language model that uses an optimized transformer architecture. Qwen3.8-Flash-Next is a causal language model with a vision encoder, hybrid attention (Gated DeltaNet and Qwen Sparse Attention), Mixture-of-Experts, gated residual streams, and n-gram embeddings. For more information, please check [here](https://huggingface.co/Qwen/Qwen3.8-Flash-Next). The NVIDIA Qwen3.8-Flash-Next-FP4-Local model is quantized with [Model Optimizer](https://github.com/NVIDIA/Model-Optimizer) using [quantization-aware distillation (QAD)](https://developer.nvidia.com/blog/how-quantization-aware-training-enables-low-precision-accuracy-recovery/).
24
+
25
+ The checkpoint occupies approximately **96 GiB on disk** and is best suited for local AI hardware. It has been validated with vLLM on the following hardware: <br>
26
+ * **NVIDIA RTX PRO 6000 Blackwell** — single GPU, with PLE (n-gram embedding table) offload. 850K tokens of FP8 KV Cache <br>
27
+ * **NVIDIA DGX Spark** — Can run with or without PLE offload. 1M tokens of FP8 KV Cache with PLE offload <br>
28
+
29
+ This model is ready for commercial or non-commercial use. <br>
30
+
31
+ ## Third-Party Community Consideration
32
+ This model is not owned or developed by NVIDIA. This model has been developed and built to a third-party's requirements for this application and use case; see link to Non-NVIDIA [(Qwen3.8-Flash-Next) Model Card](https://huggingface.co/Qwen/Qwen3.8-Flash-Next) from Qwen.
33
+
34
+ ### License/Terms of Use:
35
+ **GOVERNING DOWNLOAD TERMS:** Use of this model is governed by [NVIDIA Open Model Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-agreement/).
36
+
37
+ **ADDITIONAL INFORMATION** : The Qwen3.8-Flash-Next base model is governed by the [Qwen Community License 1.0](https://huggingface.co/Qwen/Qwen3.8-Flash-Next/blob/main/LICENSE). **Qwen3.8-Flash-Next**.
38
+
39
+ ### Deployment Geography:
40
+ Global <br>
41
+
42
+ ### Use Case: <br>
43
+ Developers looking to take off-the-shelf, pre-quantized models for deployment in AI Agent systems, chatbots, RAG systems, and other AI-powered applications. <br>
44
+
45
+ ### Release Date: <br>
46
+ Hugging Face 10/05/2026 via https://huggingface.co/nvidia/Qwen3.8-Flash-Next-FP4-Local <br>
47
+
48
+ ## References
49
+ NVIDIA Model Optimizer: https://github.com/NVIDIA/Model-Optimizer
50
+
51
+ ## Model Architecture:
52
+ **Architecture Type:** Transformers <br>
53
+ **Network Architecture:** Hybrid Attention (Gated DeltaNet and Qwen Sparse Attention) with Mixture-of-Experts (`Qwen4ExpForConditionalGeneration`) <br>
54
+ **Number of Model Parameters:** 125B in total and 6B activated, plus 51B n-gram embedding and 4B MTP <br>
55
+ **This model was developed based on [Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next)** <br>
56
+
57
+ ## Input:
58
+ **Input Type(s):** Text, Image, Video <br>
59
+ **Input Format(s):** String, Red, Green, Blue (RGB), Video (MP4/WebM) <br>
60
+ **Input Parameters:** One-Dimensional (1D), Two-Dimensional (2D), Three-Dimensional (3D) <br>
61
+ **Other Properties Related to Input:** Context length up to 262K natively and extensible up to 1,000,000 tokens. <br>
62
+
63
+ ## Output:
64
+ **Output Type(s):** Text <br>
65
+ **Output Format:** String <br>
66
+ **Output Parameters:** One-Dimensional (1D): Sequences <br>
67
+ **Other Properties Related to Output:** None <br>
68
+
69
+ Our AI models are designed and/or optimized to run on NVIDIA GPU-accelerated systems. By leveraging NVIDIA's hardware (e.g. GPU cores) and software frameworks (e.g., CUDA libraries), the model achieves faster training and inference times compared to CPU-only solutions. <br>
70
+
71
+ ## Software Integration:
72
+ **Supported Runtime Engine(s):** <br>
73
+ * **vLLM** <br>
74
+
75
+ **Supported Hardware Microarchitecture Compatibility:** <br>
76
+ * NVIDIA Blackwell <br>
77
+
78
+ **Preferred Operating System(s):** <br>
79
+ * Linux <br>
80
+
81
+ The integration of foundation and fine-tuned models into AI systems requires additional testing using use-case-specific data to ensure safe and effective deployment. Following the V-model methodology, iterative testing and validation at both unit and system levels are essential to mitigate risks, meet technical and functional requirements, and ensure compliance with safety and ethical standards before deployment.
82
+
83
+ ## Model Version(s):
84
+ The model version is FP4 QAD 1.0 and is quantized with nvidia-modelopt **v0.47.0** <br>
85
+
86
+ ## Training and Evaluation Datasets:
87
+
88
+ ## Calibration Dataset:
89
+ **Link:** Subset of [Nemotron-Post-Training-Dataset-v3](https://huggingface.co/collections/nvidia/nemotron-post-training-v3), [Nemotron-Post-Training-Dataset-v2](https://huggingface.co/datasets/nvidia/Nemotron-Post-Training-Dataset-v2) and subset of [Nemotron-Pretraining-Dataset](https://huggingface.co/collections/nvidia/nemotron-pre-training-datasets) <br>
90
+ **Data Collection Method by dataset:** Automated. <br>
91
+ **Labeling Method by dataset:** Automated. <br>
92
+ **Properties:** NVIDIA's Nemotron-Post-Training-v3 is an open collection of approximately 50 curated Supervised Fine-Tuning (SFT) and Reinforcement Learning (RL) datasets designed for the Nemotron 3 model family (Nano, Super, and Ultra). NVIDIA's Nemotron-Post-Training-Dataset-v2 is a post-training dataset curated by NVIDIA containing multi-turn conversations across diverse topics. The Nemotron-Pre-Training-Datasets collection on Hugging Face provides large-scale data corpora used to train the NVIDIA Nemotron model family <br>
93
+
94
+ ## Training Dataset:
95
+ **Link**: Subset of [Nemotron-Post-Training-Dataset-v3](https://huggingface.co/collections/nvidia/nemotron-post-training-v3), [Nemotron-Post-Training-Dataset-v2](https://huggingface.co/datasets/nvidia/Nemotron-Post-Training-Dataset-v2) and subset of [Nemotron-Pretraining-Dataset](https://huggingface.co/collections/nvidia/nemotron-pre-training-datasets); only prompts from the datasets were used for data synthesis. <br>
96
+ **Data Modality:** Text <br>
97
+ **Text Training Data Size:** 500 Million Tokens, repeated for 3 epochs <br>
98
+ **Data Collection Method by dataset:** Hybrid: Automated, manually-collected, Synthetic <br>
99
+ **Labeling Method by dataset:** Hybrid: Automated, manually-labelled, Synthetic <br>
100
+ **Properties:** The Qwen3.8-Flash-Next-FP4-Local model was trained exclusively on a post-training corpus includes synthetic data for reasoning, code, science, tool use, instruction following, structured outputs, and multilingual tasks. Dataset disclosures are published in the `nvidia/nemotron-post-training-v2` and subset of `nvidia/nemotron-post-training-v3` and subset of `nvidia/nemotron-pre-training-datasets` Hugging Face collections. <br>
101
+
102
+ ## Inference:
103
+ **Acceleration Engine:** vLLM
104
+ **Test Hardware:** NVIDIA DGX Spark and NVIDIA RTX PRO 6000 Blackwell
105
+
106
+ ## Evaluation
107
+
108
+ **Datasets:** GPQA Diamond, ToolEvalBench <br>
109
+ **Data Collection Method by dataset:** Hybrid: Automated, Manually-Collected <br>
110
+ **Labeling Method by dataset:** Hybrid: Manually-Labeled, Automated <br>
111
+ **Properties:** GPQA Diamond contains 198 graduate-level multiple-choice questions written by domain experts in biology, physics, and chemistry. ToolEvalBench is a tool-calling quality benchmark that runs deterministic multi-turn agent scenarios against mock tools, scoring tool selection, parameter precision, multi-step chains, error recovery, and structured output. <br>
112
+
113
+ The accuracy benchmark results are presented in the table below:
114
+
115
+ | Precision | GPQA Diamond | ToolEvalBench |
116
+ |:---|:---:|:---:|
117
+ | BF16 | 92.2% | 89.4% |
118
+ | **FP4-Local** | **91.5%** | **88.7%** |
119
+
120
+ > Baseline: [Qwen3.8-Flash-Next](https://huggingface.co/Qwen/Qwen3.8-Flash-Next).
121
+
122
+ ## Quantization-Aware Distillation
123
+ The BF16 teacher generates responses and supplies token-level probability and hidden-state targets. The trainable NVFP4 and MXFP8 weights are quantized and reconstructed for each forward pass. Gradients update the underlying weights so their low-precision representations better match the teacher.
124
+
125
+ The objective combines next-token probability matching using total variation, hidden-state matching, and separate losses for end-of-response and end-of-thinking behavior. These boundary losses target the teacher's decisions to continue, finish reasoning, and stop responding.
126
+
127
+ Training uses the Quatrain distillation trainer: 2,500 trunk updates followed by 1,500 joint-refinement updates, which also train the n-gram embedding tables and simulate MXFP8 shared-expert weights. Attention remains frozen, using its MXFP8 weights during joint refinement.
128
+
129
+ ### What's quantized
130
+
131
+ | Component | Stored Precision |
132
+ |:---|:---|
133
+ | Decoder routed experts | QAD NVFP4; E2M1 values, E4M3 block scales, FP32 global/input scales |
134
+ | Decoder attention | MXFP8 projection selection from the pinned source |
135
+ | MTP routed experts | NVFP4, BF16 activations; block scales stored directly |
136
+ | Vision attention linears | MXFP8 E4M3 values; UE8M0 scales per 32 weights; 54 matrices |
137
+ | Vision MLP and merger linears | NVFP4 E2M1 values; E4M3 scales per 16 weights and FP32 global scale; 56 matrices; BF16 activations |
138
+ | Other vision tensors | BF16; 223 tensors, including normalization, biases, position embeddings, and convolutions |
139
+ | Normalization parameters | 100 FP32 tensors (97 hyperconnection and 3 PLE norms); 203 BF16 tensors; original bytes preserved |
140
+
141
+ ## Inference:
142
+ **Acceleration Engine:** **vLLM** <br>
143
+ **Test Hardware:** **NVIDIA DGX Spark and NVIDIA RTX PRO 6000 Blackwell** <br>
144
+
145
+ ## Usage
146
+
147
+ This checkpoint requires community vLLM containers (provided in cooperation with the "Local Inference Lab" community organization) until all features are available in upstream vLLM.
148
+
149
+ ### NVIDIA DGX Spark
150
+
151
+ vLLM container: [`eugr/spark-vllm-b12x:latest`](https://hub.docker.com/r/eugr/spark-vllm-b12x/tags?name=latest)
152
+
153
+ Start the container. Set `HF_TOKEN` in the host environment first.
154
+
155
+ ```sh
156
+ mkdir -p $HOME/.cache/huggingface $HOME/.cache/vllm $HOME/.cache/flashinfer $HOME/.triton $HOME/.cache/b12x
157
+
158
+ docker run --rm -it \
159
+ --gpus all \
160
+ --ipc host \
161
+ --network host \
162
+ --shm-size=16g \
163
+ --ulimit memlock=-1 \
164
+ --ulimit stack=67108864 \
165
+ --security-opt seccomp=unconfined \
166
+ --security-opt apparmor=unconfined \
167
+ -e HF_TOKEN=$HF_TOKEN \
168
+ -v ~/.cache/huggingface:/root/.cache/huggingface \
169
+ -v $HOME/.cache/vllm:/root/.cache/vllm \
170
+ -v $HOME/.cache/flashinfer:/root/.cache/flashinfer \
171
+ -v $HOME/.triton:/root/.triton \
172
+ -v $HOME/.cache/b12x:/root/.cache/b12x \
173
+ --entrypoint /bin/bash \
174
+ eugr/spark-vllm-b12x:latest
175
+ ```
176
+
177
+ Then launch vLLM inside the container:
178
+
179
+ ```sh
180
+ export VLLM_PLE_TABLE_MEMORY="disk"
181
+ export B12X_DISK_BACKEND="io_uring"
182
+ export CUTE_DSL_ARCH="sm_121a"
183
+ export SAFETENSORS_FAST_GPU="1"
184
+ export VLLM_WORKER_MULTIPROC_METHOD="spawn"
185
+ export VLLM_SSM_CONV_STATE_LAYOUT="DS"
186
+ export VLLM_USE_AOT_COMPILE="1"
187
+ export VLLM_USE_MEGA_AOT_ARTIFACT="1"
188
+ export VLLM_USE_V2_MODEL_RUNNER="1"
189
+ export B12X_POLICY_MODE="auto"
190
+ export VLLM_MXFP8_LM_HEAD="1"
191
+ export VLLM_B12X_MOE_FP4_LAYER_MAX_INPUT_SCALE="w13"
192
+ export VLLM_B12X_MOE_FP4_FORCE_A16=1
193
+ export B12X_W4A16_A4_PREFILL=1
194
+
195
+ vllm serve nvidia/Qwen3.8-Flash-Next-FP4-Local \
196
+ --host 0.0.0.0 \
197
+ --port 8000 \
198
+ --tensor-parallel-size 1 \
199
+ --pipeline-parallel-size 1 \
200
+ --mamba-cache-mode align \
201
+ --enable-prefix-caching \
202
+ --enable-chunked-prefill \
203
+ --dtype bfloat16 \
204
+ --kv-cache-dtype fp8 \
205
+ --quantization modelopt_mixed \
206
+ --block-size 16 \
207
+ --load-format b12x \
208
+ --max-model-len 262144 \
209
+ --max-num-seqs 8 \
210
+ --max-num-batched-tokens 4096 \
211
+ --speculative-config '{"method":"mtp","num_speculative_tokens":4}' \
212
+ --gdn-decode-kernel b12x \
213
+ --linear-backend b12x \
214
+ --moe-backend b12x \
215
+ --no-enable-flashinfer-autotune \
216
+ --mm-encoder-tp-mode data \
217
+ --reasoning-parser qwen3 \
218
+ --tool-call-parser qwen3_xml \
219
+ --enable-auto-tool-choice \
220
+ --compilation-config '{"pass_config":{"fuse_act_quant":true}}' \
221
+ --gpu-memory-utilization 0.8
222
+ ```
223
+
224
+ ### NVIDIA RTX PRO 6000 Blackwell
225
+
226
+ vLLM container: [`ghcr.io/local-inference-lab/vllm:release-10152026`](https://github.com/local-inference-lab/blackwell-llm-docker/pkgs/container/vllm)
227
+
228
+ Simplified run command:
229
+
230
+ ```sh
231
+ docker run -d --name qwen38-tp1 --init --gpus '"device=0"' \
232
+ --restart on-failure --network host --ipc host --shm-size 32g --stop-timeout 30 \
233
+ --ulimit memlock=-1 --ulimit stack=67108864:67108864 \
234
+ -v model-cache:/root/.cache/huggingface -v qwen38-runtime:/cache \
235
+ -e PROFILE=qwen38-flash-next -e PORT=8000 \
236
+ ghcr.io/local-inference-lab/vllm:release-10152026
237
+ ```
238
+
239
+ Two step verbose commands:
240
+
241
+ Start the container. Set `HF_TOKEN` in the host environment first.
242
+
243
+ ```sh
244
+ docker run --rm -it \
245
+ --gpus all \
246
+ --ipc host \
247
+ --network host \
248
+ --shm-size=16g \
249
+ --ulimit memlock=-1 \
250
+ --ulimit stack=67108864 \
251
+ -e HF_TOKEN=$HF_TOKEN \
252
+ -v ~/.cache/huggingface:/root/.cache/huggingface \
253
+ --entrypoint /bin/bash \
254
+ ghcr.io/local-inference-lab/vllm:release-10152026
255
+ ```
256
+
257
+ Then launch vLLM inside the container:
258
+
259
+ ```sh
260
+ export B12X_DENSE_SPLITK_TURBO=1
261
+ export B12X_DYNAMIC_DETERMINISTIC_OUTPUT=0
262
+ export B12X_DYNAMIC_DIRECT_EXPERT_SCALES=1
263
+ export B12X_DYNAMIC_SKIP_SPLIT_BARRIER_RESET=1
264
+ export B12X_DYNAMIC_SPLIT_COMPUTE_MAC=224
265
+ export B12X_DYNAMIC_SPLIT_FAST_PREPARE=1
266
+ export B12X_DYNAMIC_SPLIT_LOW_SMEM=1
267
+ export B12X_DYNAMIC_SPLIT_ROUTE_COMPUTE=1
268
+ export B12X_DYNAMIC_WORK_SOURCE=persistent_grid
269
+ export B12X_W4A16_A4_PREFILL=1
270
+ export B12X_DISK_BACKEND=io_uring
271
+ export B12X_MHC_PDL=1
272
+ export B12X_PCIE_ONESHOT_BLOCK_LIMIT=4
273
+ export B12X_PCIE_ONESHOT_PDL=1
274
+ export B12X_PCIE_ONESHOT_THREADS=512
275
+ export CUDA_BINARY_LOADER_THREAD_COUNT=8
276
+ export CUDA_DEVICE_ORDER=PCI_BUS_ID
277
+ export CUDA_MODULE_LOADING=LAZY
278
+ export CUTE_DSL_ARCH=sm_120a
279
+ export INSTANTTENSOR_BACKEND=BUFFERED
280
+ export NCCL_BUFFSIZE=2097152
281
+ export NCCL_CUMEM_ENABLE=0
282
+ export NCCL_IB_DISABLE=1
283
+ export NCCL_MAX_NCHANNELS=16
284
+ export NCCL_MIN_NCHANNELS=16
285
+ export NCCL_NET_PLUGIN=spcx
286
+ export NCCL_P2P_LEVEL=SYS
287
+ export NCCL_PROTO=LL,LL128,Simple
288
+ export OMP_NUM_THREADS=2
289
+ export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
290
+ export SAFETENSORS_FAST_GPU=1
291
+ export TORCHINDUCTOR_LOOP_ORDERING_AFTER_FUSION=0
292
+ export TORCH_CUDA_ARCH_LIST=12.0a
293
+ export VLLM_B12X_DENSE_ACTIVATION_MODE=auto
294
+ export VLLM_B12X_MOE_FP4_FORCE_A16=1
295
+ export VLLM_CAUSAL_CONV1D_UPDATE_HOIST=1
296
+ export VLLM_COMPUTE_NANS_IN_LOGITS=0
297
+ export VLLM_DISABLED_KERNELS=MarlinFP8ScaledMMLinearKernel
298
+ export VLLM_ENABLE_PCIE_ALLREDUCE=1
299
+ export VLLM_GDN_SPEC_DECODE_METADATA_FASTPATH=1
300
+ export VLLM_LM_HEAD_A16=1
301
+ export VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0
302
+ export VLLM_MTP_NVFP4_LM_HEAD=1
303
+ export VLLM_MXFP8_LM_HEAD=0
304
+ export VLLM_PCIE_ALLREDUCE_BACKEND=b12x
305
+ export VLLM_PLE_CPU_OFFLOAD=1
306
+ export VLLM_QWEN3_8_FLASH_NEXT_HC_TP=0
307
+ export VLLM_QWEN3_8_FLASH_NEXT_MTP_COMPACT=1
308
+ export VLLM_QWEN3_8_FLASH_NEXT_OVERLAP=1
309
+ export VLLM_SSM_CONV_STATE_LAYOUT=DS
310
+ export VLLM_USE_FASTOKENS=1
311
+ export VLLM_USE_V2_MODEL_RUNNER=1
312
+ export VLLM_WORKER_MULTIPROC_METHOD=spawn
313
+ export TORCH_ALLOW_TF32_CUBLAS_OVERRIDE=1
314
+ export TORCH_NCCL_USE_COMM_NONBLOCKING=0
315
+
316
+ vllm serve nvidia/Qwen3.8-Flash-Next-FP4-Local \
317
+ --host 0.0.0.0 \
318
+ --port 8000 \
319
+ --async-scheduling \
320
+ --block-size 64 \
321
+ --compilation-config '{"cudagraph_mode":"FULL_AND_PIECEWISE","custom_ops":["all"]}' \
322
+ --decode-context-parallel-size 1 \
323
+ --dtype bfloat16 \
324
+ --enable-auto-tool-choice \
325
+ --enable-chunked-prefill \
326
+ --no-enable-flashinfer-autotune \
327
+ --enable-prefix-caching \
328
+ --enable-prompt-tokens-details \
329
+ --gdn-decode-kernel b12x \
330
+ --gpu-memory-utilization 0.975 \
331
+ --kv-cache-dtype fp8 \
332
+ --no-language-model-only \
333
+ --linear-backend b12x \
334
+ --load-format instanttensor \
335
+ --mamba-cache-mode align \
336
+ --mamba-ssm-cache-dtype auto \
337
+ --max-cudagraph-capture-size 64 \
338
+ --max-model-len 262144 \
339
+ --max-num-batched-tokens 6019 \
340
+ --max-num-seqs 16 \
341
+ --mm-encoder-tp-mode data \
342
+ --mm-processor-cache-gb 0.0 \
343
+ --moe-backend b12x \
344
+ --pipeline-parallel-size 1 \
345
+ --quantization modelopt_mixed \
346
+ --reasoning-parser qwen3 \
347
+ --speculative-config '{"method":"mtp","draft_sample_method":"probabilistic","rejection_sample_method":"standard","moe_backend":"b12x","num_speculative_tokens":3}' \
348
+ --tensor-parallel-size 1 \
349
+ --tool-call-parser qwen3_xml
350
+ ```
351
+
352
+ ## Ethical Considerations
353
+
354
+ NVIDIA believes Trustworthy AI is a shared responsibility and we have established policies and practices to enable development for a wide array of AI applications. When downloaded or used in accordance with our terms of service, developers should work with their internal model team to ensure this model meets requirements for the relevant industry and use case and addresses unforeseen product misuse.
355
+
356
+ For more detailed information on ethical considerations for this model, please see the Model Card++ Explainability, Bias, Safety & Security, and Privacy Subcards below.
357
+
358
+ Please report model quality, risk, security vulnerabilities or NVIDIA AI Concerns [here](https://www.nvidia.com/en-us/support/submit-security-vulnerability/).
359
+
360
+ SUBCARDS:
361
+ ## Explainability
362
+
363
+
364
+ | Field | Response |
365
+ |:----------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
366
+ | **Intended Task/Domain:** | Text generation, reasoning, summarization, and question answering. |
367
+ | **Model Type:** | Text and Image-to-text transformer |
368
+ | **Intended Users:** | This model is intended for developers, researchers, and customers building/utilizing LLMs, while balancing accuracy and efficiency. |
369
+ | **Output:** | Text String(s) |
370
+ | **Describe how the model works:** | Generates text by predicting the next word or token based on the context provided in the input sequence using multiple self-attention layers. |
371
+ | **Name the adversely impacted groups this has been tested to deliver comparable outcomes regardless of:** | Not Applicable |
372
+ | **Technical Limitations & Mitigation:** | The model was trained on data that contains toxic language and societal biases originally crawled from the internet. Therefore, the model may amplify those biases and return toxic responses especially when prompted with toxic prompts. Therefore, before deploying any applications of this model, developers should perform safety testing and tuning tailored to their specific applications of the model. |
373
+ | **Verified to have met prescribed NVIDIA quality standards:** | Yes |
374
+ | **Performance Metrics:** | Accuracy, Throughput, and user-side throughput |
375
+ | **Potential Known Risk** | The model may generate answers that may be inaccurate, omit key information, or include irrelevant or redundant text producing socially unacceptable or undesirable text, even if the prompt itself does not include anything explicitly offensive. |
376
+ | **Licensing:** | **GOVERNING DOWNLOAD TERMS:** Use of this model is governed by [NVIDIA Open Model Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-agreement/).
377
+
378
+ **ADDITIONAL INFORMATION** : The Qwen3.8-Flash-Next base model is governed by the [Qwen Community License 1.0](https://huggingface.co/Qwen/Qwen3.8-Flash-Next/blob/main/LICENSE). |
379
+
380
+
381
+ ## Bias
382
+
383
+ | Field | Response |
384
+ |:---------------------------------------------------------------------------------------------------------------:|:--------:|
385
+ | Participation considerations from adversely impacted groups [protected classes](https://www.senate.ca.gov/protected-classes) in model design and testing: | None |
386
+ | Measures taken to mitigate against unwanted bias | None |
387
+
388
+
389
+ ## Safety & Security
390
+
391
+ | Field | Response |
392
+ |:-----------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
393
+ | Model Application Field(s): | Chat, Instruction Following, Chatbot Development, Code Generation, Reasoning |
394
+ | Describe the life critical impact (if present) | Not Applicable |
395
+ | Use Case Restrictions: | Abide by the **GOVERNING DOWNLOAD TERMS:** Use of this model is governed by [NVIDIA Open Model Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-agreement/).
396
+
397
+ **ADDITIONAL INFORMATION** : The Qwen3.8-Flash-Next base model is governed by the [Qwen Community License 1.0](https://huggingface.co/Qwen/Qwen3.8-Flash-Next/blob/main/LICENSE). |
398
+ | Model and Dataset Restrictions: | The Principle of least privilege (PoLP) is applied limiting access for dataset generation. Restrictions enforce dataset access during training, and dataset license constraints adhered to. Model checkpoints are made available on Hugging Face, and may become available on cloud providers' model catalog. |
399
+
400
+
401
+ ## Privacy
402
+
403
+
404
+ | Field | Response |
405
+ | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
406
+ | **Generatable or reverse-engineerable personal data** | No
407
+ | **Personal data used to create this model** | None Known | |
408
+ | **Was consent obtained for any personal data used** | Not Applicable |
409
+ | **How often is the dataset reviewed** | Before Release |
410
+ | **Was data from user interactions with the AI model used to train the model** | No |
411
+ | **Is there provenance for all datasets used in training** | Yes |
412
+ | **Does data labeling comply with privacy laws** | Yes |
413
+ | **Is the data compliant with data-subject requests for correction or removal** | No, not possible with externally-sourced data. |
414
+ | **Applicable NVIDIA Privacy Policy** | [NVIDIA Privacy Policy](https://www.nvidia.com/en-us/about-nvidia/privacy-policy/) |
README.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: nvidia-open-model-agreement
4
+ license_link: LICENSE
5
+ base_model: Qwen/Qwen3.8-Flash-Next
6
+ base_model_relation: quantized
7
+ pipeline_tag: image-text-to-text
8
+ library_name: vllm
9
+ tags:
10
+ - qwen3.8
11
+ - qwen3.8-flash-next
12
+ - nvfp4
13
+ - modelopt
14
+ - mixture-of-experts
15
+ - vllm
16
+ - b12x
17
+ - speculative-decoding
18
+ - mtp
19
+ - dgx-spark
20
+ - gb10
21
+ - blackwell
22
+ ---
23
+
24
+ # Qwen3.8-Flash-Next FP4-Local for one DGX Spark
25
+
26
+ NVIDIA's [Qwen3.8-Flash-Next-FP4-Local](https://huggingface.co/nvidia/Qwen3.8-Flash-Next-FP4-Local) checkpoint
27
+ (unmodified), with a serving recipe for **a single DGX Spark** (GB10, 128 GB unified memory) on vLLM and the b12x
28
+ kernels. The recipe is a pinned public image plus 22 patched or new vLLM / b12x files, all in `recipe/`.
29
+
30
+ - **Agentic coding sessions: 63–64 tok/s decode** (54 ms per speculative step, 3.4–3.6 tokens accepted per step) and
31
+ 0.76 s median time to first token on follow-up turns. The public 40-prompt category harness: 72.8–73.2 tok/s median.
32
+ - **Prefill 2,130–2,160 tok/s** from 16K to 64K tokens (2,290 with the opt-in `A4_PREFILL=1`, at W4A4-level KL).
33
+ - **KL divergence 0.0492 as served** against the BF16 model on AtomicChat's neutral set (top-1 91.7%, PPL +0.8%).
34
+ - GSM8K (first 200) 96.5 %, HumanEval 95.7 % (greedy, thinking off); a needle is found at 29K, 96K and 125K tokens.
35
+
36
+ Measured on one DGX Spark at a 0.70 GPU-memory fraction (131K context, FP8 KV cache, 4 MTP draft tokens, CUDA
37
+ graphs). Changes over the stock launcher: 71.2 → 54.2 ms per decode step, 46.9 → 64 tok/s on the same sessions.
38
+
39
+ ## Fidelity
40
+
41
+ Full-vocabulary KL(BF16 ‖ served model) in llama.cpp `--kl-divergence` semantics over AtomicChat's held-out neutral
42
+ set: the original BF16 model's logits, 87 chunks of 4096 tokens, 178,089 scored positions, BF16 PPL 4.045
43
+ ([reference](https://huggingface.co/datasets/AtomicChat/Qwen3.8-Flash-Next-Uncensored-GGUF-metrics)). Every row is
44
+ measured against that same reference.
45
+
46
+ | Model | Size | Mean KLD | Top-1 | PPL ratio |
47
+ |---|---:|---:|---:|---:|
48
+ | **This repo, as served** (W4A16 experts) | 102 GB | **0.0492** | **91.7 %** | 1.008 |
49
+ | Same checkpoint, vLLM default FP4 activations (W4A4) | 102 GB | 0.0665 | 90.4 % | |
50
+ | This repo with `A4_PREFILL=1` (FP4 activations for prefill-size MoE calls) | 102 GB | 0.0665 | 90.3 % | 1.012 |
51
+ | Vardaan97 Splash Quality-PLE8 GGUF ([card](https://huggingface.co/Vardaan97/Qwen3.8-Flash-Next-Splash-GGUF)) | 129–157 GB | 0.0126 | | 1.000 |
52
+ | unsloth UD-Q4_K_XL + Q8 n-gram table (measured in the Splash card) | 141 GB | 0.0210 | 94.8 % | 1.005 |
53
+ | AtomicChat AD-4.27bpw-Q4_K_M ([card](https://huggingface.co/AtomicChat/Qwen3.8-Flash-Next-GGUF)) | 95 GB | 0.0842 | 89.5 % | 1.026 |
54
+
55
+ "As served" is the server's own forward pass through `recipe/patches/qwen4_exp/nvidia/logit_dump.py`, scored with
56
+ `recipe/kl/atomic_kld.py`. NVIDIA's FP4 checkpoint is further from BF16 than the best GGUF builds; what it buys on the
57
+ Spark is vLLM serving speed. Running the routed experts with bf16 activations (`MOE_A16`, the setting NVIDIA's own
58
+ card recommends) is both closer to BF16 and faster than the FP4-activation default.
59
+
60
+ Quality probes with thinking off and greedy decoding (`recipe/bench/quality-probe.py`):
61
+
62
+ | | GSM8K-200 | HumanEval |
63
+ |---|---:|---:|
64
+ | This repo, as served | 96.5 % | 95.7 % |
65
+
66
+ A needle planted in prompts of 28,842, 96,042 and 124,842 tokens is found at every length
67
+ (`recipe/bench/needle-test.py`), and tool calls with `tool_choice=auto` parse (`recipe/bench/test-toolcall.py`).
68
+
69
+ ## Speed
70
+
71
+ Test setup: one DGX Spark (GB10), `recipe/serve/serve.sh` with its defaults, no other clients, Spark idle otherwise.
72
+ Throughput is in tokens per second.
73
+
74
+ **Agentic coding sessions** (private session set, not published: multi-turn coding agents with tool definitions and
75
+ tool calls in the history; 1,362 decode steps per run):
76
+
77
+ | | Decode (weighted) | Decode (median per turn) | Time to first token (median) | Tokens per step |
78
+ |---|---:|---:|---:|---:|
79
+ | This release | 63.5–64.0 | 63.7–63.9 | 0.74–0.76 s | 3.40–3.56 |
80
+ | Stock launcher (image + NVIDIA's flags) | 46.9 | | | |
81
+
82
+ **Public 40-prompt category harness** (tonyd2wild's `bench_categories.py`, thinking off, temperature 0): 72.8–73.2 tok/s
83
+ median decode, 0.28 s median time to first token, 53.5 ms per step.
84
+
85
+ **Prefill** (cold, single request):
86
+
87
+ | Prompt | Prefill |
88
+ |---:|---:|
89
+ | 2K tokens | 1,696 tok/s |
90
+ | 16K tokens | 2,134–2,169 tok/s |
91
+ | 64K tokens | 2,131 tok/s |
92
+
93
+ `A4_PREFILL=1` runs prefill-size MoE calls with FP4 activations, as NVIDIA's card configures: 1,803 / 2,290 / 2,293 tok/s
94
+ at 2K / 16K / 64K tokens (+6–8%), with decode unchanged, but the KL of prompt processing rises to the W4A4 level
95
+ (table above), so it is off by default.
96
+
97
+ **Synthetic prompts** (`recipe/bench/bench-sbs.py`: a random-word document plus a question, non-streaming; decode
98
+ speed is the difference between a 400-token and a 16-token reply on a warm prefix cache). Random-word text is hard
99
+ for the drafter, so decode here is below the agentic numbers above:
100
+
101
+ | Benchmark | Prefill | Time to first token | Decode |
102
+ |---|---:|---:|---:|
103
+ | 2K prompt, 1 stream | 1,850 | 1.1 s | 51.7 |
104
+ | 2K prompts, 4 streams | 2,230 aggregate | 4.1 s | 107.0 aggregate (26.7 per stream) |
105
+ | 46K prompt, 1 stream | 2,259 | 20.3 s | 45.8 |
106
+ | 46K prompts, 4 streams | ~2,190 aggregate (184K tokens in 84 s) | 84 s | 37.4 aggregate (9.4 per stream) |
107
+
108
+ Four 46K-token contexts (184K tokens) exceed the KV cache, so vLLM preempts and recomputes; the configuration is
109
+ sized for one long agentic session at a time.
110
+
111
+ ## How it fits in one Spark
112
+
113
+ The FP4-Local checkpoint is 102 GB, including 51B parameters of n-gram (PLE) embedding tables. The launcher serves
114
+ those tables from NVMe instead of memory (b12x reads them with io_uring each step, overlapped with the first decoder
115
+ layer and kept warm with a keepalive read), so about 68 GiB of weights stay resident. vLLM gets 70% of the unified
116
+ memory; the rest stays free for the OS and other services. The KV cache holds about 175–240K tokens depending on the
117
+ boot, enough for one 131K-token request plus a smaller one.
118
+
119
+ ## What's in this repo
120
+
121
+ | Files | Contents |
122
+ |---|---|
123
+ | `tensors/`, `config.json`, tokenizer, processor and generation files, `chat_template.jinja` | NVIDIA's `nvidia/Qwen3.8-Flash-Next-FP4-Local`, unmodified. |
124
+ | `NVIDIA_MODEL_CARD.md` | NVIDIA's model card for those files. |
125
+ | `recipe/` | Launcher, patches, draft vocabulary, benchmarks, KL client, raw results (`recipe/README.md`). |
126
+ | `LICENSE`, `LICENSE.Qwen`, `NOTICE` | NVIDIA Open Model Agreement, Qwen Community License 1.0, attribution. |
127
+
128
+ ## Quick start
129
+
130
+ You need one DGX Spark (or another GB10 system with 128 GB), Docker with the NVIDIA container toolkit, and about
131
+ 110 GB of free disk on fast NVMe.
132
+
133
+ ```bash
134
+ hf download diffbot/Qwen3.8-Flash-Next-FP4-Local-DGX-Spark --local-dir ~/models/Qwen3.8-Flash-Next-FP4-Local-DGX-Spark
135
+ cd ~/models/Qwen3.8-Flash-Next-FP4-Local-DGX-Spark/recipe
136
+ API_KEY=change-me bash serve/serve.sh # OpenAI-compatible API on :8000, model qwen3.8-flash-next
137
+ ```
138
+
139
+ The first start compiles and autotunes the b12x kernels into `~/.cache/qwen38-flash-next-spark` and took 15.5 minutes
140
+ from an empty cache on our Spark; later starts take about 5 minutes. Tool calling uses vLLM's `qwen3_xml` parser and reasoning the `qwen3` parser.
141
+
142
+ ## Limitations
143
+
144
+ - Tested only on one DGX Spark with the pinned image; other GB10 systems should work but are untested.
145
+ - b12x's MoE sums expert outputs with atomics, so greedy outputs can differ between runs. `MOE_DETERMINISTIC=1`
146
+ fixes this only for FP4-activation MoE.
147
+ - `TAIL_CKPT` places the prompt checkpoint just before the prompt end; upstream vLLM's `--prefix-match-unit` does not
148
+ replace it on this image.
149
+ - Up to 4 concurrent requests (`MAX_NUM_SEQS`); the configuration is tuned for single-user agentic work.
150
+ - The quality evidence is the KL table, GSM8K-200, HumanEval and needle tests, not a full benchmark suite.
151
+
152
+ ## Credits and license
153
+
154
+ Built on the work of NVIDIA (the FP4-Local checkpoint and Model Optimizer), the Qwen team (Qwen3.8-Flash-Next),
155
+ local-inference-lab and the FlashInfer team (b12x), eugr (the Spark vLLM + b12x image), the vLLM team, AtomicChat
156
+ (the KL reference) and Vardaan97 (the Splash measurements on the same reference).
157
+
158
+ The weights are NVIDIA's, under the NVIDIA Open Model Agreement (`LICENSE`): "Licensed by NVIDIA Corporation under the
159
+ NVIDIA Open Model Agreement." The base model is under the Qwen Community License 1.0 (`LICENSE.Qwen`), which requires a
160
+ separate license from Qwen for commercial "Model as a Service" offerings. The recipe's own code is MIT; the files in
161
+ `recipe/patches/` are Apache-2.0 (`recipe/THIRD_PARTY_NOTICES.md`).
chat_template.jinja ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- set image_count = namespace(value=0) %}
2
+ {%- set video_count = namespace(value=0) %}
3
+ {%- macro render_content(content, do_vision_count, is_system_content=false) %}
4
+ {%- if content is string %}
5
+ {{- content }}
6
+ {%- elif content is iterable and content is not mapping %}
7
+ {%- for item in content %}
8
+ {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
9
+ {%- if is_system_content %}
10
+ {{- raise_exception('System message cannot contain images.') }}
11
+ {%- endif %}
12
+ {%- if do_vision_count %}
13
+ {%- set image_count.value = image_count.value + 1 %}
14
+ {%- endif %}
15
+ {%- if add_vision_id %}
16
+ {{- 'Picture ' ~ image_count.value ~ ': ' }}
17
+ {%- endif %}
18
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' }}
19
+ {%- elif 'video' in item or item.type == 'video' %}
20
+ {%- if is_system_content %}
21
+ {{- raise_exception('System message cannot contain videos.') }}
22
+ {%- endif %}
23
+ {%- if do_vision_count %}
24
+ {%- set video_count.value = video_count.value + 1 %}
25
+ {%- endif %}
26
+ {%- if add_vision_id %}
27
+ {{- 'Video ' ~ video_count.value ~ ': ' }}
28
+ {%- endif %}
29
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' }}
30
+ {%- elif 'text' in item %}
31
+ {{- item.text }}
32
+ {%- else %}
33
+ {{- raise_exception('Unexpected item type in content.') }}
34
+ {%- endif %}
35
+ {%- endfor %}
36
+ {%- elif content is none or content is undefined %}
37
+ {{- '' }}
38
+ {%- else %}
39
+ {{- raise_exception('Unexpected content type.') }}
40
+ {%- endif %}
41
+ {%- endmacro %}
42
+ {%- if not messages %}
43
+ {{- raise_exception('No messages provided.') }}
44
+ {%- endif %}
45
+ {%- set reasoning_instructions = '' %}
46
+ {%- if enable_thinking is undefined or enable_thinking is true %}
47
+ {%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}
48
+ {%- if resolved_reasoning_effort not in ('xhigh', 'medium', 'low') %}
49
+ {{- raise_exception('Unexpected reasoning effort ' ~ reasoning_effort ~ '. Supported types are xhigh (default), medium, and low.') }}
50
+ {%- endif %}
51
+ {%- if resolved_reasoning_effort == 'xhigh' %}
52
+ {%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}
53
+ {%- elif resolved_reasoning_effort == 'low' %}
54
+ {%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}
55
+ {%- endif %}
56
+ {%- endif %}
57
+ {%- if tools and tools is iterable and tools is not mapping %}
58
+ {{- '<|im_start|>system\n' }}
59
+ {%- if reasoning_instructions %}
60
+ {{- reasoning_instructions + '\n\n' }}
61
+ {%- endif %}
62
+ {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
63
+ {%- for tool in tools %}
64
+ {{- "\n" }}
65
+ {{- tool | tojson }}
66
+ {%- endfor %}
67
+ {{- "\n</tools>" }}
68
+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
69
+ {%- if messages[0].role == 'system' %}
70
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
71
+ {%- if content %}
72
+ {{- '\n\n' + content }}
73
+ {%- endif %}
74
+ {%- endif %}
75
+ {{- '<|im_end|>\n' }}
76
+ {%- else %}
77
+ {%- if messages[0].role == 'system' %}
78
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
79
+ {%- if content %}
80
+ {{- '<|im_start|>system\n' + (reasoning_instructions + '\n\n' if reasoning_instructions else '') + content + '<|im_end|>\n' }}
81
+ {%- elif reasoning_instructions %}
82
+ {{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}
83
+ {%- endif %}
84
+ {%- elif reasoning_instructions %}
85
+ {{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}
86
+ {%- endif %}
87
+ {%- endif %}
88
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
89
+ {%- for message in messages[::-1] %}
90
+ {%- set index = (messages|length - 1) - loop.index0 %}
91
+ {%- if ns.multi_step_tool and message.role == "user" %}
92
+ {%- set content = render_content(message.content, false)|trim %}
93
+ {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
94
+ {%- set ns.multi_step_tool = false %}
95
+ {%- set ns.last_query_index = index %}
96
+ {%- endif %}
97
+ {%- endif %}
98
+ {%- endfor %}
99
+ {%- if ns.multi_step_tool %}
100
+ {{- raise_exception('No user query found in messages.') }}
101
+ {%- endif %}
102
+ {%- for message in messages %}
103
+ {%- set content = render_content(message.content, true)|trim %}
104
+ {%- if message.role == "system" %}
105
+ {%- if not loop.first %}
106
+ {{- raise_exception('System message must be at the beginning.') }}
107
+ {%- endif %}
108
+ {%- elif message.role == "user" %}
109
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
110
+ {%- elif message.role == "assistant" %}
111
+ {%- set reasoning_content = '' %}
112
+ {%- if message.reasoning_content is string %}
113
+ {%- set reasoning_content = message.reasoning_content %}
114
+ {%- endif %}
115
+ {%- set reasoning_content = reasoning_content|trim %}
116
+ {%- if preserve_thinking is undefined or preserve_thinking is true or loop.index0 > ns.last_query_index %}
117
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
118
+ {%- else %}
119
+ {{- '<|im_start|>' + message.role + '\n' + content }}
120
+ {%- endif %}
121
+ {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
122
+ {%- for tool_call in message.tool_calls %}
123
+ {%- if tool_call.function is defined %}
124
+ {%- set tool_call = tool_call.function %}
125
+ {%- endif %}
126
+ {%- if loop.first %}
127
+ {%- if content|trim %}
128
+ {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
129
+ {%- else %}
130
+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
131
+ {%- endif %}
132
+ {%- else %}
133
+ {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
134
+ {%- endif %}
135
+ {%- if tool_call.arguments is defined and tool_call.arguments != '' %}
136
+ {%- for args_name, args_value in tool_call.arguments|items %}
137
+ {{- '<parameter=' + args_name + '>\n' }}
138
+ {%- set args_value = args_value | string if args_value is string else args_value | tojson | safe %}
139
+ {{- args_value }}
140
+ {{- '\n</parameter>\n' }}
141
+ {%- endfor %}
142
+ {%- endif %}
143
+ {{- '</function>\n</tool_call>' }}
144
+ {%- endfor %}
145
+ {%- endif %}
146
+ {{- '<|im_end|>\n' }}
147
+ {%- elif message.role == "tool" %}
148
+ {%- if loop.previtem and loop.previtem.role != "tool" %}
149
+ {{- '<|im_start|>user' }}
150
+ {%- endif %}
151
+ {{- '\n<tool_response>\n' }}
152
+ {{- content }}
153
+ {{- '\n</tool_response>' }}
154
+ {%- if not loop.last and loop.nextitem.role != "tool" %}
155
+ {{- '<|im_end|>\n' }}
156
+ {%- elif loop.last %}
157
+ {{- '<|im_end|>\n' }}
158
+ {%- endif %}
159
+ {%- else %}
160
+ {{- raise_exception('Unexpected message role.') }}
161
+ {%- endif %}
162
+ {%- endfor %}
163
+ {%- if add_generation_prompt %}
164
+ {{- '<|im_start|>assistant\n' }}
165
+ {%- if enable_thinking is defined and enable_thinking is false %}
166
+ {{- '<think>\n\n</think>\n\n' }}
167
+ {%- else %}
168
+ {{- '<think>\n' }}
169
+ {%- endif %}
170
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,2402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen4ExpForConditionalGeneration"
4
+ ],
5
+ "image_token_id": 248056,
6
+ "language_model_only": false,
7
+ "model_type": "qwen4_exp",
8
+ "text_config": {
9
+ "attention_bias": false,
10
+ "attention_dropout": 0.0,
11
+ "bos_token_id": 248044,
12
+ "dtype": "bfloat16",
13
+ "eos_token_id": 248044,
14
+ "full_attention_interval": 4,
15
+ "hc_count": 4,
16
+ "hc_lowrank": 320,
17
+ "head_dim": 256,
18
+ "heads_per_ngram": 8,
19
+ "hidden_act": "silu",
20
+ "hidden_size": 2560,
21
+ "indexer_budget": 2048,
22
+ "indexer_compress_ratio": 4,
23
+ "indexer_head_dim": 128,
24
+ "indexer_kv_heads": 1,
25
+ "indexer_n_heads": 4,
26
+ "initializer_range": 0.02,
27
+ "layer_types": [
28
+ "linear_attention",
29
+ "linear_attention",
30
+ "linear_attention",
31
+ "full_attention",
32
+ "linear_attention",
33
+ "linear_attention",
34
+ "linear_attention",
35
+ "full_attention",
36
+ "linear_attention",
37
+ "linear_attention",
38
+ "linear_attention",
39
+ "full_attention",
40
+ "linear_attention",
41
+ "linear_attention",
42
+ "linear_attention",
43
+ "full_attention",
44
+ "linear_attention",
45
+ "linear_attention",
46
+ "linear_attention",
47
+ "full_attention",
48
+ "linear_attention",
49
+ "linear_attention",
50
+ "linear_attention",
51
+ "full_attention",
52
+ "linear_attention",
53
+ "linear_attention",
54
+ "linear_attention",
55
+ "full_attention",
56
+ "linear_attention",
57
+ "linear_attention",
58
+ "linear_attention",
59
+ "full_attention",
60
+ "linear_attention",
61
+ "linear_attention",
62
+ "linear_attention",
63
+ "full_attention",
64
+ "linear_attention",
65
+ "linear_attention",
66
+ "linear_attention",
67
+ "full_attention",
68
+ "linear_attention",
69
+ "linear_attention",
70
+ "linear_attention",
71
+ "full_attention",
72
+ "linear_attention",
73
+ "linear_attention",
74
+ "linear_attention",
75
+ "full_attention"
76
+ ],
77
+ "linear_conv_kernel_dim": 4,
78
+ "linear_key_head_dim": 128,
79
+ "linear_num_key_heads": 16,
80
+ "linear_num_value_heads": 48,
81
+ "linear_value_head_dim": 128,
82
+ "make_ngram_vocab_size_divisible_by": 128,
83
+ "mamba_ssm_dtype": "float32",
84
+ "max_position_embeddings": 262144,
85
+ "model_type": "qwen4_exp_text",
86
+ "moe_intermediate_size": 640,
87
+ "mtp": {
88
+ "hybrid": true,
89
+ "layer_types": [
90
+ "full_attention"
91
+ ],
92
+ "mtp_use_hidden_state_from_layer": null,
93
+ "num_hidden_layers": 1,
94
+ "rope_theta": 10000000
95
+ },
96
+ "mtp_num_hidden_layers": 1,
97
+ "mtp_use_dedicated_embeddings": false,
98
+ "ngram_size": 3,
99
+ "ngram_vocab_size_base": 20000000,
100
+ "num_attention_heads": 24,
101
+ "num_experts": 512,
102
+ "num_experts_per_tok": 10,
103
+ "num_hidden_layers": 48,
104
+ "num_key_value_heads": 2,
105
+ "output_gate_type": "sigmoid",
106
+ "output_router_logits": false,
107
+ "pad_token_id": null,
108
+ "partial_rotary_factor": 0.25,
109
+ "ple_conv_kernel_size": 4,
110
+ "ple_embed_dim": 2560,
111
+ "ple_layer_ids": [
112
+ 2
113
+ ],
114
+ "rms_norm_eps": 1e-06,
115
+ "rope_parameters": {
116
+ "mrope_interleaved": true,
117
+ "mrope_section": [
118
+ 11,
119
+ 11,
120
+ 10
121
+ ],
122
+ "partial_rotary_factor": 0.25,
123
+ "rope_theta": 10000000,
124
+ "rope_type": "default"
125
+ },
126
+ "router_aux_loss_coef": 0.001,
127
+ "shared_expert_intermediate_size": 640,
128
+ "split_ngram_parts": 128,
129
+ "tie_word_embeddings": false,
130
+ "use_cache": true,
131
+ "vocab_size": 248320,
132
+ "ple_embedding_dtype": "nvfp4"
133
+ },
134
+ "tie_word_embeddings": false,
135
+ "transformers_version": "5.8.0.dev0",
136
+ "video_token_id": 248057,
137
+ "vision_config": {
138
+ "deepstack_visual_indexes": [],
139
+ "depth": 27,
140
+ "hidden_act": "gelu_pytorch_tanh",
141
+ "hidden_size": 1152,
142
+ "in_channels": 3,
143
+ "initializer_range": 0.02,
144
+ "intermediate_size": 4304,
145
+ "model_type": "qwen4_exp",
146
+ "num_heads": 16,
147
+ "num_position_embeddings": 2304,
148
+ "out_hidden_size": 2560,
149
+ "patch_size": 16,
150
+ "spatial_merge_size": 2,
151
+ "temporal_patch_size": 2
152
+ },
153
+ "vision_end_token_id": 248054,
154
+ "vision_start_token_id": 248053,
155
+ "quantization_config": {
156
+ "quant_method": "modelopt",
157
+ "quant_algo": "MIXED_PRECISION",
158
+ "kv_cache_quant_algo": null,
159
+ "ignore": [
160
+ "lm_head",
161
+ "mtp.fc_embedding",
162
+ "mtp.fc_hidden",
163
+ "mtp.pre_fc_norm_*",
164
+ "mtp.hyper_connection_mixer*",
165
+ "mtp.layers.0.self_attn*",
166
+ "mtp.layers.0.attn_hyper_connection*",
167
+ "mtp.layers.0.mlp_hyper_connection*",
168
+ "mtp.layers.0.mlp.gate",
169
+ "mtp.layers.0.mlp.shared_expert*"
170
+ ],
171
+ "quantized_layers": {
172
+ "model.language_model.layers.0.mlp.experts": {
173
+ "quant_algo": "NVFP4",
174
+ "group_size": 16,
175
+ "weight_scale_encoding": "csf"
176
+ },
177
+ "model.language_model.layers.1.mlp.experts": {
178
+ "quant_algo": "NVFP4",
179
+ "group_size": 16,
180
+ "weight_scale_encoding": "csf"
181
+ },
182
+ "model.language_model.layers.2.mlp.experts": {
183
+ "quant_algo": "NVFP4",
184
+ "group_size": 16,
185
+ "weight_scale_encoding": "csf"
186
+ },
187
+ "model.language_model.layers.3.mlp.experts": {
188
+ "quant_algo": "NVFP4",
189
+ "group_size": 16,
190
+ "weight_scale_encoding": "csf"
191
+ },
192
+ "model.language_model.layers.4.mlp.experts": {
193
+ "quant_algo": "NVFP4",
194
+ "group_size": 16,
195
+ "weight_scale_encoding": "csf"
196
+ },
197
+ "model.language_model.layers.5.mlp.experts": {
198
+ "quant_algo": "NVFP4",
199
+ "group_size": 16,
200
+ "weight_scale_encoding": "csf"
201
+ },
202
+ "model.language_model.layers.6.mlp.experts": {
203
+ "quant_algo": "NVFP4",
204
+ "group_size": 16,
205
+ "weight_scale_encoding": "csf"
206
+ },
207
+ "model.language_model.layers.7.mlp.experts": {
208
+ "quant_algo": "NVFP4",
209
+ "group_size": 16,
210
+ "weight_scale_encoding": "csf"
211
+ },
212
+ "model.language_model.layers.8.mlp.experts": {
213
+ "quant_algo": "NVFP4",
214
+ "group_size": 16,
215
+ "weight_scale_encoding": "csf"
216
+ },
217
+ "model.language_model.layers.9.mlp.experts": {
218
+ "quant_algo": "NVFP4",
219
+ "group_size": 16,
220
+ "weight_scale_encoding": "csf"
221
+ },
222
+ "model.language_model.layers.10.mlp.experts": {
223
+ "quant_algo": "NVFP4",
224
+ "group_size": 16,
225
+ "weight_scale_encoding": "csf"
226
+ },
227
+ "model.language_model.layers.11.mlp.experts": {
228
+ "quant_algo": "NVFP4",
229
+ "group_size": 16,
230
+ "weight_scale_encoding": "csf"
231
+ },
232
+ "model.language_model.layers.12.mlp.experts": {
233
+ "quant_algo": "NVFP4",
234
+ "group_size": 16,
235
+ "weight_scale_encoding": "csf"
236
+ },
237
+ "model.language_model.layers.13.mlp.experts": {
238
+ "quant_algo": "NVFP4",
239
+ "group_size": 16,
240
+ "weight_scale_encoding": "csf"
241
+ },
242
+ "model.language_model.layers.14.mlp.experts": {
243
+ "quant_algo": "NVFP4",
244
+ "group_size": 16,
245
+ "weight_scale_encoding": "csf"
246
+ },
247
+ "model.language_model.layers.15.mlp.experts": {
248
+ "quant_algo": "NVFP4",
249
+ "group_size": 16,
250
+ "weight_scale_encoding": "csf"
251
+ },
252
+ "model.language_model.layers.16.mlp.experts": {
253
+ "quant_algo": "NVFP4",
254
+ "group_size": 16,
255
+ "weight_scale_encoding": "csf"
256
+ },
257
+ "model.language_model.layers.17.mlp.experts": {
258
+ "quant_algo": "NVFP4",
259
+ "group_size": 16,
260
+ "weight_scale_encoding": "csf"
261
+ },
262
+ "model.language_model.layers.18.mlp.experts": {
263
+ "quant_algo": "NVFP4",
264
+ "group_size": 16,
265
+ "weight_scale_encoding": "csf"
266
+ },
267
+ "model.language_model.layers.19.mlp.experts": {
268
+ "quant_algo": "NVFP4",
269
+ "group_size": 16,
270
+ "weight_scale_encoding": "csf"
271
+ },
272
+ "model.language_model.layers.20.mlp.experts": {
273
+ "quant_algo": "NVFP4",
274
+ "group_size": 16,
275
+ "weight_scale_encoding": "csf"
276
+ },
277
+ "model.language_model.layers.21.mlp.experts": {
278
+ "quant_algo": "NVFP4",
279
+ "group_size": 16,
280
+ "weight_scale_encoding": "csf"
281
+ },
282
+ "model.language_model.layers.22.mlp.experts": {
283
+ "quant_algo": "NVFP4",
284
+ "group_size": 16,
285
+ "weight_scale_encoding": "csf"
286
+ },
287
+ "model.language_model.layers.23.mlp.experts": {
288
+ "quant_algo": "NVFP4",
289
+ "group_size": 16,
290
+ "weight_scale_encoding": "csf"
291
+ },
292
+ "model.language_model.layers.24.mlp.experts": {
293
+ "quant_algo": "NVFP4",
294
+ "group_size": 16,
295
+ "weight_scale_encoding": "csf"
296
+ },
297
+ "model.language_model.layers.25.mlp.experts": {
298
+ "quant_algo": "NVFP4",
299
+ "group_size": 16,
300
+ "weight_scale_encoding": "csf"
301
+ },
302
+ "model.language_model.layers.26.mlp.experts": {
303
+ "quant_algo": "NVFP4",
304
+ "group_size": 16,
305
+ "weight_scale_encoding": "csf"
306
+ },
307
+ "model.language_model.layers.27.mlp.experts": {
308
+ "quant_algo": "NVFP4",
309
+ "group_size": 16,
310
+ "weight_scale_encoding": "csf"
311
+ },
312
+ "model.language_model.layers.28.mlp.experts": {
313
+ "quant_algo": "NVFP4",
314
+ "group_size": 16,
315
+ "weight_scale_encoding": "csf"
316
+ },
317
+ "model.language_model.layers.29.mlp.experts": {
318
+ "quant_algo": "NVFP4",
319
+ "group_size": 16,
320
+ "weight_scale_encoding": "csf"
321
+ },
322
+ "model.language_model.layers.30.mlp.experts": {
323
+ "quant_algo": "NVFP4",
324
+ "group_size": 16,
325
+ "weight_scale_encoding": "csf"
326
+ },
327
+ "model.language_model.layers.31.mlp.experts": {
328
+ "quant_algo": "NVFP4",
329
+ "group_size": 16,
330
+ "weight_scale_encoding": "csf"
331
+ },
332
+ "model.language_model.layers.32.mlp.experts": {
333
+ "quant_algo": "NVFP4",
334
+ "group_size": 16,
335
+ "weight_scale_encoding": "csf"
336
+ },
337
+ "model.language_model.layers.33.mlp.experts": {
338
+ "quant_algo": "NVFP4",
339
+ "group_size": 16,
340
+ "weight_scale_encoding": "csf"
341
+ },
342
+ "model.language_model.layers.34.mlp.experts": {
343
+ "quant_algo": "NVFP4",
344
+ "group_size": 16,
345
+ "weight_scale_encoding": "csf"
346
+ },
347
+ "model.language_model.layers.35.mlp.experts": {
348
+ "quant_algo": "NVFP4",
349
+ "group_size": 16,
350
+ "weight_scale_encoding": "csf"
351
+ },
352
+ "model.language_model.layers.36.mlp.experts": {
353
+ "quant_algo": "NVFP4",
354
+ "group_size": 16,
355
+ "weight_scale_encoding": "csf"
356
+ },
357
+ "model.language_model.layers.37.mlp.experts": {
358
+ "quant_algo": "NVFP4",
359
+ "group_size": 16,
360
+ "weight_scale_encoding": "csf"
361
+ },
362
+ "model.language_model.layers.38.mlp.experts": {
363
+ "quant_algo": "NVFP4",
364
+ "group_size": 16,
365
+ "weight_scale_encoding": "csf"
366
+ },
367
+ "model.language_model.layers.39.mlp.experts": {
368
+ "quant_algo": "NVFP4",
369
+ "group_size": 16,
370
+ "weight_scale_encoding": "csf"
371
+ },
372
+ "model.language_model.layers.40.mlp.experts": {
373
+ "quant_algo": "NVFP4",
374
+ "group_size": 16,
375
+ "weight_scale_encoding": "csf"
376
+ },
377
+ "model.language_model.layers.41.mlp.experts": {
378
+ "quant_algo": "NVFP4",
379
+ "group_size": 16,
380
+ "weight_scale_encoding": "csf"
381
+ },
382
+ "model.language_model.layers.42.mlp.experts": {
383
+ "quant_algo": "NVFP4",
384
+ "group_size": 16,
385
+ "weight_scale_encoding": "csf"
386
+ },
387
+ "model.language_model.layers.43.mlp.experts": {
388
+ "quant_algo": "NVFP4",
389
+ "group_size": 16,
390
+ "weight_scale_encoding": "csf"
391
+ },
392
+ "model.language_model.layers.44.mlp.experts": {
393
+ "quant_algo": "NVFP4",
394
+ "group_size": 16,
395
+ "weight_scale_encoding": "csf"
396
+ },
397
+ "model.language_model.layers.45.mlp.experts": {
398
+ "quant_algo": "NVFP4",
399
+ "group_size": 16,
400
+ "weight_scale_encoding": "csf"
401
+ },
402
+ "model.language_model.layers.46.mlp.experts": {
403
+ "quant_algo": "NVFP4",
404
+ "group_size": 16,
405
+ "weight_scale_encoding": "csf"
406
+ },
407
+ "model.language_model.layers.47.mlp.experts": {
408
+ "quant_algo": "NVFP4",
409
+ "group_size": 16,
410
+ "weight_scale_encoding": "csf"
411
+ },
412
+ "model.language_model.layers.0.mlp.shared_expert.gate_proj": {
413
+ "quant_algo": "MXFP8",
414
+ "group_size": 32
415
+ },
416
+ "model.language_model.layers.0.mlp.shared_expert.up_proj": {
417
+ "quant_algo": "MXFP8",
418
+ "group_size": 32
419
+ },
420
+ "model.language_model.layers.0.mlp.shared_expert.down_proj": {
421
+ "quant_algo": "MXFP8",
422
+ "group_size": 32
423
+ },
424
+ "model.language_model.layers.1.mlp.shared_expert.gate_proj": {
425
+ "quant_algo": "MXFP8",
426
+ "group_size": 32
427
+ },
428
+ "model.language_model.layers.1.mlp.shared_expert.up_proj": {
429
+ "quant_algo": "MXFP8",
430
+ "group_size": 32
431
+ },
432
+ "model.language_model.layers.1.mlp.shared_expert.down_proj": {
433
+ "quant_algo": "MXFP8",
434
+ "group_size": 32
435
+ },
436
+ "model.language_model.layers.2.mlp.shared_expert.gate_proj": {
437
+ "quant_algo": "MXFP8",
438
+ "group_size": 32
439
+ },
440
+ "model.language_model.layers.2.mlp.shared_expert.up_proj": {
441
+ "quant_algo": "MXFP8",
442
+ "group_size": 32
443
+ },
444
+ "model.language_model.layers.2.mlp.shared_expert.down_proj": {
445
+ "quant_algo": "MXFP8",
446
+ "group_size": 32
447
+ },
448
+ "model.language_model.layers.3.mlp.shared_expert.gate_proj": {
449
+ "quant_algo": "MXFP8",
450
+ "group_size": 32
451
+ },
452
+ "model.language_model.layers.3.mlp.shared_expert.up_proj": {
453
+ "quant_algo": "MXFP8",
454
+ "group_size": 32
455
+ },
456
+ "model.language_model.layers.3.mlp.shared_expert.down_proj": {
457
+ "quant_algo": "MXFP8",
458
+ "group_size": 32
459
+ },
460
+ "model.language_model.layers.4.mlp.shared_expert.gate_proj": {
461
+ "quant_algo": "MXFP8",
462
+ "group_size": 32
463
+ },
464
+ "model.language_model.layers.4.mlp.shared_expert.up_proj": {
465
+ "quant_algo": "MXFP8",
466
+ "group_size": 32
467
+ },
468
+ "model.language_model.layers.4.mlp.shared_expert.down_proj": {
469
+ "quant_algo": "MXFP8",
470
+ "group_size": 32
471
+ },
472
+ "model.language_model.layers.5.mlp.shared_expert.gate_proj": {
473
+ "quant_algo": "MXFP8",
474
+ "group_size": 32
475
+ },
476
+ "model.language_model.layers.5.mlp.shared_expert.up_proj": {
477
+ "quant_algo": "MXFP8",
478
+ "group_size": 32
479
+ },
480
+ "model.language_model.layers.5.mlp.shared_expert.down_proj": {
481
+ "quant_algo": "MXFP8",
482
+ "group_size": 32
483
+ },
484
+ "model.language_model.layers.6.mlp.shared_expert.gate_proj": {
485
+ "quant_algo": "MXFP8",
486
+ "group_size": 32
487
+ },
488
+ "model.language_model.layers.6.mlp.shared_expert.up_proj": {
489
+ "quant_algo": "MXFP8",
490
+ "group_size": 32
491
+ },
492
+ "model.language_model.layers.6.mlp.shared_expert.down_proj": {
493
+ "quant_algo": "MXFP8",
494
+ "group_size": 32
495
+ },
496
+ "model.language_model.layers.7.mlp.shared_expert.gate_proj": {
497
+ "quant_algo": "MXFP8",
498
+ "group_size": 32
499
+ },
500
+ "model.language_model.layers.7.mlp.shared_expert.up_proj": {
501
+ "quant_algo": "MXFP8",
502
+ "group_size": 32
503
+ },
504
+ "model.language_model.layers.7.mlp.shared_expert.down_proj": {
505
+ "quant_algo": "MXFP8",
506
+ "group_size": 32
507
+ },
508
+ "model.language_model.layers.8.mlp.shared_expert.gate_proj": {
509
+ "quant_algo": "MXFP8",
510
+ "group_size": 32
511
+ },
512
+ "model.language_model.layers.8.mlp.shared_expert.up_proj": {
513
+ "quant_algo": "MXFP8",
514
+ "group_size": 32
515
+ },
516
+ "model.language_model.layers.8.mlp.shared_expert.down_proj": {
517
+ "quant_algo": "MXFP8",
518
+ "group_size": 32
519
+ },
520
+ "model.language_model.layers.9.mlp.shared_expert.gate_proj": {
521
+ "quant_algo": "MXFP8",
522
+ "group_size": 32
523
+ },
524
+ "model.language_model.layers.9.mlp.shared_expert.up_proj": {
525
+ "quant_algo": "MXFP8",
526
+ "group_size": 32
527
+ },
528
+ "model.language_model.layers.9.mlp.shared_expert.down_proj": {
529
+ "quant_algo": "MXFP8",
530
+ "group_size": 32
531
+ },
532
+ "model.language_model.layers.10.mlp.shared_expert.gate_proj": {
533
+ "quant_algo": "MXFP8",
534
+ "group_size": 32
535
+ },
536
+ "model.language_model.layers.10.mlp.shared_expert.up_proj": {
537
+ "quant_algo": "MXFP8",
538
+ "group_size": 32
539
+ },
540
+ "model.language_model.layers.10.mlp.shared_expert.down_proj": {
541
+ "quant_algo": "MXFP8",
542
+ "group_size": 32
543
+ },
544
+ "model.language_model.layers.11.mlp.shared_expert.gate_proj": {
545
+ "quant_algo": "MXFP8",
546
+ "group_size": 32
547
+ },
548
+ "model.language_model.layers.11.mlp.shared_expert.up_proj": {
549
+ "quant_algo": "MXFP8",
550
+ "group_size": 32
551
+ },
552
+ "model.language_model.layers.11.mlp.shared_expert.down_proj": {
553
+ "quant_algo": "MXFP8",
554
+ "group_size": 32
555
+ },
556
+ "model.language_model.layers.12.mlp.shared_expert.gate_proj": {
557
+ "quant_algo": "MXFP8",
558
+ "group_size": 32
559
+ },
560
+ "model.language_model.layers.12.mlp.shared_expert.up_proj": {
561
+ "quant_algo": "MXFP8",
562
+ "group_size": 32
563
+ },
564
+ "model.language_model.layers.12.mlp.shared_expert.down_proj": {
565
+ "quant_algo": "MXFP8",
566
+ "group_size": 32
567
+ },
568
+ "model.language_model.layers.13.mlp.shared_expert.gate_proj": {
569
+ "quant_algo": "MXFP8",
570
+ "group_size": 32
571
+ },
572
+ "model.language_model.layers.13.mlp.shared_expert.up_proj": {
573
+ "quant_algo": "MXFP8",
574
+ "group_size": 32
575
+ },
576
+ "model.language_model.layers.13.mlp.shared_expert.down_proj": {
577
+ "quant_algo": "MXFP8",
578
+ "group_size": 32
579
+ },
580
+ "model.language_model.layers.14.mlp.shared_expert.gate_proj": {
581
+ "quant_algo": "MXFP8",
582
+ "group_size": 32
583
+ },
584
+ "model.language_model.layers.14.mlp.shared_expert.up_proj": {
585
+ "quant_algo": "MXFP8",
586
+ "group_size": 32
587
+ },
588
+ "model.language_model.layers.14.mlp.shared_expert.down_proj": {
589
+ "quant_algo": "MXFP8",
590
+ "group_size": 32
591
+ },
592
+ "model.language_model.layers.15.mlp.shared_expert.gate_proj": {
593
+ "quant_algo": "MXFP8",
594
+ "group_size": 32
595
+ },
596
+ "model.language_model.layers.15.mlp.shared_expert.up_proj": {
597
+ "quant_algo": "MXFP8",
598
+ "group_size": 32
599
+ },
600
+ "model.language_model.layers.15.mlp.shared_expert.down_proj": {
601
+ "quant_algo": "MXFP8",
602
+ "group_size": 32
603
+ },
604
+ "model.language_model.layers.16.mlp.shared_expert.gate_proj": {
605
+ "quant_algo": "MXFP8",
606
+ "group_size": 32
607
+ },
608
+ "model.language_model.layers.16.mlp.shared_expert.up_proj": {
609
+ "quant_algo": "MXFP8",
610
+ "group_size": 32
611
+ },
612
+ "model.language_model.layers.16.mlp.shared_expert.down_proj": {
613
+ "quant_algo": "MXFP8",
614
+ "group_size": 32
615
+ },
616
+ "model.language_model.layers.17.mlp.shared_expert.gate_proj": {
617
+ "quant_algo": "MXFP8",
618
+ "group_size": 32
619
+ },
620
+ "model.language_model.layers.17.mlp.shared_expert.up_proj": {
621
+ "quant_algo": "MXFP8",
622
+ "group_size": 32
623
+ },
624
+ "model.language_model.layers.17.mlp.shared_expert.down_proj": {
625
+ "quant_algo": "MXFP8",
626
+ "group_size": 32
627
+ },
628
+ "model.language_model.layers.18.mlp.shared_expert.gate_proj": {
629
+ "quant_algo": "MXFP8",
630
+ "group_size": 32
631
+ },
632
+ "model.language_model.layers.18.mlp.shared_expert.up_proj": {
633
+ "quant_algo": "MXFP8",
634
+ "group_size": 32
635
+ },
636
+ "model.language_model.layers.18.mlp.shared_expert.down_proj": {
637
+ "quant_algo": "MXFP8",
638
+ "group_size": 32
639
+ },
640
+ "model.language_model.layers.19.mlp.shared_expert.gate_proj": {
641
+ "quant_algo": "MXFP8",
642
+ "group_size": 32
643
+ },
644
+ "model.language_model.layers.19.mlp.shared_expert.up_proj": {
645
+ "quant_algo": "MXFP8",
646
+ "group_size": 32
647
+ },
648
+ "model.language_model.layers.19.mlp.shared_expert.down_proj": {
649
+ "quant_algo": "MXFP8",
650
+ "group_size": 32
651
+ },
652
+ "model.language_model.layers.20.mlp.shared_expert.gate_proj": {
653
+ "quant_algo": "MXFP8",
654
+ "group_size": 32
655
+ },
656
+ "model.language_model.layers.20.mlp.shared_expert.up_proj": {
657
+ "quant_algo": "MXFP8",
658
+ "group_size": 32
659
+ },
660
+ "model.language_model.layers.20.mlp.shared_expert.down_proj": {
661
+ "quant_algo": "MXFP8",
662
+ "group_size": 32
663
+ },
664
+ "model.language_model.layers.21.mlp.shared_expert.gate_proj": {
665
+ "quant_algo": "MXFP8",
666
+ "group_size": 32
667
+ },
668
+ "model.language_model.layers.21.mlp.shared_expert.up_proj": {
669
+ "quant_algo": "MXFP8",
670
+ "group_size": 32
671
+ },
672
+ "model.language_model.layers.21.mlp.shared_expert.down_proj": {
673
+ "quant_algo": "MXFP8",
674
+ "group_size": 32
675
+ },
676
+ "model.language_model.layers.22.mlp.shared_expert.gate_proj": {
677
+ "quant_algo": "MXFP8",
678
+ "group_size": 32
679
+ },
680
+ "model.language_model.layers.22.mlp.shared_expert.up_proj": {
681
+ "quant_algo": "MXFP8",
682
+ "group_size": 32
683
+ },
684
+ "model.language_model.layers.22.mlp.shared_expert.down_proj": {
685
+ "quant_algo": "MXFP8",
686
+ "group_size": 32
687
+ },
688
+ "model.language_model.layers.23.mlp.shared_expert.gate_proj": {
689
+ "quant_algo": "MXFP8",
690
+ "group_size": 32
691
+ },
692
+ "model.language_model.layers.23.mlp.shared_expert.up_proj": {
693
+ "quant_algo": "MXFP8",
694
+ "group_size": 32
695
+ },
696
+ "model.language_model.layers.23.mlp.shared_expert.down_proj": {
697
+ "quant_algo": "MXFP8",
698
+ "group_size": 32
699
+ },
700
+ "model.language_model.layers.24.mlp.shared_expert.gate_proj": {
701
+ "quant_algo": "MXFP8",
702
+ "group_size": 32
703
+ },
704
+ "model.language_model.layers.24.mlp.shared_expert.up_proj": {
705
+ "quant_algo": "MXFP8",
706
+ "group_size": 32
707
+ },
708
+ "model.language_model.layers.24.mlp.shared_expert.down_proj": {
709
+ "quant_algo": "MXFP8",
710
+ "group_size": 32
711
+ },
712
+ "model.language_model.layers.25.mlp.shared_expert.gate_proj": {
713
+ "quant_algo": "MXFP8",
714
+ "group_size": 32
715
+ },
716
+ "model.language_model.layers.25.mlp.shared_expert.up_proj": {
717
+ "quant_algo": "MXFP8",
718
+ "group_size": 32
719
+ },
720
+ "model.language_model.layers.25.mlp.shared_expert.down_proj": {
721
+ "quant_algo": "MXFP8",
722
+ "group_size": 32
723
+ },
724
+ "model.language_model.layers.26.mlp.shared_expert.gate_proj": {
725
+ "quant_algo": "MXFP8",
726
+ "group_size": 32
727
+ },
728
+ "model.language_model.layers.26.mlp.shared_expert.up_proj": {
729
+ "quant_algo": "MXFP8",
730
+ "group_size": 32
731
+ },
732
+ "model.language_model.layers.26.mlp.shared_expert.down_proj": {
733
+ "quant_algo": "MXFP8",
734
+ "group_size": 32
735
+ },
736
+ "model.language_model.layers.27.mlp.shared_expert.gate_proj": {
737
+ "quant_algo": "MXFP8",
738
+ "group_size": 32
739
+ },
740
+ "model.language_model.layers.27.mlp.shared_expert.up_proj": {
741
+ "quant_algo": "MXFP8",
742
+ "group_size": 32
743
+ },
744
+ "model.language_model.layers.27.mlp.shared_expert.down_proj": {
745
+ "quant_algo": "MXFP8",
746
+ "group_size": 32
747
+ },
748
+ "model.language_model.layers.28.mlp.shared_expert.gate_proj": {
749
+ "quant_algo": "MXFP8",
750
+ "group_size": 32
751
+ },
752
+ "model.language_model.layers.28.mlp.shared_expert.up_proj": {
753
+ "quant_algo": "MXFP8",
754
+ "group_size": 32
755
+ },
756
+ "model.language_model.layers.28.mlp.shared_expert.down_proj": {
757
+ "quant_algo": "MXFP8",
758
+ "group_size": 32
759
+ },
760
+ "model.language_model.layers.29.mlp.shared_expert.gate_proj": {
761
+ "quant_algo": "MXFP8",
762
+ "group_size": 32
763
+ },
764
+ "model.language_model.layers.29.mlp.shared_expert.up_proj": {
765
+ "quant_algo": "MXFP8",
766
+ "group_size": 32
767
+ },
768
+ "model.language_model.layers.29.mlp.shared_expert.down_proj": {
769
+ "quant_algo": "MXFP8",
770
+ "group_size": 32
771
+ },
772
+ "model.language_model.layers.30.mlp.shared_expert.gate_proj": {
773
+ "quant_algo": "MXFP8",
774
+ "group_size": 32
775
+ },
776
+ "model.language_model.layers.30.mlp.shared_expert.up_proj": {
777
+ "quant_algo": "MXFP8",
778
+ "group_size": 32
779
+ },
780
+ "model.language_model.layers.30.mlp.shared_expert.down_proj": {
781
+ "quant_algo": "MXFP8",
782
+ "group_size": 32
783
+ },
784
+ "model.language_model.layers.31.mlp.shared_expert.gate_proj": {
785
+ "quant_algo": "MXFP8",
786
+ "group_size": 32
787
+ },
788
+ "model.language_model.layers.31.mlp.shared_expert.up_proj": {
789
+ "quant_algo": "MXFP8",
790
+ "group_size": 32
791
+ },
792
+ "model.language_model.layers.31.mlp.shared_expert.down_proj": {
793
+ "quant_algo": "MXFP8",
794
+ "group_size": 32
795
+ },
796
+ "model.language_model.layers.32.mlp.shared_expert.gate_proj": {
797
+ "quant_algo": "MXFP8",
798
+ "group_size": 32
799
+ },
800
+ "model.language_model.layers.32.mlp.shared_expert.up_proj": {
801
+ "quant_algo": "MXFP8",
802
+ "group_size": 32
803
+ },
804
+ "model.language_model.layers.32.mlp.shared_expert.down_proj": {
805
+ "quant_algo": "MXFP8",
806
+ "group_size": 32
807
+ },
808
+ "model.language_model.layers.33.mlp.shared_expert.gate_proj": {
809
+ "quant_algo": "MXFP8",
810
+ "group_size": 32
811
+ },
812
+ "model.language_model.layers.33.mlp.shared_expert.up_proj": {
813
+ "quant_algo": "MXFP8",
814
+ "group_size": 32
815
+ },
816
+ "model.language_model.layers.33.mlp.shared_expert.down_proj": {
817
+ "quant_algo": "MXFP8",
818
+ "group_size": 32
819
+ },
820
+ "model.language_model.layers.34.mlp.shared_expert.gate_proj": {
821
+ "quant_algo": "MXFP8",
822
+ "group_size": 32
823
+ },
824
+ "model.language_model.layers.34.mlp.shared_expert.up_proj": {
825
+ "quant_algo": "MXFP8",
826
+ "group_size": 32
827
+ },
828
+ "model.language_model.layers.34.mlp.shared_expert.down_proj": {
829
+ "quant_algo": "MXFP8",
830
+ "group_size": 32
831
+ },
832
+ "model.language_model.layers.35.mlp.shared_expert.gate_proj": {
833
+ "quant_algo": "MXFP8",
834
+ "group_size": 32
835
+ },
836
+ "model.language_model.layers.35.mlp.shared_expert.up_proj": {
837
+ "quant_algo": "MXFP8",
838
+ "group_size": 32
839
+ },
840
+ "model.language_model.layers.35.mlp.shared_expert.down_proj": {
841
+ "quant_algo": "MXFP8",
842
+ "group_size": 32
843
+ },
844
+ "model.language_model.layers.36.mlp.shared_expert.gate_proj": {
845
+ "quant_algo": "MXFP8",
846
+ "group_size": 32
847
+ },
848
+ "model.language_model.layers.36.mlp.shared_expert.up_proj": {
849
+ "quant_algo": "MXFP8",
850
+ "group_size": 32
851
+ },
852
+ "model.language_model.layers.36.mlp.shared_expert.down_proj": {
853
+ "quant_algo": "MXFP8",
854
+ "group_size": 32
855
+ },
856
+ "model.language_model.layers.37.mlp.shared_expert.gate_proj": {
857
+ "quant_algo": "MXFP8",
858
+ "group_size": 32
859
+ },
860
+ "model.language_model.layers.37.mlp.shared_expert.up_proj": {
861
+ "quant_algo": "MXFP8",
862
+ "group_size": 32
863
+ },
864
+ "model.language_model.layers.37.mlp.shared_expert.down_proj": {
865
+ "quant_algo": "MXFP8",
866
+ "group_size": 32
867
+ },
868
+ "model.language_model.layers.38.mlp.shared_expert.gate_proj": {
869
+ "quant_algo": "MXFP8",
870
+ "group_size": 32
871
+ },
872
+ "model.language_model.layers.38.mlp.shared_expert.up_proj": {
873
+ "quant_algo": "MXFP8",
874
+ "group_size": 32
875
+ },
876
+ "model.language_model.layers.38.mlp.shared_expert.down_proj": {
877
+ "quant_algo": "MXFP8",
878
+ "group_size": 32
879
+ },
880
+ "model.language_model.layers.39.mlp.shared_expert.gate_proj": {
881
+ "quant_algo": "MXFP8",
882
+ "group_size": 32
883
+ },
884
+ "model.language_model.layers.39.mlp.shared_expert.up_proj": {
885
+ "quant_algo": "MXFP8",
886
+ "group_size": 32
887
+ },
888
+ "model.language_model.layers.39.mlp.shared_expert.down_proj": {
889
+ "quant_algo": "MXFP8",
890
+ "group_size": 32
891
+ },
892
+ "model.language_model.layers.40.mlp.shared_expert.gate_proj": {
893
+ "quant_algo": "MXFP8",
894
+ "group_size": 32
895
+ },
896
+ "model.language_model.layers.40.mlp.shared_expert.up_proj": {
897
+ "quant_algo": "MXFP8",
898
+ "group_size": 32
899
+ },
900
+ "model.language_model.layers.40.mlp.shared_expert.down_proj": {
901
+ "quant_algo": "MXFP8",
902
+ "group_size": 32
903
+ },
904
+ "model.language_model.layers.41.mlp.shared_expert.gate_proj": {
905
+ "quant_algo": "MXFP8",
906
+ "group_size": 32
907
+ },
908
+ "model.language_model.layers.41.mlp.shared_expert.up_proj": {
909
+ "quant_algo": "MXFP8",
910
+ "group_size": 32
911
+ },
912
+ "model.language_model.layers.41.mlp.shared_expert.down_proj": {
913
+ "quant_algo": "MXFP8",
914
+ "group_size": 32
915
+ },
916
+ "model.language_model.layers.42.mlp.shared_expert.gate_proj": {
917
+ "quant_algo": "MXFP8",
918
+ "group_size": 32
919
+ },
920
+ "model.language_model.layers.42.mlp.shared_expert.up_proj": {
921
+ "quant_algo": "MXFP8",
922
+ "group_size": 32
923
+ },
924
+ "model.language_model.layers.42.mlp.shared_expert.down_proj": {
925
+ "quant_algo": "MXFP8",
926
+ "group_size": 32
927
+ },
928
+ "model.language_model.layers.43.mlp.shared_expert.gate_proj": {
929
+ "quant_algo": "MXFP8",
930
+ "group_size": 32
931
+ },
932
+ "model.language_model.layers.43.mlp.shared_expert.up_proj": {
933
+ "quant_algo": "MXFP8",
934
+ "group_size": 32
935
+ },
936
+ "model.language_model.layers.43.mlp.shared_expert.down_proj": {
937
+ "quant_algo": "MXFP8",
938
+ "group_size": 32
939
+ },
940
+ "model.language_model.layers.44.mlp.shared_expert.gate_proj": {
941
+ "quant_algo": "MXFP8",
942
+ "group_size": 32
943
+ },
944
+ "model.language_model.layers.44.mlp.shared_expert.up_proj": {
945
+ "quant_algo": "MXFP8",
946
+ "group_size": 32
947
+ },
948
+ "model.language_model.layers.44.mlp.shared_expert.down_proj": {
949
+ "quant_algo": "MXFP8",
950
+ "group_size": 32
951
+ },
952
+ "model.language_model.layers.45.mlp.shared_expert.gate_proj": {
953
+ "quant_algo": "MXFP8",
954
+ "group_size": 32
955
+ },
956
+ "model.language_model.layers.45.mlp.shared_expert.up_proj": {
957
+ "quant_algo": "MXFP8",
958
+ "group_size": 32
959
+ },
960
+ "model.language_model.layers.45.mlp.shared_expert.down_proj": {
961
+ "quant_algo": "MXFP8",
962
+ "group_size": 32
963
+ },
964
+ "model.language_model.layers.46.mlp.shared_expert.gate_proj": {
965
+ "quant_algo": "MXFP8",
966
+ "group_size": 32
967
+ },
968
+ "model.language_model.layers.46.mlp.shared_expert.up_proj": {
969
+ "quant_algo": "MXFP8",
970
+ "group_size": 32
971
+ },
972
+ "model.language_model.layers.46.mlp.shared_expert.down_proj": {
973
+ "quant_algo": "MXFP8",
974
+ "group_size": 32
975
+ },
976
+ "model.language_model.layers.47.mlp.shared_expert.gate_proj": {
977
+ "quant_algo": "MXFP8",
978
+ "group_size": 32
979
+ },
980
+ "model.language_model.layers.47.mlp.shared_expert.up_proj": {
981
+ "quant_algo": "MXFP8",
982
+ "group_size": 32
983
+ },
984
+ "model.language_model.layers.47.mlp.shared_expert.down_proj": {
985
+ "quant_algo": "MXFP8",
986
+ "group_size": 32
987
+ },
988
+ "model.language_model.layers.1.ple.ple_embedding.ngram_embedding": {
989
+ "quant_algo": "NVFP4",
990
+ "group_size": 16
991
+ },
992
+ "mtp.layers.0.mlp.experts": {
993
+ "quant_algo": "W4A16_NVFP4",
994
+ "group_size": 16
995
+ },
996
+ "model.language_model.layers.0.linear_attn.in_proj_a": {
997
+ "quant_algo": "MXFP8",
998
+ "group_size": 32
999
+ },
1000
+ "model.language_model.layers.0.linear_attn.in_proj_b": {
1001
+ "quant_algo": "MXFP8",
1002
+ "group_size": 32
1003
+ },
1004
+ "model.language_model.layers.0.linear_attn.in_proj_qkv": {
1005
+ "quant_algo": "MXFP8",
1006
+ "group_size": 32
1007
+ },
1008
+ "model.language_model.layers.0.linear_attn.in_proj_z": {
1009
+ "quant_algo": "MXFP8",
1010
+ "group_size": 32
1011
+ },
1012
+ "model.language_model.layers.0.linear_attn.out_proj": {
1013
+ "quant_algo": "MXFP8",
1014
+ "group_size": 32
1015
+ },
1016
+ "model.language_model.layers.1.linear_attn.in_proj_a": {
1017
+ "quant_algo": "MXFP8",
1018
+ "group_size": 32
1019
+ },
1020
+ "model.language_model.layers.1.linear_attn.in_proj_b": {
1021
+ "quant_algo": "MXFP8",
1022
+ "group_size": 32
1023
+ },
1024
+ "model.language_model.layers.1.linear_attn.in_proj_qkv": {
1025
+ "quant_algo": "MXFP8",
1026
+ "group_size": 32
1027
+ },
1028
+ "model.language_model.layers.1.linear_attn.in_proj_z": {
1029
+ "quant_algo": "MXFP8",
1030
+ "group_size": 32
1031
+ },
1032
+ "model.language_model.layers.1.linear_attn.out_proj": {
1033
+ "quant_algo": "MXFP8",
1034
+ "group_size": 32
1035
+ },
1036
+ "model.language_model.layers.10.linear_attn.in_proj_a": {
1037
+ "quant_algo": "MXFP8",
1038
+ "group_size": 32
1039
+ },
1040
+ "model.language_model.layers.10.linear_attn.in_proj_b": {
1041
+ "quant_algo": "MXFP8",
1042
+ "group_size": 32
1043
+ },
1044
+ "model.language_model.layers.10.linear_attn.in_proj_qkv": {
1045
+ "quant_algo": "MXFP8",
1046
+ "group_size": 32
1047
+ },
1048
+ "model.language_model.layers.10.linear_attn.in_proj_z": {
1049
+ "quant_algo": "MXFP8",
1050
+ "group_size": 32
1051
+ },
1052
+ "model.language_model.layers.10.linear_attn.out_proj": {
1053
+ "quant_algo": "MXFP8",
1054
+ "group_size": 32
1055
+ },
1056
+ "model.language_model.layers.11.self_attn.indexer.index_qk_proj": {
1057
+ "quant_algo": "MXFP8",
1058
+ "group_size": 32
1059
+ },
1060
+ "model.language_model.layers.11.self_attn.k_proj": {
1061
+ "quant_algo": "MXFP8",
1062
+ "group_size": 32
1063
+ },
1064
+ "model.language_model.layers.11.self_attn.o_proj": {
1065
+ "quant_algo": "MXFP8",
1066
+ "group_size": 32
1067
+ },
1068
+ "model.language_model.layers.11.self_attn.q_proj": {
1069
+ "quant_algo": "MXFP8",
1070
+ "group_size": 32
1071
+ },
1072
+ "model.language_model.layers.11.self_attn.v_proj": {
1073
+ "quant_algo": "MXFP8",
1074
+ "group_size": 32
1075
+ },
1076
+ "model.language_model.layers.12.linear_attn.in_proj_a": {
1077
+ "quant_algo": "MXFP8",
1078
+ "group_size": 32
1079
+ },
1080
+ "model.language_model.layers.12.linear_attn.in_proj_b": {
1081
+ "quant_algo": "MXFP8",
1082
+ "group_size": 32
1083
+ },
1084
+ "model.language_model.layers.12.linear_attn.in_proj_qkv": {
1085
+ "quant_algo": "MXFP8",
1086
+ "group_size": 32
1087
+ },
1088
+ "model.language_model.layers.12.linear_attn.in_proj_z": {
1089
+ "quant_algo": "MXFP8",
1090
+ "group_size": 32
1091
+ },
1092
+ "model.language_model.layers.12.linear_attn.out_proj": {
1093
+ "quant_algo": "MXFP8",
1094
+ "group_size": 32
1095
+ },
1096
+ "model.language_model.layers.13.linear_attn.in_proj_a": {
1097
+ "quant_algo": "MXFP8",
1098
+ "group_size": 32
1099
+ },
1100
+ "model.language_model.layers.13.linear_attn.in_proj_b": {
1101
+ "quant_algo": "MXFP8",
1102
+ "group_size": 32
1103
+ },
1104
+ "model.language_model.layers.13.linear_attn.in_proj_qkv": {
1105
+ "quant_algo": "MXFP8",
1106
+ "group_size": 32
1107
+ },
1108
+ "model.language_model.layers.13.linear_attn.in_proj_z": {
1109
+ "quant_algo": "MXFP8",
1110
+ "group_size": 32
1111
+ },
1112
+ "model.language_model.layers.13.linear_attn.out_proj": {
1113
+ "quant_algo": "MXFP8",
1114
+ "group_size": 32
1115
+ },
1116
+ "model.language_model.layers.14.linear_attn.in_proj_a": {
1117
+ "quant_algo": "MXFP8",
1118
+ "group_size": 32
1119
+ },
1120
+ "model.language_model.layers.14.linear_attn.in_proj_b": {
1121
+ "quant_algo": "MXFP8",
1122
+ "group_size": 32
1123
+ },
1124
+ "model.language_model.layers.14.linear_attn.in_proj_qkv": {
1125
+ "quant_algo": "MXFP8",
1126
+ "group_size": 32
1127
+ },
1128
+ "model.language_model.layers.14.linear_attn.in_proj_z": {
1129
+ "quant_algo": "MXFP8",
1130
+ "group_size": 32
1131
+ },
1132
+ "model.language_model.layers.14.linear_attn.out_proj": {
1133
+ "quant_algo": "MXFP8",
1134
+ "group_size": 32
1135
+ },
1136
+ "model.language_model.layers.15.self_attn.indexer.index_qk_proj": {
1137
+ "quant_algo": "MXFP8",
1138
+ "group_size": 32
1139
+ },
1140
+ "model.language_model.layers.15.self_attn.k_proj": {
1141
+ "quant_algo": "MXFP8",
1142
+ "group_size": 32
1143
+ },
1144
+ "model.language_model.layers.15.self_attn.o_proj": {
1145
+ "quant_algo": "MXFP8",
1146
+ "group_size": 32
1147
+ },
1148
+ "model.language_model.layers.15.self_attn.q_proj": {
1149
+ "quant_algo": "MXFP8",
1150
+ "group_size": 32
1151
+ },
1152
+ "model.language_model.layers.15.self_attn.v_proj": {
1153
+ "quant_algo": "MXFP8",
1154
+ "group_size": 32
1155
+ },
1156
+ "model.language_model.layers.16.linear_attn.in_proj_a": {
1157
+ "quant_algo": "MXFP8",
1158
+ "group_size": 32
1159
+ },
1160
+ "model.language_model.layers.16.linear_attn.in_proj_b": {
1161
+ "quant_algo": "MXFP8",
1162
+ "group_size": 32
1163
+ },
1164
+ "model.language_model.layers.16.linear_attn.in_proj_qkv": {
1165
+ "quant_algo": "MXFP8",
1166
+ "group_size": 32
1167
+ },
1168
+ "model.language_model.layers.16.linear_attn.in_proj_z": {
1169
+ "quant_algo": "MXFP8",
1170
+ "group_size": 32
1171
+ },
1172
+ "model.language_model.layers.16.linear_attn.out_proj": {
1173
+ "quant_algo": "MXFP8",
1174
+ "group_size": 32
1175
+ },
1176
+ "model.language_model.layers.17.linear_attn.in_proj_a": {
1177
+ "quant_algo": "MXFP8",
1178
+ "group_size": 32
1179
+ },
1180
+ "model.language_model.layers.17.linear_attn.in_proj_b": {
1181
+ "quant_algo": "MXFP8",
1182
+ "group_size": 32
1183
+ },
1184
+ "model.language_model.layers.17.linear_attn.in_proj_qkv": {
1185
+ "quant_algo": "MXFP8",
1186
+ "group_size": 32
1187
+ },
1188
+ "model.language_model.layers.17.linear_attn.in_proj_z": {
1189
+ "quant_algo": "MXFP8",
1190
+ "group_size": 32
1191
+ },
1192
+ "model.language_model.layers.17.linear_attn.out_proj": {
1193
+ "quant_algo": "MXFP8",
1194
+ "group_size": 32
1195
+ },
1196
+ "model.language_model.layers.18.linear_attn.in_proj_a": {
1197
+ "quant_algo": "MXFP8",
1198
+ "group_size": 32
1199
+ },
1200
+ "model.language_model.layers.18.linear_attn.in_proj_b": {
1201
+ "quant_algo": "MXFP8",
1202
+ "group_size": 32
1203
+ },
1204
+ "model.language_model.layers.18.linear_attn.in_proj_qkv": {
1205
+ "quant_algo": "MXFP8",
1206
+ "group_size": 32
1207
+ },
1208
+ "model.language_model.layers.18.linear_attn.in_proj_z": {
1209
+ "quant_algo": "MXFP8",
1210
+ "group_size": 32
1211
+ },
1212
+ "model.language_model.layers.18.linear_attn.out_proj": {
1213
+ "quant_algo": "MXFP8",
1214
+ "group_size": 32
1215
+ },
1216
+ "model.language_model.layers.19.self_attn.indexer.index_qk_proj": {
1217
+ "quant_algo": "MXFP8",
1218
+ "group_size": 32
1219
+ },
1220
+ "model.language_model.layers.19.self_attn.k_proj": {
1221
+ "quant_algo": "MXFP8",
1222
+ "group_size": 32
1223
+ },
1224
+ "model.language_model.layers.19.self_attn.o_proj": {
1225
+ "quant_algo": "MXFP8",
1226
+ "group_size": 32
1227
+ },
1228
+ "model.language_model.layers.19.self_attn.q_proj": {
1229
+ "quant_algo": "MXFP8",
1230
+ "group_size": 32
1231
+ },
1232
+ "model.language_model.layers.19.self_attn.v_proj": {
1233
+ "quant_algo": "MXFP8",
1234
+ "group_size": 32
1235
+ },
1236
+ "model.language_model.layers.2.linear_attn.in_proj_a": {
1237
+ "quant_algo": "MXFP8",
1238
+ "group_size": 32
1239
+ },
1240
+ "model.language_model.layers.2.linear_attn.in_proj_b": {
1241
+ "quant_algo": "MXFP8",
1242
+ "group_size": 32
1243
+ },
1244
+ "model.language_model.layers.2.linear_attn.in_proj_qkv": {
1245
+ "quant_algo": "MXFP8",
1246
+ "group_size": 32
1247
+ },
1248
+ "model.language_model.layers.2.linear_attn.in_proj_z": {
1249
+ "quant_algo": "MXFP8",
1250
+ "group_size": 32
1251
+ },
1252
+ "model.language_model.layers.2.linear_attn.out_proj": {
1253
+ "quant_algo": "MXFP8",
1254
+ "group_size": 32
1255
+ },
1256
+ "model.language_model.layers.20.linear_attn.in_proj_a": {
1257
+ "quant_algo": "MXFP8",
1258
+ "group_size": 32
1259
+ },
1260
+ "model.language_model.layers.20.linear_attn.in_proj_b": {
1261
+ "quant_algo": "MXFP8",
1262
+ "group_size": 32
1263
+ },
1264
+ "model.language_model.layers.20.linear_attn.in_proj_qkv": {
1265
+ "quant_algo": "MXFP8",
1266
+ "group_size": 32
1267
+ },
1268
+ "model.language_model.layers.20.linear_attn.in_proj_z": {
1269
+ "quant_algo": "MXFP8",
1270
+ "group_size": 32
1271
+ },
1272
+ "model.language_model.layers.20.linear_attn.out_proj": {
1273
+ "quant_algo": "MXFP8",
1274
+ "group_size": 32
1275
+ },
1276
+ "model.language_model.layers.21.linear_attn.in_proj_a": {
1277
+ "quant_algo": "MXFP8",
1278
+ "group_size": 32
1279
+ },
1280
+ "model.language_model.layers.21.linear_attn.in_proj_b": {
1281
+ "quant_algo": "MXFP8",
1282
+ "group_size": 32
1283
+ },
1284
+ "model.language_model.layers.21.linear_attn.in_proj_qkv": {
1285
+ "quant_algo": "MXFP8",
1286
+ "group_size": 32
1287
+ },
1288
+ "model.language_model.layers.21.linear_attn.in_proj_z": {
1289
+ "quant_algo": "MXFP8",
1290
+ "group_size": 32
1291
+ },
1292
+ "model.language_model.layers.21.linear_attn.out_proj": {
1293
+ "quant_algo": "MXFP8",
1294
+ "group_size": 32
1295
+ },
1296
+ "model.language_model.layers.22.linear_attn.in_proj_a": {
1297
+ "quant_algo": "MXFP8",
1298
+ "group_size": 32
1299
+ },
1300
+ "model.language_model.layers.22.linear_attn.in_proj_b": {
1301
+ "quant_algo": "MXFP8",
1302
+ "group_size": 32
1303
+ },
1304
+ "model.language_model.layers.22.linear_attn.in_proj_qkv": {
1305
+ "quant_algo": "MXFP8",
1306
+ "group_size": 32
1307
+ },
1308
+ "model.language_model.layers.22.linear_attn.in_proj_z": {
1309
+ "quant_algo": "MXFP8",
1310
+ "group_size": 32
1311
+ },
1312
+ "model.language_model.layers.22.linear_attn.out_proj": {
1313
+ "quant_algo": "MXFP8",
1314
+ "group_size": 32
1315
+ },
1316
+ "model.language_model.layers.23.self_attn.indexer.index_qk_proj": {
1317
+ "quant_algo": "MXFP8",
1318
+ "group_size": 32
1319
+ },
1320
+ "model.language_model.layers.23.self_attn.k_proj": {
1321
+ "quant_algo": "MXFP8",
1322
+ "group_size": 32
1323
+ },
1324
+ "model.language_model.layers.23.self_attn.o_proj": {
1325
+ "quant_algo": "MXFP8",
1326
+ "group_size": 32
1327
+ },
1328
+ "model.language_model.layers.23.self_attn.q_proj": {
1329
+ "quant_algo": "MXFP8",
1330
+ "group_size": 32
1331
+ },
1332
+ "model.language_model.layers.23.self_attn.v_proj": {
1333
+ "quant_algo": "MXFP8",
1334
+ "group_size": 32
1335
+ },
1336
+ "model.language_model.layers.24.linear_attn.in_proj_a": {
1337
+ "quant_algo": "MXFP8",
1338
+ "group_size": 32
1339
+ },
1340
+ "model.language_model.layers.24.linear_attn.in_proj_b": {
1341
+ "quant_algo": "MXFP8",
1342
+ "group_size": 32
1343
+ },
1344
+ "model.language_model.layers.24.linear_attn.in_proj_qkv": {
1345
+ "quant_algo": "MXFP8",
1346
+ "group_size": 32
1347
+ },
1348
+ "model.language_model.layers.24.linear_attn.in_proj_z": {
1349
+ "quant_algo": "MXFP8",
1350
+ "group_size": 32
1351
+ },
1352
+ "model.language_model.layers.24.linear_attn.out_proj": {
1353
+ "quant_algo": "MXFP8",
1354
+ "group_size": 32
1355
+ },
1356
+ "model.language_model.layers.25.linear_attn.in_proj_a": {
1357
+ "quant_algo": "MXFP8",
1358
+ "group_size": 32
1359
+ },
1360
+ "model.language_model.layers.25.linear_attn.in_proj_b": {
1361
+ "quant_algo": "MXFP8",
1362
+ "group_size": 32
1363
+ },
1364
+ "model.language_model.layers.25.linear_attn.in_proj_qkv": {
1365
+ "quant_algo": "MXFP8",
1366
+ "group_size": 32
1367
+ },
1368
+ "model.language_model.layers.25.linear_attn.in_proj_z": {
1369
+ "quant_algo": "MXFP8",
1370
+ "group_size": 32
1371
+ },
1372
+ "model.language_model.layers.25.linear_attn.out_proj": {
1373
+ "quant_algo": "MXFP8",
1374
+ "group_size": 32
1375
+ },
1376
+ "model.language_model.layers.26.linear_attn.in_proj_a": {
1377
+ "quant_algo": "MXFP8",
1378
+ "group_size": 32
1379
+ },
1380
+ "model.language_model.layers.26.linear_attn.in_proj_b": {
1381
+ "quant_algo": "MXFP8",
1382
+ "group_size": 32
1383
+ },
1384
+ "model.language_model.layers.26.linear_attn.in_proj_qkv": {
1385
+ "quant_algo": "MXFP8",
1386
+ "group_size": 32
1387
+ },
1388
+ "model.language_model.layers.26.linear_attn.in_proj_z": {
1389
+ "quant_algo": "MXFP8",
1390
+ "group_size": 32
1391
+ },
1392
+ "model.language_model.layers.26.linear_attn.out_proj": {
1393
+ "quant_algo": "MXFP8",
1394
+ "group_size": 32
1395
+ },
1396
+ "model.language_model.layers.27.self_attn.indexer.index_qk_proj": {
1397
+ "quant_algo": "MXFP8",
1398
+ "group_size": 32
1399
+ },
1400
+ "model.language_model.layers.27.self_attn.k_proj": {
1401
+ "quant_algo": "MXFP8",
1402
+ "group_size": 32
1403
+ },
1404
+ "model.language_model.layers.27.self_attn.o_proj": {
1405
+ "quant_algo": "MXFP8",
1406
+ "group_size": 32
1407
+ },
1408
+ "model.language_model.layers.27.self_attn.q_proj": {
1409
+ "quant_algo": "MXFP8",
1410
+ "group_size": 32
1411
+ },
1412
+ "model.language_model.layers.27.self_attn.v_proj": {
1413
+ "quant_algo": "MXFP8",
1414
+ "group_size": 32
1415
+ },
1416
+ "model.language_model.layers.28.linear_attn.in_proj_a": {
1417
+ "quant_algo": "MXFP8",
1418
+ "group_size": 32
1419
+ },
1420
+ "model.language_model.layers.28.linear_attn.in_proj_b": {
1421
+ "quant_algo": "MXFP8",
1422
+ "group_size": 32
1423
+ },
1424
+ "model.language_model.layers.28.linear_attn.in_proj_qkv": {
1425
+ "quant_algo": "MXFP8",
1426
+ "group_size": 32
1427
+ },
1428
+ "model.language_model.layers.28.linear_attn.in_proj_z": {
1429
+ "quant_algo": "MXFP8",
1430
+ "group_size": 32
1431
+ },
1432
+ "model.language_model.layers.28.linear_attn.out_proj": {
1433
+ "quant_algo": "MXFP8",
1434
+ "group_size": 32
1435
+ },
1436
+ "model.language_model.layers.29.linear_attn.in_proj_a": {
1437
+ "quant_algo": "MXFP8",
1438
+ "group_size": 32
1439
+ },
1440
+ "model.language_model.layers.29.linear_attn.in_proj_b": {
1441
+ "quant_algo": "MXFP8",
1442
+ "group_size": 32
1443
+ },
1444
+ "model.language_model.layers.29.linear_attn.in_proj_qkv": {
1445
+ "quant_algo": "MXFP8",
1446
+ "group_size": 32
1447
+ },
1448
+ "model.language_model.layers.29.linear_attn.in_proj_z": {
1449
+ "quant_algo": "MXFP8",
1450
+ "group_size": 32
1451
+ },
1452
+ "model.language_model.layers.29.linear_attn.out_proj": {
1453
+ "quant_algo": "MXFP8",
1454
+ "group_size": 32
1455
+ },
1456
+ "model.language_model.layers.3.self_attn.indexer.index_qk_proj": {
1457
+ "quant_algo": "MXFP8",
1458
+ "group_size": 32
1459
+ },
1460
+ "model.language_model.layers.3.self_attn.k_proj": {
1461
+ "quant_algo": "MXFP8",
1462
+ "group_size": 32
1463
+ },
1464
+ "model.language_model.layers.3.self_attn.o_proj": {
1465
+ "quant_algo": "MXFP8",
1466
+ "group_size": 32
1467
+ },
1468
+ "model.language_model.layers.3.self_attn.q_proj": {
1469
+ "quant_algo": "MXFP8",
1470
+ "group_size": 32
1471
+ },
1472
+ "model.language_model.layers.3.self_attn.v_proj": {
1473
+ "quant_algo": "MXFP8",
1474
+ "group_size": 32
1475
+ },
1476
+ "model.language_model.layers.30.linear_attn.in_proj_a": {
1477
+ "quant_algo": "MXFP8",
1478
+ "group_size": 32
1479
+ },
1480
+ "model.language_model.layers.30.linear_attn.in_proj_b": {
1481
+ "quant_algo": "MXFP8",
1482
+ "group_size": 32
1483
+ },
1484
+ "model.language_model.layers.30.linear_attn.in_proj_qkv": {
1485
+ "quant_algo": "MXFP8",
1486
+ "group_size": 32
1487
+ },
1488
+ "model.language_model.layers.30.linear_attn.in_proj_z": {
1489
+ "quant_algo": "MXFP8",
1490
+ "group_size": 32
1491
+ },
1492
+ "model.language_model.layers.30.linear_attn.out_proj": {
1493
+ "quant_algo": "MXFP8",
1494
+ "group_size": 32
1495
+ },
1496
+ "model.language_model.layers.31.self_attn.indexer.index_qk_proj": {
1497
+ "quant_algo": "MXFP8",
1498
+ "group_size": 32
1499
+ },
1500
+ "model.language_model.layers.31.self_attn.k_proj": {
1501
+ "quant_algo": "MXFP8",
1502
+ "group_size": 32
1503
+ },
1504
+ "model.language_model.layers.31.self_attn.o_proj": {
1505
+ "quant_algo": "MXFP8",
1506
+ "group_size": 32
1507
+ },
1508
+ "model.language_model.layers.31.self_attn.q_proj": {
1509
+ "quant_algo": "MXFP8",
1510
+ "group_size": 32
1511
+ },
1512
+ "model.language_model.layers.31.self_attn.v_proj": {
1513
+ "quant_algo": "MXFP8",
1514
+ "group_size": 32
1515
+ },
1516
+ "model.language_model.layers.32.linear_attn.in_proj_a": {
1517
+ "quant_algo": "MXFP8",
1518
+ "group_size": 32
1519
+ },
1520
+ "model.language_model.layers.32.linear_attn.in_proj_b": {
1521
+ "quant_algo": "MXFP8",
1522
+ "group_size": 32
1523
+ },
1524
+ "model.language_model.layers.32.linear_attn.in_proj_qkv": {
1525
+ "quant_algo": "MXFP8",
1526
+ "group_size": 32
1527
+ },
1528
+ "model.language_model.layers.32.linear_attn.in_proj_z": {
1529
+ "quant_algo": "MXFP8",
1530
+ "group_size": 32
1531
+ },
1532
+ "model.language_model.layers.32.linear_attn.out_proj": {
1533
+ "quant_algo": "MXFP8",
1534
+ "group_size": 32
1535
+ },
1536
+ "model.language_model.layers.33.linear_attn.in_proj_a": {
1537
+ "quant_algo": "MXFP8",
1538
+ "group_size": 32
1539
+ },
1540
+ "model.language_model.layers.33.linear_attn.in_proj_b": {
1541
+ "quant_algo": "MXFP8",
1542
+ "group_size": 32
1543
+ },
1544
+ "model.language_model.layers.33.linear_attn.in_proj_qkv": {
1545
+ "quant_algo": "MXFP8",
1546
+ "group_size": 32
1547
+ },
1548
+ "model.language_model.layers.33.linear_attn.in_proj_z": {
1549
+ "quant_algo": "MXFP8",
1550
+ "group_size": 32
1551
+ },
1552
+ "model.language_model.layers.33.linear_attn.out_proj": {
1553
+ "quant_algo": "MXFP8",
1554
+ "group_size": 32
1555
+ },
1556
+ "model.language_model.layers.34.linear_attn.in_proj_a": {
1557
+ "quant_algo": "MXFP8",
1558
+ "group_size": 32
1559
+ },
1560
+ "model.language_model.layers.34.linear_attn.in_proj_b": {
1561
+ "quant_algo": "MXFP8",
1562
+ "group_size": 32
1563
+ },
1564
+ "model.language_model.layers.34.linear_attn.in_proj_qkv": {
1565
+ "quant_algo": "MXFP8",
1566
+ "group_size": 32
1567
+ },
1568
+ "model.language_model.layers.34.linear_attn.in_proj_z": {
1569
+ "quant_algo": "MXFP8",
1570
+ "group_size": 32
1571
+ },
1572
+ "model.language_model.layers.34.linear_attn.out_proj": {
1573
+ "quant_algo": "MXFP8",
1574
+ "group_size": 32
1575
+ },
1576
+ "model.language_model.layers.35.self_attn.indexer.index_qk_proj": {
1577
+ "quant_algo": "MXFP8",
1578
+ "group_size": 32
1579
+ },
1580
+ "model.language_model.layers.35.self_attn.k_proj": {
1581
+ "quant_algo": "MXFP8",
1582
+ "group_size": 32
1583
+ },
1584
+ "model.language_model.layers.35.self_attn.o_proj": {
1585
+ "quant_algo": "MXFP8",
1586
+ "group_size": 32
1587
+ },
1588
+ "model.language_model.layers.35.self_attn.q_proj": {
1589
+ "quant_algo": "MXFP8",
1590
+ "group_size": 32
1591
+ },
1592
+ "model.language_model.layers.35.self_attn.v_proj": {
1593
+ "quant_algo": "MXFP8",
1594
+ "group_size": 32
1595
+ },
1596
+ "model.language_model.layers.36.linear_attn.in_proj_a": {
1597
+ "quant_algo": "MXFP8",
1598
+ "group_size": 32
1599
+ },
1600
+ "model.language_model.layers.36.linear_attn.in_proj_b": {
1601
+ "quant_algo": "MXFP8",
1602
+ "group_size": 32
1603
+ },
1604
+ "model.language_model.layers.36.linear_attn.in_proj_qkv": {
1605
+ "quant_algo": "MXFP8",
1606
+ "group_size": 32
1607
+ },
1608
+ "model.language_model.layers.36.linear_attn.in_proj_z": {
1609
+ "quant_algo": "MXFP8",
1610
+ "group_size": 32
1611
+ },
1612
+ "model.language_model.layers.36.linear_attn.out_proj": {
1613
+ "quant_algo": "MXFP8",
1614
+ "group_size": 32
1615
+ },
1616
+ "model.language_model.layers.37.linear_attn.in_proj_a": {
1617
+ "quant_algo": "MXFP8",
1618
+ "group_size": 32
1619
+ },
1620
+ "model.language_model.layers.37.linear_attn.in_proj_b": {
1621
+ "quant_algo": "MXFP8",
1622
+ "group_size": 32
1623
+ },
1624
+ "model.language_model.layers.37.linear_attn.in_proj_qkv": {
1625
+ "quant_algo": "MXFP8",
1626
+ "group_size": 32
1627
+ },
1628
+ "model.language_model.layers.37.linear_attn.in_proj_z": {
1629
+ "quant_algo": "MXFP8",
1630
+ "group_size": 32
1631
+ },
1632
+ "model.language_model.layers.37.linear_attn.out_proj": {
1633
+ "quant_algo": "MXFP8",
1634
+ "group_size": 32
1635
+ },
1636
+ "model.language_model.layers.38.linear_attn.in_proj_a": {
1637
+ "quant_algo": "MXFP8",
1638
+ "group_size": 32
1639
+ },
1640
+ "model.language_model.layers.38.linear_attn.in_proj_b": {
1641
+ "quant_algo": "MXFP8",
1642
+ "group_size": 32
1643
+ },
1644
+ "model.language_model.layers.38.linear_attn.in_proj_qkv": {
1645
+ "quant_algo": "MXFP8",
1646
+ "group_size": 32
1647
+ },
1648
+ "model.language_model.layers.38.linear_attn.in_proj_z": {
1649
+ "quant_algo": "MXFP8",
1650
+ "group_size": 32
1651
+ },
1652
+ "model.language_model.layers.38.linear_attn.out_proj": {
1653
+ "quant_algo": "MXFP8",
1654
+ "group_size": 32
1655
+ },
1656
+ "model.language_model.layers.39.self_attn.indexer.index_qk_proj": {
1657
+ "quant_algo": "MXFP8",
1658
+ "group_size": 32
1659
+ },
1660
+ "model.language_model.layers.39.self_attn.k_proj": {
1661
+ "quant_algo": "MXFP8",
1662
+ "group_size": 32
1663
+ },
1664
+ "model.language_model.layers.39.self_attn.o_proj": {
1665
+ "quant_algo": "MXFP8",
1666
+ "group_size": 32
1667
+ },
1668
+ "model.language_model.layers.39.self_attn.q_proj": {
1669
+ "quant_algo": "MXFP8",
1670
+ "group_size": 32
1671
+ },
1672
+ "model.language_model.layers.39.self_attn.v_proj": {
1673
+ "quant_algo": "MXFP8",
1674
+ "group_size": 32
1675
+ },
1676
+ "model.language_model.layers.4.linear_attn.in_proj_a": {
1677
+ "quant_algo": "MXFP8",
1678
+ "group_size": 32
1679
+ },
1680
+ "model.language_model.layers.4.linear_attn.in_proj_b": {
1681
+ "quant_algo": "MXFP8",
1682
+ "group_size": 32
1683
+ },
1684
+ "model.language_model.layers.4.linear_attn.in_proj_qkv": {
1685
+ "quant_algo": "MXFP8",
1686
+ "group_size": 32
1687
+ },
1688
+ "model.language_model.layers.4.linear_attn.in_proj_z": {
1689
+ "quant_algo": "MXFP8",
1690
+ "group_size": 32
1691
+ },
1692
+ "model.language_model.layers.4.linear_attn.out_proj": {
1693
+ "quant_algo": "MXFP8",
1694
+ "group_size": 32
1695
+ },
1696
+ "model.language_model.layers.40.linear_attn.in_proj_a": {
1697
+ "quant_algo": "MXFP8",
1698
+ "group_size": 32
1699
+ },
1700
+ "model.language_model.layers.40.linear_attn.in_proj_b": {
1701
+ "quant_algo": "MXFP8",
1702
+ "group_size": 32
1703
+ },
1704
+ "model.language_model.layers.40.linear_attn.in_proj_qkv": {
1705
+ "quant_algo": "MXFP8",
1706
+ "group_size": 32
1707
+ },
1708
+ "model.language_model.layers.40.linear_attn.in_proj_z": {
1709
+ "quant_algo": "MXFP8",
1710
+ "group_size": 32
1711
+ },
1712
+ "model.language_model.layers.40.linear_attn.out_proj": {
1713
+ "quant_algo": "MXFP8",
1714
+ "group_size": 32
1715
+ },
1716
+ "model.language_model.layers.41.linear_attn.in_proj_a": {
1717
+ "quant_algo": "MXFP8",
1718
+ "group_size": 32
1719
+ },
1720
+ "model.language_model.layers.41.linear_attn.in_proj_b": {
1721
+ "quant_algo": "MXFP8",
1722
+ "group_size": 32
1723
+ },
1724
+ "model.language_model.layers.41.linear_attn.in_proj_qkv": {
1725
+ "quant_algo": "MXFP8",
1726
+ "group_size": 32
1727
+ },
1728
+ "model.language_model.layers.41.linear_attn.in_proj_z": {
1729
+ "quant_algo": "MXFP8",
1730
+ "group_size": 32
1731
+ },
1732
+ "model.language_model.layers.41.linear_attn.out_proj": {
1733
+ "quant_algo": "MXFP8",
1734
+ "group_size": 32
1735
+ },
1736
+ "model.language_model.layers.42.linear_attn.in_proj_a": {
1737
+ "quant_algo": "MXFP8",
1738
+ "group_size": 32
1739
+ },
1740
+ "model.language_model.layers.42.linear_attn.in_proj_b": {
1741
+ "quant_algo": "MXFP8",
1742
+ "group_size": 32
1743
+ },
1744
+ "model.language_model.layers.42.linear_attn.in_proj_qkv": {
1745
+ "quant_algo": "MXFP8",
1746
+ "group_size": 32
1747
+ },
1748
+ "model.language_model.layers.42.linear_attn.in_proj_z": {
1749
+ "quant_algo": "MXFP8",
1750
+ "group_size": 32
1751
+ },
1752
+ "model.language_model.layers.42.linear_attn.out_proj": {
1753
+ "quant_algo": "MXFP8",
1754
+ "group_size": 32
1755
+ },
1756
+ "model.language_model.layers.43.self_attn.indexer.index_qk_proj": {
1757
+ "quant_algo": "MXFP8",
1758
+ "group_size": 32
1759
+ },
1760
+ "model.language_model.layers.43.self_attn.k_proj": {
1761
+ "quant_algo": "MXFP8",
1762
+ "group_size": 32
1763
+ },
1764
+ "model.language_model.layers.43.self_attn.o_proj": {
1765
+ "quant_algo": "MXFP8",
1766
+ "group_size": 32
1767
+ },
1768
+ "model.language_model.layers.43.self_attn.q_proj": {
1769
+ "quant_algo": "MXFP8",
1770
+ "group_size": 32
1771
+ },
1772
+ "model.language_model.layers.43.self_attn.v_proj": {
1773
+ "quant_algo": "MXFP8",
1774
+ "group_size": 32
1775
+ },
1776
+ "model.language_model.layers.44.linear_attn.in_proj_a": {
1777
+ "quant_algo": "MXFP8",
1778
+ "group_size": 32
1779
+ },
1780
+ "model.language_model.layers.44.linear_attn.in_proj_b": {
1781
+ "quant_algo": "MXFP8",
1782
+ "group_size": 32
1783
+ },
1784
+ "model.language_model.layers.44.linear_attn.in_proj_qkv": {
1785
+ "quant_algo": "MXFP8",
1786
+ "group_size": 32
1787
+ },
1788
+ "model.language_model.layers.44.linear_attn.in_proj_z": {
1789
+ "quant_algo": "MXFP8",
1790
+ "group_size": 32
1791
+ },
1792
+ "model.language_model.layers.44.linear_attn.out_proj": {
1793
+ "quant_algo": "MXFP8",
1794
+ "group_size": 32
1795
+ },
1796
+ "model.language_model.layers.45.linear_attn.in_proj_a": {
1797
+ "quant_algo": "MXFP8",
1798
+ "group_size": 32
1799
+ },
1800
+ "model.language_model.layers.45.linear_attn.in_proj_b": {
1801
+ "quant_algo": "MXFP8",
1802
+ "group_size": 32
1803
+ },
1804
+ "model.language_model.layers.45.linear_attn.in_proj_qkv": {
1805
+ "quant_algo": "MXFP8",
1806
+ "group_size": 32
1807
+ },
1808
+ "model.language_model.layers.45.linear_attn.in_proj_z": {
1809
+ "quant_algo": "MXFP8",
1810
+ "group_size": 32
1811
+ },
1812
+ "model.language_model.layers.45.linear_attn.out_proj": {
1813
+ "quant_algo": "MXFP8",
1814
+ "group_size": 32
1815
+ },
1816
+ "model.language_model.layers.46.linear_attn.in_proj_a": {
1817
+ "quant_algo": "MXFP8",
1818
+ "group_size": 32
1819
+ },
1820
+ "model.language_model.layers.46.linear_attn.in_proj_b": {
1821
+ "quant_algo": "MXFP8",
1822
+ "group_size": 32
1823
+ },
1824
+ "model.language_model.layers.46.linear_attn.in_proj_qkv": {
1825
+ "quant_algo": "MXFP8",
1826
+ "group_size": 32
1827
+ },
1828
+ "model.language_model.layers.46.linear_attn.in_proj_z": {
1829
+ "quant_algo": "MXFP8",
1830
+ "group_size": 32
1831
+ },
1832
+ "model.language_model.layers.46.linear_attn.out_proj": {
1833
+ "quant_algo": "MXFP8",
1834
+ "group_size": 32
1835
+ },
1836
+ "model.language_model.layers.47.self_attn.indexer.index_qk_proj": {
1837
+ "quant_algo": "MXFP8",
1838
+ "group_size": 32
1839
+ },
1840
+ "model.language_model.layers.47.self_attn.k_proj": {
1841
+ "quant_algo": "MXFP8",
1842
+ "group_size": 32
1843
+ },
1844
+ "model.language_model.layers.47.self_attn.o_proj": {
1845
+ "quant_algo": "MXFP8",
1846
+ "group_size": 32
1847
+ },
1848
+ "model.language_model.layers.47.self_attn.q_proj": {
1849
+ "quant_algo": "MXFP8",
1850
+ "group_size": 32
1851
+ },
1852
+ "model.language_model.layers.47.self_attn.v_proj": {
1853
+ "quant_algo": "MXFP8",
1854
+ "group_size": 32
1855
+ },
1856
+ "model.language_model.layers.5.linear_attn.in_proj_a": {
1857
+ "quant_algo": "MXFP8",
1858
+ "group_size": 32
1859
+ },
1860
+ "model.language_model.layers.5.linear_attn.in_proj_b": {
1861
+ "quant_algo": "MXFP8",
1862
+ "group_size": 32
1863
+ },
1864
+ "model.language_model.layers.5.linear_attn.in_proj_qkv": {
1865
+ "quant_algo": "MXFP8",
1866
+ "group_size": 32
1867
+ },
1868
+ "model.language_model.layers.5.linear_attn.in_proj_z": {
1869
+ "quant_algo": "MXFP8",
1870
+ "group_size": 32
1871
+ },
1872
+ "model.language_model.layers.5.linear_attn.out_proj": {
1873
+ "quant_algo": "MXFP8",
1874
+ "group_size": 32
1875
+ },
1876
+ "model.language_model.layers.6.linear_attn.in_proj_a": {
1877
+ "quant_algo": "MXFP8",
1878
+ "group_size": 32
1879
+ },
1880
+ "model.language_model.layers.6.linear_attn.in_proj_b": {
1881
+ "quant_algo": "MXFP8",
1882
+ "group_size": 32
1883
+ },
1884
+ "model.language_model.layers.6.linear_attn.in_proj_qkv": {
1885
+ "quant_algo": "MXFP8",
1886
+ "group_size": 32
1887
+ },
1888
+ "model.language_model.layers.6.linear_attn.in_proj_z": {
1889
+ "quant_algo": "MXFP8",
1890
+ "group_size": 32
1891
+ },
1892
+ "model.language_model.layers.6.linear_attn.out_proj": {
1893
+ "quant_algo": "MXFP8",
1894
+ "group_size": 32
1895
+ },
1896
+ "model.language_model.layers.7.self_attn.indexer.index_qk_proj": {
1897
+ "quant_algo": "MXFP8",
1898
+ "group_size": 32
1899
+ },
1900
+ "model.language_model.layers.7.self_attn.k_proj": {
1901
+ "quant_algo": "MXFP8",
1902
+ "group_size": 32
1903
+ },
1904
+ "model.language_model.layers.7.self_attn.o_proj": {
1905
+ "quant_algo": "MXFP8",
1906
+ "group_size": 32
1907
+ },
1908
+ "model.language_model.layers.7.self_attn.q_proj": {
1909
+ "quant_algo": "MXFP8",
1910
+ "group_size": 32
1911
+ },
1912
+ "model.language_model.layers.7.self_attn.v_proj": {
1913
+ "quant_algo": "MXFP8",
1914
+ "group_size": 32
1915
+ },
1916
+ "model.language_model.layers.8.linear_attn.in_proj_a": {
1917
+ "quant_algo": "MXFP8",
1918
+ "group_size": 32
1919
+ },
1920
+ "model.language_model.layers.8.linear_attn.in_proj_b": {
1921
+ "quant_algo": "MXFP8",
1922
+ "group_size": 32
1923
+ },
1924
+ "model.language_model.layers.8.linear_attn.in_proj_qkv": {
1925
+ "quant_algo": "MXFP8",
1926
+ "group_size": 32
1927
+ },
1928
+ "model.language_model.layers.8.linear_attn.in_proj_z": {
1929
+ "quant_algo": "MXFP8",
1930
+ "group_size": 32
1931
+ },
1932
+ "model.language_model.layers.8.linear_attn.out_proj": {
1933
+ "quant_algo": "MXFP8",
1934
+ "group_size": 32
1935
+ },
1936
+ "model.language_model.layers.9.linear_attn.in_proj_a": {
1937
+ "quant_algo": "MXFP8",
1938
+ "group_size": 32
1939
+ },
1940
+ "model.language_model.layers.9.linear_attn.in_proj_b": {
1941
+ "quant_algo": "MXFP8",
1942
+ "group_size": 32
1943
+ },
1944
+ "model.language_model.layers.9.linear_attn.in_proj_qkv": {
1945
+ "quant_algo": "MXFP8",
1946
+ "group_size": 32
1947
+ },
1948
+ "model.language_model.layers.9.linear_attn.in_proj_z": {
1949
+ "quant_algo": "MXFP8",
1950
+ "group_size": 32
1951
+ },
1952
+ "model.language_model.layers.9.linear_attn.out_proj": {
1953
+ "quant_algo": "MXFP8",
1954
+ "group_size": 32
1955
+ },
1956
+ "mtp.layers.48.mlp.experts": {
1957
+ "quant_algo": "W4A16_NVFP4",
1958
+ "group_size": 16
1959
+ },
1960
+ "model.visual.blocks.0.attn.proj": {
1961
+ "quant_algo": "MXFP8",
1962
+ "group_size": 32
1963
+ },
1964
+ "model.visual.blocks.0.attn.qkv": {
1965
+ "quant_algo": "MXFP8",
1966
+ "group_size": 32
1967
+ },
1968
+ "model.visual.blocks.1.attn.proj": {
1969
+ "quant_algo": "MXFP8",
1970
+ "group_size": 32
1971
+ },
1972
+ "model.visual.blocks.1.attn.qkv": {
1973
+ "quant_algo": "MXFP8",
1974
+ "group_size": 32
1975
+ },
1976
+ "model.visual.blocks.10.attn.proj": {
1977
+ "quant_algo": "MXFP8",
1978
+ "group_size": 32
1979
+ },
1980
+ "model.visual.blocks.10.attn.qkv": {
1981
+ "quant_algo": "MXFP8",
1982
+ "group_size": 32
1983
+ },
1984
+ "model.visual.blocks.11.attn.proj": {
1985
+ "quant_algo": "MXFP8",
1986
+ "group_size": 32
1987
+ },
1988
+ "model.visual.blocks.11.attn.qkv": {
1989
+ "quant_algo": "MXFP8",
1990
+ "group_size": 32
1991
+ },
1992
+ "model.visual.blocks.12.attn.proj": {
1993
+ "quant_algo": "MXFP8",
1994
+ "group_size": 32
1995
+ },
1996
+ "model.visual.blocks.12.attn.qkv": {
1997
+ "quant_algo": "MXFP8",
1998
+ "group_size": 32
1999
+ },
2000
+ "model.visual.blocks.13.attn.proj": {
2001
+ "quant_algo": "MXFP8",
2002
+ "group_size": 32
2003
+ },
2004
+ "model.visual.blocks.13.attn.qkv": {
2005
+ "quant_algo": "MXFP8",
2006
+ "group_size": 32
2007
+ },
2008
+ "model.visual.blocks.14.attn.proj": {
2009
+ "quant_algo": "MXFP8",
2010
+ "group_size": 32
2011
+ },
2012
+ "model.visual.blocks.14.attn.qkv": {
2013
+ "quant_algo": "MXFP8",
2014
+ "group_size": 32
2015
+ },
2016
+ "model.visual.blocks.15.attn.proj": {
2017
+ "quant_algo": "MXFP8",
2018
+ "group_size": 32
2019
+ },
2020
+ "model.visual.blocks.15.attn.qkv": {
2021
+ "quant_algo": "MXFP8",
2022
+ "group_size": 32
2023
+ },
2024
+ "model.visual.blocks.16.attn.proj": {
2025
+ "quant_algo": "MXFP8",
2026
+ "group_size": 32
2027
+ },
2028
+ "model.visual.blocks.16.attn.qkv": {
2029
+ "quant_algo": "MXFP8",
2030
+ "group_size": 32
2031
+ },
2032
+ "model.visual.blocks.17.attn.proj": {
2033
+ "quant_algo": "MXFP8",
2034
+ "group_size": 32
2035
+ },
2036
+ "model.visual.blocks.17.attn.qkv": {
2037
+ "quant_algo": "MXFP8",
2038
+ "group_size": 32
2039
+ },
2040
+ "model.visual.blocks.18.attn.proj": {
2041
+ "quant_algo": "MXFP8",
2042
+ "group_size": 32
2043
+ },
2044
+ "model.visual.blocks.18.attn.qkv": {
2045
+ "quant_algo": "MXFP8",
2046
+ "group_size": 32
2047
+ },
2048
+ "model.visual.blocks.19.attn.proj": {
2049
+ "quant_algo": "MXFP8",
2050
+ "group_size": 32
2051
+ },
2052
+ "model.visual.blocks.19.attn.qkv": {
2053
+ "quant_algo": "MXFP8",
2054
+ "group_size": 32
2055
+ },
2056
+ "model.visual.blocks.2.attn.proj": {
2057
+ "quant_algo": "MXFP8",
2058
+ "group_size": 32
2059
+ },
2060
+ "model.visual.blocks.2.attn.qkv": {
2061
+ "quant_algo": "MXFP8",
2062
+ "group_size": 32
2063
+ },
2064
+ "model.visual.blocks.20.attn.proj": {
2065
+ "quant_algo": "MXFP8",
2066
+ "group_size": 32
2067
+ },
2068
+ "model.visual.blocks.20.attn.qkv": {
2069
+ "quant_algo": "MXFP8",
2070
+ "group_size": 32
2071
+ },
2072
+ "model.visual.blocks.21.attn.proj": {
2073
+ "quant_algo": "MXFP8",
2074
+ "group_size": 32
2075
+ },
2076
+ "model.visual.blocks.21.attn.qkv": {
2077
+ "quant_algo": "MXFP8",
2078
+ "group_size": 32
2079
+ },
2080
+ "model.visual.blocks.22.attn.proj": {
2081
+ "quant_algo": "MXFP8",
2082
+ "group_size": 32
2083
+ },
2084
+ "model.visual.blocks.22.attn.qkv": {
2085
+ "quant_algo": "MXFP8",
2086
+ "group_size": 32
2087
+ },
2088
+ "model.visual.blocks.23.attn.proj": {
2089
+ "quant_algo": "MXFP8",
2090
+ "group_size": 32
2091
+ },
2092
+ "model.visual.blocks.23.attn.qkv": {
2093
+ "quant_algo": "MXFP8",
2094
+ "group_size": 32
2095
+ },
2096
+ "model.visual.blocks.24.attn.proj": {
2097
+ "quant_algo": "MXFP8",
2098
+ "group_size": 32
2099
+ },
2100
+ "model.visual.blocks.24.attn.qkv": {
2101
+ "quant_algo": "MXFP8",
2102
+ "group_size": 32
2103
+ },
2104
+ "model.visual.blocks.25.attn.proj": {
2105
+ "quant_algo": "MXFP8",
2106
+ "group_size": 32
2107
+ },
2108
+ "model.visual.blocks.25.attn.qkv": {
2109
+ "quant_algo": "MXFP8",
2110
+ "group_size": 32
2111
+ },
2112
+ "model.visual.blocks.26.attn.proj": {
2113
+ "quant_algo": "MXFP8",
2114
+ "group_size": 32
2115
+ },
2116
+ "model.visual.blocks.26.attn.qkv": {
2117
+ "quant_algo": "MXFP8",
2118
+ "group_size": 32
2119
+ },
2120
+ "model.visual.blocks.3.attn.proj": {
2121
+ "quant_algo": "MXFP8",
2122
+ "group_size": 32
2123
+ },
2124
+ "model.visual.blocks.3.attn.qkv": {
2125
+ "quant_algo": "MXFP8",
2126
+ "group_size": 32
2127
+ },
2128
+ "model.visual.blocks.4.attn.proj": {
2129
+ "quant_algo": "MXFP8",
2130
+ "group_size": 32
2131
+ },
2132
+ "model.visual.blocks.4.attn.qkv": {
2133
+ "quant_algo": "MXFP8",
2134
+ "group_size": 32
2135
+ },
2136
+ "model.visual.blocks.5.attn.proj": {
2137
+ "quant_algo": "MXFP8",
2138
+ "group_size": 32
2139
+ },
2140
+ "model.visual.blocks.5.attn.qkv": {
2141
+ "quant_algo": "MXFP8",
2142
+ "group_size": 32
2143
+ },
2144
+ "model.visual.blocks.6.attn.proj": {
2145
+ "quant_algo": "MXFP8",
2146
+ "group_size": 32
2147
+ },
2148
+ "model.visual.blocks.6.attn.qkv": {
2149
+ "quant_algo": "MXFP8",
2150
+ "group_size": 32
2151
+ },
2152
+ "model.visual.blocks.7.attn.proj": {
2153
+ "quant_algo": "MXFP8",
2154
+ "group_size": 32
2155
+ },
2156
+ "model.visual.blocks.7.attn.qkv": {
2157
+ "quant_algo": "MXFP8",
2158
+ "group_size": 32
2159
+ },
2160
+ "model.visual.blocks.8.attn.proj": {
2161
+ "quant_algo": "MXFP8",
2162
+ "group_size": 32
2163
+ },
2164
+ "model.visual.blocks.8.attn.qkv": {
2165
+ "quant_algo": "MXFP8",
2166
+ "group_size": 32
2167
+ },
2168
+ "model.visual.blocks.9.attn.proj": {
2169
+ "quant_algo": "MXFP8",
2170
+ "group_size": 32
2171
+ },
2172
+ "model.visual.blocks.9.attn.qkv": {
2173
+ "quant_algo": "MXFP8",
2174
+ "group_size": 32
2175
+ },
2176
+ "model.visual.blocks.0.mlp.linear_fc1": {
2177
+ "quant_algo": "W4A16_NVFP4",
2178
+ "group_size": 16
2179
+ },
2180
+ "model.visual.blocks.0.mlp.linear_fc2": {
2181
+ "quant_algo": "W4A16_NVFP4",
2182
+ "group_size": 16
2183
+ },
2184
+ "model.visual.blocks.1.mlp.linear_fc1": {
2185
+ "quant_algo": "W4A16_NVFP4",
2186
+ "group_size": 16
2187
+ },
2188
+ "model.visual.blocks.1.mlp.linear_fc2": {
2189
+ "quant_algo": "W4A16_NVFP4",
2190
+ "group_size": 16
2191
+ },
2192
+ "model.visual.blocks.10.mlp.linear_fc1": {
2193
+ "quant_algo": "W4A16_NVFP4",
2194
+ "group_size": 16
2195
+ },
2196
+ "model.visual.blocks.10.mlp.linear_fc2": {
2197
+ "quant_algo": "W4A16_NVFP4",
2198
+ "group_size": 16
2199
+ },
2200
+ "model.visual.blocks.11.mlp.linear_fc1": {
2201
+ "quant_algo": "W4A16_NVFP4",
2202
+ "group_size": 16
2203
+ },
2204
+ "model.visual.blocks.11.mlp.linear_fc2": {
2205
+ "quant_algo": "W4A16_NVFP4",
2206
+ "group_size": 16
2207
+ },
2208
+ "model.visual.blocks.12.mlp.linear_fc1": {
2209
+ "quant_algo": "W4A16_NVFP4",
2210
+ "group_size": 16
2211
+ },
2212
+ "model.visual.blocks.12.mlp.linear_fc2": {
2213
+ "quant_algo": "W4A16_NVFP4",
2214
+ "group_size": 16
2215
+ },
2216
+ "model.visual.blocks.13.mlp.linear_fc1": {
2217
+ "quant_algo": "W4A16_NVFP4",
2218
+ "group_size": 16
2219
+ },
2220
+ "model.visual.blocks.13.mlp.linear_fc2": {
2221
+ "quant_algo": "W4A16_NVFP4",
2222
+ "group_size": 16
2223
+ },
2224
+ "model.visual.blocks.14.mlp.linear_fc1": {
2225
+ "quant_algo": "W4A16_NVFP4",
2226
+ "group_size": 16
2227
+ },
2228
+ "model.visual.blocks.14.mlp.linear_fc2": {
2229
+ "quant_algo": "W4A16_NVFP4",
2230
+ "group_size": 16
2231
+ },
2232
+ "model.visual.blocks.15.mlp.linear_fc1": {
2233
+ "quant_algo": "W4A16_NVFP4",
2234
+ "group_size": 16
2235
+ },
2236
+ "model.visual.blocks.15.mlp.linear_fc2": {
2237
+ "quant_algo": "W4A16_NVFP4",
2238
+ "group_size": 16
2239
+ },
2240
+ "model.visual.blocks.16.mlp.linear_fc1": {
2241
+ "quant_algo": "W4A16_NVFP4",
2242
+ "group_size": 16
2243
+ },
2244
+ "model.visual.blocks.16.mlp.linear_fc2": {
2245
+ "quant_algo": "W4A16_NVFP4",
2246
+ "group_size": 16
2247
+ },
2248
+ "model.visual.blocks.17.mlp.linear_fc1": {
2249
+ "quant_algo": "W4A16_NVFP4",
2250
+ "group_size": 16
2251
+ },
2252
+ "model.visual.blocks.17.mlp.linear_fc2": {
2253
+ "quant_algo": "W4A16_NVFP4",
2254
+ "group_size": 16
2255
+ },
2256
+ "model.visual.blocks.18.mlp.linear_fc1": {
2257
+ "quant_algo": "W4A16_NVFP4",
2258
+ "group_size": 16
2259
+ },
2260
+ "model.visual.blocks.18.mlp.linear_fc2": {
2261
+ "quant_algo": "W4A16_NVFP4",
2262
+ "group_size": 16
2263
+ },
2264
+ "model.visual.blocks.19.mlp.linear_fc1": {
2265
+ "quant_algo": "W4A16_NVFP4",
2266
+ "group_size": 16
2267
+ },
2268
+ "model.visual.blocks.19.mlp.linear_fc2": {
2269
+ "quant_algo": "W4A16_NVFP4",
2270
+ "group_size": 16
2271
+ },
2272
+ "model.visual.blocks.2.mlp.linear_fc1": {
2273
+ "quant_algo": "W4A16_NVFP4",
2274
+ "group_size": 16
2275
+ },
2276
+ "model.visual.blocks.2.mlp.linear_fc2": {
2277
+ "quant_algo": "W4A16_NVFP4",
2278
+ "group_size": 16
2279
+ },
2280
+ "model.visual.blocks.20.mlp.linear_fc1": {
2281
+ "quant_algo": "W4A16_NVFP4",
2282
+ "group_size": 16
2283
+ },
2284
+ "model.visual.blocks.20.mlp.linear_fc2": {
2285
+ "quant_algo": "W4A16_NVFP4",
2286
+ "group_size": 16
2287
+ },
2288
+ "model.visual.blocks.21.mlp.linear_fc1": {
2289
+ "quant_algo": "W4A16_NVFP4",
2290
+ "group_size": 16
2291
+ },
2292
+ "model.visual.blocks.21.mlp.linear_fc2": {
2293
+ "quant_algo": "W4A16_NVFP4",
2294
+ "group_size": 16
2295
+ },
2296
+ "model.visual.blocks.22.mlp.linear_fc1": {
2297
+ "quant_algo": "W4A16_NVFP4",
2298
+ "group_size": 16
2299
+ },
2300
+ "model.visual.blocks.22.mlp.linear_fc2": {
2301
+ "quant_algo": "W4A16_NVFP4",
2302
+ "group_size": 16
2303
+ },
2304
+ "model.visual.blocks.23.mlp.linear_fc1": {
2305
+ "quant_algo": "W4A16_NVFP4",
2306
+ "group_size": 16
2307
+ },
2308
+ "model.visual.blocks.23.mlp.linear_fc2": {
2309
+ "quant_algo": "W4A16_NVFP4",
2310
+ "group_size": 16
2311
+ },
2312
+ "model.visual.blocks.24.mlp.linear_fc1": {
2313
+ "quant_algo": "W4A16_NVFP4",
2314
+ "group_size": 16
2315
+ },
2316
+ "model.visual.blocks.24.mlp.linear_fc2": {
2317
+ "quant_algo": "W4A16_NVFP4",
2318
+ "group_size": 16
2319
+ },
2320
+ "model.visual.blocks.25.mlp.linear_fc1": {
2321
+ "quant_algo": "W4A16_NVFP4",
2322
+ "group_size": 16
2323
+ },
2324
+ "model.visual.blocks.25.mlp.linear_fc2": {
2325
+ "quant_algo": "W4A16_NVFP4",
2326
+ "group_size": 16
2327
+ },
2328
+ "model.visual.blocks.26.mlp.linear_fc1": {
2329
+ "quant_algo": "W4A16_NVFP4",
2330
+ "group_size": 16
2331
+ },
2332
+ "model.visual.blocks.26.mlp.linear_fc2": {
2333
+ "quant_algo": "W4A16_NVFP4",
2334
+ "group_size": 16
2335
+ },
2336
+ "model.visual.blocks.3.mlp.linear_fc1": {
2337
+ "quant_algo": "W4A16_NVFP4",
2338
+ "group_size": 16
2339
+ },
2340
+ "model.visual.blocks.3.mlp.linear_fc2": {
2341
+ "quant_algo": "W4A16_NVFP4",
2342
+ "group_size": 16
2343
+ },
2344
+ "model.visual.blocks.4.mlp.linear_fc1": {
2345
+ "quant_algo": "W4A16_NVFP4",
2346
+ "group_size": 16
2347
+ },
2348
+ "model.visual.blocks.4.mlp.linear_fc2": {
2349
+ "quant_algo": "W4A16_NVFP4",
2350
+ "group_size": 16
2351
+ },
2352
+ "model.visual.blocks.5.mlp.linear_fc1": {
2353
+ "quant_algo": "W4A16_NVFP4",
2354
+ "group_size": 16
2355
+ },
2356
+ "model.visual.blocks.5.mlp.linear_fc2": {
2357
+ "quant_algo": "W4A16_NVFP4",
2358
+ "group_size": 16
2359
+ },
2360
+ "model.visual.blocks.6.mlp.linear_fc1": {
2361
+ "quant_algo": "W4A16_NVFP4",
2362
+ "group_size": 16
2363
+ },
2364
+ "model.visual.blocks.6.mlp.linear_fc2": {
2365
+ "quant_algo": "W4A16_NVFP4",
2366
+ "group_size": 16
2367
+ },
2368
+ "model.visual.blocks.7.mlp.linear_fc1": {
2369
+ "quant_algo": "W4A16_NVFP4",
2370
+ "group_size": 16
2371
+ },
2372
+ "model.visual.blocks.7.mlp.linear_fc2": {
2373
+ "quant_algo": "W4A16_NVFP4",
2374
+ "group_size": 16
2375
+ },
2376
+ "model.visual.blocks.8.mlp.linear_fc1": {
2377
+ "quant_algo": "W4A16_NVFP4",
2378
+ "group_size": 16
2379
+ },
2380
+ "model.visual.blocks.8.mlp.linear_fc2": {
2381
+ "quant_algo": "W4A16_NVFP4",
2382
+ "group_size": 16
2383
+ },
2384
+ "model.visual.blocks.9.mlp.linear_fc1": {
2385
+ "quant_algo": "W4A16_NVFP4",
2386
+ "group_size": 16
2387
+ },
2388
+ "model.visual.blocks.9.mlp.linear_fc2": {
2389
+ "quant_algo": "W4A16_NVFP4",
2390
+ "group_size": 16
2391
+ },
2392
+ "model.visual.merger.linear_fc1": {
2393
+ "quant_algo": "W4A16_NVFP4",
2394
+ "group_size": 16
2395
+ },
2396
+ "model.visual.merger.linear_fc2": {
2397
+ "quant_algo": "W4A16_NVFP4",
2398
+ "group_size": 16
2399
+ }
2400
+ }
2401
+ }
2402
+ }
generation_config.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 248044,
3
+ "do_sample": true,
4
+ "eos_token_id": [
5
+ 248046,
6
+ 248044
7
+ ],
8
+ "pad_token_id": 248044,
9
+ "temperature": 1.0,
10
+ "top_k": 20,
11
+ "top_p": 0.95
12
+ }
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
preprocessor_config.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "size": {
3
+ "longest_edge": 16777216,
4
+ "shortest_edge": 65536
5
+ },
6
+ "patch_size": 16,
7
+ "temporal_patch_size": 2,
8
+ "merge_size": 2,
9
+ "image_mean": [
10
+ 0.5,
11
+ 0.5,
12
+ 0.5
13
+ ],
14
+ "image_std": [
15
+ 0.5,
16
+ 0.5,
17
+ 0.5
18
+ ],
19
+ "processor_class": "Qwen3VLProcessor",
20
+ "image_processor_type": "Qwen2VLImageProcessorFast"
21
+ }
recipe/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Diffbot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
recipe/README.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Recipe: Qwen3.8-Flash-Next FP4-Local on one DGX Spark
2
+
3
+ Everything needed to reproduce the published configuration: a launcher for a pinned public vLLM + b12x image, the
4
+ files mounted over that image, the benchmark suite and the KL client.
5
+
6
+ | Path | Contents |
7
+ |---|---|
8
+ | `serve/serve.sh` | The launcher. Every flag is documented inline and on by default; `FLAG=0` reverts that change. |
9
+ | `serve/seccomp.json` | Docker's default seccomp profile plus `io_uring_setup/enter/register`, which b12x needs to read the n-gram (PLE) tables from disk. |
10
+ | `serve/draft_vocab_64k.json` | 65,536 token ids for the MTP drafter's output head (see `DRAFT_VOCAB`). |
11
+ | `patches/` | Modified and new vLLM / b12x files, mounted read-only over the image (Apache-2.0; `THIRD_PARTY_NOTICES.md`). |
12
+ | `bench/` | The Diffbot release probe suite: tool calls, needle, GSM8K-200 + HumanEval, synthetic prefill/decode. |
13
+ | `kl/` | `atomic_kld.py`: scores the server's prompt logits against AtomicChat's BF16 llama.cpp reference. |
14
+ | `results/` | Raw outputs behind the numbers in the model card. |
15
+
16
+ ## The image
17
+
18
+ `eugr/spark-vllm-b12x@sha256:03a956544e9f9b4b7ba91690469fbe7848bdbef29a7d2e0f5788368467bb3595` (2026-10-15 nightly:
19
+ vLLM `0.1.dev21830+g8244d1c44` with the V2 model runner, flashinfer-python 0.7.1 with the experimental b12x port).
20
+ It already contains everything NVIDIA's model card asks for; the launcher adds the flags the card's command omits
21
+ (`--moe-backend b12x`, `--linear-backend b12x`, `VLLM_SSM_CONV_STATE_LAYOUT=DS`) and serves the 51B-parameter n-gram
22
+ (PLE) tables from NVMe (`VLLM_PLE_TABLE_MEMORY=disk`), so about 68 GiB of weights are resident.
23
+
24
+ ## What the patches change
25
+
26
+ Speeds are server inter-token latency per MTP verify step (4 draft tokens), measured on a private set of real
27
+ multi-turn agentic coding sessions ("replay") and on the public 40-prompt category harness. Each step was A/B tested
28
+ against the configuration before it, with tool calls and output checks; the numbers below are the cumulative effect.
29
+
30
+ | Flag | Files | Change | Effect |
31
+ |---|---|---|---|
32
+ | `MXFP8_LM_HEAD` | (image) | b12x MXFP8 LM head with bf16 activations instead of a bf16 sm80 fallback | ~3% faster steps |
33
+ | `SMALL_GEMV` | `ops/small_gemv.py`, `model.py` | split-K Triton GEMV for the MoE router at decode | |
34
+ | `HC_FP8`, `HC_FUSE` | `ops/hc_fp8_gemv.py`, `hyperconnection.py` | rowwise-FP8 split-K GEMVs for the HyperConnection mix at decode; scaled SiLU and gate mix fused in (4 → 2 launches) | |
35
+ | `MXFP8_GEMV` | `ops/mxfp8_gemv.py`, `vllm_kernels/linear/mxfp8/b12x.py` | split-K Triton GEMV over b12x's packed MXFP8 weights for narrow decode projections | |
36
+ | `DRAFT_VOCAB` | `mtp.py`, `ops/mxfp8_gemv.py` | the MTP drafter scores a 64K-token subset through the target's MXFP8 LM head rows | |
37
+ | `MTP_FP8`, `MTP_BF16_KV` | `mtp.py` | FP8 GEMVs for the drafter's bf16 projections; bf16 KV for the draft layer | |
38
+ | `TARGET_FP8` | `model.py` | FP8 GEMVs for the target's remaining bf16 projections (PLE key/value) | |
39
+ | `GEMMA_NORM` | `ops/gemma_norm.py` | one Triton launch per GemmaRMSNorm (stock: ~10 eager ops) | |
40
+ | `SHARED_FUSE` | `model.py`, `ops/small_gemv.py` | shared-expert tail in one kernel at decode | |
41
+ | `GDN_DEFER` | `gdn_defer.py`, `ops/gdn_deferred.py` | GDN verify writes per-token records; one commit after acceptance | replay 60.2 → 58.4 ms/step |
42
+ | `PLE_OVERLAP`, `PLE_ASYNC`, `PLE_KEEPALIVE_US` | `b12x_ple.py`, `b12x/loader/*`, `disk_table.py` | n-gram table reads overlap decoder layer 0; reads enqueued as a stream host function; NVMe kept out of deep idle | ~1–2 ms/step |
43
+ | `MOE_A16` | (image env) | routed experts with bf16 activations (W4A16) instead of the checkpoint default FP4 activations | KLD 0.0665 → 0.049, and faster |
44
+ | `CSF_INLINE` | `b12x/moe/...` | the decode MoE kernel decodes NVFP4-CSF scales inline instead of a separate expansion pass; bit-identical | replay 57.5 → 54.2 ms/step |
45
+ | `TAIL_CKPT` | `vllm_core/v1/core/boundary_checkpoint.py` | the prefix-cache checkpoint of a prompt is placed ≤31 tokens before its end instead of up to a whole prefill chunk earlier | agent follow-up TTFT 3.7 → 0.76 s at 4096-token chunks |
46
+
47
+ From the stock launcher to this configuration: replay 71.2 → 54.2 ms per verify step, 46.9 → ~64 tok/s weighted
48
+ decode.
49
+
50
+ Measurement-only flags (off by default): `LOGIT_DUMP` (prompt logits for the KL client), `EXPERT_STATS`,
51
+ `MOE_DETERMINISTIC` (b12x MoE sums top-k expert outputs with bf16 atomics, so outputs vary run to run; this switch makes
52
+ FP4-activation MoE deterministic at +4% decode and +28% TTFT, and has no effect with `MOE_A16`).
53
+
54
+ `--prefix-match-unit 16` (upstream vLLM) does not replace `TAIL_CKPT` here: it changes where a cache hit may land,
55
+ not where the GDN state is saved; with it and `TAIL_CKPT=off`, the replay's median TTFT was 3.74 s.
56
+
57
+ ## Reproducing the numbers
58
+
59
+ ```bash
60
+ cd recipe/bench && bash fetch-evaldata.sh
61
+ export API_KEY=none SERVED_MODEL=qwen3.8-flash-next EVALDATA=$PWD/evaldata
62
+ python3 test-toolcall.py
63
+ for reps in 1800 6000 7800; do python3 needle-test.py $reps; done # ~29K, ~97K, ~126K tokens
64
+ python3 quality-probe.py # GSM8K first 200 + HumanEval, greedy, thinking off
65
+ for c in 2000 46000; do for n in 1 4; do CTX=$c CONC=$n python3 bench-sbs.py; done; done
66
+ ```
67
+
68
+ KL (needs about 90 GB of disk for the reference): download `kld/base-neutral.*` from the dataset
69
+ `AtomicChat/Qwen3.8-Flash-Next-Uncensored-GGUF-metrics` (revision `09e4a4c2`), start the server without `API_KEY` and
70
+ with `LOGIT_DUMP=kld-fwd TAIL_CKPT=off` (one forward pass per 4096-token chunk), then:
71
+
72
+ ```bash
73
+ python3 kl/atomic_kld.py http://127.0.0.1:8000 qwen3.8-flash-next kld/base-neutral.kld.00.part \
74
+ ~/.cache/qwen38-flash-next-spark/kld-fwd results/kl-neutral.json
75
+ ```
recipe/THIRD_PARTY_NOTICES.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Third-party notices
2
+
3
+ ## Model weights
4
+
5
+ | Component | License | Where |
6
+ |---|---|---|
7
+ | nvidia/Qwen3.8-Flash-Next-FP4-Local (NVIDIA ModelOpt QAD NVFP4 checkpoint), redistributed unmodified | NVIDIA Open Model Agreement | `../LICENSE`, `../NOTICE`, `../NVIDIA_MODEL_CARD.md` |
8
+ | Qwen/Qwen3.8-Flash-Next (base model) | Qwen Community License 1.0 | `../LICENSE.Qwen` |
9
+
10
+ The Qwen Community License requires a separate license from Qwen before commercial "Model as a Service" or
11
+ "AI Work Assistant" use, and a visible model name in products above 100M monthly active users or US$20M monthly
12
+ revenue. Read `../LICENSE.Qwen` before serving the model to third parties.
13
+
14
+ ## Code in `patches/` (Apache-2.0)
15
+
16
+ Mounted over the files of the pinned image `eugr/spark-vllm-b12x@sha256:03a956544e9f9b4b7ba91690469fbe7848bdbef29a7d2e0f5788368467bb3595`
17
+ (vLLM `0.1.dev21830+g8244d1c44`, flashinfer-python 0.7.1 with the experimental b12x port).
18
+
19
+ Modified upstream files (originals: vLLM, Copyright the vLLM project, Apache-2.0; b12x, Copyright
20
+ local-inference-lab and the FlashInfer contributors, Apache-2.0). Each is changed by Diffbot as described in
21
+ `patches/README.md`:
22
+
23
+ - `qwen4_exp/nvidia/model.py`, `hyperconnection.py`, `mtp.py`, `b12x_ple.py` (vLLM `models/qwen4_exp/nvidia/`)
24
+ - `vllm_kernels/linear/mxfp8/b12x.py` (vLLM `model_executor/kernels/linear/mxfp8/b12x.py`)
25
+ - `vllm_core/v1/core/boundary_checkpoint.py` (vLLM `v1/core/boundary_checkpoint.py`)
26
+ - `b12x/moe/_shared/kernels/w4a16/kernel.py`, `b12x/moe/fused_moe/_impl.py`, `b12x/moe/fused_moe/_preparation.py`
27
+ - `b12x/loader/_row_plan.h`, `b12x/loader/_ple_reader.c`, `b12x/loader/_storage.c`, `b12x/sequence/_shared/disk_table.py`
28
+
29
+ New files by Diffbot, Apache-2.0 like the code they plug into:
30
+
31
+ - `qwen4_exp/nvidia/gdn_defer.py`, `logit_dump.py`, `expert_stats.py`
32
+ - `qwen4_exp/nvidia/ops/small_gemv.py`, `hc_fp8_gemv.py`, `mxfp8_gemv.py`, `nvfp4_gemv.py`, `gemma_norm.py`, `gdn_deferred.py`
33
+
34
+ The inline NVFP4-CSF scale decode in `b12x/moe/_shared/kernels/w4a16/kernel.py` was written independently of,
35
+ and does the same job as, upstream b12x PR #475 (merged 2026-10-06), which the pinned image's FlashInfer port
36
+ does not include yet.
37
+
38
+ ## Other code in `recipe/` (MIT, `LICENSE`)
39
+
40
+ `serve/`, `bench/` (the Diffbot release probe suite: `quality-probe.py`, `needle-test.py`, `test-toolcall.py`,
41
+ `bench-sbs.py`, `fetch-evaldata.sh`) and `kl/`.
42
+
43
+ ## Data used by the benchmarks
44
+
45
+ - GSM8K and HumanEval (OpenAI, MIT), downloaded by `bench/fetch-evaldata.sh`.
46
+ - AtomicChat's llama.cpp KL reference: the original BF16 Qwen3.8-Flash-Next's logits over their held-out neutral
47
+ set, 87 chunks at 4096 context (`kld/base-neutral.kld.*`, dataset
48
+ https://huggingface.co/datasets/AtomicChat/Qwen3.8-Flash-Next-Uncensored-GGUF-metrics at revision
49
+ `09e4a4c2`), used by `kl/atomic_kld.py`.
50
+ - The 40-prompt category benchmark is tonyd2wild's `bench_categories.py` (not redistributed here).
recipe/bench/bench-sbs.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Side-by-side on the REAL workload shape. Non-streaming (no client parse cap).
3
+ PREFILL: unique uncached prompt per stream, max_tokens=1 -> wall time is prefill.
4
+ DECODE : warm the prefix cache, then short(16)/long(400) pair -> subtraction cancels prefill."""
5
+ import json, os, random, sys, threading, time, urllib.request
6
+ K=os.environ["API_KEY"]
7
+ M=os.environ.get("SERVED_MODEL","deepseek-v4.1-flash"); CTX=int(os.environ.get("CTX","46000"))
8
+ CONC=int(os.environ.get("CONC","1")); URL=f"http://localhost:{os.environ.get('PORT','8000')}/v1/chat/completions"
9
+ W="alpha beta gamma delta epsilon zeta eta theta iota kappa system network memory kernel buffer thread packet cluster tensor matrix".split()
10
+ def prompt(seed):
11
+ r=random.Random(seed)
12
+ return ("Reference document:\n"+" ".join(r.choice(W) for _ in range(int(CTX*0.95)))+
13
+ "\n\nIgnore the document above. Write a detailed technical explanation of how speculative decoding works in LLM inference.")
14
+ def call(p,nmax):
15
+ body=json.dumps({"model":M,"max_tokens":nmax,"temperature":0,"messages":[{"role":"user","content":p}]}).encode()
16
+ req=urllib.request.Request(URL,data=body,headers={"Content-Type":"application/json","Authorization":f"Bearer {K}"})
17
+ t0=time.time()
18
+ with urllib.request.urlopen(req,timeout=1800) as r: d=json.loads(r.read())
19
+ return time.time()-t0, d["usage"]["completion_tokens"], d["usage"]["prompt_tokens"]
20
+ bar=threading.Barrier(CONC); res=[None]*CONC
21
+ def worker(i):
22
+ seed=i*7919+int(time.time())
23
+ p=prompt(seed)
24
+ try:
25
+ bar.wait(); t_pf,_,ptok=call(p,1) # uncached prefill
26
+ call(p,16) # warm
27
+ bar.wait(); t_s,n_s,_=call(p,16); t_l,n_l,_=call(p,400)
28
+ dec=(n_l-n_s)/(t_l-t_s) if t_l>t_s else 0.0
29
+ res[i]=dict(prefill_s=t_pf, ptok=ptok, prefill_tps=ptok/t_pf, decode_tps=dec)
30
+ except Exception as e:
31
+ res[i]=dict(err=str(e)[:150])
32
+ ts=[threading.Thread(target=worker,args=(i,)) for i in range(CONC)]
33
+ [t.start() for t in ts]; [t.join() for t in ts]
34
+ ok=[r for r in res if r and "err" not in r]; bad=[r for r in res if r and "err" in r]
35
+ if bad: print(f" ERRORS: {bad}")
36
+ if ok:
37
+ print(f" {M} ctx={CTX} conc={CONC}: prompt_tok={ok[0]['ptok']} | "
38
+ f"prefill {sum(o['prefill_tps'] for o in ok):8.0f} tok/s agg ({sum(o['prefill_tps'] for o in ok)/len(ok):7.0f}/stream, TTFT {max(o['prefill_s'] for o in ok):5.1f}s) | "
39
+ f"decode {sum(o['decode_tps'] for o in ok):6.1f} tok/s agg ({sum(o['decode_tps'] for o in ok)/len(ok):5.1f}/stream)")
recipe/bench/fetch-evaldata.sh ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Download the quality-probe datasets into bench/evaldata: GSM8K test (answers reduced to the final number) and
3
+ # HumanEval (164 problems). Both are MIT-licensed OpenAI releases.
4
+ set -euo pipefail
5
+ D=${EVALDATA:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/evaldata}; mkdir -p "$D"
6
+ python3 - "$D" <<'PY'
7
+ import gzip, json, sys, urllib.request
8
+ d = sys.argv[1]
9
+ g = urllib.request.urlopen("https://raw.githubusercontent.com/openai/grade-school-math/master/grade_school_math/data/test.jsonl").read().decode()
10
+ with open(f"{d}/gsm8k-test.jsonl", "w") as f:
11
+ for line in g.splitlines():
12
+ r = json.loads(line)
13
+ f.write(json.dumps({"question": r["question"], "answer": r["answer"].split("####")[-1].strip().replace(",", "")}) + "\n")
14
+ h = gzip.decompress(urllib.request.urlopen("https://raw.githubusercontent.com/openai/human-eval/master/data/HumanEval.jsonl.gz").read()).decode()
15
+ with open(f"{d}/humaneval.jsonl", "w") as f:
16
+ for line in h.splitlines():
17
+ r = json.loads(line)
18
+ f.write(json.dumps({k: r[k] for k in ("task_id", "prompt", "test", "entry_point")}) + "\n")
19
+ print("wrote", d)
20
+ PY
recipe/bench/needle-test.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Long-context needle probe: ~29K-token prompt with a codename buried mid-document.
3
+ Exercises the sparse-MLA PREFILL orchestrator (>64 tokens) + top-k decode. Prints prompt tokens, TTFT-ish latency, found?"""
4
+ import os,re,json,time,urllib.request,sys
5
+ key=os.environ["API_KEY"]
6
+ H={"Content-Type":"application/json","Authorization":f"Bearer {key}"}
7
+ reps=int(sys.argv[1]) if len(sys.argv)>1 else 1800
8
+ filler=("The quarterly report discusses supply chains, logistics, and regional demand across several markets. ")*reps
9
+ doc=filler[:len(filler)//2]+" The secret project codename is PELICAN-42. "+filler[len(filler)//2:]
10
+ b={"model":os.environ.get("SERVED_MODEL","deepseek-v4.1-flash"),"messages":[{"role":"user","content":doc+"\n\nWhat is the secret project codename? Answer with just the codename."}],
11
+ "max_tokens":60,"temperature":0,"chat_template_kwargs":json.loads(os.environ.get("TEMPLATE_KWARGS",'{"enable_thinking": false}'))}
12
+ t=time.time(); r=json.load(urllib.request.urlopen(urllib.request.Request(f"http://127.0.0.1:{os.environ.get('PORT','8000')}/v1/chat/completions",data=json.dumps(b).encode(),headers=H),timeout=900)); dt=time.time()-t
13
+ m=r["choices"][0]["message"]; txt=(m.get("content") or "")+(m.get("reasoning") or m.get("reasoning_content") or "")
14
+ ok="PELICAN-42" in txt
15
+ print(f"prompt_tokens={r['usage']['prompt_tokens']:,} | {dt:.1f}s (~{r['usage']['prompt_tokens']/dt:,.0f} tok/s prefill) | found={ok} | {txt.strip()[:70]!r}")
16
+ print("NEEDLE OK" if ok else "NEEDLE MISS")
recipe/bench/quality-probe.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Quick quality probe for an OpenAI-compatible server: GSM8K subset + HumanEval pass@1.
3
+
4
+ Greedy, thinking OFF by default (most sensitive to weight damage). Same prompts for every model.
5
+ Env: PORT (8000), SERVED_MODEL, API_KEY (required), EVALDATA (bench/evaldata; see fetch-evaldata.sh), N_GSM (200), CONC (4),
6
+ THINKING (0/1), MAX_TOKENS (4096), OUT (jsonl path), SKIP_HE=1 to skip HumanEval.
7
+ Usage: API_KEY=... PORT=8000 python3 quality-probe.py
8
+ """
9
+ import json, os, re, subprocess, sys, tempfile, time, urllib.request
10
+ from concurrent.futures import ThreadPoolExecutor
11
+
12
+ D = os.environ.get("EVALDATA", os.path.join(os.path.dirname(os.path.abspath(__file__)), "evaldata"))
13
+ PORT = os.environ.get("PORT", "8000")
14
+ MODEL = os.environ.get("SERVED_MODEL", "deepseek-v4.1-flash")
15
+ KEY = os.environ["API_KEY"]
16
+ N_GSM = int(os.environ.get("N_GSM", "200"))
17
+ CONC = int(os.environ.get("CONC", "4"))
18
+ THINK = os.environ.get("THINKING", "0") == "1"
19
+ MAXT = int(os.environ.get("MAX_TOKENS", "4096"))
20
+ OUT = os.environ.get("OUT", f"/tmp/quality-{MODEL.replace('/', '_')}-{int(time.time())}.jsonl")
21
+
22
+
23
+ def chat(content):
24
+ body = {"model": MODEL, "messages": [{"role": "user", "content": content}],
25
+ "temperature": 0, "max_tokens": MAXT,
26
+ "chat_template_kwargs": {"enable_thinking": THINK, "thinking": THINK}}
27
+ req = urllib.request.Request(f"http://127.0.0.1:{PORT}/v1/chat/completions", json.dumps(body).encode(),
28
+ {"Content-Type": "application/json", "Authorization": f"Bearer {KEY}"})
29
+ for attempt in range(3):
30
+ try:
31
+ with urllib.request.urlopen(req, timeout=900) as r:
32
+ d = json.load(r)
33
+ m = d["choices"][0]["message"]
34
+ return (m.get("content") or ""), d["choices"][0].get("finish_reason"), d.get("usage", {})
35
+ except Exception as e: # noqa: BLE001
36
+ err = str(e)
37
+ time.sleep(2)
38
+ return f"<<error {err}>>", "error", {}
39
+
40
+
41
+ def num(s):
42
+ s = s.replace(",", "").replace("$", "")
43
+ m = re.findall(r"[Ff]inal answer[:\s]*\**\s*(-?\d+(?:\.\d+)?)", s) or re.findall(r"-?\d+(?:\.\d+)?", s)
44
+ return m[-1] if m else None
45
+
46
+
47
+ def gsm_one(r):
48
+ c, fin, u = chat(r["question"] + "\n\nSolve step by step, then end with a line 'Final answer: <number>'.")
49
+ p = num(c)
50
+ ok = p is not None and abs(float(p) - float(r["answer"])) < 1e-6
51
+ return {"task": "gsm8k", "ok": ok, "pred": p, "ref": r["answer"], "finish": fin, "out_tokens": u.get("completion_tokens")}
52
+
53
+
54
+ def he_one(r):
55
+ c, fin, u = chat("Complete this Python function. Reply with the complete function (including imports and the "
56
+ "signature) in one ```python code block, no explanation.\n\n```python\n" + r["prompt"] + "```")
57
+ blocks = re.findall(r"```(?:python)?\n(.*?)```", c, re.S)
58
+ code = blocks[0] if blocks else c
59
+ if f"def {r['entry_point']}" not in code:
60
+ code = r["prompt"] + code
61
+ prog = code + "\n\n" + r["test"] + f"\n\ncheck({r['entry_point']})\n"
62
+ with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
63
+ f.write(prog)
64
+ try:
65
+ ok = subprocess.run([sys.executable, f.name], capture_output=True, timeout=15).returncode == 0
66
+ except subprocess.TimeoutExpired:
67
+ ok = False
68
+ os.unlink(f.name)
69
+ return {"task": "humaneval", "id": r["task_id"], "ok": ok, "finish": fin, "out_tokens": u.get("completion_tokens")}
70
+
71
+
72
+ def run(fn, rows, name):
73
+ t = time.time()
74
+ with ThreadPoolExecutor(CONC) as ex:
75
+ res = list(ex.map(fn, rows))
76
+ acc = sum(r["ok"] for r in res) / len(res)
77
+ trunc = sum(r["finish"] == "length" for r in res); errs = sum(r["finish"] == "error" for r in res)
78
+ print(f"{MODEL} {name}: {sum(r['ok'] for r in res)}/{len(res)} = {acc:.1%} (truncated {trunc}, errors {errs}, {time.time()-t:.0f}s)", flush=True)
79
+ return res
80
+
81
+
82
+ if __name__ == "__main__":
83
+ gsm = [json.loads(l) for l in open(f"{D}/gsm8k-test.jsonl")][:N_GSM]
84
+ res = run(gsm_one, gsm, f"GSM8K first {len(gsm)}")
85
+ if os.environ.get("SKIP_HE") != "1":
86
+ he = [json.loads(l) for l in open(f"{D}/humaneval.jsonl")]
87
+ res += run(he_one, he, f"HumanEval {len(he)}")
88
+ with open(OUT, "w") as f:
89
+ for r in res:
90
+ f.write(json.dumps(r) + "\n")
91
+ print("wrote", OUT, "| thinking", THINK)
recipe/bench/test-toolcall.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Verify tool_choice=auto + reasoning parser (what agent harnesses send)."""
3
+ import json, os, re, urllib.request, sys
4
+ key=os.environ["API_KEY"]
5
+ H={"Content-Type":"application/json","Authorization":f"Bearer {key}"}
6
+ tools=[{"type":"function","function":{"name":"web_search","description":"Search the web","parameters":{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}}}]
7
+ def call(msgs, tool_choice="auto", n=300):
8
+ b={"model":os.environ.get("SERVED_MODEL","deepseek-v4.1-flash"),"messages":msgs,"tools":tools,"tool_choice":tool_choice,"max_tokens":n,"temperature":0}
9
+ req=urllib.request.Request(f"http://127.0.0.1:{os.environ.get('PORT','8000')}/v1/chat/completions",data=json.dumps(b).encode(),headers=H)
10
+ try:
11
+ r=json.load(urllib.request.urlopen(req,timeout=300))
12
+ except urllib.error.HTTPError as e:
13
+ print("HTTP", e.code, e.read().decode()[:300]); sys.exit(1)
14
+ m=r["choices"][0]["message"]
15
+ return m, r["choices"][0].get("finish_reason"), r["usage"]
16
+ m,fr,u=call([{"role":"user","content":"Who won the 2026 Tour de France? Use the web_search tool."}])
17
+ print("finish_reason:", fr, "| tool_calls:", json.dumps(m.get("tool_calls"))[:200] if m.get("tool_calls") else None)
18
+ print("reasoning field present:", bool(m.get("reasoning") or m.get("reasoning_content")), "| content:", (m.get("content") or "")[:80].replace("\n"," "))
19
+ m2,fr2,u2=call([{"role":"user","content":"What is 2+2? Answer briefly, no tools needed."}])
20
+ print("no-tool case -> finish:", fr2, "| tool_calls:", bool(m2.get("tool_calls")), "| reasoning:", bool(m2.get("reasoning") or m2.get("reasoning_content")), "| content:", (m2.get("content") or "")[:60].replace("\n"," "))
21
+ print("TOOLCALL TEST OK" if (m.get("tool_calls") and fr=="tool_calls") else "TOOLCALL TEST: no tool call emitted (check parser)")
recipe/kl/atomic_kld.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Replay a llama.cpp --kl-divergence-base file through the vLLM server (forward-side dump)
2
+ and score it the way llama-perplexity --kl-divergence does.
3
+
4
+ atomic_kld.py BASE_URL MODEL BASE_FILE DUMP_DIR OUT.json [MAX_CHUNKS]
5
+
6
+ The base file: "_logits_", n_ctx u32, n_vocab i32, n_chunk i32, tokens i32[n_ctx*n_chunk],
7
+ then per chunk (n_ctx - 1 - n_ctx/2) rows of nv = 2*((n_vocab+1)//2) + 4 uint16: two floats
8
+ (scale, min_log_prob) and n_vocab quantized log-probs, log p = scale*q + min_log_prob.
9
+ Each chunk's n_ctx tokens are sent as one prompt (fresh cache_salt); positions n_ctx/2 ..
10
+ n_ctx-2 are scored, KL summing base tokens with log p > -16 (llama.cpp's truncation).
11
+ """
12
+ import json
13
+ import os
14
+ import sys
15
+ import time
16
+ import urllib.request
17
+
18
+ import numpy as np
19
+ import torch
20
+
21
+
22
+ def post(url, body):
23
+ req = urllib.request.Request(url, json.dumps(body).encode(), {"Content-Type": "application/json"})
24
+ return json.load(urllib.request.urlopen(req, timeout=3600))
25
+
26
+
27
+ class Base:
28
+ def __init__(self, paths):
29
+ self.paths = paths
30
+ self.sizes = [os.path.getsize(p) for p in paths]
31
+ head = self.read(0, 20)
32
+ assert head[:8] == b"_logits_", head[:8]
33
+ self.n_ctx, self.n_vocab, self.n_chunk = np.frombuffer(head[8:20], dtype=np.int32).tolist()
34
+ self.nv = 2 * ((self.n_vocab + 1) // 2) + 4
35
+ self.rows = self.n_ctx - 1 - self.n_ctx // 2
36
+ self.tok_off = 20
37
+ self.data_off = 20 + 4 * self.n_ctx * self.n_chunk
38
+ if self.n_chunk <= 0: # header written before the chunk count was known
39
+ self.n_chunk = (sum(self.sizes) - 20) // (4 * self.n_ctx + 2 * self.nv * self.rows)
40
+ self.data_off = 20 + 4 * self.n_ctx * self.n_chunk
41
+ expect = self.data_off + 2 * self.nv * self.rows * self.n_chunk
42
+ assert expect == sum(self.sizes), (expect, sum(self.sizes))
43
+
44
+ def read(self, off, n):
45
+ out = bytearray()
46
+ for p, size in zip(self.paths, self.sizes):
47
+ if off >= size:
48
+ off -= size
49
+ continue
50
+ with open(p, "rb") as f:
51
+ f.seek(off)
52
+ chunk = f.read(min(n - len(out), size - off))
53
+ out += chunk
54
+ off = 0
55
+ if len(out) == n:
56
+ break
57
+ return bytes(out)
58
+
59
+ def tokens(self, c):
60
+ raw = self.read(self.tok_off + 4 * self.n_ctx * c, 4 * self.n_ctx)
61
+ return np.frombuffer(raw, dtype=np.int32).tolist()
62
+
63
+ def logprobs(self, c):
64
+ raw = self.read(self.data_off + 2 * self.nv * self.rows * c, 2 * self.nv * self.rows)
65
+ q = np.frombuffer(raw, dtype=np.uint16).reshape(self.rows, self.nv)
66
+ d = q[:, :4].copy().view(np.float32) # [rows, 2]: scale, min_log_prob
67
+ lp = torch.from_numpy(q[:, 4:4 + self.n_vocab].astype(np.float32))
68
+ lp = lp * torch.from_numpy(d[:, :1]) + torch.from_numpy(d[:, 1:2])
69
+ return lp, q[:, 4:4 + self.n_vocab] == 0
70
+
71
+
72
+ def score_chunk(base_lp, base_zero, ours, nxt):
73
+ lq = torch.log_softmax(ours.double(), -1)
74
+ keep = (base_lp > -16.0) & ~torch.from_numpy(base_zero)
75
+ p = torch.where(keep, base_lp.double().exp(), torch.zeros((), dtype=torch.float64))
76
+ kld = (p * (base_lp.double() - lq)).where(keep, torch.zeros((), dtype=torch.float64)).sum(-1)
77
+ same = base_lp.argmax(-1) == lq.argmax(-1)
78
+ nll_q = -lq.gather(1, nxt[:, None])[:, 0]
79
+ nll_b = -base_lp.double().gather(1, nxt[:, None])[:, 0]
80
+ dp = (-nll_q).exp() - (-nll_b).exp()
81
+ return kld, same, nll_q, nll_b, dp
82
+
83
+
84
+ def main(base_url, model, base_file, dump_dir, out, max_chunks=None):
85
+ parts = sorted(p for p in [base_file] + [base_file.replace(".00.part", f".{i:02d}.part") for i in range(1, 8)]
86
+ if os.path.exists(p))
87
+ base = Base(list(dict.fromkeys(parts)))
88
+ n = base.n_chunk if max_chunks is None else min(base.n_chunk, int(max_chunks))
89
+ print(f"base: n_ctx {base.n_ctx} vocab {base.n_vocab} chunks {base.n_chunk} (scoring {n}), {base.rows} rows/chunk", flush=True)
90
+ first = base.n_ctx // 2
91
+ acc = {k: [] for k in ("kld", "same", "nll_q", "nll_b", "dp")}
92
+ for c in range(n):
93
+ toks = base.tokens(c)
94
+ name = f"atomic-{c:03d}"
95
+ for ext in (".bin", ".idx"):
96
+ if os.path.exists(os.path.join(dump_dir, name + ext)):
97
+ os.remove(os.path.join(dump_dir, name + ext))
98
+ with open(os.path.join(dump_dir, "ARM"), "w") as f:
99
+ f.write(f"{name} {first}")
100
+ t0 = time.time()
101
+ try:
102
+ post(base_url + "/v1/completions", {"model": model, "prompt": toks, "max_tokens": 1,
103
+ "temperature": 0, "cache_salt": os.urandom(8).hex()})
104
+ finally:
105
+ os.remove(os.path.join(dump_dir, "ARM"))
106
+ idx = [tuple(map(int, l.split())) for l in open(os.path.join(dump_dir, name + ".idx"))]
107
+ assert idx[0][0] == first and sum(r for _, r, _ in idx) >= base.rows, idx
108
+ ours = torch.from_numpy(np.fromfile(os.path.join(dump_dir, name + ".bin"), dtype=np.float32)
109
+ .reshape(-1, base.n_vocab)[: base.rows])
110
+ os.remove(os.path.join(dump_dir, name + ".bin"))
111
+ os.remove(os.path.join(dump_dir, name + ".idx"))
112
+ base_lp, base_zero = base.logprobs(c)
113
+ nxt = torch.tensor(toks[first + 1:first + 1 + base.rows], dtype=torch.long)
114
+ for k, v in zip(acc, score_chunk(base_lp, base_zero, ours, nxt)):
115
+ acc[k].append(v)
116
+ kl = torch.cat(acc["kld"])
117
+ print(f"chunk {c + 1}/{n} ({time.time() - t0:.1f}s): running mean KLD {kl.mean():.5f} "
118
+ f"± {kl.std() / kl.numel() ** 0.5:.5f}, same top-1 {100 * torch.cat(acc['same']).double().mean():.2f}%", flush=True)
119
+ kl, same, nq, nb, dp = (torch.cat(acc[k]) for k in acc)
120
+ q = torch.quantile(kl.float()[torch.randperm(kl.numel())[: min(kl.numel(), 1_000_000)]],
121
+ torch.tensor([0.5, 0.9, 0.99, 0.999]))
122
+ res = {
123
+ "chunks": n, "positions": kl.numel(),
124
+ "mean_kld": kl.mean().item(), "kld_se": (kl.std() / kl.numel() ** 0.5).item(),
125
+ "median_kld": q[0].item(), "p90_kld": q[1].item(), "p99_kld": q[2].item(), "p999_kld": q[3].item(),
126
+ "same_top1_pct": 100 * same.double().mean().item(),
127
+ "ppl_q": nq.mean().exp().item(), "ppl_base": nb.mean().exp().item(),
128
+ "ln_ppl_ratio": (nq.mean() - nb.mean()).item(),
129
+ "rms_dp_pct": 100 * dp.pow(2).mean().sqrt().item(),
130
+ }
131
+ json.dump(res, open(out, "w"), indent=1)
132
+ print(json.dumps(res, indent=1))
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main(*sys.argv[1:7])
recipe/patches/b12x/loader/_ple_reader.c ADDED
@@ -0,0 +1,614 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Included by _storage.c. File payloads are never mapped or retained between runs. */
2
+ #include <fcntl.h>
3
+ #include <time.h>
4
+ #ifdef B12X_HAVE_LIBURING
5
+ #include <liburing.h>
6
+ #endif
7
+
8
+ #include "_row_plan.h"
9
+ #define PLE_CAPSULE "b12x.ple_reader"
10
+
11
+ typedef struct ple_reader ple_reader_t;
12
+ typedef struct {
13
+ char* buffer;
14
+ size_t job;
15
+ } ple_slot_t;
16
+
17
+ /* One stream-ordered read enqueued by ple_reader_run_async. */
18
+ #define PLE_ASYNC_RING 8
19
+ typedef struct {
20
+ ple_reader_t* reader;
21
+ const char* ids;
22
+ char *weights, *scales;
23
+ size_t count;
24
+ int pending; /* __atomic: set at enqueue, cleared by the host function */
25
+ } ple_async_t;
26
+
27
+ struct ple_reader {
28
+ ple_plan_t plan;
29
+ bool poisoned;
30
+ pthread_mutex_t api_mutex;
31
+ unsigned slots_count;
32
+ ple_slot_t* slots;
33
+ char* buffers;
34
+ char *weights, *scales;
35
+ ple_async_t async_jobs[PLE_ASYNC_RING];
36
+ unsigned async_next;
37
+ int async_failed; /* __atomic; async_failure is valid once set */
38
+ failure_t async_failure;
39
+ /* NVMe keep-alive (B12X_PLE_KEEPALIVE_US); see ple_keepalive_main. */
40
+ pthread_t keepalive_thread;
41
+ bool keepalive_started;
42
+ int keepalive_stop; /* __atomic */
43
+ long long last_read_ns; /* __atomic, CLOCK_MONOTONIC */
44
+ long keepalive_us;
45
+ char* keepalive_buffer;
46
+ #ifdef B12X_HAVE_LIBURING
47
+ struct io_uring ring;
48
+ bool ring_ready;
49
+ bool files_registered;
50
+ int* registered_fds;
51
+ struct iovec* iovecs;
52
+ unsigned* free_slots;
53
+ #endif
54
+ };
55
+
56
+ #ifdef B12X_HAVE_LIBURING
57
+ static void ple_error(ple_reader_t* reader, const char* operation, int error) {
58
+ if (!reader->plan.failure.message[0])
59
+ snprintf(reader->plan.failure.message, sizeof(reader->plan.failure.message), "%s: %s",
60
+ operation, strerror(error));
61
+ }
62
+
63
+ static bool ple_scatter(ple_reader_t* reader, size_t index, char* buffer, int result) {
64
+ ple_job_t* job = &reader->plan.jobs[index];
65
+ reader->plan.read_calls++;
66
+ if (result > 0) reader->plan.read_bytes += (unsigned)result;
67
+ if (result < 0) {
68
+ ple_error(reader, "io_uring READ_FIXED", -result);
69
+ return false;
70
+ }
71
+ if ((unsigned)result != job->expected) {
72
+ if (!reader->plan.failure.message[0])
73
+ snprintf(reader->plan.failure.message, sizeof(reader->plan.failure.message),
74
+ "short PLE read at offset %lld: expected %u bytes, received %d; source changed or "
75
+ "was truncated",
76
+ (long long)job->offset, job->expected, result);
77
+ return false;
78
+ }
79
+ for (size_t i = job->begin; i < job->end; i++) {
80
+ ple_fragment_t* fragment = &reader->plan.fragments[i];
81
+ memcpy((fragment->scale ? reader->scales : reader->weights) + fragment->destination,
82
+ buffer + (fragment->offset - job->offset), fragment->length);
83
+ }
84
+ return true;
85
+ }
86
+
87
+ static void ple_release(ple_reader_t* reader) {
88
+ if (!reader) return;
89
+ if (reader->keepalive_started) {
90
+ __atomic_store_n(&reader->keepalive_stop, 1, __ATOMIC_RELEASE);
91
+ pthread_join(reader->keepalive_thread, NULL);
92
+ }
93
+ free(reader->keepalive_buffer);
94
+ if (reader->ring_ready) io_uring_queue_exit(&reader->ring);
95
+ free(reader->iovecs);
96
+ free(reader->free_slots);
97
+ free(reader->registered_fds);
98
+ for (size_t i = 0; i < reader->plan.file_count; i++) close(reader->plan.files[i].fd);
99
+ free(reader->plan.files);
100
+ free(reader->plan.sources);
101
+ free(reader->buffers);
102
+ free(reader->slots);
103
+ free(reader->plan.fragments);
104
+ free(reader->plan.jobs);
105
+ pthread_mutex_destroy(&reader->api_mutex);
106
+ free(reader);
107
+ }
108
+
109
+ static void ple_delete(PyObject* capsule) {
110
+ ple_reader_t* reader = PyCapsule_GetPointer(capsule, PLE_CAPSULE);
111
+ if (reader) {
112
+ Py_BEGIN_ALLOW_THREADS ple_release(reader);
113
+ Py_END_ALLOW_THREADS
114
+ }
115
+ }
116
+ #endif
117
+
118
+ static PyObject* py_ple_reader(PyObject* self, PyObject* args) {
119
+ (void)self;
120
+ long long shard_rows, padded_rows, tp_start, tp_end, weight_bytes, scale_bytes, max_lookups;
121
+ int depth;
122
+ if (!PyArg_ParseTuple(args, "LLLLLLLi", &shard_rows, &padded_rows, &tp_start, &tp_end,
123
+ &weight_bytes, &scale_bytes, &max_lookups, &depth))
124
+ return NULL;
125
+ #ifndef B12X_HAVE_LIBURING
126
+ return PyErr_Format(PyExc_RuntimeError,
127
+ "io_uring PLE support is unavailable: install liburing development headers "
128
+ "and pkg-config, then rebuild the b12x native loader");
129
+ #else
130
+ if (shard_rows <= 0 || padded_rows <= 0 || tp_start < 0 || tp_end < tp_start ||
131
+ tp_end > padded_rows || weight_bytes <= 0 || scale_bytes < 0 || max_lookups <= 0 ||
132
+ depth <= 0 || depth > 32768 || weight_bytes > PY_SSIZE_T_MAX / max_lookups ||
133
+ scale_bytes > PY_SSIZE_T_MAX / max_lookups || max_lookups > PY_SSIZE_T_MAX / 8)
134
+ return PyErr_Format(PyExc_ValueError, "invalid PLE reader geometry or I/O capacity");
135
+ /* At most ceil((row_bytes + block - 1) / block) fragments per plane. */
136
+ size_t per_row = (size_t)weight_bytes / PLE_BLOCK + 2;
137
+ if (scale_bytes) per_row += (size_t)scale_bytes / PLE_BLOCK + 2;
138
+ if ((size_t)max_lookups > SIZE_MAX / per_row ||
139
+ (size_t)max_lookups * per_row > SIZE_MAX / sizeof(ple_fragment_t) ||
140
+ (size_t)max_lookups * per_row > SIZE_MAX / sizeof(ple_job_t))
141
+ return PyErr_Format(PyExc_OverflowError, "PLE batch metadata is too large");
142
+ ple_reader_t* reader = calloc(1, sizeof(*reader));
143
+ if (!reader) return PyErr_NoMemory();
144
+ /* Only an initialized API mutex reaches ple_release. */
145
+ int error = pthread_mutex_init(&reader->api_mutex, NULL);
146
+ if (error) {
147
+ free(reader);
148
+ return PyErr_Format(PyExc_RuntimeError, "PLE mutex: %s", strerror(error));
149
+ }
150
+ reader->plan.shard_rows = shard_rows;
151
+ reader->plan.padded_rows = padded_rows;
152
+ reader->plan.tp_start = tp_start;
153
+ reader->plan.tp_end = tp_end;
154
+ reader->plan.weight_bytes = weight_bytes;
155
+ reader->plan.scale_bytes = scale_bytes;
156
+ reader->plan.max_lookups = max_lookups;
157
+ reader->plan.capacity = (size_t)max_lookups * per_row;
158
+ reader->slots_count = (unsigned)depth;
159
+ reader->slots = calloc(reader->slots_count, sizeof(*reader->slots));
160
+ reader->plan.fragments = calloc(reader->plan.capacity, sizeof(*reader->plan.fragments));
161
+ reader->plan.jobs = calloc(reader->plan.capacity, sizeof(*reader->plan.jobs));
162
+ error = posix_memalign((void**)&reader->buffers, PLE_BLOCK,
163
+ (size_t)reader->slots_count * PLE_READ_MAX);
164
+ if (!reader->slots || !reader->plan.fragments || !reader->plan.jobs || error) {
165
+ ple_release(reader);
166
+ return PyErr_NoMemory();
167
+ }
168
+ for (unsigned i = 0; i < reader->slots_count; i++) {
169
+ reader->slots[i].buffer = reader->buffers + (size_t)i * PLE_READ_MAX;
170
+ }
171
+ error = io_uring_queue_init(reader->slots_count, &reader->ring, 0);
172
+ if (error < 0) {
173
+ ple_release(reader);
174
+ return PyErr_Format(
175
+ PyExc_RuntimeError,
176
+ "io_uring initialization failed: %s; enable io_uring in the kernel/container policy",
177
+ strerror(-error));
178
+ }
179
+ reader->ring_ready = true;
180
+ reader->iovecs = calloc(reader->slots_count, sizeof(*reader->iovecs));
181
+ reader->free_slots = calloc(reader->slots_count, sizeof(*reader->free_slots));
182
+ if (!reader->iovecs || !reader->free_slots) {
183
+ ple_release(reader);
184
+ return PyErr_NoMemory();
185
+ }
186
+ for (unsigned i = 0; i < reader->slots_count; i++) {
187
+ reader->iovecs[i].iov_base = reader->slots[i].buffer;
188
+ reader->iovecs[i].iov_len = PLE_READ_MAX;
189
+ }
190
+ error = io_uring_register_buffers(&reader->ring, reader->iovecs, reader->slots_count);
191
+ if (error < 0) {
192
+ ple_release(reader);
193
+ return PyErr_Format(PyExc_RuntimeError,
194
+ "io_uring buffer registration failed: %s; raise the locked-memory limit or "
195
+ "reduce queue_depth",
196
+ strerror(-error));
197
+ }
198
+ PyObject* capsule = PyCapsule_New(reader, PLE_CAPSULE, ple_delete);
199
+ if (!capsule) ple_release(reader);
200
+ return capsule;
201
+ #endif
202
+ }
203
+
204
+ static int ple_register_files(ple_plan_t* plan, void* owner) {
205
+ (void)plan;
206
+ ple_reader_t* reader = owner;
207
+ #ifdef B12X_HAVE_LIBURING
208
+ int* fds = realloc(reader->registered_fds, reader->plan.file_count * sizeof(*fds));
209
+ if (!fds) return -ENOMEM;
210
+ reader->registered_fds = fds;
211
+ for (size_t i = 0; i < reader->plan.file_count; i++) fds[i] = reader->plan.files[i].fd;
212
+ if (reader->files_registered) {
213
+ int error = io_uring_unregister_files(&reader->ring);
214
+ if (error < 0) {
215
+ reader->poisoned = true;
216
+ return error;
217
+ }
218
+ reader->files_registered = false;
219
+ }
220
+ int error = io_uring_register_files(&reader->ring, fds, (unsigned)reader->plan.file_count);
221
+ if (!error)
222
+ reader->files_registered = true;
223
+ else
224
+ reader->poisoned = true;
225
+ return error;
226
+ #else
227
+ (void)reader;
228
+ return 0;
229
+ #endif
230
+ }
231
+
232
+ static PyObject* py_ple_reader_add(PyObject* self, PyObject* args) {
233
+ (void)self;
234
+ PyObject* capsule;
235
+ const char* path;
236
+ long long shard, offset;
237
+ int scale;
238
+ if (!PyArg_ParseTuple(args, "OLsLp", &capsule, &shard, &path, &offset, &scale)) return NULL;
239
+ ple_reader_t* reader = PyCapsule_GetPointer(capsule, PLE_CAPSULE);
240
+ if (!reader) return NULL;
241
+ failure_t failure = {{0}};
242
+ int error;
243
+ Py_BEGIN_ALLOW_THREADS pthread_mutex_lock(&reader->api_mutex);
244
+ reader->plan.failure.message[0] = 0;
245
+ error =
246
+ ple_add_source(&reader->plan, shard, path, offset, scale != 0, ple_register_files, reader);
247
+ failure = reader->plan.failure;
248
+ pthread_mutex_unlock(&reader->api_mutex);
249
+ Py_END_ALLOW_THREADS if (error == EINVAL) return PyErr_Format(
250
+ PyExc_ValueError, "invalid PLE source shard, offset, or scale plane");
251
+ if (error == EOVERFLOW)
252
+ return PyErr_Format(PyExc_OverflowError, "PLE source byte range overflows int64");
253
+ if (error)
254
+ return PyErr_Format(
255
+ PyExc_RuntimeError, "%s",
256
+ failure.message[0] ? failure.message : "invalid PLE source geometry or byte range");
257
+ Py_RETURN_NONE;
258
+ }
259
+
260
+ #ifdef B12X_HAVE_LIBURING
261
+ /* Prepare a whole free-slot wave before entering the kernel. A short positive
262
+ * submit accounts for exactly that many SQEs; retry only the remaining entries.
263
+ * A failed submit may leave pending SQEs, so stop issuing, drain every submitted
264
+ * CQE, and retire the ring without submitting those pending entries. */
265
+ static void ple_submit_wave(ple_reader_t* reader, unsigned free_count, size_t* next,
266
+ unsigned* outstanding) {
267
+ size_t remaining = reader->plan.job_count - *next;
268
+ unsigned count = remaining < free_count ? (unsigned)remaining : free_count;
269
+ for (unsigned i = 0; i < count; i++) {
270
+ unsigned slot = reader->free_slots[i];
271
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&reader->ring);
272
+ if (!sqe) {
273
+ ple_error(reader, "io_uring SQ capacity", ENOSPC);
274
+ reader->poisoned = true;
275
+ return;
276
+ }
277
+ ple_job_t* job = &reader->plan.jobs[*next];
278
+ reader->slots[slot].job = (*next)++;
279
+ io_uring_prep_read_fixed(sqe, (int)job->file, reader->slots[slot].buffer, job->length,
280
+ job->offset, (int)slot);
281
+ sqe->flags |= IOSQE_FIXED_FILE;
282
+ io_uring_sqe_set_data64(sqe, slot);
283
+ }
284
+ unsigned pending = count;
285
+ while (pending) {
286
+ reader->plan.submit_calls++;
287
+ int result = io_uring_submit(&reader->ring);
288
+ if (result == -EINTR) continue;
289
+ if (result <= 0) {
290
+ ple_error(reader, "io_uring submit", result < 0 ? -result : EIO);
291
+ reader->poisoned = true;
292
+ return;
293
+ }
294
+ *outstanding += (unsigned)result;
295
+ pending -= (unsigned)result;
296
+ }
297
+ }
298
+
299
+ static void ple_uring_run(ple_reader_t* reader) {
300
+ unsigned outstanding = 0;
301
+ size_t next = 0;
302
+ for (unsigned i = 0; i < reader->slots_count; i++) reader->free_slots[i] = i;
303
+ ple_submit_wave(reader, reader->slots_count, &next, &outstanding);
304
+ while (outstanding) {
305
+ struct io_uring_cqe* cqe = NULL;
306
+ /* wait/peek do not submit pending SQEs when draining a failed submit. */
307
+ int result = io_uring_wait_cqe(&reader->ring, &cqe);
308
+ if (result < 0) {
309
+ if (result != -EINTR && result != -EAGAIN) {
310
+ ple_error(reader, "io_uring completion wait", -result);
311
+ reader->poisoned = true;
312
+ }
313
+ continue;
314
+ }
315
+ unsigned free_count = 0, head;
316
+ io_uring_for_each_cqe(&reader->ring, head, cqe) {
317
+ unsigned slot = (unsigned)io_uring_cqe_get_data64(cqe);
318
+ int status = cqe->res;
319
+ outstanding--;
320
+ ple_scatter(reader, reader->slots[slot].job, reader->slots[slot].buffer, status);
321
+ reader->free_slots[free_count++] = slot;
322
+ if (!outstanding) break;
323
+ }
324
+ io_uring_cq_advance(&reader->ring, free_count);
325
+ /* Do not prepare refill SQEs until every collected completion has been
326
+ * checked. An I/O error therefore leaves no speculative refill entries. */
327
+ if (!reader->plan.failure.message[0] && next < reader->plan.job_count)
328
+ ple_submit_wave(reader, free_count, &next, &outstanding);
329
+ }
330
+ if (reader->poisoned) {
331
+ io_uring_queue_exit(&reader->ring);
332
+ reader->ring_ready = false;
333
+ }
334
+ }
335
+ #endif
336
+
337
+ static bool ple_overlap(const Py_buffer* a, size_t a_bytes, const Py_buffer* b, size_t b_bytes) {
338
+ uintptr_t x = (uintptr_t)a->buf, y = (uintptr_t)b->buf;
339
+ return a_bytes && b_bytes && (x <= y ? y - x < a_bytes : x - y < b_bytes);
340
+ }
341
+
342
+ /* Borrow and validate the caller's ID/destination buffers for count rows. */
343
+ static int ple_buffers(ple_reader_t* reader, PyObject* ids_object, PyObject* weights_object,
344
+ PyObject* scales_object, long long count, Py_buffer* ids,
345
+ Py_buffer* weights, Py_buffer* scales) {
346
+ if (count < 0 || (uint64_t)count > reader->plan.max_lookups) {
347
+ PyErr_SetString(PyExc_ValueError, "PLE lookup count exceeds batch capacity");
348
+ return -1;
349
+ }
350
+ if (PyObject_GetBuffer(ids_object, ids, PyBUF_CONTIG_RO) < 0) return -1;
351
+ if (PyObject_GetBuffer(weights_object, weights, PyBUF_CONTIG) < 0) goto failed;
352
+ if (reader->plan.scale_bytes && PyObject_GetBuffer(scales_object, scales, PyBUF_CONTIG) < 0)
353
+ goto failed;
354
+ if (!reader->plan.scale_bytes && scales_object != Py_None) {
355
+ PyErr_SetString(PyExc_ValueError, "PLE reader has no scale plane; pass None");
356
+ goto failed;
357
+ }
358
+ size_t id_bytes = (size_t)count * 8;
359
+ size_t weight_bytes = (size_t)count * reader->plan.weight_bytes;
360
+ size_t scale_bytes = (size_t)count * reader->plan.scale_bytes;
361
+ if ((size_t)ids->len < id_bytes || (size_t)weights->len < weight_bytes ||
362
+ (size_t)scales->len < scale_bytes || !PyBuffer_IsContiguous(ids, 'C') ||
363
+ !PyBuffer_IsContiguous(weights, 'C') ||
364
+ (reader->plan.scale_bytes && !PyBuffer_IsContiguous(scales, 'C'))) {
365
+ PyErr_SetString(PyExc_ValueError,
366
+ "PLE buffers must be C-contiguous and cover count rows (IDs are native signed "
367
+ "int64 bytes)");
368
+ goto failed;
369
+ }
370
+ if (ple_overlap(ids, id_bytes, weights, weight_bytes) ||
371
+ ple_overlap(ids, id_bytes, scales, scale_bytes) ||
372
+ ple_overlap(weights, weight_bytes, scales, scale_bytes)) {
373
+ PyErr_SetString(PyExc_ValueError, "PLE IDs and destination byte ranges must not overlap");
374
+ goto failed;
375
+ }
376
+ return 0;
377
+ failed:
378
+ if (ids->obj) PyBuffer_Release(ids);
379
+ if (weights->obj) PyBuffer_Release(weights);
380
+ if (scales->obj) PyBuffer_Release(scales);
381
+ return -1;
382
+ }
383
+
384
+ static void ple_release_buffers(Py_buffer* ids, Py_buffer* weights, Py_buffer* scales) {
385
+ PyBuffer_Release(ids);
386
+ PyBuffer_Release(weights);
387
+ if (scales->obj) PyBuffer_Release(scales);
388
+ }
389
+
390
+ /* Plan and read count rows; no Python API, so it also runs as a CUDA host function. */
391
+ static failure_t ple_execute(ple_reader_t* reader, const char* ids, char* weights, char* scales,
392
+ size_t count) {
393
+ failure_t failure = {{0}};
394
+ pthread_mutex_lock(&reader->api_mutex);
395
+ struct timespec start, planned_at, end;
396
+ clock_gettime(CLOCK_MONOTONIC, &start);
397
+ __atomic_store_n(&reader->last_read_ns, (long long)start.tv_sec * 1000000000LL + start.tv_nsec,
398
+ __ATOMIC_RELEASE);
399
+ reader->plan.failure.message[0] = 0;
400
+ reader->plan.lookups = count;
401
+ reader->plan.requested_bytes = reader->plan.read_bytes = reader->plan.read_calls = 0;
402
+ reader->plan.unique_blocks = reader->plan.coalesced_reads = reader->plan.submit_calls = 0;
403
+ reader->plan.planning_seconds = 0;
404
+ if (reader->poisoned)
405
+ snprintf(reader->plan.failure.message, sizeof(reader->plan.failure.message),
406
+ "PLE io_uring reader is unusable after a submission/completion failure; create a new "
407
+ "reader");
408
+ else {
409
+ bool planned = ple_plan(&reader->plan, ids, weights, scales, count);
410
+ clock_gettime(CLOCK_MONOTONIC, &planned_at);
411
+ reader->plan.planning_seconds = (double)(planned_at.tv_sec - start.tv_sec) +
412
+ (double)(planned_at.tv_nsec - start.tv_nsec) * 1e-9;
413
+ #ifdef B12X_HAVE_LIBURING
414
+ reader->weights = weights;
415
+ reader->scales = scales;
416
+ if (planned && reader->plan.job_count) ple_uring_run(reader);
417
+ #else
418
+ (void)planned;
419
+ #endif
420
+ }
421
+ clock_gettime(CLOCK_MONOTONIC, &end);
422
+ reader->plan.execution_seconds =
423
+ (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) * 1e-9;
424
+ failure = reader->plan.failure;
425
+ pthread_mutex_unlock(&reader->api_mutex);
426
+ return failure;
427
+ }
428
+
429
+ static PyObject* py_ple_reader_run(PyObject* self, PyObject* args) {
430
+ (void)self;
431
+ PyObject *capsule, *ids_object, *weights_object, *scales_object;
432
+ long long count;
433
+ if (!PyArg_ParseTuple(args, "OOOOL", &capsule, &ids_object, &weights_object, &scales_object,
434
+ &count))
435
+ return NULL;
436
+ ple_reader_t* reader = PyCapsule_GetPointer(capsule, PLE_CAPSULE);
437
+ if (!reader) return NULL;
438
+ Py_buffer ids = {0}, weights = {0}, scales = {0};
439
+ if (ple_buffers(reader, ids_object, weights_object, scales_object, count, &ids, &weights,
440
+ &scales) < 0)
441
+ return NULL;
442
+ failure_t failure;
443
+ Py_BEGIN_ALLOW_THREADS failure =
444
+ ple_execute(reader, ids.buf, weights.buf, scales.buf, (size_t)count);
445
+ Py_END_ALLOW_THREADS ple_release_buffers(&ids, &weights, &scales);
446
+ if (failure.message[0]) return PyErr_Format(PyExc_RuntimeError, "%s", failure.message);
447
+ Py_RETURN_NONE;
448
+ }
449
+
450
+ /* CUDA host function: the stream reaches it after the ID copy and before the
451
+ * decode kernel, so the CPU thread that enqueued it never waits on the read. */
452
+ static void CUDART_CB ple_host_read(void* data) {
453
+ ple_async_t* job = data;
454
+ ple_reader_t* reader = job->reader;
455
+ failure_t failure = ple_execute(reader, job->ids, job->weights, job->scales, job->count);
456
+ if (failure.message[0] && !__atomic_load_n(&reader->async_failed, __ATOMIC_ACQUIRE)) {
457
+ reader->async_failure = failure;
458
+ __atomic_store_n(&reader->async_failed, 1, __ATOMIC_RELEASE);
459
+ }
460
+ /* Rows were copied into write-combined mapped memory; complete those stores
461
+ * before the driver releases the stream to the decode kernel. */
462
+ #if defined(__aarch64__)
463
+ __asm__ __volatile__("dsb sy" ::: "memory");
464
+ #else
465
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
466
+ #endif
467
+ __atomic_store_n(&job->pending, 0, __ATOMIC_RELEASE);
468
+ }
469
+
470
+ #ifdef B12X_HAVE_LIBURING
471
+ /* NVMe autonomous power-state transitions park the drive after tens of ms idle,
472
+ * and each decode step's burst of row reads (~60 ms apart) then pays the wake-up:
473
+ * fio on GB10 measured p99 2.2 ms per burst after 60 ms idle vs 0.44 ms with 1 ms
474
+ * gaps, and the GPU waits for the slowest read. While reads are recent (2 s), this
475
+ * thread keeps the drive busy with one aligned 4 KiB read every keepalive_us. */
476
+ #define PLE_KEEPALIVE_WINDOW_NS 2000000000LL
477
+
478
+ static long long ple_now_ns(void) {
479
+ struct timespec now;
480
+ clock_gettime(CLOCK_MONOTONIC, &now);
481
+ return (long long)now.tv_sec * 1000000000LL + now.tv_nsec;
482
+ }
483
+
484
+ static void* ple_keepalive_main(void* data) {
485
+ ple_reader_t* reader = data;
486
+ unsigned seed = 0x9e3779b9u;
487
+ while (!__atomic_load_n(&reader->keepalive_stop, __ATOMIC_ACQUIRE)) {
488
+ long long idle = ple_now_ns() - __atomic_load_n(&reader->last_read_ns, __ATOMIC_ACQUIRE);
489
+ long sleep_us = 20000;
490
+ if (idle < PLE_KEEPALIVE_WINDOW_NS && reader->plan.file_count) {
491
+ seed = seed * 1664525u + 1013904223u;
492
+ ple_file_t* file = &reader->plan.files[(seed >> 8) % reader->plan.file_count];
493
+ int64_t blocks = file->bytes / PLE_BLOCK;
494
+ if (blocks > 0) {
495
+ seed = seed * 1664525u + 1013904223u;
496
+ off_t offset = (off_t)((((uint64_t)seed << 16) ^ (uint64_t)idle) % (uint64_t)blocks) * PLE_BLOCK;
497
+ (void)!pread(file->fd, reader->keepalive_buffer, PLE_BLOCK, offset);
498
+ }
499
+ sleep_us = reader->keepalive_us;
500
+ }
501
+ struct timespec pause = {sleep_us / 1000000, (sleep_us % 1000000) * 1000};
502
+ nanosleep(&pause, NULL);
503
+ }
504
+ return NULL;
505
+ }
506
+
507
+ /* Start the keep-alive once rows are being read (sources are frozen by then). */
508
+ static void ple_keepalive_maybe_start(ple_reader_t* reader) {
509
+ if (reader->keepalive_started || reader->keepalive_us < 0) return;
510
+ reader->keepalive_us = -1; /* one attempt; any failure leaves it off */
511
+ const char* setting = getenv("B12X_PLE_KEEPALIVE_US");
512
+ long interval = setting ? strtol(setting, NULL, 10) : 0;
513
+ if (interval <= 0) return;
514
+ if (posix_memalign((void**)&reader->keepalive_buffer, PLE_BLOCK, PLE_BLOCK) != 0) {
515
+ reader->keepalive_buffer = NULL;
516
+ return;
517
+ }
518
+ reader->keepalive_us = interval;
519
+ if (pthread_create(&reader->keepalive_thread, NULL, ple_keepalive_main, reader) != 0) {
520
+ reader->keepalive_us = -1;
521
+ return;
522
+ }
523
+ reader->keepalive_started = true;
524
+ }
525
+ #endif
526
+
527
+ /* Enqueue the read on a CUDA stream. Buffers must stay valid until the stream
528
+ * passes this point; a failed read is raised by the next call. */
529
+ static PyObject* py_ple_reader_run_async(PyObject* self, PyObject* args) {
530
+ (void)self;
531
+ PyObject *capsule, *ids_object, *weights_object, *scales_object;
532
+ long long count;
533
+ unsigned long long stream;
534
+ if (!PyArg_ParseTuple(args, "OOOOLK", &capsule, &ids_object, &weights_object, &scales_object,
535
+ &count, &stream))
536
+ return NULL;
537
+ ple_reader_t* reader = PyCapsule_GetPointer(capsule, PLE_CAPSULE);
538
+ if (!reader) return NULL;
539
+ #ifdef B12X_HAVE_LIBURING
540
+ ple_keepalive_maybe_start(reader);
541
+ #endif
542
+ if (__atomic_load_n(&reader->async_failed, __ATOMIC_ACQUIRE)) {
543
+ failure_t failure = reader->async_failure;
544
+ __atomic_store_n(&reader->async_failed, 0, __ATOMIC_RELEASE);
545
+ return PyErr_Format(PyExc_RuntimeError, "asynchronous PLE read failed: %s",
546
+ failure.message);
547
+ }
548
+ ple_async_t* job = &reader->async_jobs[reader->async_next % PLE_ASYNC_RING];
549
+ if (__atomic_load_n(&job->pending, __ATOMIC_ACQUIRE))
550
+ return PyErr_Format(PyExc_RuntimeError, "more than %d asynchronous PLE reads in flight",
551
+ PLE_ASYNC_RING);
552
+ Py_buffer ids = {0}, weights = {0}, scales = {0};
553
+ if (ple_buffers(reader, ids_object, weights_object, scales_object, count, &ids, &weights,
554
+ &scales) < 0)
555
+ return NULL;
556
+ reader->async_next++;
557
+ job->reader = reader;
558
+ job->ids = ids.buf;
559
+ job->weights = weights.buf;
560
+ job->scales = scales.buf;
561
+ job->count = (size_t)count;
562
+ __atomic_store_n(&job->pending, 1, __ATOMIC_RELEASE);
563
+ ple_release_buffers(&ids, &weights, &scales);
564
+ cudaError_t error =
565
+ cudaLaunchHostFunc((cudaStream_t)(uintptr_t)stream, ple_host_read, job);
566
+ if (error != cudaSuccess) {
567
+ __atomic_store_n(&job->pending, 0, __ATOMIC_RELEASE);
568
+ return PyErr_Format(PyExc_RuntimeError, "cudaLaunchHostFunc failed: %s",
569
+ cudaGetErrorString(error));
570
+ }
571
+ Py_RETURN_NONE;
572
+ }
573
+
574
+ static PyObject* py_ple_reader_stats(PyObject* self, PyObject* capsule) {
575
+ (void)self;
576
+ ple_reader_t* reader = PyCapsule_GetPointer(capsule, PLE_CAPSULE);
577
+ if (!reader) return NULL;
578
+ uint64_t lookups, requested, bytes, calls, blocks, coalesced, submits, staging, metadata;
579
+ double seconds, planning;
580
+ Py_BEGIN_ALLOW_THREADS pthread_mutex_lock(&reader->api_mutex);
581
+ lookups = reader->plan.lookups;
582
+ requested = reader->plan.requested_bytes;
583
+ bytes = reader->plan.read_bytes;
584
+ calls = reader->plan.read_calls;
585
+ blocks = reader->plan.unique_blocks;
586
+ coalesced = reader->plan.coalesced_reads;
587
+ submits = reader->plan.submit_calls;
588
+ seconds = reader->plan.execution_seconds;
589
+ planning = reader->plan.planning_seconds;
590
+ staging = (uint64_t)reader->slots_count * PLE_READ_MAX;
591
+ metadata =
592
+ sizeof(*reader) +
593
+ reader->plan.capacity * (sizeof(*reader->plan.fragments) + sizeof(*reader->plan.jobs)) +
594
+ reader->slots_count * sizeof(*reader->slots) +
595
+ reader->plan.source_count * sizeof(*reader->plan.sources) +
596
+ reader->plan.file_count * sizeof(*reader->plan.files);
597
+ #ifdef B12X_HAVE_LIBURING
598
+ metadata += reader->slots_count * (sizeof(*reader->iovecs) + sizeof(*reader->free_slots));
599
+ metadata += reader->plan.file_count * sizeof(*reader->registered_fds);
600
+ if (reader->ring_ready) {
601
+ metadata += reader->ring.sq.ring_sz;
602
+ if (reader->ring.cq.ring_ptr != reader->ring.sq.ring_ptr) metadata += reader->ring.cq.ring_sz;
603
+ metadata += reader->ring.sq.ring_entries * sizeof(struct io_uring_sqe);
604
+ }
605
+ #endif
606
+ pthread_mutex_unlock(&reader->api_mutex);
607
+ Py_END_ALLOW_THREADS return Py_BuildValue(
608
+ "{s:K,s:K,s:K,s:K,s:K,s:K,s:K,s:K,s:K,s:d,s:d}", "lookups", (unsigned long long)lookups,
609
+ "requested_bytes", (unsigned long long)requested, "read_bytes", (unsigned long long)bytes,
610
+ "read_calls", (unsigned long long)calls, "unique_blocks", (unsigned long long)blocks,
611
+ "coalesced_reads", (unsigned long long)coalesced, "submit_calls", (unsigned long long)submits,
612
+ "staging_bytes", (unsigned long long)staging, "metadata_bytes", (unsigned long long)metadata,
613
+ "execution_seconds", seconds, "planning_seconds", planning);
614
+ }
recipe/patches/b12x/loader/_row_plan.h ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Shared immutable row-source validation and allocation-free block planning. */
2
+ #ifndef B12X_ROW_PLAN_H
3
+ #define B12X_ROW_PLAN_H
4
+ #include <fcntl.h>
5
+ #include <stdlib.h>
6
+ #include <time.h>
7
+ #define PLE_BLOCK 4096u
8
+ #define PLE_READ_MAX 65536u
9
+
10
+ typedef struct {
11
+ int fd;
12
+ dev_t device;
13
+ ino_t inode;
14
+ int64_t bytes;
15
+ } ple_file_t;
16
+
17
+ typedef struct {
18
+ int64_t shard;
19
+ int64_t offset;
20
+ size_t file;
21
+ bool scale;
22
+ } ple_source_t;
23
+
24
+ typedef struct {
25
+ size_t file;
26
+ int64_t offset;
27
+ unsigned length;
28
+ size_t destination;
29
+ bool scale;
30
+ } ple_fragment_t;
31
+
32
+ typedef struct {
33
+ size_t file, begin, end;
34
+ int64_t offset;
35
+ unsigned length, expected;
36
+ } ple_job_t;
37
+
38
+ typedef struct {
39
+ int64_t shard_rows, padded_rows, tp_start, tp_end;
40
+ size_t weight_bytes, scale_bytes, max_lookups, capacity;
41
+ ple_fragment_t* fragments;
42
+ ple_job_t* jobs;
43
+ size_t fragment_count, job_count;
44
+ ple_file_t* files;
45
+ ple_source_t* sources;
46
+ size_t file_count, source_count;
47
+ failure_t failure;
48
+ uint64_t lookups, requested_bytes, read_bytes, read_calls;
49
+ uint64_t unique_blocks, coalesced_reads, submit_calls;
50
+ double execution_seconds, planning_seconds;
51
+ } ple_plan_t;
52
+
53
+ static inline int ple_source_compare(const void* left, const void* right) {
54
+ const ple_source_t *a = left, *b = right;
55
+ if (a->shard != b->shard) return a->shard < b->shard ? -1 : 1;
56
+ return (int)a->scale - (int)b->scale;
57
+ }
58
+
59
+ static inline void ple_plan_release(ple_plan_t* reader) {
60
+ for (size_t i = 0; i < reader->file_count; i++) close(reader->files[i].fd);
61
+ free(reader->files);
62
+ free(reader->sources);
63
+ free(reader->fragments);
64
+ free(reader->jobs);
65
+ }
66
+
67
+ static inline int ple_plan_init(ple_plan_t* reader, int64_t shard_rows, int64_t padded_rows,
68
+ int64_t tp_start, int64_t tp_end, int64_t weight_bytes,
69
+ int64_t scale_bytes, int64_t max_lookups) {
70
+ if (shard_rows <= 0 || padded_rows <= 0 || tp_start < 0 || tp_end < tp_start ||
71
+ tp_end > padded_rows || weight_bytes <= 0 || scale_bytes < 0 || max_lookups <= 0 ||
72
+ weight_bytes > PY_SSIZE_T_MAX / max_lookups || scale_bytes > PY_SSIZE_T_MAX / max_lookups ||
73
+ max_lookups > PY_SSIZE_T_MAX / 8)
74
+ return EINVAL;
75
+ size_t per_row = (size_t)weight_bytes / PLE_BLOCK + 2;
76
+ if (scale_bytes) per_row += (size_t)scale_bytes / PLE_BLOCK + 2;
77
+ if ((size_t)max_lookups > SIZE_MAX / per_row ||
78
+ (size_t)max_lookups * per_row > SIZE_MAX / sizeof(ple_fragment_t) ||
79
+ (size_t)max_lookups * per_row > SIZE_MAX / sizeof(ple_job_t))
80
+ return EOVERFLOW;
81
+ reader->shard_rows = shard_rows;
82
+ reader->padded_rows = padded_rows;
83
+ reader->tp_start = tp_start;
84
+ reader->tp_end = tp_end;
85
+ reader->weight_bytes = weight_bytes;
86
+ reader->scale_bytes = scale_bytes;
87
+ reader->max_lookups = max_lookups;
88
+ reader->capacity = (size_t)max_lookups * per_row;
89
+ reader->fragments = calloc(reader->capacity, sizeof(*reader->fragments));
90
+ reader->jobs = calloc(reader->capacity, sizeof(*reader->jobs));
91
+ return reader->fragments && reader->jobs ? 0 : ENOMEM;
92
+ }
93
+
94
+ static inline int ple_add_source(ple_plan_t* reader, int64_t shard, const char* path,
95
+ int64_t offset, bool scale,
96
+ int (*register_files)(ple_plan_t*, void*), void* owner) {
97
+ if (shard < 0 || shard > (reader->padded_rows - 1) / reader->shard_rows || offset < 0 ||
98
+ (scale && !reader->scale_bytes))
99
+ return EINVAL;
100
+ int64_t first = shard * reader->shard_rows;
101
+ int64_t rows = reader->padded_rows - first;
102
+ if (rows > reader->shard_rows) rows = reader->shard_rows;
103
+ size_t row_bytes = scale ? reader->scale_bytes : reader->weight_bytes;
104
+ if (rows > (INT64_MAX - offset) / (int64_t)row_bytes) return EOVERFLOW;
105
+ if (first >= reader->tp_end || first + rows <= reader->tp_start) return 0;
106
+ failure_t failure = {{0}};
107
+ ple_source_t key = {.shard = shard, .scale = scale};
108
+ if (reader->source_count &&
109
+ bsearch(&key, reader->sources, reader->source_count, sizeof(key), ple_source_compare)) {
110
+ snprintf(failure.message, sizeof(failure.message),
111
+ "PLE source shard/plane is already registered");
112
+ } else {
113
+ /* Reject FIFOs via fstat without blocking waiting for a writer. */
114
+ /* B12X_PLE_BUFFERED=1 reads through the page cache so hot n-gram rows
115
+ * stay resident in otherwise free RAM (evictable under memory pressure). */
116
+ const char* buffered = getenv("B12X_PLE_BUFFERED");
117
+ int direct = (buffered && buffered[0] == '1') ? 0 : O_DIRECT;
118
+ int fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK | direct);
119
+ struct stat status;
120
+ if (fd < 0)
121
+ system_error(&failure, "open PLE source");
122
+ else if (fstat(fd, &status) != 0)
123
+ system_error(&failure, "fstat PLE source");
124
+ else if (!S_ISREG(status.st_mode) || offset + rows * (int64_t)row_bytes > status.st_size)
125
+ snprintf(failure.message, sizeof(failure.message),
126
+ "PLE source range exceeds a regular file's size");
127
+ else {
128
+ size_t file = 0;
129
+ while (file < reader->file_count && (reader->files[file].device != status.st_dev ||
130
+ reader->files[file].inode != status.st_ino))
131
+ file++;
132
+ ple_source_t* sources =
133
+ realloc(reader->sources, (reader->source_count + 1) * sizeof(*sources));
134
+ if (!sources)
135
+ snprintf(failure.message, sizeof(failure.message),
136
+ "could not allocate PLE source descriptor");
137
+ else {
138
+ reader->sources = sources;
139
+ if (file == reader->file_count) {
140
+ ple_file_t* files = realloc(reader->files, (file + 1) * sizeof(*files));
141
+ if (!files)
142
+ snprintf(failure.message, sizeof(failure.message),
143
+ "could not allocate PLE file descriptor");
144
+ else {
145
+ reader->files = files;
146
+ reader->files[file] = (ple_file_t){fd, status.st_dev, status.st_ino, status.st_size};
147
+ reader->file_count++;
148
+ fd = -1;
149
+ int error = register_files ? register_files(reader, owner) : 0;
150
+ if (error < 0) {
151
+ snprintf(failure.message, sizeof(failure.message), "row file registration failed: %s",
152
+ strerror(-error));
153
+ }
154
+ }
155
+ } else if (reader->files[file].bytes != status.st_size) {
156
+ snprintf(failure.message, sizeof(failure.message),
157
+ "PLE source file size changed during registration");
158
+ }
159
+ if (!failure.message[0]) {
160
+ reader->sources[reader->source_count++] = (ple_source_t){shard, offset, file, scale != 0};
161
+ qsort(reader->sources, reader->source_count, sizeof(*sources), ple_source_compare);
162
+ }
163
+ }
164
+ }
165
+ if (fd >= 0) close(fd);
166
+ }
167
+ if (failure.message[0] && !reader->failure.message[0]) reader->failure = failure;
168
+ return failure.message[0] ? EIO : 0;
169
+ }
170
+
171
+ static inline int ple_fragment_compare(const void* left, const void* right) {
172
+ const ple_fragment_t *a = left, *b = right;
173
+ if (a->file != b->file) return a->file < b->file ? -1 : 1;
174
+ return a->offset < b->offset ? -1 : a->offset > b->offset;
175
+ }
176
+
177
+ /* libc qsort may allocate a merge buffer on every run. Heap-sort in place to
178
+ * keep all batch metadata persistent, including for duplicate-heavy batches. */
179
+ static inline void ple_sift(ple_fragment_t* items, size_t root, size_t count) {
180
+ ple_fragment_t value = items[root];
181
+ while (root < count / 2) {
182
+ size_t child = root * 2 + 1;
183
+ if (child + 1 < count && ple_fragment_compare(&items[child], &items[child + 1]) < 0) child++;
184
+ if (ple_fragment_compare(&value, &items[child]) >= 0) break;
185
+ items[root] = items[child];
186
+ root = child;
187
+ }
188
+ items[root] = value;
189
+ }
190
+
191
+ static inline void ple_sort(ple_plan_t* reader) {
192
+ ple_fragment_t* items = reader->fragments;
193
+ size_t count = reader->fragment_count;
194
+ if (count < 1024) {
195
+ for (size_t i = count / 2; i > 0; i--) ple_sift(items, i - 1, count);
196
+ for (size_t end = count; end > 1; end--) {
197
+ ple_fragment_t value = items[end - 1];
198
+ items[end - 1] = items[0];
199
+ items[0] = value;
200
+ ple_sift(items, 0, end - 1);
201
+ }
202
+ return;
203
+ }
204
+ /* Jobs are built only after sorting. Reuse that fixed-capacity allocation
205
+ * as radix scratch instead of allocating another prefill-sized buffer. */
206
+ _Static_assert(sizeof(ple_job_t) >= sizeof(ple_fragment_t),
207
+ "job allocation must cover fragment sort scratch");
208
+ ple_fragment_t* scratch = (ple_fragment_t*)reader->jobs;
209
+ ple_fragment_t *source = items, *target = scratch;
210
+ uint64_t offset_variation = 0, file_variation = 0;
211
+ for (size_t i = 1; i < count; i++) {
212
+ offset_variation |= (uint64_t)(items[i].offset ^ items[0].offset);
213
+ file_variation |= (uint64_t)(items[i].file ^ items[0].file);
214
+ }
215
+ /* Stable LSD passes sort offset first, then the primary file key. Skip
216
+ * constant bytes, including the file key for a single-container table. */
217
+ for (int file = 0; file < 2; file++) {
218
+ uint64_t variation = file ? file_variation : offset_variation;
219
+ for (unsigned shift = 0; shift < 64; shift += 8) {
220
+ if (((variation >> shift) & 255) == 0) continue;
221
+ size_t positions[256] = {0};
222
+ for (size_t i = 0; i < count; i++) {
223
+ uint64_t key = file ? (uint64_t)source[i].file : (uint64_t)source[i].offset;
224
+ positions[(key >> shift) & 255]++;
225
+ }
226
+ size_t prefix = 0;
227
+ for (unsigned i = 0; i < 256; i++) {
228
+ size_t size = positions[i];
229
+ positions[i] = prefix;
230
+ prefix += size;
231
+ }
232
+ for (size_t i = 0; i < count; i++) {
233
+ uint64_t key = file ? (uint64_t)source[i].file : (uint64_t)source[i].offset;
234
+ target[positions[(key >> shift) & 255]++] = source[i];
235
+ }
236
+ ple_fragment_t* swap = source;
237
+ source = target;
238
+ target = swap;
239
+ }
240
+ }
241
+ if (source != items) memcpy(items, source, count * sizeof(*items));
242
+ }
243
+
244
+ static inline bool ple_plan(ple_plan_t* reader, const char* ids, char* weights, char* scales,
245
+ size_t count) {
246
+ reader->fragment_count = reader->job_count = 0;
247
+ for (size_t i = 0; i < count; i++) {
248
+ int64_t id;
249
+ memcpy(&id, ids + i * sizeof(id), sizeof(id));
250
+ if (id < reader->tp_start || id >= reader->tp_end) {
251
+ if (weights) memset(weights + i * reader->weight_bytes, 0, reader->weight_bytes);
252
+ if (scales && reader->scale_bytes)
253
+ memset(scales + i * reader->scale_bytes, 0, reader->scale_bytes);
254
+ continue;
255
+ }
256
+ for (int plane = 0; plane < (reader->scale_bytes ? 2 : 1); plane++) {
257
+ ple_source_t key = {.shard = id / reader->shard_rows, .scale = plane != 0};
258
+ ple_source_t* source = reader->source_count
259
+ ? bsearch(&key, reader->sources, reader->source_count, sizeof(key),
260
+ ple_source_compare)
261
+ : NULL;
262
+ if (!source) {
263
+ snprintf(reader->failure.message, sizeof(reader->failure.message),
264
+ "missing PLE %s source for shard %lld", plane ? "scale" : "weight",
265
+ (long long)key.shard);
266
+ return false;
267
+ }
268
+ size_t bytes = plane ? reader->scale_bytes : reader->weight_bytes;
269
+ size_t destination = i * bytes;
270
+ int64_t offset = source->offset + (id % reader->shard_rows) * (int64_t)bytes;
271
+ reader->requested_bytes += bytes;
272
+ while (bytes) {
273
+ unsigned length = PLE_BLOCK - (uint64_t)offset % PLE_BLOCK;
274
+ if (bytes < length) length = (unsigned)bytes;
275
+ reader->fragments[reader->fragment_count++] =
276
+ (ple_fragment_t){source->file, offset, length, destination, plane != 0};
277
+ offset += length;
278
+ destination += length;
279
+ bytes -= length;
280
+ }
281
+ }
282
+ }
283
+ ple_sort(reader);
284
+ size_t cursor = 0;
285
+ while (cursor < reader->fragment_count) {
286
+ size_t begin = cursor;
287
+ ple_fragment_t* fragment = &reader->fragments[cursor];
288
+ size_t file = fragment->file;
289
+ int64_t first = fragment->offset & ~(int64_t)(PLE_BLOCK - 1);
290
+ int64_t last = first;
291
+ reader->unique_blocks++;
292
+ cursor++;
293
+ while (cursor < reader->fragment_count) {
294
+ fragment = &reader->fragments[cursor];
295
+ int64_t block = fragment->offset & ~(int64_t)(PLE_BLOCK - 1);
296
+ if (fragment->file != file ||
297
+ (block != last && (block - last != PLE_BLOCK || block - first >= PLE_READ_MAX)))
298
+ break;
299
+ if (block != last) {
300
+ reader->unique_blocks++;
301
+ last = block;
302
+ }
303
+ cursor++;
304
+ }
305
+ unsigned aligned = (unsigned)(last - first) + PLE_BLOCK;
306
+ int64_t available = reader->files[file].bytes - first;
307
+ unsigned expected = available < aligned ? (unsigned)available : aligned;
308
+ reader->jobs[reader->job_count++] = (ple_job_t){file, begin, cursor, first, aligned, expected};
309
+ if (last != first) reader->coalesced_reads++;
310
+ }
311
+ return true;
312
+ }
313
+
314
+ #endif
recipe/patches/b12x/loader/_storage.c ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #define _GNU_SOURCE
2
+ #define PY_SSIZE_T_CLEAN
3
+ #include <Python.h>
4
+ #include <cuda_runtime_api.h>
5
+ #include <errno.h>
6
+ #include <pthread.h>
7
+ #include <stdbool.h>
8
+ #include <stdint.h>
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+ #include <sys/stat.h>
13
+ #include <unistd.h>
14
+
15
+ typedef char require_64_bit_offsets[(sizeof(off_t) == 8 && sizeof(size_t) == 8) ? 1 : -1];
16
+
17
+ typedef struct {
18
+ char message[512];
19
+ } failure_t;
20
+
21
+ static bool cuda_ok(cudaError_t status, const char* operation, failure_t* failure) {
22
+ if (status == cudaSuccess) return true;
23
+ snprintf(failure->message, sizeof(failure->message), "%s: %s", operation,
24
+ cudaGetErrorString(status));
25
+ return false;
26
+ }
27
+
28
+ static void system_error(failure_t* failure, const char* operation) {
29
+ snprintf(failure->message, sizeof(failure->message), "%s: %s", operation, strerror(errno));
30
+ }
31
+
32
+ static PyObject* py_capabilities(PyObject* self, PyObject* args) {
33
+ (void)self;
34
+ int device;
35
+ if (!PyArg_ParseTuple(args, "i", &device)) return NULL;
36
+ static const struct {
37
+ const char* name;
38
+ enum cudaDeviceAttr attribute;
39
+ } keys[] = {
40
+ {"integrated", cudaDevAttrIntegrated},
41
+ {"can_map_host_memory", cudaDevAttrCanMapHostMemory},
42
+ {"managed_memory", cudaDevAttrManagedMemory},
43
+ {"concurrent_managed_access", cudaDevAttrConcurrentManagedAccess},
44
+ {"pageable_memory_access", cudaDevAttrPageableMemoryAccess},
45
+ {"host_page_tables", cudaDevAttrPageableMemoryAccessUsesHostPageTables},
46
+ {"host_register_supported", cudaDevAttrHostRegisterSupported},
47
+ {"registered_host_pointer", cudaDevAttrCanUseHostPointerForRegisteredMem},
48
+ };
49
+ PyObject* result = PyDict_New();
50
+ if (!result) return NULL;
51
+ for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
52
+ int value;
53
+ failure_t failure;
54
+ if (!cuda_ok(cudaDeviceGetAttribute(&value, keys[i].attribute, device),
55
+ "cudaDeviceGetAttribute", &failure)) {
56
+ Py_DECREF(result);
57
+ return PyErr_Format(PyExc_RuntimeError, "%s", failure.message);
58
+ }
59
+ PyObject* number = PyLong_FromLong(value);
60
+ if (!number || PyDict_SetItemString(result, keys[i].name, number) != 0) {
61
+ Py_XDECREF(number);
62
+ Py_DECREF(result);
63
+ return NULL;
64
+ }
65
+ Py_DECREF(number);
66
+ }
67
+ return result;
68
+ }
69
+
70
+ // The batch reader uses definitions from the direct reader.
71
+ // clang-format off
72
+ #include "_direct.c"
73
+ #include "_batch.c"
74
+ #include "_ple_reader.c"
75
+ // clang-format on
76
+
77
+ static PyMethodDef methods[] = {
78
+ {"ple_reader", py_ple_reader, METH_VARARGS, NULL},
79
+ {"ple_reader_add", py_ple_reader_add, METH_VARARGS, NULL},
80
+ {"ple_reader_run", py_ple_reader_run, METH_VARARGS, NULL},
81
+ {"ple_reader_run_async", py_ple_reader_run_async, METH_VARARGS, NULL},
82
+ {"ple_reader_stats", py_ple_reader_stats, METH_O,
83
+ "Last-call counters; staging_bytes and metadata_bytes describe persistent allocations."},
84
+ {"batch_executor", py_batch_executor, METH_VARARGS, NULL},
85
+ {"batch_execute", py_batch_execute, METH_VARARGS, NULL},
86
+ {"batch_stats", py_batch_stats, METH_O, NULL},
87
+ {"direct_reader", py_direct_reader, METH_VARARGS, NULL},
88
+ {"direct_bytes", py_direct_bytes, METH_VARARGS, NULL},
89
+ {"direct_stats", py_direct_stats, METH_O, NULL},
90
+ {"capabilities", py_capabilities, METH_VARARGS, NULL},
91
+ {NULL, NULL, 0, NULL},
92
+ };
93
+ static PyModuleDef module = {
94
+ PyModuleDef_HEAD_INIT,
95
+ .m_name = "_b12x_loader_storage",
96
+ .m_size = -1,
97
+ .m_methods = methods,
98
+ };
99
+
100
+ PyMODINIT_FUNC PyInit__b12x_loader_storage(void) {
101
+ PyObject* result = PyModule_Create(&module);
102
+ if (result && PyModule_AddIntConstant(result, "ABI_VERSION", 1) < 0) {
103
+ Py_DECREF(result);
104
+ return NULL;
105
+ }
106
+ return result;
107
+ }
recipe/patches/qwen4_exp/nvidia/b12x_ple.py ADDED
@@ -0,0 +1,1064 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+ """B12X-backed position-learning enhancement for Qwen4Exp."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from collections.abc import Iterable
8
+ from dataclasses import replace
9
+ import os
10
+ from typing import Any
11
+
12
+ import torch
13
+ from torch import nn
14
+
15
+ import vllm.envs as envs
16
+ from vllm.distributed import (
17
+ get_tensor_model_parallel_rank,
18
+ get_tensor_model_parallel_world_size,
19
+ tensor_model_parallel_all_reduce,
20
+ )
21
+ from vllm.forward_context import get_forward_context
22
+ from vllm.logger import init_logger
23
+ from vllm.model_executor.models.utils import AutoWeightsLoader
24
+ from vllm.model_executor.weight_transfer import (
25
+ copy_weight,
26
+ flush_weight_transfers,
27
+ get_file_tensor_source,
28
+ materialize_weight,
29
+ )
30
+ from vllm.platforms import current_platform
31
+ from vllm.utils.b12x import (
32
+ B12xPreparationUnit,
33
+ B12xWorkload,
34
+ get_b12x_ple,
35
+ get_b12x_ple_embedding,
36
+ set_b12x_preparation_provider,
37
+ )
38
+ from vllm.utils.torch_utils import direct_register_custom_op
39
+
40
+ from ..config import Qwen4ExpTextConfig
41
+ from .ple_shared_table import (
42
+ SharedTableConfig,
43
+ SharedTableGeometry,
44
+ checkpoint_identity,
45
+ open_shared_table,
46
+ )
47
+
48
+ logger = init_logger(__name__)
49
+
50
+ _PLE_SPLITTING_OPS = (
51
+ "vllm::qwen4_exp_b12x_ple_embedding",
52
+ "vllm::qwen4_exp_b12x_ple",
53
+ )
54
+
55
+
56
+
57
+ # Patch knobs: io_uring queue depth for the disk PLE reader, and periodic
58
+ # reader-stats logging every N prepare_disk calls (0 disables).
59
+ _PLE_STATS_EVERY = int(os.environ.get("VLLM_PLE_STATS", "0"))
60
+ # Overlap the disk PLE read with the first decoder layer: prepare_disk runs on a side
61
+ # stream and the forward waits (cuStreamWaitValue32, also inside CUDA graphs) only where
62
+ # the PLE layer consumes the embeddings. Flag protocol: the main stream clears the flag
63
+ # in prepare_disk, the side stream sets it after the lookup kernel; forwards only wait
64
+ # for 1 (never reset), so warm-up/capture forwards without a fresh prepare cannot hang.
65
+ _PLE_OVERLAP = os.environ.get("VLLM_QWEN38_PLE_OVERLAP", "0") == "1"
66
+
67
+
68
+ def _stream_value32(stream: int, ptr: int, value: int, *, wait: bool) -> None:
69
+ from cuda.bindings import driver as cu
70
+
71
+ if wait:
72
+ result = cu.cuStreamWaitValue32(
73
+ stream, ptr, value, cu.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_EQ
74
+ )
75
+ else:
76
+ result = cu.cuStreamWriteValue32(stream, ptr, value, 0)
77
+ if result[0] != cu.CUresult.CUDA_SUCCESS:
78
+ raise RuntimeError(f"PLE overlap stream memory op failed: {result[0]}")
79
+
80
+ def _register_ple_compilation_context(
81
+ compilation_config: Any,
82
+ layer_name: str,
83
+ layer: nn.Module,
84
+ ) -> None:
85
+ """Keep request-dependent PLE transactions outside piecewise graphs.
86
+
87
+ Piecewise graph dispatch is keyed by padded token count, while PLE hashing
88
+ and recurrent-state routing also depend on the live request count and query
89
+ boundaries. These custom operators must therefore execute as partition
90
+ boundaries so a graph compiled for one request layout cannot replay stale
91
+ PLE metadata for another layout.
92
+ """
93
+ static_context = compilation_config.static_forward_context
94
+ if layer_name in static_context:
95
+ raise ValueError(f"duplicate layer name: {layer_name}")
96
+ static_context[layer_name] = layer
97
+
98
+ splitting_ops = compilation_config.splitting_ops
99
+ if splitting_ops is not None:
100
+ for op_name in _PLE_SPLITTING_OPS:
101
+ if op_name not in splitting_ops:
102
+ splitting_ops.append(op_name)
103
+
104
+
105
+ def _b12x_module(name: str) -> Any:
106
+ api = {
107
+ "ple": get_b12x_ple,
108
+ "ple_embedding": get_b12x_ple_embedding,
109
+ }[name]()
110
+ if api is None:
111
+ raise ImportError(
112
+ f"Qwen4Exp requires b12x.sequence.{name}; install the b12x serving extra"
113
+ )
114
+ return api
115
+
116
+
117
+ def _resolve_ple_table_memory(
118
+ additional_config: Any, embedding_dtype: str = "bfloat16"
119
+ ) -> str:
120
+ """Translate the public offload policy into a b12x storage mode."""
121
+ if isinstance(additional_config, dict) and "ple_table_memory" in additional_config:
122
+ table_memory = additional_config["ple_table_memory"]
123
+ else:
124
+ table_memory = envs.VLLM_PLE_TABLE_MEMORY
125
+ if table_memory is None:
126
+ if envs.is_set("VLLM_PLE_CPU_OFFLOAD"):
127
+ return "mapped_host" if envs.VLLM_PLE_CPU_OFFLOAD else "device"
128
+ return "io_uring" if embedding_dtype == "bfloat16" else "device"
129
+ if table_memory in ("ram", "shared"):
130
+ return "mapped_host"
131
+ if table_memory == "disk":
132
+ return "io_uring"
133
+ if table_memory == "device":
134
+ return "device"
135
+ raise ValueError(
136
+ "additional_config.ple_table_memory must be 'device', "
137
+ f"'ram', 'shared', or 'disk', got {table_memory!r}"
138
+ )
139
+
140
+
141
+ def _resolve_ple_shared_table(vllm_config: Any) -> SharedTableConfig | None:
142
+ """Return the host-shared table settings when ``shared`` is selected."""
143
+ additional_config = vllm_config.additional_config
144
+ config = additional_config if isinstance(additional_config, dict) else {}
145
+ table_memory = config.get("ple_table_memory", envs.VLLM_PLE_TABLE_MEMORY)
146
+ if table_memory != "shared":
147
+ return None
148
+ model_config = vllm_config.model_config
149
+ identity = config.get(
150
+ "ple_shared_table_identity", envs.VLLM_PLE_SHARED_TABLE_IDENTITY
151
+ )
152
+ return SharedTableConfig(
153
+ directory=str(
154
+ config.get("ple_shared_table_dir", envs.VLLM_PLE_SHARED_TABLE_DIR)
155
+ ),
156
+ role=str(config.get("ple_shared_table_role", envs.VLLM_PLE_SHARED_TABLE_ROLE)),
157
+ lock_timeout_s=float(
158
+ config.get(
159
+ "ple_shared_table_lock_timeout_s",
160
+ envs.VLLM_PLE_SHARED_TABLE_LOCK_TIMEOUT_S,
161
+ )
162
+ ),
163
+ model_path=str(model_config.model),
164
+ revision=model_config.revision,
165
+ identity=str(identity)
166
+ if identity
167
+ else checkpoint_identity(str(model_config.model), model_config.revision),
168
+ )
169
+
170
+
171
+ def _copy_embedding_shard(
172
+ destination: torch.Tensor,
173
+ loaded_weight: torch.Tensor,
174
+ *,
175
+ checkpoint_start: int,
176
+ tp_start: int,
177
+ tp_end: int,
178
+ ) -> torch.Tensor | None:
179
+ checkpoint_end = checkpoint_start + loaded_weight.shape[0]
180
+ overlap_start = max(checkpoint_start, tp_start)
181
+ overlap_end = min(checkpoint_end, tp_end)
182
+ if overlap_start >= overlap_end:
183
+ return None
184
+ rows = overlap_end - overlap_start
185
+ source = loaded_weight.narrow(0, overlap_start - checkpoint_start, rows)
186
+ target = destination.narrow(0, overlap_start - tp_start, rows)
187
+ with torch.no_grad():
188
+ if target.device.type == "cpu" and source.is_meta:
189
+ source = materialize_weight(source)
190
+ copy_weight(target, source)
191
+ return target
192
+
193
+
194
+ class _NGramEmbeddingStorage(nn.Module):
195
+ def __init__(
196
+ self,
197
+ layout: Any,
198
+ shard_rows: int,
199
+ *,
200
+ shared_table: SharedTableConfig | None = None,
201
+ embedding_dim: int = 0,
202
+ dense_layer_ordinal: int = 0,
203
+ ) -> None:
204
+ super().__init__()
205
+ self.disk_table = None
206
+ self._table_storage = None
207
+ self._shared_table = None
208
+ if layout.caps.table_memory == "io_uring":
209
+ api = _b12x_module("ple_embedding")
210
+ self.disk_table = api.DiskTable(
211
+ layout,
212
+ shard_rows,
213
+ queue_depth=int(os.environ.get("VLLM_PLE_QUEUE_DEPTH", "64")),
214
+ )
215
+ tensors: dict[str, torch.Tensor | None] = {"weight": None}
216
+ for name in ("weight_scale", "weight_scale_2"):
217
+ shape = getattr(layout, f"{name}_shape")
218
+ tensors[name] = (
219
+ torch.empty(
220
+ shape,
221
+ dtype=getattr(layout, f"{name}_dtype"),
222
+ device=layout.caps.device,
223
+ )
224
+ if shape == (1,)
225
+ else None
226
+ )
227
+ elif shared_table is not None:
228
+ geometry = SharedTableGeometry.from_layout(
229
+ layout,
230
+ model_path=shared_table.model_path,
231
+ revision=shared_table.revision,
232
+ identity=shared_table.identity,
233
+ embedding_dim=embedding_dim,
234
+ dense_layer_ordinal=dense_layer_ordinal,
235
+ )
236
+ self._shared_table = open_shared_table(
237
+ geometry, shared_table, device=layout.caps.device
238
+ )
239
+ try:
240
+ self._table_storage = layout.allocate_storage(
241
+ host_allocator=self._shared_table.region
242
+ )
243
+ except BaseException:
244
+ self._shared_table.close()
245
+ raise
246
+ tensors = {
247
+ name: getattr(self._table_storage, name)
248
+ for name in ("weight", "weight_scale", "weight_scale_2")
249
+ }
250
+ else:
251
+ self._table_storage = layout.allocate_storage()
252
+ tensors = {
253
+ name: getattr(self._table_storage, name)
254
+ for name in ("weight", "weight_scale", "weight_scale_2")
255
+ }
256
+ for name, tensor in tensors.items():
257
+ self.register_parameter(
258
+ name,
259
+ nn.Parameter(tensor, requires_grad=False)
260
+ if tensor is not None
261
+ else None,
262
+ )
263
+
264
+ @property
265
+ def weight_load_view(self) -> torch.Tensor | None:
266
+ return self._table_storage.weight_load_view if self._table_storage else None
267
+
268
+ @property
269
+ def weight_scale_load_view(self) -> torch.Tensor | None:
270
+ if self._table_storage is None:
271
+ return self.weight_scale
272
+ return self._table_storage.weight_scale_load_view
273
+
274
+ @property
275
+ def weight_scale_2_load_view(self) -> torch.Tensor | None:
276
+ if self._table_storage is None:
277
+ return self.weight_scale_2
278
+ return self._table_storage.weight_scale_2_load_view
279
+
280
+ @property
281
+ def mapped_host_nbytes(self) -> int:
282
+ return int(self._table_storage.mapped_host_nbytes) if self._table_storage else 0
283
+
284
+ @property
285
+ def shared_table_attached(self) -> bool:
286
+ """Whether another process already filled every row of this table."""
287
+ return self._shared_table is not None and self._shared_table.attached
288
+
289
+ def abort_shared_table(self) -> None:
290
+ """Give up a shared table this process was populating."""
291
+ if self._shared_table is not None and self._shared_table.populating:
292
+ self._shared_table.abort()
293
+
294
+ def publish_shared_table(self) -> None:
295
+ """Mark a populated shared table complete for other processes."""
296
+ if self._shared_table is None or not self._shared_table.populating:
297
+ return
298
+ flush_weight_transfers()
299
+ torch.accelerator.synchronize()
300
+ self._shared_table.publish()
301
+
302
+
303
+ class B12xNGramEmbedding(nn.Module):
304
+ """Prime-hashed learned n-gram embedding with fixed b12x storage."""
305
+
306
+ _STORAGE_MODES = {
307
+ "bfloat16": "bf16",
308
+ "float8_e4m3fn": "fp8_e4m3_per_tensor",
309
+ "float4_e2m1fn_x2": "nvfp4_group16",
310
+ "nvfp4": "nvfp4_group16",
311
+ "nvfp4_group16": "nvfp4_group16",
312
+ "uint8": "nvfp4_group16",
313
+ }
314
+
315
+ def __init__(
316
+ self,
317
+ config: Qwen4ExpTextConfig,
318
+ embedding_dim: int,
319
+ ple_dense_layer_id: int,
320
+ max_total_tokens: int,
321
+ max_num_reqs: int,
322
+ owner_prefix: str,
323
+ prefix: str,
324
+ dtype: torch.dtype,
325
+ table_memory: str,
326
+ shared_table: SharedTableConfig | None = None,
327
+ ) -> None:
328
+ super().__init__()
329
+ self.embedding_dim = int(embedding_dim)
330
+ self.max_total_tokens = int(max_total_tokens)
331
+ self.max_num_reqs = int(max_num_reqs)
332
+ self.ngram_size = int(config.ngram_size)
333
+ self.heads_per_ngram = int(config.heads_per_ngram)
334
+ self.ngram_heads = (self.ngram_size - 1) * self.heads_per_ngram
335
+ self.requires_disk_preparation = table_memory == "io_uring"
336
+ self._disk_prepared_tokens = -1
337
+ self._disk_prepared = False
338
+ self.head_dim = self.embedding_dim // self.ngram_heads
339
+ self.eos_token_id = int(config.eos_token_id)
340
+ self.split_ngram_parts = int(getattr(config, "split_ngram_parts", 512))
341
+ self.owner_prefix = owner_prefix
342
+ self.embedding_storage_dtype = str(
343
+ getattr(config, "ple_embedding_dtype", "bfloat16")
344
+ )
345
+ if self.embedding_storage_dtype not in self._STORAGE_MODES:
346
+ raise NotImplementedError(
347
+ "Qwen4Exp PLE embedding storage dtype "
348
+ f"{self.embedding_storage_dtype!r} is unsupported"
349
+ )
350
+ self._quant_mode = self._STORAGE_MODES[self.embedding_storage_dtype]
351
+ self._embedding_load_ranges: set[tuple[int, int]] = set()
352
+ self._scale_load_ranges: set[tuple[int, int]] = set()
353
+ self._weight_scale_loaded = False
354
+ self._weight_scale_2_loaded = False
355
+ self._embedding_validated = False
356
+
357
+ device = torch.device(current_platform.current_device())
358
+ common_caps = {
359
+ "device": device,
360
+ "max_tokens": self.max_total_tokens,
361
+ "max_seqs": self.max_num_reqs,
362
+ "vocab_size": int(config.vocab_size),
363
+ "eos_token_id": self.eos_token_id,
364
+ "max_order": self.ngram_size,
365
+ "heads_per_order": self.heads_per_ngram,
366
+ "dense_layer_ordinal": int(ple_dense_layer_id),
367
+ "base_table_size": int(config.ngram_vocab_size_base),
368
+ "table_alignment": int(config.make_ngram_vocab_size_divisible_by),
369
+ }
370
+ api = _b12x_module("ple_embedding")
371
+ self._caps = api.Caps(
372
+ **common_caps,
373
+ embedding_dim=self.embedding_dim,
374
+ tp_size=get_tensor_model_parallel_world_size(),
375
+ tp_rank=get_tensor_model_parallel_rank(),
376
+ quant_mode=self._quant_mode,
377
+ table_memory=table_memory,
378
+ output_dtype=dtype,
379
+ )
380
+ self._geometry = api.compute_geometry(self._caps)
381
+ geometry_tensors = api.allocate_geometry(self._geometry, device=device)
382
+ self._table_layout = api.storage_layout(self._caps, geometry=self._geometry)
383
+ self.register_buffer("layer_multipliers", geometry_tensors.multipliers)
384
+ self.register_buffer("ngram_heads_offsets", geometry_tensors.table_offsets)
385
+ self.register_buffer("ngram_heads_vocab_sizes", geometry_tensors.prime_sizes)
386
+ self._plans: dict[int, object] = {}
387
+ shard_rows = (
388
+ self._table_layout.padded_vocab_size + self.split_ngram_parts - 1
389
+ ) // self.split_ngram_parts
390
+ self.ngram_embedding = _NGramEmbeddingStorage(
391
+ self._table_layout,
392
+ shard_rows,
393
+ shared_table=shared_table,
394
+ embedding_dim=self.embedding_dim,
395
+ dense_layer_ordinal=int(ple_dense_layer_id),
396
+ )
397
+ if self.ngram_embedding.mapped_host_nbytes:
398
+ logger.info(
399
+ "Using %.2f GiB of %s CUDA-mapped host memory for this TP rank's "
400
+ "PLE table",
401
+ self.ngram_embedding.mapped_host_nbytes / (1 << 30),
402
+ "shared" if shared_table is not None else "private",
403
+ )
404
+ scratch_spec = self._table_layout.scratch_specs()[0]
405
+ self.register_buffer(
406
+ "_scratch",
407
+ torch.empty(scratch_spec.shape, dtype=scratch_spec.dtype, device=device),
408
+ persistent=False,
409
+ )
410
+ self.register_buffer(
411
+ "_token_ids",
412
+ torch.empty(self.max_total_tokens, dtype=torch.int64, device=device),
413
+ persistent=False,
414
+ )
415
+ self.register_buffer(
416
+ "_query_start_loc",
417
+ torch.empty(self.max_num_reqs + 1, dtype=torch.int32, device=device),
418
+ persistent=False,
419
+ )
420
+ self.register_buffer(
421
+ "_committed_history",
422
+ torch.empty(
423
+ self.max_num_reqs,
424
+ self.ngram_size - 1,
425
+ dtype=torch.int64,
426
+ device=device,
427
+ ),
428
+ persistent=False,
429
+ )
430
+ self.register_buffer(
431
+ "_num_seqs",
432
+ torch.zeros(1, dtype=torch.int32, device=device),
433
+ persistent=False,
434
+ )
435
+ self.register_buffer(
436
+ "_num_tokens",
437
+ torch.zeros(1, dtype=torch.int32, device=device),
438
+ persistent=False,
439
+ )
440
+ self.register_buffer(
441
+ "_embedding_out",
442
+ torch.empty(
443
+ self._table_layout.output_shape,
444
+ dtype=self._table_layout.output_dtype,
445
+ device=device,
446
+ ),
447
+ persistent=False,
448
+ )
449
+ if not getattr(self, "b12x_preparation_suppressed", False):
450
+ set_b12x_preparation_provider(self, self)
451
+
452
+ def _declare_embedding_plan(self, token_count: int):
453
+ api = _b12x_module("ple_embedding")
454
+ return api.plan(
455
+ replace(self._caps, max_tokens=token_count),
456
+ geometry=self._geometry,
457
+ prime_sizes=self.ngram_heads_vocab_sizes,
458
+ table_offsets=self.ngram_heads_offsets,
459
+ multipliers=self.layer_multipliers,
460
+ )
461
+
462
+ def _capacity_for(self, token_count: int) -> int:
463
+ if not 0 <= token_count <= self.max_total_tokens:
464
+ raise ValueError("PLE embedding token count exceeds capacity")
465
+ if not self.requires_disk_preparation and token_count in self._plans:
466
+ return token_count
467
+ return self.max_total_tokens
468
+
469
+ def _plan_for(self, token_count: int):
470
+ """Resolve a live token count within the declared embedding capacity."""
471
+ capacity = self._capacity_for(token_count)
472
+ plan = self._plans.get(capacity)
473
+ if plan is None:
474
+ plan = self._declare_embedding_plan(capacity)
475
+ self._plans[capacity] = plan
476
+ return plan
477
+
478
+ def _bind_embedding(self, token_count: int):
479
+ capacity = self._capacity_for(token_count)
480
+ api = _b12x_module("ple_embedding")
481
+ return api.bind(
482
+ self._plan_for(token_count),
483
+ scratch=self._scratch,
484
+ weight=self.ngram_embedding.weight,
485
+ weight_scale=self.ngram_embedding.weight_scale,
486
+ weight_scale_2=self.ngram_embedding.weight_scale_2,
487
+ token_ids=self._token_ids[:capacity],
488
+ query_start_loc=self._query_start_loc,
489
+ committed_history=self._committed_history,
490
+ num_seqs=self._num_seqs,
491
+ num_tokens=self._num_tokens,
492
+ out=self._embedding_out[:capacity],
493
+ disk_table=self.ngram_embedding.disk_table,
494
+ )
495
+
496
+ def _bind_embedding_state(self, state: Any, token_count: int):
497
+ capacity = (
498
+ self.max_total_tokens if self.requires_disk_preparation else token_count
499
+ )
500
+ return state.bind(
501
+ scratch=self._scratch,
502
+ weight=self.ngram_embedding.weight,
503
+ weight_scale=self.ngram_embedding.weight_scale,
504
+ weight_scale_2=self.ngram_embedding.weight_scale_2,
505
+ token_ids=self._token_ids[:capacity],
506
+ query_start_loc=self._query_start_loc,
507
+ committed_history=self._committed_history,
508
+ num_seqs=self._num_seqs,
509
+ num_tokens=self._num_tokens,
510
+ out=self._embedding_out[:capacity],
511
+ disk_table=self.ngram_embedding.disk_table,
512
+ )
513
+
514
+ def _request_name(self, token_count: int) -> str:
515
+ return f"{self.owner_prefix}.ple_embedding.m{token_count}"
516
+
517
+ def get_b12x_preparation_units(
518
+ self, layer: torch.nn.Module, workload: B12xWorkload
519
+ ) -> tuple[B12xPreparationUnit, ...]:
520
+ if layer is not self:
521
+ raise ValueError("PLE embedding preparation owner mismatch")
522
+ self._validate_embedding_loaded()
523
+ requests = []
524
+ plans = self._plans
525
+ token_counts = (
526
+ (self.max_total_tokens,)
527
+ if self.requires_disk_preparation
528
+ else tuple(sorted({self.max_total_tokens, *workload.fixed_token_counts}))
529
+ )
530
+ # Plans are declared once per token count; a later collection reuses
531
+ # them so prepared state stays installed.
532
+ for token_count in token_counts:
533
+ plan = plans.get(token_count)
534
+ if plan is None:
535
+ plan = self._declare_embedding_plan(token_count)
536
+ plans[token_count] = plan
537
+ requests.append(
538
+ plan.request(
539
+ name=self._request_name(token_count),
540
+ prepare_call=self._embedding_prepare_call(token_count),
541
+ )
542
+ )
543
+ if not requests:
544
+ return ()
545
+ return (
546
+ B12xPreparationUnit(
547
+ name="PLE embedding",
548
+ key=self.owner_prefix,
549
+ requests=tuple(requests),
550
+ stage="weights",
551
+ ),
552
+ )
553
+
554
+ def _embedding_prepare_call(self, token_count: int):
555
+ def prepare(state):
556
+ from b12x.preparation import PreparedCall
557
+
558
+ # Prime one real row through the checkpoint-owned table and
559
+ # geometry. These are the only mutable staging rows this callback
560
+ # touches; unlike a pool clone they are restored when the call is
561
+ # released and never replace checkpoint parameters.
562
+ capacity = (
563
+ self.max_total_tokens if self.requires_disk_preparation else token_count
564
+ )
565
+ token_ids = self._token_ids[:1].clone()
566
+ query_start = self._query_start_loc[:2].clone()
567
+ history = self._committed_history[:1].clone()
568
+ num_seqs = self._num_seqs.clone()
569
+ num_tokens = self._num_tokens.clone()
570
+ output = self._embedding_out[:1].clone()
571
+
572
+ def produce() -> None:
573
+ self._token_ids[0] = self.eos_token_id
574
+ self._query_start_loc[:2].zero_()
575
+ self._query_start_loc[1] = 1
576
+ self._committed_history[0].fill_(self.eos_token_id)
577
+ self._num_seqs.fill_(1)
578
+ self._num_tokens.fill_(1)
579
+
580
+ def restore() -> None:
581
+ self._token_ids[:1].copy_(token_ids)
582
+ self._query_start_loc[:2].copy_(query_start)
583
+ self._committed_history[:1].copy_(history)
584
+ self._num_seqs.copy_(num_seqs)
585
+ self._num_tokens.copy_(num_tokens)
586
+ self._embedding_out[:1].copy_(output)
587
+
588
+ binding = self._bind_embedding_state(state, capacity)
589
+ return PreparedCall(
590
+ run=lambda: state.run(binding, token_count=1),
591
+ produce=produce,
592
+ restore=restore,
593
+ owners=(binding,),
594
+ )
595
+
596
+ return prepare
597
+
598
+ def _prepare_inputs(
599
+ self,
600
+ input_ids: torch.Tensor,
601
+ query_start_loc: torch.Tensor,
602
+ ngram_context: torch.Tensor,
603
+ ) -> int:
604
+ token_count = input_ids.numel()
605
+ num_seqs = query_start_loc.numel() - 1
606
+ if token_count > self.max_total_tokens or num_seqs > self.max_num_reqs:
607
+ raise ValueError(
608
+ "PLE hashing capacity exceeded: "
609
+ f"tokens={token_count}/{self.max_total_tokens}, "
610
+ f"requests={num_seqs}/{self.max_num_reqs}"
611
+ )
612
+ if tuple(ngram_context.shape) != (num_seqs, self.ngram_size - 1):
613
+ raise ValueError(
614
+ "ngram_context must have shape "
615
+ f"{(num_seqs, self.ngram_size - 1)}, got "
616
+ f"{tuple(ngram_context.shape)}"
617
+ )
618
+ self._token_ids.fill_(self.eos_token_id)
619
+ self._token_ids[:token_count].copy_(input_ids.reshape(-1).to(torch.int64))
620
+ self._query_start_loc.zero_()
621
+ self._query_start_loc[: num_seqs + 1].copy_(query_start_loc.to(torch.int32))
622
+ self._committed_history.fill_(self.eos_token_id)
623
+ self._committed_history[:num_seqs].copy_(ngram_context.to(torch.int64))
624
+ self._num_seqs.fill_(num_seqs)
625
+ self._num_tokens.copy_(query_start_loc[num_seqs : num_seqs + 1])
626
+ return token_count
627
+
628
+ def _run_embedding(
629
+ self,
630
+ input_ids: torch.Tensor,
631
+ query_start_loc: torch.Tensor,
632
+ ngram_context: torch.Tensor,
633
+ ) -> None:
634
+ self._validate_embedding_loaded()
635
+ self._plan_for(input_ids.numel())
636
+ token_count = self._prepare_inputs(input_ids, query_start_loc, ngram_context)
637
+ _b12x_module("ple_embedding").run(
638
+ self._bind_embedding(token_count), token_count=token_count
639
+ )
640
+
641
+ def prepare_disk(
642
+ self,
643
+ input_ids: torch.Tensor,
644
+ query_start_loc: torch.Tensor,
645
+ ngram_context: torch.Tensor,
646
+ ) -> None:
647
+ """Produce local embeddings before the model's graph is replayed."""
648
+ if not self.requires_disk_preparation:
649
+ raise RuntimeError("prepare_disk requires an io_uring PLE table")
650
+ if torch.compiler.is_compiling() or torch.cuda.is_current_stream_capturing():
651
+ raise RuntimeError("Disk PLE preparation must run outside CUDA graphs")
652
+ self._disk_prepared_tokens = -1
653
+ self._disk_prepared = False
654
+ if _PLE_OVERLAP:
655
+ ready = self._ple_ready_flag()
656
+ main = torch.cuda.current_stream()
657
+ side = self.__dict__.get("_ple_side_stream")
658
+ if side is None:
659
+ side = self.__dict__["_ple_side_stream"] = torch.cuda.Stream()
660
+ _stream_value32(main.cuda_stream, ready.data_ptr(), 0, wait=False)
661
+ side.wait_stream(main)
662
+ try:
663
+ with torch.cuda.stream(side):
664
+ self._run_embedding(input_ids, query_start_loc, ngram_context)
665
+ finally:
666
+ # Release the forward even if the read failed; the error surfaces here.
667
+ _stream_value32(side.cuda_stream, ready.data_ptr(), 1, wait=False)
668
+ else:
669
+ self._run_embedding(input_ids, query_start_loc, ngram_context)
670
+ self._disk_prepared_tokens = input_ids.numel()
671
+ self._disk_prepared = True
672
+ if _PLE_STATS_EVERY:
673
+ self._ple_stats_calls = getattr(self, "_ple_stats_calls", 0) + 1
674
+ if self._ple_stats_calls % _PLE_STATS_EVERY == 0:
675
+ st = self.ngram_embedding.disk_table.stats()
676
+ logger.info(
677
+ "PLE disk read: tokens=%d lookups=%d unique_blocks=%d read_calls=%d "
678
+ "coalesced=%d submits=%d read_bytes=%d exec_ms=%.3f plan_ms=%.3f",
679
+ input_ids.numel(), st["lookups"], st["unique_blocks"],
680
+ st["read_calls"], st["coalesced_reads"], st["submit_calls"],
681
+ st["read_bytes"], st["execution_seconds"] * 1e3,
682
+ st["planning_seconds"] * 1e3,
683
+ )
684
+
685
+ def prepare_dummy_output(self, num_tokens: int) -> None:
686
+ """Initialize graph-visible profiling output without reading the table."""
687
+ if not self.requires_disk_preparation:
688
+ raise RuntimeError("Dummy disk output requires an io_uring table")
689
+ if torch.compiler.is_compiling() or torch.cuda.is_current_stream_capturing():
690
+ raise RuntimeError(
691
+ "Dummy disk PLE preparation must run outside CUDA graphs"
692
+ )
693
+ if not 0 <= num_tokens <= self.max_total_tokens:
694
+ raise ValueError("Dummy PLE output exceeds token capacity")
695
+ self._embedding_out[:num_tokens].zero_()
696
+ if _PLE_OVERLAP:
697
+ self._ple_ready_flag().fill_(1)
698
+ self._disk_prepared_tokens = num_tokens
699
+ self._disk_prepared = True
700
+
701
+ def _ple_ready_flag(self) -> torch.Tensor:
702
+ ready = self.__dict__.get("_ple_ready")
703
+ if ready is None:
704
+ ready = torch.ones(1, dtype=torch.int32, device=self._embedding_out.device)
705
+ self.__dict__["_ple_ready"] = ready
706
+ logger.info("Qwen3.8 PLE disk reads overlap the first decoder layer.")
707
+ return ready
708
+
709
+ def _validate_embedding_loaded(self) -> None:
710
+ if self._embedding_validated:
711
+ return
712
+ try:
713
+ _require_embedding_coverage(self)
714
+ except ValueError:
715
+ # Waiting replicas must not attach to a table this process cannot fill.
716
+ abort_shared_table = getattr(
717
+ self.ngram_embedding, "abort_shared_table", None
718
+ )
719
+ if abort_shared_table is not None:
720
+ abort_shared_table()
721
+ raise
722
+ self._embedding_validated = True
723
+ publish_shared_table = getattr(
724
+ self.ngram_embedding, "publish_shared_table", None
725
+ )
726
+ if publish_shared_table is not None:
727
+ publish_shared_table()
728
+
729
+ def forward(
730
+ self,
731
+ input_ids: torch.Tensor,
732
+ query_start_loc: torch.Tensor,
733
+ ngram_context: torch.Tensor,
734
+ ) -> torch.Tensor:
735
+ input_ids = input_ids.reshape(-1)
736
+ if self.requires_disk_preparation:
737
+ # Batch counts must not become per-step Dynamo specialization guards.
738
+ if not self._disk_prepared or (
739
+ not torch.compiler.is_compiling()
740
+ and self._disk_prepared_tokens < input_ids.shape[0]
741
+ ):
742
+ raise RuntimeError(
743
+ "Disk PLE output is not prepared; call prepare_disk before forward"
744
+ )
745
+ elif torch.compiler.is_compiling():
746
+ torch.ops.vllm.qwen4_exp_b12x_ple_embedding(
747
+ input_ids,
748
+ query_start_loc,
749
+ ngram_context,
750
+ self._embedding_out,
751
+ self.owner_prefix,
752
+ )
753
+ else:
754
+ self._run_embedding(input_ids, query_start_loc, ngram_context)
755
+ if _PLE_OVERLAP and self.requires_disk_preparation:
756
+ ready = self.__dict__.get("_ple_ready")
757
+ if ready is None:
758
+ if torch.cuda.is_current_stream_capturing():
759
+ raise RuntimeError("PLE overlap flag must exist before graph capture")
760
+ ready = self._ple_ready_flag()
761
+ _stream_value32(
762
+ torch.cuda.current_stream().cuda_stream, ready.data_ptr(), 1, wait=True
763
+ )
764
+ embeddings = self._embedding_out[: input_ids.shape[0]]
765
+ if get_tensor_model_parallel_world_size() > 1:
766
+ embeddings = tensor_model_parallel_all_reduce(embeddings)
767
+ return embeddings
768
+
769
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
770
+ persistent_buffers = {
771
+ "layer_multipliers": self.layer_multipliers,
772
+ "ngram_heads_offsets": self.ngram_heads_offsets,
773
+ "ngram_heads_vocab_sizes": self.ngram_heads_vocab_sizes,
774
+ }
775
+ loaded: set[str] = set()
776
+ regular_weights: list[tuple[str, torch.Tensor]] = []
777
+ shard_prefix = "ngram_embedding.shard_"
778
+ embedding = self.ngram_embedding
779
+ org_vocab_size = self._table_layout.padded_vocab_size
780
+ tp_start = self._table_layout.shard_start
781
+ tp_end = self._table_layout.shard_end
782
+ shard_size = (
783
+ org_vocab_size + self.split_ngram_parts - 1
784
+ ) // self.split_ngram_parts
785
+
786
+ for name, loaded_weight in weights:
787
+ leaf_name = name.rsplit(".", 1)[-1]
788
+ if leaf_name.startswith("hashstats_") or leaf_name == "token_lookup":
789
+ continue
790
+ if name in persistent_buffers:
791
+ buffer = persistent_buffers[name]
792
+ if tuple(buffer.shape) != tuple(loaded_weight.shape):
793
+ raise ValueError(
794
+ f"shape mismatch for {name}: expected {tuple(buffer.shape)}, "
795
+ f"got {tuple(loaded_weight.shape)}"
796
+ )
797
+ checkpoint_value = loaded_weight.to(
798
+ device=buffer.device, dtype=buffer.dtype
799
+ )
800
+ if not torch.equal(buffer, checkpoint_value):
801
+ raise ValueError(
802
+ f"checkpoint {name} does not match planned PLE geometry"
803
+ )
804
+ loaded.add(name)
805
+ continue
806
+ if name == "ngram_embedding.weight_scale":
807
+ if self._quant_mode != "fp8_e4m3_per_tensor":
808
+ regular_weights.append((name, loaded_weight))
809
+ continue
810
+ expected_shape = self._table_layout.weight_scale_shape
811
+ if tuple(loaded_weight.shape) != expected_shape:
812
+ raise ValueError(
813
+ "shape mismatch for PLE embedding scale: expected "
814
+ f"{expected_shape}, got {tuple(loaded_weight.shape)}"
815
+ )
816
+ if loaded_weight.dtype != self._table_layout.weight_scale_dtype:
817
+ raise TypeError(
818
+ "PLE embedding weight_scale must have dtype "
819
+ f"{self._table_layout.weight_scale_dtype}, "
820
+ f"got {loaded_weight.dtype}"
821
+ )
822
+ with torch.no_grad():
823
+ target = getattr(
824
+ embedding, "weight_scale_load_view", embedding.weight_scale
825
+ )
826
+ assert target is not None
827
+ target.copy_(
828
+ loaded_weight.to(
829
+ device=target.device,
830
+ dtype=target.dtype,
831
+ )
832
+ )
833
+ self._weight_scale_loaded = True
834
+ self._embedding_validated = False
835
+ loaded.add(name)
836
+ continue
837
+ if name == "ngram_embedding.weight_scale_2":
838
+ if self._quant_mode != "nvfp4_group16":
839
+ regular_weights.append((name, loaded_weight))
840
+ continue
841
+ expected_shape = self._table_layout.weight_scale_2_shape
842
+ if loaded_weight.ndim == 0 and expected_shape == (1,):
843
+ loaded_weight = loaded_weight.reshape(1)
844
+ if tuple(loaded_weight.shape) != expected_shape:
845
+ raise ValueError(
846
+ "shape mismatch for PLE embedding weight_scale_2: expected "
847
+ f"{expected_shape}, got {tuple(loaded_weight.shape)}"
848
+ )
849
+ if loaded_weight.dtype != self._table_layout.weight_scale_2_dtype:
850
+ raise TypeError(
851
+ "PLE embedding weight_scale_2 must have dtype "
852
+ f"{self._table_layout.weight_scale_2_dtype}, got "
853
+ f"{loaded_weight.dtype}"
854
+ )
855
+ with torch.no_grad():
856
+ target = getattr(
857
+ embedding,
858
+ "weight_scale_2_load_view",
859
+ embedding.weight_scale_2,
860
+ )
861
+ assert target is not None
862
+ target.copy_(
863
+ loaded_weight.to(device=target.device, dtype=target.dtype)
864
+ )
865
+ self._weight_scale_2_loaded = True
866
+ self._embedding_validated = False
867
+ loaded.add(name)
868
+ continue
869
+ if name.startswith(shard_prefix):
870
+ shard_and_suffix = name[len(shard_prefix) :]
871
+ shard_text, separator, suffix = shard_and_suffix.partition(".")
872
+ if not shard_text.isdigit():
873
+ regular_weights.append((name, loaded_weight))
874
+ continue
875
+ shard_index = int(shard_text)
876
+ if shard_index >= self.split_ngram_parts:
877
+ raise ValueError(
878
+ f"PLE shard {shard_index} exceeds "
879
+ f"split_ngram_parts={self.split_ngram_parts}"
880
+ )
881
+ checkpoint_start = shard_index * shard_size
882
+ expected_rows = max(
883
+ 0,
884
+ min(shard_size, org_vocab_size - checkpoint_start),
885
+ )
886
+ if separator != "." or suffix not in {"weight", "weight_scale"}:
887
+ regular_weights.append((name, loaded_weight))
888
+ continue
889
+ if suffix == "weight":
890
+ expected_shape = (expected_rows, self._table_layout.weight_shape[1])
891
+ expected_dtype = self._table_layout.weight_dtype
892
+ else:
893
+ if self._quant_mode != "nvfp4_group16":
894
+ regular_weights.append((name, loaded_weight))
895
+ continue
896
+ assert self._table_layout.weight_scale_shape is not None
897
+ assert self._table_layout.weight_scale_dtype is not None
898
+ expected_shape = (
899
+ expected_rows,
900
+ self._table_layout.weight_scale_shape[1],
901
+ )
902
+ expected_dtype = self._table_layout.weight_scale_dtype
903
+ if tuple(loaded_weight.shape) != expected_shape:
904
+ raise ValueError(
905
+ f"shape mismatch for PLE shard {shard_index} {suffix}: "
906
+ f"expected {expected_shape}, got {tuple(loaded_weight.shape)}"
907
+ )
908
+ if loaded_weight.dtype != expected_dtype:
909
+ raise TypeError(
910
+ f"PLE shard {shard_index} {suffix} must have dtype "
911
+ f"{expected_dtype}, got {loaded_weight.dtype}"
912
+ )
913
+ overlap_start = max(checkpoint_start, tp_start)
914
+ overlap_end = min(checkpoint_start + expected_rows, tp_end)
915
+ disk_table = getattr(embedding, "disk_table", None)
916
+ if disk_table is not None:
917
+ source = get_file_tensor_source(loaded_weight)
918
+ if source is None:
919
+ raise ValueError(
920
+ "File-backed PLE tables require safetensors weights "
921
+ "from the b12x, instanttensor, fastsafetensors, or "
922
+ "safetensors loader"
923
+ )
924
+ if source.shape != expected_shape or source.dtype != expected_dtype:
925
+ raise ValueError(f"file source geometry does not match {name}")
926
+ if overlap_start < overlap_end:
927
+ disk_table.add_shard(
928
+ shard_index,
929
+ source.path,
930
+ source.offset,
931
+ scale=suffix == "weight_scale",
932
+ )
933
+ elif getattr(embedding, "shared_table_attached", False):
934
+ # The attached table already holds every row of this shard.
935
+ pass
936
+ else:
937
+ parameter = getattr(embedding, suffix)
938
+ destination = getattr(
939
+ embedding, f"{suffix}_load_view", parameter.data
940
+ )
941
+ assert destination is not None
942
+ _copy_embedding_shard(
943
+ destination,
944
+ loaded_weight,
945
+ checkpoint_start=checkpoint_start,
946
+ tp_start=tp_start,
947
+ tp_end=tp_end,
948
+ )
949
+ if overlap_start < overlap_end:
950
+ load_ranges = (
951
+ self._embedding_load_ranges
952
+ if suffix == "weight"
953
+ else self._scale_load_ranges
954
+ )
955
+ load_ranges.add((overlap_start, overlap_end))
956
+ self._embedding_validated = False
957
+ loaded.add(f"ngram_embedding.{suffix}")
958
+ continue
959
+ regular_weights.append((name, loaded_weight))
960
+
961
+ if regular_weights:
962
+ loaded.update(AutoWeightsLoader(self).load_weights(regular_weights))
963
+ return loaded
964
+
965
+
966
+ def _require_embedding_coverage(embedding: B12xNGramEmbedding) -> None:
967
+ """Raise when the loaded shards or scales do not cover the local table."""
968
+ covered_until = embedding._table_layout.shard_start
969
+ for start, end in sorted(embedding._embedding_load_ranges):
970
+ if start > covered_until:
971
+ raise ValueError(
972
+ "PLE embedding shards do not cover the local table: "
973
+ f"expected row {covered_until}, got {start}"
974
+ )
975
+ covered_until = max(covered_until, end)
976
+ if covered_until != embedding._table_layout.shard_end:
977
+ raise ValueError(
978
+ "PLE embedding shards do not cover the local table: "
979
+ f"stopped at row {covered_until}, "
980
+ f"expected {embedding._table_layout.shard_end}"
981
+ )
982
+
983
+ if embedding._table_layout.weight_scale_shape is not None:
984
+ if embedding._quant_mode == "fp8_e4m3_per_tensor":
985
+ if not embedding._weight_scale_loaded:
986
+ raise ValueError("FP8 PLE embedding checkpoint is missing weight_scale")
987
+ else:
988
+ scale_covered_until = embedding._table_layout.shard_start
989
+ for start, end in sorted(embedding._scale_load_ranges):
990
+ if start > scale_covered_until:
991
+ raise ValueError(
992
+ "NVFP4 PLE scale shards do not cover the local table: "
993
+ f"expected row {scale_covered_until}, got {start}"
994
+ )
995
+ scale_covered_until = max(scale_covered_until, end)
996
+ if scale_covered_until != embedding._table_layout.shard_end:
997
+ raise ValueError(
998
+ "NVFP4 PLE scale shards do not cover the local table: "
999
+ f"stopped at row {scale_covered_until}, expected "
1000
+ f"{embedding._table_layout.shard_end}"
1001
+ )
1002
+ if (
1003
+ embedding._table_layout.weight_scale_2_shape is not None
1004
+ and not embedding._weight_scale_2_loaded
1005
+ ):
1006
+ raise ValueError("NVFP4 PLE embedding checkpoint is missing weight_scale_2")
1007
+
1008
+
1009
+ def _ple_embedding_op(
1010
+ input_ids: torch.Tensor,
1011
+ query_start_loc: torch.Tensor,
1012
+ ngram_context: torch.Tensor,
1013
+ out: torch.Tensor,
1014
+ layer_name: str,
1015
+ ) -> None:
1016
+ layer = get_forward_context().no_compile_layers[layer_name]
1017
+ layer.ple_embedding._run_embedding(input_ids, query_start_loc, ngram_context)
1018
+
1019
+
1020
+ def _ple_embedding_fake(
1021
+ input_ids: torch.Tensor,
1022
+ query_start_loc: torch.Tensor,
1023
+ ngram_context: torch.Tensor,
1024
+ out: torch.Tensor,
1025
+ layer_name: str,
1026
+ ) -> None:
1027
+ return
1028
+
1029
+
1030
+ def _ple_op(
1031
+ residual: torch.Tensor,
1032
+ key: torch.Tensor,
1033
+ value: torch.Tensor,
1034
+ query_start_loc: torch.Tensor,
1035
+ out: torch.Tensor,
1036
+ layer_name: str,
1037
+ ) -> None:
1038
+ layer = get_forward_context().no_compile_layers[layer_name]
1039
+ layer._run_ple(residual, key, value, query_start_loc)
1040
+
1041
+
1042
+ def _ple_fake(
1043
+ residual: torch.Tensor,
1044
+ key: torch.Tensor,
1045
+ value: torch.Tensor,
1046
+ query_start_loc: torch.Tensor,
1047
+ out: torch.Tensor,
1048
+ layer_name: str,
1049
+ ) -> None:
1050
+ return
1051
+
1052
+
1053
+ direct_register_custom_op(
1054
+ op_name="qwen4_exp_b12x_ple_embedding",
1055
+ op_func=_ple_embedding_op,
1056
+ mutates_args=["out"],
1057
+ fake_impl=_ple_embedding_fake,
1058
+ )
1059
+ direct_register_custom_op(
1060
+ op_name="qwen4_exp_b12x_ple",
1061
+ op_func=_ple_op,
1062
+ mutates_args=["out"],
1063
+ fake_impl=_ple_fake,
1064
+ )
recipe/patches/qwen4_exp/nvidia/expert_stats.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Distinct routed experts per small-batch MoE call (VLLM_QWEN38_EXPERT_STATS=<json path>).
3
+
4
+ Measurement only. Each router gate also takes the top-k of its logits for calls of at
5
+ most 16 tokens (decode / spec-verify shapes) and accumulates, on device and CUDA-graph
6
+ safe, the number of distinct experts the call touched. Touch <path>.req and a daemon
7
+ thread writes the running totals per gate to the JSON path (warmup and capture runs
8
+ count too, so diff two snapshots around the measured workload).
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import threading
14
+ import time
15
+
16
+ import torch
17
+
18
+ _MAX_TOKENS = 16
19
+ _gates: list = []
20
+
21
+
22
+ def install(gate: torch.nn.Module, name: str, top_k: int, num_experts: int, path: str) -> None:
23
+ stock = gate.forward
24
+ # Allocated here, not lazily: a first call inside CUDA-graph capture would put the
25
+ # buffer in the graph pool and re-zero it on every replay.
26
+ stats = {"buf": torch.zeros(_MAX_TOKENS + 1, 2, dtype=torch.int64, device="cuda")}
27
+
28
+ def forward(x):
29
+ out = stock(x)
30
+ logits = out[0] if isinstance(out, tuple) else out
31
+ t = logits.shape[0] if logits.dim() == 2 else 0
32
+ if 0 < t <= _MAX_TOKENS:
33
+ ids = logits.topk(top_k, dim=-1).indices.flatten()
34
+ seen = torch.zeros(num_experts, dtype=torch.int64, device=logits.device).scatter_(0, ids, 1)
35
+ stats["buf"][t, 0] += seen.sum()
36
+ stats["buf"][t, 1] += 1
37
+ return out
38
+
39
+ gate.forward = forward
40
+ _gates.append((name, stats))
41
+ if len(_gates) == 1:
42
+ threading.Thread(target=_writer, args=(path,), daemon=True).start()
43
+
44
+
45
+ def _writer(path: str) -> None:
46
+ # Touches CUDA only when asked (<path>.req appears): a device read from this thread
47
+ # during the server's CUDA-graph capture would invalidate the capture.
48
+ while True:
49
+ time.sleep(1)
50
+ if not os.path.exists(path + ".req"):
51
+ continue
52
+ try:
53
+ os.remove(path + ".req")
54
+ except OSError:
55
+ pass
56
+ out = {}
57
+ for name, stats in _gates:
58
+ buf = stats.get("buf")
59
+ if buf is None:
60
+ continue
61
+ rows = buf.tolist()
62
+ out[name] = {str(t): {"distinct_sum": d, "calls": c} for t, (d, c) in enumerate(rows) if c}
63
+ try:
64
+ with open(path + ".tmp", "w") as f:
65
+ json.dump(out, f)
66
+ os.replace(path + ".tmp", path)
67
+ except OSError:
68
+ pass
recipe/patches/qwen4_exp/nvidia/gdn_defer.py ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Deferred-commit GDN verify (VLLM_QWEN38_GDN_DEFER=1); kernels in ops/gdn_deferred.py.
3
+
4
+ Verify: the uniform b12x spec-decode path keeps its metadata staging and output norm
5
+ but runs gdn_verify, which writes per-token records instead of 5 full fp32 states per
6
+ layer, and marks the request rows it deferred (a device flag; graph-replay safe).
7
+
8
+ Commit: once acceptance is known, gdn_commit replays the accepted tokens of every
9
+ deferred row in all GDN layers (one launch) and writes the state column the next step
10
+ reads. It runs at the first of the runner's postprocess hooks that can read state
11
+ columns: boundary-checkpoint capture (before it copies), model-state postprocess
12
+ (before align block saves), or the end of postprocess_sampled (logits-only steps,
13
+ committed with every verify column written exactly as stock verify would).
14
+
15
+ Layers that ever ran stock verify inside a CUDA-graph capture (no records yet) stay on
16
+ the stock path for good, so a layer is committed iff its graphs record.
17
+ """
18
+
19
+ import threading
20
+
21
+ import torch
22
+ import triton
23
+
24
+ from vllm.logger import init_logger
25
+
26
+ logger = init_logger(__name__)
27
+
28
+ _tls = threading.local()
29
+ _layers: list = []
30
+ _shared: dict = {"valid": None, "tables": None, "key": None}
31
+ _DEBUG = __import__("os").environ.get("VLLM_QWEN38_GDN_DEBUG", "0") == "1"
32
+ _FORCE_FULL = __import__("os").environ.get("VLLM_QWEN38_GDN_FULL", "0") == "1"
33
+ _SYNC = __import__("os").environ.get("VLLM_QWEN38_GDN_SYNC", "0") == "1"
34
+ # Ordinary rows commit on a side stream that overlaps the MTP draft phase; the main
35
+ # stream joins it at the next execute_model. Rows that need every column (block
36
+ # crossing, capture, logits-only) still commit on the main stream first.
37
+ _ASYNC = __import__("os").environ.get("VLLM_QWEN38_GDN_COMMIT_ASYNC", "0") == "1"
38
+
39
+
40
+ def _join():
41
+ """Make the current stream wait for an in-flight side-stream commit."""
42
+ ev = _shared.get("pending")
43
+ if ev is not None:
44
+ torch.cuda.current_stream().wait_event(ev)
45
+ _shared["pending"] = None
46
+
47
+
48
+ def _snapshot(name, src):
49
+ """Copy a small per-step tensor into a persistent buffer (main stream), so the
50
+ side-stream commit reads stable values whatever the main stream does next."""
51
+ buf = _shared.get(name)
52
+ if buf is None or buf.numel() < src.numel() or buf.dtype != src.dtype:
53
+ buf = _shared[name] = torch.empty(max(src.numel(), 64), dtype=src.dtype, device=src.device)
54
+ out = buf[: src.numel()].view(src.shape)
55
+ out.copy_(src)
56
+ return out
57
+
58
+
59
+ def _records(layer):
60
+ rec = layer.__dict__.get("_qwen38_gdn_records")
61
+ if rec is not None or layer.__dict__.get("_qwen38_gdn_stock"):
62
+ return rec
63
+ staging = getattr(layer, "_b12x_decode_staging", None)
64
+ if staging is None or torch.cuda.is_current_stream_capturing():
65
+ return None
66
+ dev = layer.kv_cache[1].device
67
+ cap = layer._b12x_max_seqs * layer._b12x_state_index_columns
68
+ vh, kh = layer._b12x_local_value_heads, layer._b12x_local_key_heads
69
+ rec = (
70
+ torch.zeros(cap, vh, device=dev, dtype=torch.float32),
71
+ torch.zeros(cap, vh, 128, device=dev, dtype=torch.float32),
72
+ torch.zeros(cap, kh, 128, device=dev, dtype=torch.float32),
73
+ )
74
+ layer.__dict__["_qwen38_gdn_records"] = rec
75
+ _layers.append(layer)
76
+ if _shared["valid"] is None:
77
+ _shared["valid"] = torch.zeros(layer._b12x_max_seqs, dtype=torch.int32, device=dev)
78
+ logger.info("Qwen3.8 GDN verify defers state writes to a post-acceptance commit.")
79
+ _shared["key"] = None
80
+ return rec
81
+
82
+
83
+ def _tables():
84
+ key = tuple(
85
+ (layer.kv_cache[1].data_ptr(), layer._b12x_decode_staging.state_indices.data_ptr())
86
+ for layer in _layers
87
+ )
88
+ if key != _shared["key"]:
89
+ dev = _layers[0].kv_cache[1].device
90
+ cols = (
91
+ [layer.kv_cache[1].data_ptr() for layer in _layers],
92
+ [layer.__dict__["_qwen38_gdn_records"][0].data_ptr() for layer in _layers],
93
+ [layer.__dict__["_qwen38_gdn_records"][1].data_ptr() for layer in _layers],
94
+ [layer.__dict__["_qwen38_gdn_records"][2].data_ptr() for layer in _layers],
95
+ # each layer's own staged block ids (layers sit in different KV-cache groups)
96
+ [layer._b12x_decode_staging.state_indices.data_ptr() for layer in _layers],
97
+ )
98
+ ref = _layers[0]._b12x_decode_staging.state_indices
99
+ for layer in _layers:
100
+ si = layer._b12x_decode_staging.state_indices
101
+ if si.dtype != torch.int32 or si.shape != ref.shape or si.stride() != ref.stride():
102
+ raise RuntimeError("GDN defer needs identical int32 staged index layouts")
103
+ _shared["tables"] = tuple(torch.tensor(c, dtype=torch.int64, device=dev) for c in cols)
104
+ _shared["key"] = key
105
+ strides = {layer.kv_cache[1].stride(0) for layer in _layers}
106
+ if len(strides) != 1:
107
+ raise RuntimeError("GDN defer needs one state slot stride across layers")
108
+ return _shared["tables"]
109
+
110
+
111
+ def _commit(capture_tokens=None, force_full=False):
112
+ from .ops.gdn_deferred import gdn_commit
113
+
114
+ ctx = _tls.ctx
115
+ ctx["done"] = True
116
+ if _SHADOW:
117
+ if _shared["valid"] is not None:
118
+ _shared["valid"].zero_()
119
+ return
120
+ valid = _shared["valid"]
121
+ num_sampled = ctx["num_sampled"]
122
+ if valid is None or not _layers or not isinstance(num_sampled, torch.Tensor):
123
+ return
124
+ runner = ctx["runner"]
125
+ layer0 = _layers[0]
126
+ st = layer0._b12x_decode_staging
127
+ cc = runner.cache_config
128
+ block = int(getattr(cc, "mamba_block_size", None) or cc.block_size)
129
+ if _DEBUG:
130
+ _shared["debug_commits"] = _shared.get("debug_commits", 0) + 1
131
+ k = _shared["debug_commits"]
132
+ if 15 <= k <= 55:
133
+ torch.cuda.synchronize()
134
+ logger.info(
135
+ "GDN defer commit #%d: valid=%s nseq=%s nacc=%s qsl=%s idx0=%s nsamp=%s map=%s cap=%s",
136
+ k, valid.tolist(), st.num_seqs.item(), st.num_accepted_tokens.tolist()[:2],
137
+ st.query_start_loc.tolist()[:3], st.state_indices[0].tolist(),
138
+ num_sampled.tolist(), ctx["idx_mapping"].tolist(),
139
+ None if capture_tokens is None else int(capture_tokens.max()),
140
+ )
141
+ _join()
142
+ args = (_tables(), st.state_indices, st.num_accepted_tokens, st.query_start_loc, st.num_seqs, valid)
143
+ kw = dict(key_heads=layer0._b12x_local_key_heads, value_heads=layer0._b12x_local_value_heads)
144
+ tail = (block, layer0.kv_cache[1].stride(0), layer0._b12x_max_seqs)
145
+ ncomp = runner.req_states.num_computed_tokens.gpu
146
+ full = force_full or _FORCE_FULL
147
+ if _ASYNC and capture_tokens is None and not full:
148
+ gdn_commit(*args, num_sampled, ctx["idx_mapping"], ncomp, None, *tail, row_mode=1, **kw)
149
+ ns = _snapshot("snap_ns", num_sampled)
150
+ rm = _snapshot("snap_map", ctx["idx_mapping"])
151
+ nc = _snapshot("snap_nc", ncomp)
152
+ main = torch.cuda.current_stream()
153
+ side = _shared.get("side")
154
+ if side is None:
155
+ side = _shared["side"] = torch.cuda.Stream(device=valid.device)
156
+ _shared["ev_fork"], _shared["ev_done"] = torch.cuda.Event(), torch.cuda.Event()
157
+ _shared["ev_fork"].record(main)
158
+ side.wait_event(_shared["ev_fork"])
159
+ with torch.cuda.stream(side):
160
+ gdn_commit(*args, ns, rm, nc, None, *tail, row_mode=2, **kw)
161
+ valid.zero_()
162
+ _shared["ev_done"].record(side)
163
+ _shared["pending"] = _shared["ev_done"]
164
+ else:
165
+ gdn_commit(*args, num_sampled, ctx["idx_mapping"], ncomp, capture_tokens, *tail,
166
+ force_full=full, **kw)
167
+ valid.zero_()
168
+ if _SYNC:
169
+ torch.cuda.synchronize()
170
+ if _DEBUG and _shared.get('debug_streams', 0) < 3:
171
+ _shared['debug_streams'] = _shared.get('debug_streams', 0) + 1
172
+ logger.info('GDN defer commit stream %s', torch.cuda.current_stream())
173
+
174
+
175
+ _SHADOW = __import__("os").environ.get("VLLM_QWEN38_GDN_SHADOW", "0") == "1"
176
+
177
+
178
+ def _shadow_compare(layer, rec, stock_post, kwargs):
179
+ """Debug (eager only): run gdn_verify into a scratch output from the pristine source
180
+ state, then the stock verify (which drives the model and writes every column), and
181
+ compare outputs and torch-replayed per-token states against stock's columns."""
182
+ from .ops.gdn_deferred import gdn_verify
183
+
184
+ state = layer.kv_cache[1]
185
+ si, nacc, qsl = kwargs["state_indices"], kwargs["num_accepted_tokens"], kwargs["query_start_loc"]
186
+ nreq = int(kwargs["num_requests"])
187
+ staging = layer._b12x_decode_staging
188
+ # stage like stock (our kernel reads the staging copies)
189
+ import triton as _tr
190
+ from vllm.model_executor.layers.mamba.gdn import qwen_gdn_linear_attn as qg
191
+
192
+ qg._stage_b12x_gdn_metadata_kernel[(1,)](
193
+ qsl, si, nacc, staging.query_start_loc, staging.state_indices,
194
+ staging.num_accepted_tokens, staging.num_seqs, staging.num_tokens, nreq,
195
+ qsl.stride(0), nacc.stride(0), si.stride(0), si.stride(1),
196
+ INPUT_COLUMNS=si.shape[1], STATE_COLUMNS=layer._b12x_state_index_columns,
197
+ MAX_SEQS=layer._b12x_max_seqs, HAS_ACCEPTED=True,
198
+ BLOCK=_tr.next_power_of_2(max(layer._b12x_max_seqs + 1, layer._b12x_max_seqs * layer._b12x_state_index_columns)),
199
+ num_warps=4,
200
+ )
201
+ src_slot = int(staging.state_indices[0, int(staging.num_accepted_tokens[0]) - 1])
202
+ src = state[src_slot].clone()
203
+ T = int(staging.query_start_loc[1] - staging.query_start_loc[0])
204
+ ours = kwargs["core_attn_out"].clone()
205
+ gdn_verify(
206
+ kwargs["mixed_qkv"], kwargs["a"], kwargs["b"], layer.A_log, layer.dt_bias, state,
207
+ staging.state_indices, staging.num_accepted_tokens, staging.query_start_loc,
208
+ staging.num_seqs, _shared["valid"], ours[: kwargs["mixed_qkv"].shape[0]], rec,
209
+ layer.head_k_dim ** -0.5, layer._b12x_max_seqs,
210
+ key_heads=layer._b12x_local_key_heads, value_heads=layer._b12x_local_value_heads,
211
+ )
212
+ from .ops.gdn_deferred import gdn_gated_rmsnorm
213
+
214
+ m = kwargs["mixed_qkv"].shape[0]
215
+ gdn_gated_rmsnorm(ours[:m], kwargs["output_gate"], layer.norm.weight, staging.num_tokens,
216
+ layer.layer_norm_epsilon, layer._b12x_local_value_heads,
217
+ layer.norm.activation == "sigmoid")
218
+ result = stock_post(layer, **kwargs)
219
+ stock_out = kwargs["core_attn_out"][:m].float()
220
+ out_err = ((ours[:m].float() - stock_out).abs().max() / stock_out.abs().max().clamp_min(1e-30)).item()
221
+ if layer is _layers[0] and _shared.setdefault("shadow_logs", 0) < 12:
222
+ _shared["shadow_logs"] += 1
223
+ g, d, kk = rec
224
+ S = src.double()
225
+ errs = []
226
+ for t in range(T):
227
+ kv = kk[t].double()[torch.arange(48, device=S.device) // 3] # [48,128]
228
+ S = S * g[t].double()[:, None, None] + d[t].double()[:, :, None] * kv[:, None, :]
229
+ ref = state[int(staging.state_indices[0, t])].double()
230
+ errs.append(f"{((S - ref).abs().max() / ref.abs().max().clamp_min(1e-30)).item():.1e}")
231
+ logger.info(
232
+ "GDN shadow: layer0 T=%d src_slot=%d nacc=%d cols=%s | normed output rel err %.2e | "
233
+ "state replay rel err per column %s",
234
+ T, src_slot, int(staging.num_accepted_tokens[0]), staging.state_indices[0].tolist(), out_err, errs,
235
+ )
236
+ return result
237
+
238
+
239
+ def install() -> None:
240
+ from vllm.model_executor.layers.mamba.gdn import qwen_gdn_linear_attn as qg
241
+ from vllm.v1.worker.gpu import boundary_checkpoint as bc
242
+ from vllm.v1.worker.gpu import model_runner as mr
243
+ from vllm.v1.worker.gpu.model_states import mamba_hybrid as mh
244
+
245
+ from .ops.gdn_deferred import gdn_gated_rmsnorm, gdn_verify
246
+
247
+ Attn = qg.QwenGatedDeltaNetAttention
248
+ if getattr(Attn, "_qwen38_gdn_defer", False):
249
+ return
250
+ stock_fused = Attn._forward_core_fused_norm
251
+ stock_uniform = Attn._forward_core_decode_b12x_fused_norm
252
+ stock_post = Attn._run_b12x_gdn_decode_post_conv
253
+
254
+ def fused(self, mixed_qkv, b, a, output_gate, core_attn_out):
255
+ # Every forward starts from "nothing deferred"; the uniform path re-marks rows.
256
+ valid = _shared["valid"]
257
+ if valid is not None and _layers and self is _layers[0]:
258
+ valid.zero_()
259
+ return stock_fused(self, mixed_qkv, b, a, output_gate, core_attn_out)
260
+
261
+ def uniform(self, mixed_qkv, b, a, output_gate, core_attn_out, attn_metadata):
262
+ _tls.spec = attn_metadata.spec_sequence_masks is not None
263
+ try:
264
+ return stock_uniform(
265
+ self, mixed_qkv=mixed_qkv, b=b, a=a, output_gate=output_gate,
266
+ core_attn_out=core_attn_out, attn_metadata=attn_metadata,
267
+ )
268
+ finally:
269
+ _tls.spec = False
270
+
271
+ def post(self, *, mixed_qkv, b, a, output_gate, core_attn_out, state_indices,
272
+ query_start_loc, num_accepted_tokens, num_requests):
273
+ kwargs = dict(
274
+ mixed_qkv=mixed_qkv, b=b, a=a, output_gate=output_gate,
275
+ core_attn_out=core_attn_out, state_indices=state_indices,
276
+ query_start_loc=query_start_loc, num_accepted_tokens=num_accepted_tokens,
277
+ num_requests=num_requests,
278
+ )
279
+ rec = None
280
+ if (
281
+ getattr(_tls, "spec", False)
282
+ and num_accepted_tokens is not None
283
+ and state_indices.shape[1] > 1
284
+ and core_attn_out.dim() == 3
285
+ and core_attn_out.stride(2) == 1
286
+ and mixed_qkv.stride(1) == 1
287
+ ):
288
+ rec = _records(self)
289
+ if _DEBUG and torch.cuda.is_current_stream_capturing():
290
+ key = "debug_capture_deferred" if rec is not None else "debug_capture_stock"
291
+ _shared[key] = _shared.get(key, 0) + 1
292
+ if _shared[key] in (1, 36, 72):
293
+ logger.info("GDN defer capture: %s calls=%d (spec=%s cols=%d) stream %s", key, _shared[key],
294
+ getattr(_tls, "spec", False), state_indices.shape[1], torch.cuda.current_stream())
295
+ if rec is not None and _SHADOW and not torch.cuda.is_current_stream_capturing():
296
+ return _shadow_compare(self, rec, stock_post, kwargs)
297
+ if rec is None:
298
+ if torch.cuda.is_current_stream_capturing() and "_qwen38_gdn_records" not in self.__dict__:
299
+ self.__dict__["_qwen38_gdn_stock"] = True
300
+ return stock_post(self, **kwargs)
301
+ staging = self._b12x_decode_staging
302
+ num_input_tokens = mixed_qkv.shape[0]
303
+ if (
304
+ num_input_tokens > self._b12x_max_tokens
305
+ or num_requests > self._b12x_max_seqs
306
+ or state_indices.shape[1] > self._b12x_state_index_columns
307
+ or num_input_tokens > rec[0].shape[0]
308
+ ):
309
+ raise ValueError("b12x GDN capacity exceeded (deferred verify)")
310
+ qg._stage_b12x_gdn_metadata_kernel[(1,)](
311
+ query_start_loc, state_indices, num_accepted_tokens,
312
+ staging.query_start_loc, staging.state_indices, staging.num_accepted_tokens,
313
+ staging.num_seqs, staging.num_tokens,
314
+ num_requests,
315
+ query_start_loc.stride(0),
316
+ num_accepted_tokens.stride(0),
317
+ state_indices.stride(0),
318
+ state_indices.stride(1),
319
+ INPUT_COLUMNS=state_indices.shape[1],
320
+ STATE_COLUMNS=self._b12x_state_index_columns,
321
+ MAX_SEQS=self._b12x_max_seqs,
322
+ HAS_ACCEPTED=True,
323
+ BLOCK=triton.next_power_of_2(
324
+ max(self._b12x_max_seqs + 1, self._b12x_max_seqs * self._b12x_state_index_columns)
325
+ ),
326
+ num_warps=4,
327
+ )
328
+ out = core_attn_out[:num_input_tokens]
329
+ gdn_verify(
330
+ mixed_qkv, a, b, self.A_log, self.dt_bias, self.kv_cache[1],
331
+ staging.state_indices, staging.num_accepted_tokens, staging.query_start_loc,
332
+ staging.num_seqs, _shared["valid"], out, rec, self.head_k_dim ** -0.5,
333
+ self._b12x_max_seqs, key_heads=self._b12x_local_key_heads,
334
+ value_heads=self._b12x_local_value_heads,
335
+ )
336
+ gdn_gated_rmsnorm(
337
+ out, output_gate, self.norm.weight, staging.num_tokens, self.layer_norm_epsilon,
338
+ self._b12x_local_value_heads, self.norm.activation == "sigmoid",
339
+ )
340
+
341
+ Attn._forward_core_fused_norm = fused
342
+ Attn._forward_core_decode_b12x_fused_norm = uniform
343
+ Attn._run_b12x_gdn_decode_post_conv = post
344
+ Attn._qwen38_gdn_defer = True
345
+
346
+ Runner = mr.GPUModelRunner
347
+ stock_pps = Runner.postprocess_sampled
348
+
349
+ def postprocess_sampled(self, idx_mapping, sampled_token_ids, num_sampled, num_rejected,
350
+ query_start_loc=None, boundary_capture=None, logits_only=False):
351
+ if not _layers:
352
+ return stock_pps(self, idx_mapping, sampled_token_ids, num_sampled, num_rejected,
353
+ query_start_loc, boundary_capture, logits_only=logits_only)
354
+ if _DEBUG and _shared.setdefault("debug_pps", 0) < 3:
355
+ _shared["debug_pps"] += 1
356
+ logger.info("GDN defer: postprocess_sampled hook (layers=%d, num_sampled type %s, capture %s, logits_only %s)", len(_layers), type(num_sampled).__name__, boundary_capture is not None, logits_only)
357
+ _tls.ctx = {"num_sampled": num_sampled, "idx_mapping": idx_mapping,
358
+ "runner": self, "done": False}
359
+ try:
360
+ out = stock_pps(self, idx_mapping, sampled_token_ids, num_sampled, num_rejected,
361
+ query_start_loc, boundary_capture, logits_only=logits_only)
362
+ if not _tls.ctx["done"]:
363
+ # postprocess_state was skipped (logits-only): reproduce stock columns.
364
+ _commit(force_full=True)
365
+ return out
366
+ finally:
367
+ _tls.ctx = None
368
+
369
+ Runner.postprocess_sampled = postprocess_sampled
370
+
371
+ if _ASYNC:
372
+ stock_exec = Runner.execute_model
373
+
374
+ def execute_model(self, *args, **kwargs):
375
+ # Before anything (state migration, staging, the forward) touches GDN state.
376
+ _join()
377
+ return stock_exec(self, *args, **kwargs)
378
+
379
+ Runner.execute_model = execute_model
380
+ logger.info("Qwen3.8 GDN commit runs on a side stream overlapping the draft phase.")
381
+
382
+ stock_capture = bc.BoundaryCheckpointState.capture_mamba
383
+
384
+ def capture_mamba(self, idx_mapping, capture, *, accepted_state_committed=False):
385
+ ctx = getattr(_tls, "ctx", None)
386
+ if ctx is not None and not ctx["done"]:
387
+ _commit(capture_tokens=capture[0])
388
+ return stock_capture(self, idx_mapping, capture,
389
+ accepted_state_committed=accepted_state_committed)
390
+
391
+ bc.BoundaryCheckpointState.capture_mamba = capture_mamba
392
+
393
+ stock_pstate = mh.MambaHybridModelState.postprocess_state
394
+
395
+ def postprocess_state(self, idx_mapping, num_sampled, num_computed_tokens=None):
396
+ ctx = getattr(_tls, "ctx", None)
397
+ if ctx is not None and not ctx["done"]:
398
+ _commit()
399
+ return stock_pstate(self, idx_mapping, num_sampled, num_computed_tokens)
400
+
401
+ mh.MambaHybridModelState.postprocess_state = postprocess_state
402
+ logger.info("Qwen3.8 GDN deferred commit installed (verify records + post-acceptance commit).")
recipe/patches/qwen4_exp/nvidia/hyperconnection.py ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+ """HyperConnection (Gated Residual) utilities — NVIDIA model variant.
4
+
5
+ Implements the HyperConnection residual scheme proposed in
6
+ "HyperConnections" (https://arxiv.org/abs/2409.19606). This NVIDIA variant
7
+ delays each HC combine to the following HC mix boundary. HC glue kernels,
8
+ including fused combine+RMSNorm, live in ``ops/hc.py``; projections remain
9
+ standard vLLM Linear modules.
10
+
11
+ Hidden states between layers have shape ``[..., HC*HS]`` with HS inner
12
+ (HC outer, HS inner — checkpoint-native layout).
13
+
14
+ Typical usage inside a transformer decoder layer::
15
+
16
+ self.attn_hc = GatedResidual(hc_config)
17
+
18
+ hidden_states, block_input, injection = self.attn_hc.mix(hidden_states)
19
+ attention_output = attention(block_input)
20
+ hidden_states, block_input, injection = self.mlp_hc.combine_and_mix(
21
+ hidden_states, attention_output, injection
22
+ )
23
+ """
24
+
25
+ from collections.abc import Iterable, Sequence
26
+ from typing import Any
27
+
28
+ import torch
29
+ from torch import nn
30
+
31
+ import vllm.envs as envs
32
+ from vllm.distributed import (
33
+ get_tensor_model_parallel_rank,
34
+ get_tensor_model_parallel_world_size,
35
+ tensor_model_parallel_all_gather,
36
+ )
37
+ from vllm.logger import init_logger
38
+ from vllm.model_executor.layers.linear import (
39
+ MergedColumnParallelLinear,
40
+ ReplicatedLinear,
41
+ )
42
+ from vllm.model_executor.models.utils import maybe_prefix
43
+ from vllm.model_executor.weight_transfer import copy_weight
44
+ from vllm.platforms import current_platform
45
+ from vllm.utils.b12x import (
46
+ B12xPreparationUnit,
47
+ B12xWorkload,
48
+ PreparationResourceUnavailableError,
49
+ get_b12x_hyperconnection,
50
+ set_b12x_preparation_provider,
51
+ )
52
+
53
+ from ..common.hyperconnection import (
54
+ GroupedGemmaRMSNorm,
55
+ HyperConnectionConfig,
56
+ )
57
+ from .ops.hc import (
58
+ grouped_gemma_rmsnorm,
59
+ hc_combine,
60
+ hc_combine_norm,
61
+ hc_gate_mix,
62
+ hc_silu,
63
+ )
64
+
65
+ logger = init_logger(__name__)
66
+
67
+ import os as _os
68
+
69
+ # Rowwise-FP8 copies of the mix weights for decode-sized calls (ops/hc_fp8_gemv.py).
70
+ _HC_FP8 = _os.environ.get("VLLM_QWEN38_HC_FP8", "0") == "1"
71
+ # With _HC_FP8 at decode: scaled SiLU fused into the down projection's split-K reduce
72
+ # and the gate mix fused into the up projection (two launches instead of four).
73
+ _HC_FUSE = _HC_FP8 and _os.environ.get("VLLM_QWEN38_HC_FUSE", "0") == "1"
74
+
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Gated-residual variant
78
+ # ---------------------------------------------------------------------------
79
+ def _hyperconnection_api() -> Any:
80
+ api = get_b12x_hyperconnection()
81
+ if api is None:
82
+ raise ImportError(
83
+ "Qwen4Exp requires b12x.norm.hyperconnection; "
84
+ "install the b12x serving extra"
85
+ )
86
+ return api
87
+
88
+
89
+ class _ShardedDownProjection(ReplicatedLinear):
90
+ """Local bottleneck rows with replicated injection rows in one GEMM."""
91
+
92
+ def __init__(
93
+ self,
94
+ config: HyperConnectionConfig,
95
+ tp_rank: int,
96
+ tp_size: int,
97
+ use_combine: bool,
98
+ prefix: str,
99
+ ) -> None:
100
+ self.hc_tp_rank = tp_rank
101
+ self.full_lowrank = config.hc_lowrank
102
+ self.local_lowrank = config.hc_lowrank // tp_size
103
+ self.injection_size = config.hc_count if use_combine else 0
104
+ rows = self.local_lowrank + self.injection_size
105
+ self.padding = -rows % 16
106
+ super().__init__(
107
+ config.hc_count * config.hidden_size,
108
+ rows + self.padding,
109
+ bias=False,
110
+ params_dtype=config.params_dtype,
111
+ return_bias=False,
112
+ disable_tp=True,
113
+ prefix=prefix,
114
+ )
115
+
116
+ def weight_loader(
117
+ self,
118
+ param: torch.Tensor,
119
+ loaded_weight: torch.Tensor,
120
+ shard_id: int | None = None,
121
+ ) -> None:
122
+ if shard_id is None and self.injection_size:
123
+ self.weight_loader(param, loaded_weight[: self.full_lowrank], 0)
124
+ self.weight_loader(
125
+ param,
126
+ loaded_weight[
127
+ self.full_lowrank : self.full_lowrank + self.injection_size
128
+ ],
129
+ 1,
130
+ )
131
+ return
132
+ if shard_id in (None, 0):
133
+ assert loaded_weight.shape == (self.full_lowrank, self.input_size)
134
+ source = loaded_weight.narrow(
135
+ 0, self.hc_tp_rank * self.local_lowrank, self.local_lowrank
136
+ )
137
+ destination = param.data[: self.local_lowrank]
138
+ elif shard_id == 1 and self.injection_size:
139
+ assert loaded_weight.shape == (self.injection_size, self.input_size)
140
+ source = loaded_weight
141
+ destination = param.data[
142
+ self.local_lowrank : self.local_lowrank + self.injection_size
143
+ ]
144
+ else:
145
+ raise ValueError(f"Invalid HC projection shard: {shard_id}")
146
+ copy_weight(destination, source)
147
+ if self.padding and shard_id != 1:
148
+ copy_weight(
149
+ param.data[-self.padding :],
150
+ torch.zeros(
151
+ self.padding, self.input_size, dtype=param.dtype, device="cpu"
152
+ ),
153
+ )
154
+
155
+ def load_weights(
156
+ self, weights: Iterable[tuple[str, torch.Tensor]]
157
+ ) -> Iterable[str]:
158
+ for name, weight in weights:
159
+ if name != "weight":
160
+ raise ValueError(f"Unexpected HC projection parameter: {name}")
161
+ self.weight_loader(self.weight, weight, getattr(weight, "shard_id", None))
162
+ yield name
163
+
164
+
165
+ class _ShardedUpProjection(ReplicatedLinear):
166
+ """Shard features within every residual stream, preserving stream order."""
167
+
168
+ def __init__(
169
+ self, config: HyperConnectionConfig, tp_rank: int, tp_size: int, prefix: str
170
+ ) -> None:
171
+ self.hc_tp_rank = tp_rank
172
+ self.streams = config.hc_count
173
+ self.full_hidden = config.hidden_size
174
+ self.local_hidden = config.hidden_size // tp_size
175
+ super().__init__(
176
+ config.hc_lowrank,
177
+ self.streams * self.local_hidden,
178
+ bias=False,
179
+ params_dtype=config.params_dtype,
180
+ return_bias=False,
181
+ disable_tp=True,
182
+ prefix=prefix,
183
+ )
184
+
185
+ def weight_loader(self, param: torch.Tensor, loaded_weight: torch.Tensor) -> None:
186
+ assert loaded_weight.shape == (
187
+ self.streams * self.full_hidden,
188
+ self.input_size,
189
+ )
190
+ # Separate views retain b12x's file-backed source ranges during loading.
191
+ for stream in range(self.streams):
192
+ source = loaded_weight.narrow(
193
+ 0,
194
+ stream * self.full_hidden + self.hc_tp_rank * self.local_hidden,
195
+ self.local_hidden,
196
+ )
197
+ destination = param.data.narrow(
198
+ 0, stream * self.local_hidden, self.local_hidden
199
+ )
200
+ copy_weight(destination, source)
201
+
202
+
203
+ class HyperConnectionWorkspace(nn.Module):
204
+ """Fixed-capacity storage shared by all HC modules in one model."""
205
+
206
+ def __init__(self, config: HyperConnectionConfig, max_tokens: int) -> None:
207
+ super().__init__()
208
+ if not config.hc_per_branch_norm:
209
+ raise NotImplementedError(
210
+ "Qwen4Exp requires one RMSNorm group per HC stream"
211
+ )
212
+ self.config = config
213
+ self.max_tokens = int(max_tokens)
214
+ self.device = torch.device(current_platform.current_device())
215
+ tp_size = get_tensor_model_parallel_world_size()
216
+ self.tp_size = (
217
+ tp_size
218
+ if envs.VLLM_QWEN3_8_FLASH_NEXT_HC_TP
219
+ and current_platform.is_cuda()
220
+ and config.params_dtype == torch.bfloat16
221
+ and config.hc_lowrank % tp_size == 0
222
+ and config.hidden_size % tp_size == 0
223
+ else 1
224
+ )
225
+ self.tp_rank = get_tensor_model_parallel_rank() if self.tp_size > 1 else 0
226
+ width = config.hc_count * config.hidden_size
227
+ factory = dict(device=self.device, dtype=config.params_dtype)
228
+ self.register_buffer(
229
+ "normalized", torch.empty(max_tokens, width, **factory), persistent=False
230
+ )
231
+ self.register_buffer(
232
+ "bottleneck",
233
+ torch.empty(max_tokens, config.hc_lowrank, **factory),
234
+ persistent=False,
235
+ )
236
+ self.register_buffer(
237
+ "block_input",
238
+ torch.empty(max_tokens, config.hidden_size, **factory),
239
+ persistent=False,
240
+ )
241
+ if self.tp_size > 1:
242
+ local_hidden = config.hidden_size // self.tp_size
243
+ local_lowrank = config.hc_lowrank // self.tp_size
244
+ logger.info_once(
245
+ "Sharding HyperConnection projections across %d ranks: "
246
+ "local bottleneck=%d, features/stream=%d; FP32 down outputs.",
247
+ self.tp_size,
248
+ local_lowrank,
249
+ local_hidden,
250
+ )
251
+ down_width = local_lowrank + config.hc_count
252
+ down_width += -down_width % 16
253
+ for name, columns in (
254
+ ("local_normalized", config.hc_count * local_hidden),
255
+ ("local_bottleneck", local_lowrank),
256
+ ("local_block_input", local_hidden),
257
+ ("local_gates", config.hc_count * local_hidden),
258
+ ("local_down", down_width),
259
+ ):
260
+ self.register_buffer(
261
+ name, torch.empty(max_tokens, columns, **factory), persistent=False
262
+ )
263
+ self.register_buffer(
264
+ "local_down_fp32",
265
+ torch.empty(
266
+ max_tokens, down_width, device=self.device, dtype=torch.float32
267
+ ),
268
+ persistent=False,
269
+ )
270
+
271
+ def caps(self, max_tokens: int, *, local: bool = False):
272
+ api = _hyperconnection_api()
273
+ partitions = self.tp_size if local else 1
274
+ return api.Caps(
275
+ device=self.device,
276
+ max_tokens=max_tokens,
277
+ hidden_size=self.config.hidden_size // partitions,
278
+ streams=self.config.hc_count,
279
+ lowrank=self.config.hc_lowrank // partitions,
280
+ dtype=self.config.params_dtype,
281
+ )
282
+
283
+ def bind(self, plan, tokens: int, *, local: bool = False):
284
+ return _hyperconnection_api().bind(
285
+ plan,
286
+ normalized=self.local_normalized if local else self.normalized,
287
+ bottleneck=self.local_bottleneck if local else self.bottleneck,
288
+ block_input=self.local_block_input if local else self.block_input,
289
+ tokens=tokens,
290
+ )
291
+
292
+
293
+ class GatedResidual(nn.Module):
294
+ """Gated HyperConnection with learnable low-rank mixing and injection.
295
+
296
+ ``combine_and_mix()`` runs the pre pipeline (grouped GemmaRMSNorm -> merged
297
+ low-rank down+inject GEMM -> silu -> up GEMM -> sigmoid -> gated mean
298
+ over the HC streams). When passed a pending block output, it fuses its
299
+ residual combine with the RMSNorm. A missing injection selects unit-weight
300
+ combine. Final mixers use ``use_combine=False`` and do not produce a new
301
+ injection.
302
+
303
+ TP shards bottleneck rows and output features within each stream, with a
304
+ gather at each boundary. The down GEMM returns FP32 before its BF16 cast.
305
+ Norm and residual state remain replicated; TP1 uses standard Linear dispatch.
306
+ """
307
+
308
+ def __init__(
309
+ self,
310
+ config: HyperConnectionConfig,
311
+ use_combine: bool = True,
312
+ prefix: str = "",
313
+ *,
314
+ workspace: HyperConnectionWorkspace | None = None,
315
+ ) -> None:
316
+ super().__init__()
317
+ self.config = config
318
+ self.tp_size = workspace.tp_size if workspace is not None else 1
319
+ self.tp_rank = workspace.tp_rank if workspace is not None else 0
320
+ self.lora_rank = config.hc_lowrank // self.tp_size
321
+ self.hc_count = config.hc_count
322
+ self.hidden_size = config.hidden_size
323
+ self.use_combine = use_combine
324
+
325
+ norm_size = (
326
+ self.hyper_hidden_size if config.hc_per_branch_norm else config.hidden_size
327
+ )
328
+ group_size = config.hidden_size if config.hc_per_branch_norm else None
329
+ # Normalize each H-sized HC stream independently while retaining a
330
+ # separate affine weight for every element of the HC*H layout.
331
+ self.hc_norm = GroupedGemmaRMSNorm(
332
+ norm_size,
333
+ eps=config.rms_norm_eps,
334
+ group_size=group_size,
335
+ dtype=config.params_dtype,
336
+ )
337
+
338
+ # -- vLLM Linear weights --------------------------------------------
339
+ # The merged skinny-GEMM shape is physically padded to 16 rows to ensure
340
+ # good alignment and performant implementation chosen by CuBLAS heuristics.
341
+ self.pad_size = (-(self.lora_rank + self.hc_count)) % 16 if use_combine else 0
342
+ if self.tp_size > 1:
343
+ name = (
344
+ "input_mix_weight_down_block_inject"
345
+ if use_combine
346
+ else "input_mix_weight_down"
347
+ )
348
+ setattr(
349
+ self,
350
+ name,
351
+ _ShardedDownProjection(
352
+ config,
353
+ self.tp_rank,
354
+ self.tp_size,
355
+ use_combine,
356
+ maybe_prefix(prefix, name),
357
+ ),
358
+ )
359
+ elif use_combine:
360
+ self.input_mix_weight_down_block_inject = MergedColumnParallelLinear(
361
+ self.hyper_hidden_size,
362
+ [self.lora_rank, self.hc_count]
363
+ + ([self.pad_size] if self.pad_size else []),
364
+ bias=False,
365
+ params_dtype=config.params_dtype,
366
+ quant_config=None,
367
+ prefix=maybe_prefix(prefix, "input_mix_weight_down_block_inject"),
368
+ return_bias=False,
369
+ disable_tp=True,
370
+ )
371
+ else:
372
+ self.input_mix_weight_down = ReplicatedLinear(
373
+ self.hyper_hidden_size,
374
+ self.lora_rank,
375
+ bias=False,
376
+ params_dtype=config.params_dtype,
377
+ quant_config=None,
378
+ prefix=maybe_prefix(prefix, "input_mix_weight_down"),
379
+ return_bias=False,
380
+ )
381
+ self.input_mix_weight_up = (
382
+ _ShardedUpProjection(
383
+ config,
384
+ self.tp_rank,
385
+ self.tp_size,
386
+ maybe_prefix(prefix, "input_mix_weight_up"),
387
+ )
388
+ if self.tp_size > 1
389
+ else ReplicatedLinear(
390
+ self.lora_rank,
391
+ self.hyper_hidden_size,
392
+ bias=False,
393
+ params_dtype=config.params_dtype,
394
+ quant_config=None,
395
+ prefix=maybe_prefix(prefix, "input_mix_weight_up"),
396
+ return_bias=False,
397
+ )
398
+ )
399
+ object.__setattr__(self, "_workspace", workspace)
400
+ self._preparation_prefix = prefix or "qwen4_exp.hyperconnection"
401
+ self._plans: dict[str, object] = {}
402
+ if workspace is not None and not getattr(
403
+ self, "b12x_preparation_suppressed", False
404
+ ):
405
+ set_b12x_preparation_provider(self, self)
406
+
407
+ def mix(self, hidden_states: torch.Tensor):
408
+ if self.workspace is not None:
409
+ normalized = _hyperconnection_api().run_grouped_rmsnorm(
410
+ hidden_states,
411
+ self.hc_norm.weight,
412
+ eps=self.config.rms_norm_eps,
413
+ binding=self._binding(hidden_states, "grouped_rmsnorm"),
414
+ )
415
+ else:
416
+ normalized = grouped_gemma_rmsnorm(
417
+ hidden_states,
418
+ self.hc_norm.weight,
419
+ self.config.rms_norm_eps,
420
+ self.hc_count,
421
+ )
422
+ block_input, injection = self._mix_normalized(normalized)
423
+ return hidden_states, block_input, injection
424
+
425
+ def combine_and_mix(self, hidden_states, prev_block_output, prev_injection):
426
+ if prev_block_output is None:
427
+ return self.mix(hidden_states)
428
+ if self.workspace is not None and prev_injection is not None:
429
+ combined, normalized = _hyperconnection_api().run_combine_norm(
430
+ hidden_states,
431
+ prev_block_output,
432
+ prev_injection,
433
+ self.hc_norm.weight,
434
+ eps=self.config.rms_norm_eps,
435
+ plan=self._plan_for("combine_norm"),
436
+ )
437
+ else:
438
+ combined, normalized = hc_combine_norm(
439
+ hidden_states,
440
+ prev_block_output,
441
+ prev_injection,
442
+ self.hc_norm.weight,
443
+ self.config.rms_norm_eps,
444
+ self.hc_count,
445
+ )
446
+ block_input, injection = self._mix_normalized(normalized)
447
+ return combined, block_input, injection
448
+
449
+ def combine(self, hidden_states, block_output, injection):
450
+ if self.workspace is not None and injection is not None:
451
+ return _hyperconnection_api().run_combine(
452
+ hidden_states,
453
+ block_output,
454
+ injection,
455
+ plan=self._plan_for("combine"),
456
+ )
457
+ return hc_combine(hidden_states, block_output, injection, self.hc_count)
458
+
459
+ @property
460
+ def hyper_hidden_size(self) -> int:
461
+ return self.hc_count * self.hidden_size
462
+
463
+ def _request_name(self, operation: str, tokens: int) -> str:
464
+ return f"{self._preparation_prefix}.hc.{operation}.m{tokens}"
465
+
466
+ def get_b12x_preparation_units(
467
+ self, layer: torch.nn.Module, workload: B12xWorkload
468
+ ) -> Sequence[B12xPreparationUnit]:
469
+ if layer is not self:
470
+ raise ValueError("HC preparation owner mismatch")
471
+ if self.workspace is None or workload.stage != "weights":
472
+ return ()
473
+ if self.hc_norm.weight.is_meta:
474
+ return ()
475
+ if workload.max_tokens > self.workspace.max_tokens:
476
+ raise PreparationResourceUnavailableError(
477
+ f"{self._preparation_prefix} HC workspace capacity "
478
+ f"{self.workspace.max_tokens} cannot serve {workload.max_tokens}"
479
+ )
480
+ api = _hyperconnection_api()
481
+ operations = (
482
+ "grouped_rmsnorm",
483
+ "scaled_silu",
484
+ "gate_mean",
485
+ "combine",
486
+ "combine_norm",
487
+ )
488
+ requests = []
489
+ tokens = workload.max_tokens
490
+ plans = {}
491
+ for operation in operations:
492
+ local = self.tp_size > 1 and operation in ("scaled_silu", "gate_mean")
493
+ plan = api.plan(
494
+ self.workspace.caps(tokens, local=True)
495
+ if local
496
+ else self.workspace.caps(tokens),
497
+ invocation={"operation": operation, "eps": self.config.rms_norm_eps},
498
+ )
499
+ plans[operation] = plan
500
+ requests.append(
501
+ plan.request(
502
+ name=self._request_name(operation, tokens),
503
+ prepare_call=self._prepare_call(operation, tokens),
504
+ benchmark_call=self._benchmark_call(operation, tokens),
505
+ )
506
+ )
507
+ self._plans = plans
508
+ return (
509
+ B12xPreparationUnit(
510
+ name="HYPERCONNECTION",
511
+ key=(self._preparation_prefix, tokens),
512
+ requests=tuple(requests),
513
+ stage="weights",
514
+ ),
515
+ )
516
+
517
+ def _prepare_call(self, operation: str, tokens: int):
518
+ """Prime the installed operation against its durable workspace."""
519
+ return self._call_factory(operation, tokens, benchmark=False)
520
+
521
+ def _benchmark_call(self, operation: str, tokens: int):
522
+ """Measure an isolated binding; it is never retained for serving."""
523
+ return self._call_factory(operation, tokens, benchmark=True)
524
+
525
+ def _call_factory(self, operation: str, tokens: int, *, benchmark: bool):
526
+ def prepare(state):
527
+ from b12x.norm.hyperconnection import _impl
528
+ from b12x.preparation import PreparedCall
529
+
530
+ factory = dict(device=self.workspace.device, dtype=self.config.params_dtype)
531
+ local = self.tp_size > 1 and operation in ("scaled_silu", "gate_mean")
532
+ hidden_size = (
533
+ self.hidden_size // self.tp_size if local else self.hidden_size
534
+ )
535
+ width = self.hc_count * hidden_size
536
+ activation_inputs = []
537
+ activation_owners: list[torch.Tensor] = []
538
+ owners: tuple[torch.Tensor, ...]
539
+
540
+ def activation(shape):
541
+ value = torch.empty(shape, **factory)
542
+ template = torch.arange(value.numel(), **factory).reshape(shape)
543
+ template.div_(max(value.numel(), 1))
544
+ activation_inputs.append((value, template))
545
+ activation_owners.extend((value, template))
546
+ return value
547
+
548
+ def produce():
549
+ for value, template in activation_inputs:
550
+ value.copy_(template)
551
+
552
+ # Serving priming writes its owned workspace. Trials instead own
553
+ # independent output storage so no measured binding can escape.
554
+ def output(shape, serving):
555
+ return torch.empty(shape, **factory) if benchmark else serving
556
+
557
+ if operation == "grouped_rmsnorm":
558
+ source = activation((tokens, width))
559
+ out = output((tokens, width), self.workspace.normalized)
560
+ run = lambda: _impl.run_grouped_rmsnorm_impl(
561
+ source,
562
+ self.hc_norm.weight,
563
+ eps=self.config.rms_norm_eps,
564
+ plan=state,
565
+ out=out,
566
+ )
567
+ owners = (out,)
568
+ elif operation == "scaled_silu":
569
+ source = activation((tokens, self.lora_rank))
570
+ out = output(
571
+ (tokens, self.lora_rank),
572
+ self.workspace.local_bottleneck
573
+ if local
574
+ else self.workspace.bottleneck,
575
+ )
576
+ run = lambda: _impl.run_scaled_silu_impl(source, plan=state, out=out)
577
+ owners = (out,)
578
+ elif operation == "gate_mean":
579
+ source = activation((tokens, width))
580
+ gates = activation((tokens, width))
581
+ out = output(
582
+ (tokens, hidden_size),
583
+ self.workspace.local_block_input
584
+ if local
585
+ else self.workspace.block_input,
586
+ )
587
+ run = lambda: _impl.run_gate_mean_impl(
588
+ source, gates, plan=state, out=out
589
+ )
590
+ owners = (out,)
591
+ else:
592
+ hidden = activation((tokens, width))
593
+ block = activation((tokens, self.hidden_size))
594
+ injection = activation((tokens, self.hc_count))
595
+ if operation == "combine":
596
+ run = lambda: _impl.run_combine_impl(
597
+ hidden,
598
+ block,
599
+ injection,
600
+ plan=state,
601
+ )
602
+ else:
603
+ run = lambda: _impl.run_combine_norm_impl(
604
+ hidden,
605
+ block,
606
+ injection,
607
+ self.hc_norm.weight,
608
+ eps=self.config.rms_norm_eps,
609
+ plan=state,
610
+ )
611
+ owners = ()
612
+ return PreparedCall(
613
+ run=run,
614
+ produce=produce,
615
+ owners=(*activation_owners, *owners),
616
+ )
617
+
618
+ return prepare
619
+
620
+ def _plan_for(self, operation: str):
621
+ try:
622
+ return self._plans[operation]
623
+ except KeyError:
624
+ raise PreparationResourceUnavailableError(
625
+ f"{self._preparation_prefix} lacks a declared {operation} plan"
626
+ ) from None
627
+
628
+ @property
629
+ def workspace(self) -> HyperConnectionWorkspace:
630
+ return self._workspace
631
+
632
+ def _binding(self, hidden_states: torch.Tensor, operation: str):
633
+ if self.tp_size > 1 and operation in ("scaled_silu", "gate_mean"):
634
+ return self.workspace.bind(
635
+ self._plan_for(operation), hidden_states.shape[0], local=True
636
+ )
637
+ return self.workspace.bind(self._plan_for(operation), hidden_states.shape[0])
638
+
639
+ def _hc_fp8(self):
640
+ """Rowwise-FP8 (down_w8, down_scale, up_w8, up_scale), or None.
641
+
642
+ Built on the first call outside CUDA graph capture (the profile run),
643
+ so the copies are counted before the KV cache is sized.
644
+ """
645
+ cache = self.__dict__.get("_hc_fp8_cache")
646
+ if cache is not None or not _HC_FP8 or torch.cuda.is_current_stream_capturing():
647
+ return cache
648
+ from .ops.hc_fp8_gemv import quantize_rowwise_fp8
649
+
650
+ down = (
651
+ self.input_mix_weight_down_block_inject
652
+ if self.use_combine
653
+ else self.input_mix_weight_down
654
+ )
655
+ cache = (
656
+ *quantize_rowwise_fp8(down.weight),
657
+ *quantize_rowwise_fp8(self.input_mix_weight_up.weight),
658
+ )
659
+ self.__dict__["_hc_fp8_cache"] = cache
660
+ logger.info_once("Qwen3.8 HyperConnection mix uses rowwise-FP8 decode GEMVs.")
661
+ return cache
662
+
663
+ def _mix_normalized(self, normalized: torch.Tensor):
664
+ if self.tp_size > 1:
665
+ return self._mix_sharded(normalized)
666
+ api = _hyperconnection_api() if self.workspace is not None else None
667
+ fp8 = self._hc_fp8()
668
+ if fp8 is not None and normalized.shape[0] <= 16:
669
+ from .ops.hc_fp8_gemv import fp8_gemv
670
+ else:
671
+ fp8 = None
672
+ if fp8 is not None and _HC_FUSE and normalized.stride(1) == 1:
673
+ from .ops.hc_fp8_gemv import (
674
+ _FUSED_DOWN,
675
+ _MIX_CFG,
676
+ fp8_gemv_down_silu,
677
+ fp8_gemv_down_silu_fused,
678
+ fp8_gemv_gate_mix,
679
+ )
680
+
681
+ down = fp8_gemv_down_silu_fused if _FUSED_DOWN else fp8_gemv_down_silu
682
+ projected, bottleneck = down(
683
+ normalized, fp8[0], fp8[1], self.lora_rank, self.hc_count
684
+ )
685
+ injection = (
686
+ projected[:, self.lora_rank : self.lora_rank + self.hc_count]
687
+ if self.use_combine
688
+ else None
689
+ )
690
+ block_input = fp8_gemv_gate_mix(
691
+ bottleneck, fp8[2], fp8[3], normalized, self.hc_count, *_MIX_CFG
692
+ )
693
+ return block_input, injection
694
+ if fp8 is not None and self.use_combine:
695
+ down_and_injection = fp8_gemv(normalized, fp8[0], fp8[1])
696
+ projected_down = down_and_injection[:, : self.lora_rank]
697
+ injection = down_and_injection[
698
+ :, self.lora_rank : self.lora_rank + self.hc_count
699
+ ]
700
+ elif fp8 is not None:
701
+ projected_down = fp8_gemv(normalized, fp8[0], fp8[1])
702
+ injection = None
703
+ elif self.use_combine:
704
+ down_and_injection = self.input_mix_weight_down_block_inject(normalized)
705
+ projected_down = down_and_injection[:, : self.lora_rank]
706
+ injection_start = self.lora_rank
707
+ # The projection owner stays live through the downstream residual
708
+ # combine; readers consume row-strided slices without staging.
709
+ injection = down_and_injection[
710
+ :, injection_start : injection_start + self.hc_count
711
+ ]
712
+ else:
713
+ projected_down = self.input_mix_weight_down(normalized)
714
+ injection = None
715
+
716
+ bottleneck = (
717
+ api.run_scaled_silu(
718
+ projected_down, binding=self._binding(normalized, "scaled_silu")
719
+ )
720
+ if api is not None
721
+ else hc_silu(projected_down, self.hc_count)
722
+ )
723
+ gate_logits = (
724
+ fp8_gemv(bottleneck, fp8[2], fp8[3])
725
+ if fp8 is not None
726
+ else self.input_mix_weight_up(bottleneck)
727
+ )
728
+ block_input = (
729
+ api.run_gate_mean(
730
+ normalized, gate_logits, binding=self._binding(normalized, "gate_mean")
731
+ )
732
+ if api is not None
733
+ else hc_gate_mix(normalized, gate_logits, self.hc_count)
734
+ )
735
+ return block_input, injection
736
+
737
+ def _mix_sharded(self, normalized: torch.Tensor):
738
+ api = _hyperconnection_api()
739
+ workspace = self.workspace
740
+ tokens = normalized.shape[0]
741
+ down = (
742
+ self.input_mix_weight_down_block_inject
743
+ if self.use_combine
744
+ else self.input_mix_weight_down
745
+ )
746
+ width = down.weight.shape[0]
747
+ projected_fp32 = workspace.local_down_fp32.view(-1)[: tokens * width].view(
748
+ tokens, width
749
+ )
750
+ torch.mm(normalized, down.weight.T, out=projected_fp32, out_dtype=torch.float32)
751
+ projected = workspace.local_down.view(-1)[: tokens * width].view(tokens, width)
752
+ projected.copy_(projected_fp32)
753
+ injection = (
754
+ projected[:, self.lora_rank : self.lora_rank + self.hc_count]
755
+ if self.use_combine
756
+ else None
757
+ )
758
+ bottleneck = api.run_scaled_silu(
759
+ projected[:, : self.lora_rank],
760
+ binding=self._binding(normalized, "scaled_silu"),
761
+ )
762
+ bottleneck = tensor_model_parallel_all_gather(bottleneck, dim=-1)
763
+ logits = workspace.local_gates[:tokens]
764
+ torch.mm(bottleneck, self.input_mix_weight_up.weight.T, out=logits)
765
+ local_hidden = self.hidden_size // self.tp_size
766
+ local_normalized = workspace.local_normalized[:tokens]
767
+ local_normalized.view(tokens, self.hc_count, local_hidden).copy_(
768
+ normalized.view(tokens, self.hc_count, self.hidden_size)[
769
+ :, :, self.tp_rank * local_hidden : (self.tp_rank + 1) * local_hidden
770
+ ]
771
+ )
772
+ block_input = api.run_gate_mean(
773
+ local_normalized, logits, binding=self._binding(normalized, "gate_mean")
774
+ )
775
+ return tensor_model_parallel_all_gather(block_input, dim=-1), injection
776
+
777
+
778
+ __all__ = [
779
+ "GatedResidual",
780
+ "GroupedGemmaRMSNorm",
781
+ "HyperConnectionConfig",
782
+ ]
recipe/patches/qwen4_exp/nvidia/logit_dump.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Full-vocabulary prompt-logit dump for KLD measurement (VLLM_QWEN38_LOGIT_DUMP=<dir>).
3
+
4
+ Use the forward-side dump (maybe_dump_forward, armed by <dir>/ARM) for measurements:
5
+ on this stack a request that asks for prompt logprobs gets a different, run-to-run
6
+ non-deterministic forward, so the prompt-logprob hook below is kept for diagnosis only.
7
+
8
+ For every request that asks for prompt logprobs, each prefill chunk's logits (the
9
+ same ``compute_logits`` the prompt-logprob path uses) are appended as float32 rows to
10
+ ``<dir>/<req_id>.bin``, with ``<first position> <rows> <vocab>`` per chunk in
11
+ ``<req_id>.idx``. Row j of a chunk holds the logits at prompt position first + j
12
+ (the distribution over the token at first + j + 1). Measurement only: it copies
13
+ every row to the host.
14
+ """
15
+
16
+ import os
17
+ import re
18
+
19
+ import numpy as np
20
+ import torch
21
+
22
+ from vllm.logger import init_logger
23
+
24
+ logger = init_logger(__name__)
25
+
26
+ _ROWS_PER_CALL = 512
27
+ _DIAG = os.environ.get("VLLM_QWEN38_LOGIT_DUMP_DIAG", "0") == "1"
28
+ _last_forward: dict = {}
29
+
30
+
31
+ def note_forward_output(hidden: torch.Tensor) -> None:
32
+ """Diagnostic: snapshot the model's final hidden states as the forward returns them."""
33
+ if _DIAG and not torch.cuda.is_current_stream_capturing() and hidden.shape[0] > 16:
34
+ _last_forward["hs"] = hidden.detach().clone()
35
+
36
+
37
+ def _diagnose(logits_fn, hidden_states, input_batch) -> None:
38
+ n = int(input_batch.num_tokens)
39
+ hs = hidden_states[:n]
40
+ snap = _last_forward.get("hs")
41
+ msg = [f"rows {n}"]
42
+ if snap is not None and snap.shape[0] >= n:
43
+ d = (hs.float() - snap[:n].float()).abs()
44
+ msg.append(f"hidden vs forward-end snapshot: max {d.max().item():.3e} rows changed "
45
+ f"{int((d.amax(-1) > 0).sum())}/{n}")
46
+ with torch.inference_mode():
47
+ one_a = logits_fn(hs[n - 1:n]).float()
48
+ one_b = logits_fn(hs[n - 1:n]).float()
49
+ chunk = logits_fn(hs[max(0, n - 512):n]).float()[-1:]
50
+ msg.append(f"last row: M=1 repeat max|d| {(one_a - one_b).abs().max().item():.3e}, "
51
+ f"M=1 vs M={min(n, 512)} max|d| {(one_a - chunk).abs().max().item():.3e}")
52
+ if snap is not None and snap.shape[0] >= n:
53
+ one_s = logits_fn(snap[n - 1:n]).float()
54
+ msg.append(f"M=1 on snapshot vs current max|d| {(one_a - one_s).abs().max().item():.3e}")
55
+ logger.info("LOGIT DIAG: %s", " | ".join(msg))
56
+
57
+
58
+ def maybe_dump_forward(out_dir, compute_logits, hidden: torch.Tensor, positions: torch.Tensor) -> None:
59
+ """Forward-side dump, independent of prompt logprobs: while ``<out_dir>/ARM`` exists
60
+ (its text: dump name, optional first position to keep), every eager forward of more than 16 rows appends its
61
+ full-vocab float32 logits to ``<out_dir>/<name>.bin`` and ``<first position> <rows>
62
+ <vocab>`` to ``<name>.idx``. Arm only around one request at a time."""
63
+ if hidden.shape[0] <= 16 or torch.cuda.is_current_stream_capturing():
64
+ return
65
+ arm = os.path.join(out_dir, "ARM")
66
+ if not os.path.exists(arm):
67
+ return
68
+ words = open(arm).read().split()
69
+ name = re.sub(r"[^A-Za-z0-9_.-]", "_", words[0] if words else "") or "dump"
70
+ min_pos = int(words[1]) if len(words) > 1 else 0
71
+ path = os.path.join(out_dir, name)
72
+ pos = positions[0] if positions.dim() == 2 else positions
73
+ first = int(pos[0].item())
74
+ skip = min(hidden.shape[0], max(0, min_pos - first))
75
+ if skip == hidden.shape[0]:
76
+ return
77
+ hidden, first = hidden[skip:], first + skip
78
+ rows = hidden.shape[0]
79
+ vocab = 0
80
+ with torch.inference_mode(), open(path + ".bin", "ab") as f:
81
+ for s in range(0, rows, _ROWS_PER_CALL):
82
+ logits = compute_logits(hidden[s:s + _ROWS_PER_CALL]).float()
83
+ vocab = logits.shape[-1]
84
+ logits.cpu().numpy().tofile(f)
85
+ with open(path + ".idx", "a") as f:
86
+ f.write(f"{first} {rows} {vocab}\n")
87
+
88
+
89
+ def install(out_dir: str) -> None:
90
+ from vllm.v1.worker.gpu.sample import prompt_logprob as pl
91
+
92
+ Worker = pl.PromptLogprobsWorker
93
+ if getattr(Worker, "_qwen38_logit_dump", False):
94
+ return
95
+ os.makedirs(out_dir, exist_ok=True)
96
+ stock = Worker.compute_prompt_logprobs
97
+
98
+ def compute_prompt_logprobs(self, logits_fn, hidden_states, input_batch, all_token_ids,
99
+ num_computed_tokens, prompt_lens):
100
+ need = self.uses_prompt_logprobs[input_batch.idx_mapping_np]
101
+ if np.any(need) and _DIAG:
102
+ _diagnose(logits_fn, hidden_states, input_batch)
103
+ if np.any(need):
104
+ qsl = input_batch.query_start_loc_np
105
+ first = input_batch.num_computed_prefill_tokens_np
106
+ for i, req_id in enumerate(input_batch.req_ids):
107
+ start, end = int(qsl[i]), int(qsl[i + 1])
108
+ if not need[i] or start >= end:
109
+ continue
110
+ path = os.path.join(out_dir, re.sub(r"[^A-Za-z0-9_.-]", "_", req_id))
111
+ with torch.inference_mode(), open(path + ".bin", "ab") as f:
112
+ for s in range(start, end, _ROWS_PER_CALL):
113
+ e = min(end, s + _ROWS_PER_CALL)
114
+ logits_fn(hidden_states[s:e]).float().cpu().numpy().tofile(f)
115
+ vocab = int(logits_fn(hidden_states[start:start + 1]).shape[-1])
116
+ with open(path + ".idx", "a") as f:
117
+ f.write(f"{int(first[i])} {end - start} {vocab}\n")
118
+ return stock(self, logits_fn, hidden_states, input_batch, all_token_ids,
119
+ num_computed_tokens, prompt_lens)
120
+
121
+ Worker.compute_prompt_logprobs = compute_prompt_logprobs
122
+ Worker._qwen38_logit_dump = True
123
+ logger.info("Qwen3.8 prompt-logit dump to %s (measurement only).", out_dir)
recipe/patches/qwen4_exp/nvidia/model.py ADDED
@@ -0,0 +1,1310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+ """Inference-only Qwen4Exp model."""
4
+
5
+ from collections.abc import Callable, Iterable
6
+ from itertools import islice
7
+
8
+ import torch
9
+ from torch import nn
10
+
11
+ from vllm import envs
12
+ from vllm.config import VllmConfig
13
+ from vllm.distributed import get_pp_group
14
+ from vllm.model_executor.layers.fused_moe.utils import (
15
+ is_model_fused_shared_expert_compatible,
16
+ )
17
+ from vllm.model_executor.layers.logits_processor import LogitsProcessor
18
+ import os as _os
19
+
20
+ from vllm.logger import init_logger
21
+
22
+ logger = init_logger(__name__)
23
+
24
+ _FP8_LM_HEAD = _os.environ.get("VLLM_QWEN38_FP8_LM_HEAD", "0") == "1"
25
+ _SMALL_GEMV = _os.environ.get("VLLM_QWEN38_SMALL_GEMV", "0") == "1"
26
+ # Rowwise-FP8 decode GEMVs for the target's remaining plain bf16 projections
27
+ # (PLE key/value projections; ~65 MB per step through sm80 WMMA fallbacks).
28
+ _TARGET_FP8 = _os.environ.get("VLLM_QWEN38_TARGET_FP8", "0") == "1"
29
+ # Fused shared-expert tail at decode: SiLU*mul prologue + MXFP8 down GEMV +
30
+ # sigmoid(shared_expert_gate) epilogue in one kernel (stock: 5-6 small kernels).
31
+ _SHARED_FUSE = _os.environ.get("VLLM_QWEN38_SHARED_FUSE", "0") == "1"
32
+ _LOGIT_DUMP_DIAG = _os.environ.get("VLLM_QWEN38_LOGIT_DUMP_DIAG", "0") == "1"
33
+ _LOGIT_DUMP_DIR = _os.environ.get("VLLM_QWEN38_LOGIT_DUMP", "")
34
+ # One-launch GemmaRMSNorm (QSA q_norm/k_norm, MTP norms): the stock residual-free
35
+ # path runs ~10 eager torch ops per call with compilation off.
36
+ if _os.environ.get("VLLM_QWEN38_GEMMA_NORM", "0") == "1":
37
+ from .ops.gemma_norm import install_gemma_norm
38
+
39
+ install_gemma_norm()
40
+ # Deferred-commit GDN verify: per-token records at verify, one post-acceptance commit.
41
+ if _os.environ.get("VLLM_QWEN38_GDN_DEFER", "0") == "1":
42
+ from .gdn_defer import install as _install_gdn_defer
43
+
44
+ _install_gdn_defer()
45
+ # Measurement only: full-vocab prompt logits to disk for KLD against a reference.
46
+ if _os.environ.get("VLLM_QWEN38_LOGIT_DUMP"):
47
+ from .logit_dump import install as _install_logit_dump
48
+
49
+ _install_logit_dump(_os.environ["VLLM_QWEN38_LOGIT_DUMP"])
50
+ from vllm.model_executor.layers.mamba.gdn.qwen_gdn_linear_attn import (
51
+ QwenGatedDeltaNetAttention,
52
+ )
53
+ from vllm.model_executor.layers.mamba.mamba_utils import (
54
+ MambaStateCopyFunc,
55
+ MambaStateCopyFuncCalculator,
56
+ MambaStateCopyFuncsByType,
57
+ MambaStateDtypeCalculator,
58
+ MambaStateShapeCalculator,
59
+ )
60
+ from vllm.model_executor.layers.quantization import QuantizationConfig
61
+ from vllm.model_executor.layers.vocab_parallel_embedding import (
62
+ ParallelLMHead,
63
+ VocabParallelEmbedding,
64
+ )
65
+ from vllm.model_executor.models.interfaces import (
66
+ HasInnerState,
67
+ IsHybrid,
68
+ MixtureOfExperts,
69
+ MultiModalEmbeddings,
70
+ SupportsLoRA,
71
+ SupportsMRoPE,
72
+ SupportsPP,
73
+ _require_is_multimodal,
74
+ )
75
+ from vllm.model_executor.models.qwen3_5 import (
76
+ Qwen3_5ForConditionalGeneration,
77
+ Qwen3_5Model,
78
+ )
79
+ from vllm.model_executor.models.qwen3_next import (
80
+ Qwen3NextAttention,
81
+ Qwen3NextMLP,
82
+ Qwen3NextSparseMoeBlock,
83
+ )
84
+ from vllm.model_executor.models.qwen3_vl import (
85
+ Qwen3_VisionTransformer,
86
+ Qwen3VLDummyInputsBuilder,
87
+ Qwen3VLMultiModalProcessor,
88
+ Qwen3VLProcessingInfo,
89
+ )
90
+ from vllm.model_executor.models.utils import (
91
+ AutoWeightsLoader,
92
+ StageMissingLayer,
93
+ WeightsMapper,
94
+ _merge_multimodal_embeddings,
95
+ extract_layer_index,
96
+ make_empty_intermediate_tensors_factory,
97
+ make_layers,
98
+ maybe_fuse_shared_experts,
99
+ maybe_prefix,
100
+ )
101
+ from vllm.multimodal import MULTIMODAL_REGISTRY
102
+ from vllm.multimodal.inputs import MultiModalFeatureSpec
103
+ from vllm.sequence import IntermediateTensors
104
+ from vllm.tokenizers.registry import cached_tokenizer_from_config
105
+ from vllm.transformers_utils.configs.qwen4_exp import (
106
+ Qwen4ExpTextConfig,
107
+ )
108
+ from vllm.v1.attention.backends.registry import MambaAttentionBackendEnum
109
+ from vllm.v1.kv_cache_interface import MambaSpec
110
+
111
+ from ..config import Qwen4ExpConfig
112
+ from .b12x_ple import B12xNGramEmbedding, _resolve_ple_table_memory
113
+ from .b12x_qsa import Qwen4ExpQSAAttention as B12xQSAAttention
114
+ from .backend import uses_b12x
115
+ from .hyperconnection import (
116
+ GatedResidual,
117
+ HyperConnectionConfig,
118
+ HyperConnectionWorkspace,
119
+ )
120
+ from .low_latency_gemm import enable_qwen4_exp_low_latency_gemm
121
+ from .ple_layer import Qwen4ExpPLELayer
122
+ from .qsa import Qwen4ExpQSAAttention
123
+
124
+
125
+ def _is_file_backed_ple_weight(name: str) -> bool:
126
+ _, marker, shard_suffix = name.rpartition(
127
+ ".ple.ple_embedding.ngram_embedding.shard_"
128
+ )
129
+ shard_index, separator, suffix = shard_suffix.partition(".")
130
+ return bool(
131
+ marker
132
+ and separator
133
+ and shard_index.isdigit()
134
+ and suffix in {"weight", "weight_scale"}
135
+ )
136
+
137
+
138
+ def without_modelopt_fp4(
139
+ quant_config: QuantizationConfig | None,
140
+ ) -> QuantizationConfig | None:
141
+ """Return ``None`` for weights excluded from Qwen4Exp ModelOpt-FP4."""
142
+
143
+ if quant_config is not None and quant_config.get_name() == "modelopt_fp4":
144
+ return None
145
+ return quant_config
146
+
147
+
148
+ def _remap_qsa_cache_scale_name(
149
+ name: str,
150
+ qsa_layer_ids: frozenset[int],
151
+ ) -> str:
152
+ """Map serialized main-cache scales onto the merged QSA owner.
153
+
154
+ Regular attention keeps cache scales below its ``attn`` child. QSA owns
155
+ that cache directly, so only QSA layers need the final path component
156
+ moved to the owner's persistent ``_k_scale``/``_v_scale`` buffers.
157
+ """
158
+
159
+ scale_suffixes = {
160
+ "k_proj.k_scale": "_k_scale",
161
+ "k_proj.output_scale": "_k_scale",
162
+ "attn.k_scale": "_k_scale",
163
+ "attn._k_scale": "_k_scale",
164
+ "k_scale": "_k_scale",
165
+ "_k_scale": "_k_scale",
166
+ "v_proj.v_scale": "_v_scale",
167
+ "v_proj.output_scale": "_v_scale",
168
+ "attn.v_scale": "_v_scale",
169
+ "attn._v_scale": "_v_scale",
170
+ "v_scale": "_v_scale",
171
+ "_v_scale": "_v_scale",
172
+ }
173
+ for layer_id in qsa_layer_ids:
174
+ marker = f"layers.{layer_id}.self_attn."
175
+ marker_start = name.find(marker)
176
+ if marker_start < 0 or (marker_start > 0 and name[marker_start - 1] != "."):
177
+ continue
178
+ suffix = name[marker_start + len(marker) :]
179
+ mapped_suffix = scale_suffixes.get(suffix)
180
+ if mapped_suffix is not None:
181
+ return f"{name[: marker_start + len(marker)]}{mapped_suffix}"
182
+ return name
183
+
184
+
185
+ _QWEN4_EXP_IGNORED_MISSING_SUFFIXES = [
186
+ ".bias",
187
+ "_bias",
188
+ ".k_scale",
189
+ "_k_scale",
190
+ ".v_scale",
191
+ "_v_scale",
192
+ "_weight_scale",
193
+ "_input_scale",
194
+ ]
195
+
196
+ # The checkpoint stores these projections separately; runtime packs each group
197
+ # into adjacent logical shards of a MergedColumnParallelLinear.
198
+ _EXTRA_WEIGHTS_MAPPER = WeightsMapper(
199
+ orig_to_new_stacked={
200
+ "hyper_connection.input_mix_weight_down.weight": (
201
+ "hyper_connection.input_mix_weight_down_block_inject.weight",
202
+ 0,
203
+ ),
204
+ "hyper_connection.block_inject_weight.weight": (
205
+ "hyper_connection.input_mix_weight_down_block_inject.weight",
206
+ 1,
207
+ ),
208
+ "ple.key_proj": ("ple.kv_proj", 0),
209
+ "ple.value_proj": ("ple.kv_proj", 1),
210
+ }
211
+ )
212
+
213
+
214
+ def _install_small_gemv_gate(gate: torch.nn.Module) -> None:
215
+ """Router GEMM fast path (VLLM_QWEN38_SMALL_GEMV=1). GB10 (sm_121) runs the
216
+ bf16 [tokens, 2560] x [512, 2560]^T router on an sm80 WMMA fallback at
217
+ ~27 us against a ~10 us bandwidth floor; decode-sized batches use the
218
+ split-K Triton GEMV instead. Overrides only this instance's forward, so
219
+ parameter names (gate.weight) and checkpoint loading are unchanged."""
220
+ if getattr(gate, "bias", None) is not None or not hasattr(gate, "weight"):
221
+ return
222
+ from vllm.models.qwen4_exp.nvidia.ops.small_gemv import small_mm
223
+
224
+ stock_forward = gate.forward
225
+
226
+ def forward(x: torch.Tensor):
227
+ if x.dim() == 2:
228
+ out = small_mm(x, gate.weight, x.dtype)
229
+ return out if getattr(gate, "return_bias", True) is False else (out, None)
230
+ return stock_forward(x)
231
+
232
+ gate.forward = forward
233
+
234
+
235
+ def _install_fused_shared_expert(mlp: torch.nn.Module) -> None:
236
+ """Decode rows (<= 16) of a shared-expert MLP with an MXFP8 b12x down_proj
237
+ and a sigmoid expert gate run gate_up + one fused kernel
238
+ (ops.mxfp8_gemv.mxfp8_gemv_swiglu_gate); anything else keeps the stock path."""
239
+ from vllm.models.qwen4_exp.nvidia.ops.mxfp8_gemv import mxfp8_gemv_swiglu_gate
240
+
241
+ stock = mlp.forward
242
+
243
+ def forward(x):
244
+ down = mlp.down_proj
245
+ packed = getattr(down, "b12x_mxfp8_packed_weight", None)
246
+ gate = getattr(mlp, "expert_gate", None)
247
+ if (
248
+ packed is None
249
+ or gate is None
250
+ or x.dim() != 2
251
+ or x.shape[0] > 16
252
+ or x.dtype != torch.bfloat16
253
+ or packed.weight.values.is_meta
254
+ or getattr(down, "bias", None) is not None
255
+ ):
256
+ return stock(x)
257
+ gate_up, _ = mlp.gate_up_proj(x)
258
+ return mxfp8_gemv_swiglu_gate(
259
+ gate_up,
260
+ x if x.stride(1) == 1 else x.contiguous(),
261
+ gate.weight.view(-1),
262
+ packed.weight.values,
263
+ packed.weight.scale_rows[0],
264
+ down.b12x_mxfp8_output_size,
265
+ packed.in_features,
266
+ )
267
+
268
+ mlp.forward = forward
269
+
270
+
271
+ class Qwen4ExpSparseMoeBlock(Qwen3NextSparseMoeBlock):
272
+ """Qwen3Next MoE with Qwen4Exp HC validation."""
273
+
274
+ def __init__(self, vllm_config: VllmConfig, prefix: str = "") -> None:
275
+ parallel_config = vllm_config.parallel_config
276
+ if parallel_config.use_sequence_parallel_moe:
277
+ raise NotImplementedError(
278
+ "Qwen4Exp HC does not support sequence-parallel MoE"
279
+ )
280
+ super().__init__(vllm_config=vllm_config, prefix=prefix)
281
+ config = vllm_config.model_config.hf_text_config
282
+ self.n_shared_experts = int(config.shared_expert_intermediate_size > 0)
283
+ if _SMALL_GEMV:
284
+ _install_small_gemv_gate(self.gate)
285
+ if _SHARED_FUSE and getattr(self, "shared_expert", None) is not None:
286
+ _install_fused_shared_expert(self.shared_expert)
287
+ if _os.environ.get("VLLM_QWEN38_EXPERT_STATS"):
288
+ from .expert_stats import install as _install_expert_stats
289
+
290
+ _install_expert_stats(self.gate, prefix, config.num_experts_per_tok,
291
+ config.num_experts, _os.environ["VLLM_QWEN38_EXPERT_STATS"])
292
+
293
+
294
+ class Qwen4ExpDecoderLayer(nn.Module):
295
+ def __init__(
296
+ self,
297
+ vllm_config: VllmConfig,
298
+ layer_type: str,
299
+ prefix: str = "",
300
+ *,
301
+ workspace: HyperConnectionWorkspace | None = None,
302
+ ) -> None:
303
+ super().__init__()
304
+ config: Qwen4ExpTextConfig = vllm_config.model_config.hf_text_config
305
+ model_config = vllm_config.model_config
306
+ cache_config = vllm_config.cache_config
307
+ quant_config = vllm_config.quant_config
308
+
309
+ self.config = config
310
+ self.layer_type = layer_type
311
+ self.layer_idx = extract_layer_index(prefix)
312
+ if vllm_config.parallel_config.use_sequence_parallel_moe:
313
+ raise NotImplementedError(
314
+ "Qwen4Exp HC does not support sequence-parallel MoE"
315
+ )
316
+ self.ple: Qwen4ExpPLELayer | None = None
317
+ ple_layer_ids = config.ple_layer_ids
318
+ if (self.layer_idx + 1) in ple_layer_ids:
319
+ ple_layer_ids_sorted = sorted(set(ple_layer_ids))
320
+ ple_dense_layer_id_map = {
321
+ abs_id: idx for idx, abs_id in enumerate(ple_layer_ids_sorted)
322
+ }
323
+ ple_dense_layer_id = ple_dense_layer_id_map[self.layer_idx + 1]
324
+ self.ple = Qwen4ExpPLELayer(
325
+ config,
326
+ vllm_config=vllm_config,
327
+ layer_idx=self.layer_idx,
328
+ ple_dense_layer_id=ple_dense_layer_id,
329
+ prefix=f"{prefix}.ple",
330
+ )
331
+
332
+ if layer_type == "linear_attention":
333
+ self.linear_attn = QwenGatedDeltaNetAttention(
334
+ config,
335
+ vllm_config=vllm_config,
336
+ prefix=f"{prefix}.linear_attn",
337
+ gqa_interleaved_layout=False,
338
+ overlap_input_projections=uses_b12x(vllm_config)
339
+ and envs.VLLM_QWEN3_8_FLASH_NEXT_OVERLAP,
340
+ )
341
+ elif layer_type == "full_attention":
342
+ use_qsa = getattr(config, "indexer_n_heads", None) is not None
343
+ if not use_qsa:
344
+ self.self_attn = Qwen3NextAttention(
345
+ config,
346
+ model_config=model_config,
347
+ cache_config=cache_config,
348
+ quant_config=quant_config,
349
+ prefix=f"{prefix}.self_attn",
350
+ )
351
+ else:
352
+ attention_cls = (
353
+ B12xQSAAttention if uses_b12x(vllm_config) else Qwen4ExpQSAAttention
354
+ )
355
+ self.self_attn = attention_cls(
356
+ vllm_config=vllm_config,
357
+ config=config,
358
+ layer_id=self.layer_idx,
359
+ quant_config=quant_config,
360
+ prefix=f"{prefix}.self_attn",
361
+ )
362
+ else:
363
+ raise ValueError(f"Invalid layer_type {layer_type}")
364
+
365
+ mlp_only_layers = getattr(config, "mlp_only_layers", [])
366
+ num_experts = getattr(config, "num_experts", 0) or 0
367
+ absolute_layer_id = self.layer_idx + 1
368
+ is_moe_layer = self.layer_idx not in mlp_only_layers and (
369
+ num_experts > 0 and absolute_layer_id % config.decoder_sparse_step == 0
370
+ )
371
+ if is_moe_layer:
372
+ self.mlp = Qwen4ExpSparseMoeBlock(
373
+ vllm_config=vllm_config, prefix=f"{prefix}.mlp"
374
+ )
375
+ else:
376
+ self.mlp = Qwen3NextMLP(
377
+ hidden_size=config.hidden_size,
378
+ intermediate_size=config.intermediate_size,
379
+ hidden_act=config.hidden_act,
380
+ quant_config=quant_config,
381
+ prefix=f"{prefix}.mlp",
382
+ )
383
+
384
+ hc_config = HyperConnectionConfig(
385
+ hc_count=config.hc_count,
386
+ hidden_size=config.hidden_size,
387
+ params_dtype=torch.bfloat16,
388
+ hc_lowrank=config.hc_lowrank,
389
+ rms_norm_eps=config.rms_norm_eps,
390
+ hc_per_branch_norm=True,
391
+ )
392
+ self.attn_hyper_connection = GatedResidual(
393
+ hc_config,
394
+ workspace=workspace,
395
+ prefix=maybe_prefix(prefix, "attn_hyper_connection"),
396
+ )
397
+ self.mlp_hyper_connection = GatedResidual(
398
+ hc_config,
399
+ workspace=workspace,
400
+ prefix=maybe_prefix(prefix, "mlp_hyper_connection"),
401
+ )
402
+
403
+ def forward(
404
+ self,
405
+ hidden_states: torch.Tensor,
406
+ prev_block_output: torch.Tensor | None,
407
+ prev_injection: torch.Tensor | None,
408
+ positions: torch.Tensor,
409
+ *,
410
+ input_ids: torch.Tensor | None,
411
+ query_start_loc: torch.Tensor | None,
412
+ ngram_context: torch.Tensor | None,
413
+ output_indices: torch.Tensor | None = None,
414
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
415
+ if prev_block_output is None:
416
+ assert prev_injection is None
417
+ attn_hc = self.attn_hyper_connection
418
+ if self.ple is not None:
419
+ # PLE adds directly to the multi-stream state, so pending HC state
420
+ # must be materialized before the addition.
421
+ if prev_block_output is not None:
422
+ hidden_states = attn_hc.combine(
423
+ hidden_states, prev_block_output, prev_injection
424
+ )
425
+ prev_block_output = prev_injection = None
426
+
427
+ if input_ids is None or query_start_loc is None or ngram_context is None:
428
+ raise RuntimeError("PLE inputs were not prepared")
429
+ hidden_states = self.ple(
430
+ hidden_states,
431
+ input_ids,
432
+ query_start_loc,
433
+ ngram_context,
434
+ )
435
+
436
+ # Fuse a pending combine with this HC module's mix when possible.
437
+ if prev_block_output is not None:
438
+ hidden_states, block_input, injection = attn_hc.combine_and_mix(
439
+ hidden_states, prev_block_output, prev_injection
440
+ )
441
+ else:
442
+ hidden_states, block_input, injection = attn_hc.mix(hidden_states)
443
+
444
+ if self.layer_type == "linear_attention":
445
+ attn_out = self.linear_attn(hidden_states=block_input)
446
+ elif self.layer_type == "full_attention":
447
+ attn_out = self.self_attn(
448
+ hidden_states=block_input,
449
+ positions=positions,
450
+ )
451
+ else:
452
+ raise ValueError("Invalid layer_type")
453
+
454
+ if output_indices is not None:
455
+ hidden_states = hidden_states[output_indices]
456
+ attn_out = attn_out[output_indices]
457
+ assert injection is not None
458
+ injection = injection[output_indices]
459
+ mlp_hc = self.mlp_hyper_connection
460
+ hidden_states, block_input, injection = mlp_hc.combine_and_mix(
461
+ hidden_states, attn_out, injection
462
+ )
463
+ mlp_out = self.mlp(block_input)
464
+ return hidden_states, mlp_out, injection
465
+
466
+
467
+ class Qwen4ExpMixtureOfExperts(MixtureOfExperts):
468
+ """Expose Qwen4Exp routed experts through vLLM's EPLB protocol."""
469
+
470
+ def set_moe_parameters(self, layers: Iterable[nn.Module]) -> None:
471
+ self.moe_layers = []
472
+ self.moe_mlp_layers = []
473
+ example_moe = None
474
+ for layer in layers:
475
+ if isinstance(layer, Qwen4ExpDecoderLayer) and isinstance(
476
+ layer.mlp, Qwen4ExpSparseMoeBlock
477
+ ):
478
+ example_moe = layer.mlp
479
+ self.moe_mlp_layers.append(layer.mlp)
480
+ self.moe_layers.append(layer.mlp.experts)
481
+
482
+ self.num_moe_layers = len(self.moe_layers)
483
+ if example_moe is None:
484
+ self.num_expert_groups = 0
485
+ self.num_shared_experts = 0
486
+ self.num_logical_experts = 0
487
+ self.num_physical_experts = 0
488
+ self.num_local_physical_experts = 0
489
+ self.num_routed_experts = 0
490
+ self.num_redundant_experts = 0
491
+ return
492
+
493
+ self.num_expert_groups = 1
494
+ self.num_shared_experts = example_moe.n_shared_experts
495
+ self.num_logical_experts = example_moe.n_logical_experts
496
+ self.num_physical_experts = example_moe.n_physical_experts
497
+ self.num_local_physical_experts = example_moe.n_local_physical_experts
498
+ self.num_routed_experts = example_moe.n_routed_experts
499
+ self.num_redundant_experts = example_moe.n_redundant_experts
500
+
501
+ def update_physical_experts_metadata(
502
+ self,
503
+ num_physical_experts: int,
504
+ num_local_physical_experts: int,
505
+ ) -> None:
506
+ assert self.num_local_physical_experts == num_local_physical_experts
507
+ self.num_physical_experts = num_physical_experts
508
+ self.num_local_physical_experts = num_local_physical_experts
509
+ self.num_redundant_experts = num_physical_experts - self.num_logical_experts
510
+ for moe in self.moe_mlp_layers:
511
+ moe.n_physical_experts = num_physical_experts
512
+ moe.n_local_physical_experts = num_local_physical_experts
513
+ moe.n_redundant_experts = self.num_redundant_experts
514
+ moe.experts.update_expert_map()
515
+
516
+
517
+ class Qwen4ExpModel(nn.Module):
518
+ hf_to_vllm_mapper = Qwen3_5Model.hf_to_vllm_mapper | _EXTRA_WEIGHTS_MAPPER
519
+
520
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
521
+ super().__init__()
522
+ config: Qwen4ExpTextConfig = vllm_config.model_config.hf_text_config
523
+ self.config = config
524
+ self._use_b12x = uses_b12x(vllm_config)
525
+ self.num_redundant_experts = (
526
+ vllm_config.parallel_config.eplb_config.num_redundant_experts
527
+ )
528
+ self.vocab_size = config.vocab_size
529
+ self._qsa_layer_ids = frozenset(
530
+ layer_idx
531
+ for layer_idx, layer_type in enumerate(config.layer_types)
532
+ if layer_type == "full_attention"
533
+ and getattr(config, "indexer_n_heads", None) is not None
534
+ )
535
+ self.embed_tokens = VocabParallelEmbedding(
536
+ self.vocab_size,
537
+ config.hidden_size,
538
+ prefix=maybe_prefix(prefix, "embed_tokens"),
539
+ )
540
+ hc_config = HyperConnectionConfig(
541
+ hc_count=config.hc_count,
542
+ hidden_size=config.hidden_size,
543
+ params_dtype=torch.bfloat16,
544
+ hc_lowrank=config.hc_lowrank,
545
+ rms_norm_eps=config.rms_norm_eps,
546
+ hc_per_branch_norm=True,
547
+ )
548
+ self.hyper_connection_workspace = (
549
+ HyperConnectionWorkspace(
550
+ hc_config, vllm_config.scheduler_config.max_num_batched_tokens
551
+ )
552
+ if self._use_b12x
553
+ else None
554
+ )
555
+
556
+ def get_layer(prefix: str) -> Qwen4ExpDecoderLayer:
557
+ layer_idx = extract_layer_index(prefix)
558
+ return Qwen4ExpDecoderLayer(
559
+ vllm_config,
560
+ layer_type=config.layer_types[layer_idx],
561
+ workspace=self.hyper_connection_workspace,
562
+ prefix=prefix,
563
+ )
564
+
565
+ self.start_layer, self.end_layer, self.layers = make_layers(
566
+ config.num_hidden_layers, get_layer, prefix=f"{prefix}.layers"
567
+ )
568
+ self.is_fused_shared_expert_enabled = is_model_fused_shared_expert_compatible(
569
+ self.layers,
570
+ Qwen4ExpSparseMoeBlock,
571
+ "mlp",
572
+ )
573
+ intermediate_size = config.hidden_size * config.hc_count
574
+ self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
575
+ ["hidden_states"], intermediate_size
576
+ )
577
+
578
+ self.hyper_connection_mixer: GatedResidual | None
579
+ if get_pp_group().is_last_rank:
580
+ hc_config = HyperConnectionConfig(
581
+ hc_count=config.hc_count,
582
+ hidden_size=config.hidden_size,
583
+ params_dtype=torch.bfloat16,
584
+ hc_lowrank=config.hc_lowrank,
585
+ rms_norm_eps=config.rms_norm_eps,
586
+ hc_per_branch_norm=True,
587
+ )
588
+ self.hyper_connection_mixer = GatedResidual(
589
+ hc_config,
590
+ use_combine=False,
591
+ workspace=self.hyper_connection_workspace,
592
+ prefix=maybe_prefix(prefix, "hyper_connection_mixer"),
593
+ )
594
+ else:
595
+ self.hyper_connection_mixer = None
596
+
597
+ spec_config = vllm_config.speculative_config
598
+ # MTP HC multi-stream outputs: when speculative method=="mtp" and the
599
+ # model uses HC with hc_count>1, retain the pre-final-mixer multi-stream
600
+ # hidden state [T, hc_count*H] so the MTP drafter can feed a real
601
+ # multi-stream backbone hidden on its first step (scheme A). Derived
602
+ # purely from config (NOT node identity) so P/D nodes stay consistent.
603
+ needs_mtp_hidden = (
604
+ spec_config is not None
605
+ and getattr(spec_config, "method", None) == "mtp"
606
+ and get_pp_group().is_last_rank
607
+ )
608
+ if needs_mtp_hidden:
609
+ self.register_buffer(
610
+ "_mtp_hidden_buffer",
611
+ torch.empty(
612
+ vllm_config.scheduler_config.max_num_batched_tokens,
613
+ config.hc_count * config.hidden_size,
614
+ dtype=vllm_config.model_config.dtype,
615
+ ),
616
+ persistent=False,
617
+ )
618
+ else:
619
+ self.register_buffer("_mtp_hidden_buffer", None, persistent=False)
620
+
621
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
622
+ return self.embed_tokens(input_ids)
623
+
624
+ def forward(
625
+ self,
626
+ input_ids: torch.Tensor | None,
627
+ positions: torch.Tensor,
628
+ intermediate_tensors: IntermediateTensors | None = None,
629
+ inputs_embeds: torch.Tensor | None = None,
630
+ query_start_loc: torch.Tensor | None = None,
631
+ ngram_context: torch.Tensor | None = None,
632
+ deepstack_input_embeds: IntermediateTensors | None = None,
633
+ ) -> torch.Tensor | IntermediateTensors:
634
+ if get_pp_group().is_first_rank:
635
+ if inputs_embeds is not None:
636
+ hidden_states = inputs_embeds
637
+ else:
638
+ if input_ids is None:
639
+ raise ValueError("input_ids or inputs_embeds is required")
640
+ hidden_states = self.embed_input_ids(input_ids)
641
+ hidden_states = hidden_states.repeat(1, self.config.hc_count)
642
+ else:
643
+ if intermediate_tensors is None:
644
+ raise ValueError("pipeline stage requires intermediate tensors")
645
+ hidden_states = intermediate_tensors["hidden_states"]
646
+
647
+ block_output = None
648
+ injection = None
649
+ last_layer = None
650
+ for layer_idx, layer in islice(
651
+ enumerate(self.layers), self.start_layer, self.end_layer
652
+ ):
653
+ last_layer = layer
654
+ hidden_states, block_output, injection = layer(
655
+ hidden_states=hidden_states,
656
+ prev_block_output=block_output,
657
+ prev_injection=injection,
658
+ positions=positions,
659
+ input_ids=input_ids,
660
+ query_start_loc=query_start_loc,
661
+ ngram_context=ngram_context,
662
+ )
663
+ if deepstack_input_embeds is not None and layer_idx < len(
664
+ deepstack_input_embeds
665
+ ):
666
+ deepstack_embed = deepstack_input_embeds[
667
+ f"deepstack_input_embeds_{layer_idx}"
668
+ ]
669
+ deepstack_embed = (
670
+ deepstack_embed.unsqueeze(-2)
671
+ .expand(
672
+ *deepstack_embed.shape[:-1],
673
+ self.config.hc_count,
674
+ self.config.hidden_size,
675
+ )
676
+ .flatten(-2)
677
+ )
678
+ # Deepstack is an external addition to the materialized
679
+ # multi-stream state and therefore terminates delayed combine.
680
+ hidden_states = layer.mlp_hyper_connection.combine(
681
+ hidden_states, block_output, injection
682
+ )
683
+ block_output = None
684
+ injection = None
685
+ hidden_states = hidden_states + deepstack_embed
686
+
687
+ if not get_pp_group().is_last_rank:
688
+ # PP transports one tensor, not the delayed HC tuple. Materialize
689
+ # with the HC module that produced the pending injection.
690
+ if last_layer is not None and block_output is not None:
691
+ hidden_states = last_layer.mlp_hyper_connection.combine(
692
+ hidden_states, block_output, injection
693
+ )
694
+ return IntermediateTensors({"hidden_states": hidden_states})
695
+
696
+ # The final mixer consumes the last pending combine and returns both
697
+ # the sampled single stream and the materialized multi-stream state.
698
+ final_mixer = self.hyper_connection_mixer
699
+ assert final_mixer is not None
700
+ multi_hidden, sample_hidden_states, _ = final_mixer.combine_and_mix(
701
+ hidden_states, block_output, injection
702
+ )
703
+ if self._mtp_hidden_buffer is not None:
704
+ # Capture the pre-final-mixer multi-stream hidden state
705
+ # [T, hc_count*H] for the MTP drafter (zero extra compute:
706
+ # this tensor is needed by the final mixer regardless).
707
+ num_tokens = multi_hidden.shape[0]
708
+ self._mtp_hidden_buffer[:num_tokens].copy_(multi_hidden)
709
+ if _LOGIT_DUMP_DIAG:
710
+ from .logit_dump import note_forward_output
711
+
712
+ note_forward_output(sample_hidden_states)
713
+ return sample_hidden_states
714
+
715
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
716
+ weights = (
717
+ (
718
+ _remap_qsa_cache_scale_name(name, self._qsa_layer_ids),
719
+ weight,
720
+ )
721
+ for name, weight in weights
722
+ )
723
+ weights = maybe_fuse_shared_experts(
724
+ weights,
725
+ enabled=self.is_fused_shared_expert_enabled,
726
+ n_routed_experts=getattr(self.config, "num_experts", 0) or 0,
727
+ n_shared_experts=1,
728
+ ckpt_prefix="mlp.shared_expert",
729
+ )
730
+ # Non-persistent PLE state rebuilt in __init__; skip any ckpt
731
+ # column for them.
732
+ skip_substrs = (
733
+ "hashstats_",
734
+ "token_lookup",
735
+ "hyper_connection_mixer.block_inject_weight",
736
+ )
737
+ mapper = self.hf_to_vllm_mapper | WeightsMapper(
738
+ orig_to_new_substr={substr: None for substr in skip_substrs}
739
+ )
740
+ # The final HC mixer only exists on the last PP rank; earlier ranks
741
+ # must drop its checkpoint weights instead of failing to place them.
742
+ ignore_prefixes = (
743
+ None
744
+ if self.hyper_connection_mixer is not None
745
+ else ["hyper_connection_mixer."]
746
+ )
747
+ loader = AutoWeightsLoader(
748
+ self,
749
+ ignore_unexpected_prefixes=ignore_prefixes,
750
+ ignore_unexpected_suffixes=_QWEN4_EXP_IGNORED_MISSING_SUFFIXES.copy(),
751
+ )
752
+ loaded = loader.load_weights(
753
+ weights,
754
+ mapper=mapper,
755
+ )
756
+ if _TARGET_FP8:
757
+ from vllm.logger import init_logger
758
+
759
+ from .ops.hc_fp8_gemv import install_fp8_decode_linears
760
+
761
+ names = install_fp8_decode_linears(self)
762
+ init_logger(__name__).info(
763
+ "Qwen3.8 target: %d bf16 projections use rowwise-FP8 decode GEMVs: %s",
764
+ len(names), ", ".join(names),
765
+ )
766
+ return loaded
767
+
768
+
769
+ class Qwen4ExpForCausalLM(
770
+ nn.Module,
771
+ HasInnerState,
772
+ SupportsLoRA,
773
+ SupportsMRoPE,
774
+ SupportsPP,
775
+ Qwen4ExpMixtureOfExperts,
776
+ IsHybrid,
777
+ ):
778
+ packed_modules_mapping = {
779
+ "qkv_proj": ["q_proj", "k_proj", "v_proj"],
780
+ "gate_up_proj": ["gate_proj", "up_proj"],
781
+ "kv_proj": ["key_proj", "value_proj"],
782
+ "in_proj_qkvz": ["in_proj_qkv", "in_proj_z"],
783
+ "in_proj_ba": ["in_proj_b", "in_proj_a"],
784
+ "input_mix_weight_down_block_inject": [
785
+ "input_mix_weight_down",
786
+ "block_inject_weight",
787
+ "_input_mix_padding",
788
+ ],
789
+ }
790
+ hf_to_vllm_mapper = WeightsMapper(
791
+ orig_to_new_prefix={"model.language_model.": "model."}
792
+ )
793
+ requires_raw_input_tokens = True
794
+
795
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
796
+ super().__init__()
797
+ config: Qwen4ExpTextConfig = vllm_config.model_config.hf_text_config
798
+ self.vllm_config = vllm_config
799
+ self.model_config = vllm_config.model_config
800
+ self.quant_config = vllm_config.quant_config
801
+ self.config = config
802
+ self.scheduler_config = vllm_config.scheduler_config
803
+ if vllm_config.cache_config.mamba_cache_mode == "all":
804
+ raise NotImplementedError(
805
+ "Qwen4Exp currently does not support 'all' prefix caching, "
806
+ "please use '--mamba-cache-mode=align' instead"
807
+ )
808
+ self.model = Qwen4ExpModel(
809
+ vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
810
+ )
811
+ self.lm_head = ParallelLMHead(
812
+ config.vocab_size,
813
+ config.hidden_size,
814
+ prefix=maybe_prefix(prefix, "lm_head"),
815
+ )
816
+ self.logits_processor = LogitsProcessor(config.vocab_size, lm_head=self.lm_head)
817
+ self.make_empty_intermediate_tensors = (
818
+ self.model.make_empty_intermediate_tensors
819
+ )
820
+ self.set_moe_parameters(self.model.layers)
821
+ if not uses_b12x(vllm_config):
822
+ enable_qwen4_exp_low_latency_gemm(self, self.model_config.dtype)
823
+
824
+ @staticmethod
825
+ def get_model_state_cls():
826
+ from .model_state import Qwen4ExpModelState
827
+
828
+ return Qwen4ExpModelState
829
+
830
+ def forward(
831
+ self,
832
+ input_ids: torch.Tensor | None,
833
+ positions: torch.Tensor,
834
+ intermediate_tensors: IntermediateTensors | None = None,
835
+ inputs_embeds: torch.Tensor | None = None,
836
+ **kwargs: object,
837
+ ) -> torch.Tensor | IntermediateTensors:
838
+ # Forward kwargs unchanged so the runner's _maybe_add_ngram_kwargs
839
+ # path (query_start_loc / ngram_context) reaches Qwen4ExpModel.
840
+ return self.model(
841
+ input_ids,
842
+ positions,
843
+ intermediate_tensors,
844
+ inputs_embeds,
845
+ **kwargs,
846
+ )
847
+
848
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
849
+ return self.model.embed_input_ids(input_ids)
850
+
851
+ @classmethod
852
+ def get_ple_mamba_state_dtype_from_config(
853
+ cls,
854
+ vllm_config: VllmConfig,
855
+ ) -> tuple[torch.dtype, ...]:
856
+ return MambaStateDtypeCalculator.short_conv_state_dtype(
857
+ vllm_config.model_config.dtype,
858
+ vllm_config.cache_config.mamba_cache_dtype,
859
+ )
860
+
861
+ @classmethod
862
+ def get_ple_mamba_state_shape_from_config(
863
+ cls, vllm_config: VllmConfig
864
+ ) -> tuple[tuple[int, int]]:
865
+ hf_config = vllm_config.model_config.hf_text_config
866
+ conv_kernel_size = hf_config.ple_conv_kernel_size
867
+ short_conv_dilation = hf_config.ngram_size
868
+ conv_state_len = (conv_kernel_size - 1) * short_conv_dilation
869
+ num_spec = (
870
+ vllm_config.speculative_config.num_speculative_tokens
871
+ if vllm_config.speculative_config
872
+ else 0
873
+ )
874
+ hc_count = hf_config.hc_count
875
+ hc_hidden_size = hf_config.hidden_size * hc_count
876
+ return MambaStateShapeCalculator.short_conv_state_shape(
877
+ tp_world_size=1,
878
+ intermediate_size=hc_hidden_size,
879
+ conv_kernel=conv_state_len + 1,
880
+ num_spec=num_spec,
881
+ )
882
+
883
+ @classmethod
884
+ def get_gdn_mamba_state_dtype_from_config(
885
+ cls, vllm_config: VllmConfig
886
+ ) -> tuple[torch.dtype, torch.dtype]:
887
+ return MambaStateDtypeCalculator.gated_delta_net_state_dtype(
888
+ vllm_config.model_config.dtype,
889
+ vllm_config.cache_config.mamba_cache_dtype,
890
+ vllm_config.cache_config.mamba_ssm_cache_dtype,
891
+ )
892
+
893
+ @classmethod
894
+ def get_gdn_mamba_state_shape_from_config(
895
+ cls, vllm_config: VllmConfig
896
+ ) -> tuple[tuple[int, int], tuple[int, int]]:
897
+ parallel_config = vllm_config.parallel_config
898
+ hf_config = vllm_config.model_config.hf_text_config
899
+ tp_size = parallel_config.tensor_parallel_size
900
+ num_spec = (
901
+ vllm_config.speculative_config.num_speculative_tokens
902
+ if vllm_config.speculative_config
903
+ else 0
904
+ )
905
+ return MambaStateShapeCalculator.gated_delta_net_state_shape(
906
+ tp_size,
907
+ hf_config.linear_num_key_heads,
908
+ hf_config.linear_num_value_heads,
909
+ hf_config.linear_key_head_dim,
910
+ hf_config.linear_value_head_dim,
911
+ hf_config.linear_conv_kernel_dim,
912
+ num_spec,
913
+ )
914
+
915
+ @classmethod
916
+ def get_mamba_state_dtype_from_config(
917
+ cls,
918
+ vllm_config: VllmConfig,
919
+ ) -> tuple[torch.dtype, torch.dtype]:
920
+ return cls.get_gdn_mamba_state_dtype_from_config(vllm_config)
921
+
922
+ @classmethod
923
+ def get_mamba_state_shape_from_config(
924
+ cls, vllm_config: VllmConfig
925
+ ) -> tuple[tuple[int, int], tuple[int, int]]:
926
+ return cls.get_gdn_mamba_state_shape_from_config(vllm_config)
927
+
928
+ @classmethod
929
+ def get_mamba_state_copy_func(
930
+ cls,
931
+ ) -> tuple[MambaStateCopyFunc, MambaStateCopyFunc]:
932
+ return MambaStateCopyFuncCalculator.gated_delta_net_state_copy_func()
933
+
934
+ @classmethod
935
+ def get_mamba_state_copy_funcs(
936
+ cls,
937
+ mamba_types: set[MambaAttentionBackendEnum],
938
+ ) -> MambaStateCopyFuncsByType:
939
+ copy_funcs_by_type = {
940
+ MambaAttentionBackendEnum.GDN_ATTN: cls.get_mamba_state_copy_func(),
941
+ MambaAttentionBackendEnum.SHORT_CONV: (
942
+ MambaStateCopyFuncCalculator.short_conv_state_copy_func()
943
+ ),
944
+ }
945
+ missing_types = mamba_types - copy_funcs_by_type.keys()
946
+ assert not missing_types, f"missing state copy funcs for {missing_types}"
947
+ return {
948
+ mamba_type: copy_funcs_by_type[mamba_type] for mamba_type in mamba_types
949
+ }
950
+
951
+ @classmethod
952
+ def get_mamba_specs_from_config(
953
+ cls, vllm_config: VllmConfig
954
+ ) -> tuple[MambaSpec, ...]:
955
+ """Return all MambaSpecs for this model (GDN layers + PLE layer).
956
+
957
+ The PLE layer uses a separate short_conv MambaSpec whose page_size_bytes
958
+ may exceed the GDN spec; callers should take the maximum.
959
+ """
960
+ return (
961
+ MambaSpec(
962
+ shapes=cls.get_gdn_mamba_state_shape_from_config(vllm_config),
963
+ dtypes=cls.get_gdn_mamba_state_dtype_from_config(vllm_config),
964
+ block_size=-1,
965
+ ),
966
+ MambaSpec(
967
+ shapes=cls.get_ple_mamba_state_shape_from_config(vllm_config),
968
+ dtypes=cls.get_ple_mamba_state_dtype_from_config(vllm_config),
969
+ block_size=-1,
970
+ tp_replicated=True,
971
+ ),
972
+ )
973
+
974
+ def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor | None:
975
+ if _FP8_LM_HEAD and hidden_states.dim() == 2:
976
+ head = self._fp8_lm_head()
977
+ if head is not None:
978
+ return self._fp8_logits(hidden_states, head)
979
+ return self.logits_processor(self.lm_head, hidden_states)
980
+
981
+ # --- Opt-in rowwise-fp8 target lm_head (VLLM_QWEN38_FP8_LM_HEAD=1) ---------
982
+ # The bf16 head is a 248320 x 2560 (1.27 GB) read on every decode step and the
983
+ # GB10 decode step is bandwidth-bound (~5.4 ms of a ~72 ms step); fp8 halves it.
984
+ # Changes verify logits slightly, so it is not bitwise lossless.
985
+ def _fp8_lm_head(self):
986
+ head = getattr(self, "_fp8_head_cache", None)
987
+ if head is not None:
988
+ return head
989
+ if torch.cuda.is_current_stream_capturing():
990
+ return None # never quantize inside a graph capture; bf16 path instead
991
+ lp = self.logits_processor
992
+ if lp.soft_cap is not None or lp.scale != 1.0 or lp.logits_as_input:
993
+ return None
994
+ with torch.no_grad():
995
+ w = self.lm_head.weight.detach()
996
+ row_max = w.abs().amax(dim=1, keepdim=True).float().clamp(min=1e-6)
997
+ w8 = (w.float() * (448.0 / row_max)).to(torch.float8_e4m3fn)
998
+ row_scale = (row_max / 448.0).to(w.dtype).reshape(1, -1)
999
+ unit = torch.ones(1, dtype=torch.float32, device=w.device)
1000
+ self._fp8_head_cache = (w8, row_scale, unit)
1001
+ logger.info("Qwen4Exp target lm_head uses a rowwise-fp8 copy.")
1002
+ return self._fp8_head_cache
1003
+
1004
+ def _fp8_logits(self, hidden_states: torch.Tensor, head) -> torch.Tensor | None:
1005
+ w8, row_scale, unit = head
1006
+ act_max = hidden_states.abs().amax(dim=-1, keepdim=True).clamp(min=1e-6)
1007
+ a8 = (hidden_states * (448.0 / act_max)).to(torch.float8_e4m3fn)
1008
+ logits = torch._scaled_mm(
1009
+ a8, w8.t(), scale_a=unit, scale_b=unit, out_dtype=hidden_states.dtype
1010
+ )
1011
+ logits = logits * row_scale * (act_max / 448.0).to(hidden_states.dtype)
1012
+ lp = self.logits_processor
1013
+ logits = lp._gather_logits(logits)
1014
+ if logits is not None:
1015
+ logits = logits[..., : lp.org_vocab_size]
1016
+ return logits
1017
+
1018
+ def get_mtp_target_hidden_states(self) -> torch.Tensor | None:
1019
+ return self.model._mtp_hidden_buffer
1020
+
1021
+ def get_mrope_input_positions(
1022
+ self,
1023
+ input_tokens: list[int],
1024
+ mm_features: list[MultiModalFeatureSpec],
1025
+ ) -> tuple[torch.Tensor, int]:
1026
+ positions = torch.arange(len(input_tokens), dtype=torch.long)
1027
+ return positions.unsqueeze(0).expand(3, -1), 0
1028
+
1029
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1030
+ mapper = self.hf_to_vllm_mapper | WeightsMapper(
1031
+ orig_to_new_substr={"mtp.": None}
1032
+ )
1033
+ loader = AutoWeightsLoader(
1034
+ self,
1035
+ ignore_unexpected_suffixes=_QWEN4_EXP_IGNORED_MISSING_SUFFIXES.copy(),
1036
+ )
1037
+ return loader.load_weights(weights, mapper=mapper)
1038
+
1039
+ def compute_logits_local(self, hidden_states: torch.Tensor) -> torch.Tensor:
1040
+ return self.logits_processor(self.lm_head, hidden_states, skip_gather=True)
1041
+
1042
+ @property
1043
+ def checkpoint_file_weight_filter(self) -> Callable[[str], bool] | None:
1044
+ if not (uses_b12x(self.vllm_config) and self.config.ple_layer_ids):
1045
+ return None
1046
+ if (
1047
+ _resolve_ple_table_memory(
1048
+ self.vllm_config.additional_config, self.config.ple_embedding_dtype
1049
+ )
1050
+ == "io_uring"
1051
+ ):
1052
+ return _is_file_backed_ple_weight
1053
+ embeddings = [
1054
+ module
1055
+ for module in self.modules()
1056
+ if isinstance(module, B12xNGramEmbedding)
1057
+ ]
1058
+ # Attached shared tables already hold every shard row: skip reading them.
1059
+ if embeddings and all(
1060
+ embedding.ngram_embedding.shared_table_attached for embedding in embeddings
1061
+ ):
1062
+ return _is_file_backed_ple_weight
1063
+ return None
1064
+
1065
+
1066
+ class Qwen4ExpProcessingInfo(Qwen3VLProcessingInfo):
1067
+ def get_hf_config(self) -> Qwen4ExpConfig:
1068
+ return self.ctx.get_hf_config(Qwen4ExpConfig)
1069
+
1070
+
1071
+ @MULTIMODAL_REGISTRY.register_processor(
1072
+ Qwen3VLMultiModalProcessor,
1073
+ info=Qwen4ExpProcessingInfo,
1074
+ dummy_inputs=Qwen3VLDummyInputsBuilder,
1075
+ )
1076
+ class Qwen4ExpForConditionalGeneration(
1077
+ Qwen3_5ForConditionalGeneration,
1078
+ HasInnerState,
1079
+ Qwen4ExpMixtureOfExperts,
1080
+ ):
1081
+ """Qwen3-VL vision tower backed by the Qwen4Exp language model."""
1082
+
1083
+ requires_raw_input_tokens = True
1084
+
1085
+ packed_modules_mapping = Qwen3_5ForConditionalGeneration.packed_modules_mapping | {
1086
+ "kv_proj": ["key_proj", "value_proj"],
1087
+ "input_mix_weight_down_block_inject": [
1088
+ "input_mix_weight_down",
1089
+ "block_inject_weight",
1090
+ "_input_mix_padding",
1091
+ ],
1092
+ }
1093
+
1094
+ @staticmethod
1095
+ def get_model_state_cls():
1096
+ from .model_state import Qwen4ExpModelState
1097
+
1098
+ return Qwen4ExpModelState
1099
+
1100
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = "model") -> None:
1101
+ nn.Module.__init__(self)
1102
+ config: Qwen4ExpConfig = vllm_config.model_config.hf_config
1103
+ quant_config = vllm_config.quant_config
1104
+ multimodal_config = vllm_config.model_config.multimodal_config
1105
+ if multimodal_config is None:
1106
+ raise ValueError(
1107
+ "Qwen4ExpForConditionalGeneration requires multimodal_config"
1108
+ )
1109
+
1110
+ self.config = config
1111
+ self.model_config = vllm_config.model_config
1112
+ self.multimodal_config = multimodal_config
1113
+ self.language_model_only = multimodal_config.language_model_only
1114
+ if self.language_model_only:
1115
+ self.use_data_parallel = False
1116
+ self.is_multimodal_pruning_enabled = False
1117
+ self.video_pruning_method = None
1118
+ self.video_pruning_rate = 0.0
1119
+ self._tokenizer = None
1120
+ self.visual = StageMissingLayer("vision_tower")
1121
+ self._tower_model_names = []
1122
+ else:
1123
+ self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
1124
+ self._init_video_pruning(multimodal_config)
1125
+ self._tokenizer = cached_tokenizer_from_config(vllm_config.model_config)
1126
+
1127
+ with self._mark_tower_model(vllm_config, {"image", "video"}):
1128
+ self.visual = Qwen3_VisionTransformer(
1129
+ config.vision_config,
1130
+ norm_eps=config.text_config.rms_norm_eps,
1131
+ quant_config=quant_config,
1132
+ prefix=maybe_prefix(prefix, "visual"),
1133
+ )
1134
+
1135
+ self.use_deepstack = (
1136
+ not self.language_model_only
1137
+ and bool(config.vision_config.deepstack_visual_indexes)
1138
+ and not isinstance(self.visual, StageMissingLayer)
1139
+ )
1140
+ self.deepstack_num_level = (
1141
+ len(config.vision_config.deepstack_visual_indexes)
1142
+ if self.use_deepstack
1143
+ else 0
1144
+ )
1145
+ self.visual_dim = config.vision_config.out_hidden_size
1146
+ self.multiscale_dim = self.visual_dim * self.deepstack_num_level
1147
+
1148
+ if self.use_deepstack:
1149
+ self.deepstack_input_embeds = [
1150
+ torch.zeros(
1151
+ vllm_config.scheduler_config.max_num_batched_tokens,
1152
+ config.text_config.hidden_size,
1153
+ )
1154
+ for _ in range(self.deepstack_num_level)
1155
+ ]
1156
+ self.deepstack_input_embeds_num_tokens = 0
1157
+
1158
+ with self._mark_language_model(vllm_config):
1159
+ self.language_model = Qwen4ExpForCausalLM(
1160
+ vllm_config=vllm_config,
1161
+ prefix=maybe_prefix(prefix, "language_model"),
1162
+ )
1163
+
1164
+ self.make_empty_intermediate_tensors = (
1165
+ self.language_model.make_empty_intermediate_tensors
1166
+ )
1167
+ if not get_pp_group().is_first_rank and self.use_deepstack:
1168
+ assert self.language_model.model.start_layer >= len(
1169
+ config.vision_config.deepstack_visual_indexes
1170
+ ), (
1171
+ "start_layer should be greater than or equal to "
1172
+ "len(deepstack_visual_indexes)"
1173
+ )
1174
+ self.set_moe_parameters(self.language_model.model.layers)
1175
+
1176
+ def embed_input_ids(
1177
+ self,
1178
+ input_ids: torch.Tensor,
1179
+ multimodal_embeddings: MultiModalEmbeddings | None = None,
1180
+ *,
1181
+ is_multimodal: torch.Tensor | None = None,
1182
+ ) -> torch.Tensor:
1183
+ inputs_embeds = self._embed_text_input_ids(
1184
+ input_ids,
1185
+ self.language_model.embed_input_ids,
1186
+ is_multimodal=is_multimodal,
1187
+ )
1188
+ if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
1189
+ return inputs_embeds
1190
+ if self.language_model_only:
1191
+ raise ValueError(
1192
+ "Qwen4Exp language_model_only does not accept multimodal embeddings"
1193
+ )
1194
+
1195
+ is_multimodal = _require_is_multimodal(is_multimodal)
1196
+ if self.use_deepstack:
1197
+ deepstack_input_embeds, multimodal_embeddings = (
1198
+ self._compute_deepstack_embeds(
1199
+ inputs_embeds=inputs_embeds,
1200
+ multimodal_embeddings=multimodal_embeddings,
1201
+ is_multimodal=is_multimodal,
1202
+ )
1203
+ )
1204
+ else:
1205
+ deepstack_input_embeds = None
1206
+
1207
+ inputs_embeds = _merge_multimodal_embeddings(
1208
+ inputs_embeds=inputs_embeds,
1209
+ multimodal_embeddings=multimodal_embeddings,
1210
+ is_multimodal=is_multimodal,
1211
+ )
1212
+ if deepstack_input_embeds is not None:
1213
+ self._set_deepstack_input_embeds(deepstack_input_embeds)
1214
+ return inputs_embeds
1215
+
1216
+ def get_mtp_target_hidden_states(self) -> torch.Tensor | None:
1217
+ return self.language_model.get_mtp_target_hidden_states()
1218
+
1219
+ def forward(
1220
+ self,
1221
+ input_ids: torch.Tensor | None,
1222
+ positions: torch.Tensor,
1223
+ intermediate_tensors: IntermediateTensors | None = None,
1224
+ inputs_embeds: torch.Tensor | None = None,
1225
+ **kwargs: object,
1226
+ ) -> torch.Tensor | IntermediateTensors:
1227
+ if intermediate_tensors is not None:
1228
+ inputs_embeds = None
1229
+ if inputs_embeds is not None and get_pp_group().is_first_rank:
1230
+ deepstack_input_embeds = self._get_deepstack_input_embeds(
1231
+ inputs_embeds.size(0)
1232
+ )
1233
+ else:
1234
+ deepstack_input_embeds = None
1235
+
1236
+ hidden_states = self.language_model.model(
1237
+ input_ids=input_ids,
1238
+ positions=positions,
1239
+ intermediate_tensors=intermediate_tensors,
1240
+ inputs_embeds=inputs_embeds,
1241
+ query_start_loc=kwargs.get("query_start_loc"),
1242
+ ngram_context=kwargs.get("ngram_context"),
1243
+ deepstack_input_embeds=deepstack_input_embeds,
1244
+ )
1245
+ if inputs_embeds is not None and get_pp_group().is_first_rank:
1246
+ self._clear_deepstack_input_embeds(inputs_embeds.size(0))
1247
+ if _LOGIT_DUMP_DIR and isinstance(hidden_states, torch.Tensor):
1248
+ from .logit_dump import maybe_dump_forward
1249
+
1250
+ maybe_dump_forward(_LOGIT_DUMP_DIR, self.language_model.compute_logits,
1251
+ hidden_states, positions)
1252
+ return hidden_states
1253
+
1254
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1255
+ mapper = self.hf_to_vllm_mapper | WeightsMapper(
1256
+ orig_to_new_substr={"mtp.": None},
1257
+ orig_to_new_prefix={"visual.": None} if self.language_model_only else {},
1258
+ )
1259
+ loader = AutoWeightsLoader(
1260
+ self,
1261
+ ignore_unexpected_suffixes=_QWEN4_EXP_IGNORED_MISSING_SUFFIXES.copy(),
1262
+ )
1263
+ return loader.load_weights(weights, mapper=mapper)
1264
+
1265
+ @classmethod
1266
+ def get_mamba_state_dtype_from_config(
1267
+ cls,
1268
+ vllm_config: VllmConfig,
1269
+ ) -> tuple[torch.dtype, torch.dtype]:
1270
+ return Qwen4ExpForCausalLM.get_mamba_state_dtype_from_config(vllm_config)
1271
+
1272
+ @classmethod
1273
+ def get_mamba_state_shape_from_config(
1274
+ cls,
1275
+ vllm_config: VllmConfig,
1276
+ ) -> tuple[tuple[int, int], tuple[int, int]]:
1277
+ return Qwen4ExpForCausalLM.get_mamba_state_shape_from_config(vllm_config)
1278
+
1279
+ @classmethod
1280
+ def get_mamba_state_copy_func(
1281
+ cls,
1282
+ ) -> tuple[MambaStateCopyFunc, MambaStateCopyFunc]:
1283
+ return Qwen4ExpForCausalLM.get_mamba_state_copy_func()
1284
+
1285
+ @classmethod
1286
+ def get_mamba_state_copy_funcs(
1287
+ cls,
1288
+ mamba_types: set[MambaAttentionBackendEnum],
1289
+ ) -> MambaStateCopyFuncsByType:
1290
+ return Qwen4ExpForCausalLM.get_mamba_state_copy_funcs(mamba_types)
1291
+
1292
+ @classmethod
1293
+ def get_mamba_specs_from_config(
1294
+ cls, vllm_config: VllmConfig
1295
+ ) -> tuple[MambaSpec, ...]:
1296
+ return Qwen4ExpForCausalLM.get_mamba_specs_from_config(vllm_config)
1297
+
1298
+ @property
1299
+ def checkpoint_file_weight_filter(self) -> Callable[[str], bool] | None:
1300
+ return self.language_model.checkpoint_file_weight_filter
1301
+
1302
+
1303
+ __all__ = [
1304
+ "Qwen4ExpDecoderLayer",
1305
+ "Qwen4ExpForCausalLM",
1306
+ "Qwen4ExpForConditionalGeneration",
1307
+ "Qwen4ExpMixtureOfExperts",
1308
+ "Qwen4ExpModel",
1309
+ "Qwen4ExpSparseMoeBlock",
1310
+ ]
recipe/patches/qwen4_exp/nvidia/mtp.py ADDED
@@ -0,0 +1,968 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+ """Inference-only Qwen4Exp multi-token predictor."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import os
9
+ import weakref
10
+ from collections.abc import Iterable, Sequence
11
+ from typing import Any
12
+
13
+ import regex as re
14
+ import torch
15
+ from torch import nn
16
+
17
+ import vllm.envs as envs
18
+ from vllm.compilation.decorators import support_torch_compile
19
+ from vllm.config import VllmConfig, replace, set_current_vllm_config
20
+ from vllm.distributed import get_pp_group
21
+ from vllm.model_executor.layers.fused_moe.utils import (
22
+ is_model_fused_shared_expert_compatible,
23
+ )
24
+ from vllm.model_executor.layers.linear import ColumnParallelLinear, ReplicatedLinear
25
+ from vllm.model_executor.layers.logits_processor import LogitsProcessor
26
+ from vllm.model_executor.layers.vocab_parallel_embedding import (
27
+ ParallelLMHead,
28
+ VocabParallelEmbedding,
29
+ )
30
+ from vllm.model_executor.model_loader.utils import configure_quant_config
31
+ from vllm.model_executor.models.interfaces import LocalArgmaxMixin, SupportsPP
32
+ from vllm.model_executor.models.qwen3_5 import Qwen3_5Model
33
+ from vllm.model_executor.models.utils import (
34
+ AutoWeightsLoader,
35
+ PPMissingLayer,
36
+ WeightsMapper,
37
+ get_draft_quant_config,
38
+ make_empty_intermediate_tensors_factory,
39
+ maybe_fuse_shared_experts,
40
+ maybe_prefix,
41
+ )
42
+ from vllm.logger import init_logger
43
+ from vllm.platforms import current_platform
44
+ from vllm.sequence import IntermediateTensors
45
+ from vllm.utils.b12x import (
46
+ B12xPreparationUnit,
47
+ B12xWorkload,
48
+ PreparationResourceUnavailableError,
49
+ b12x_layer,
50
+ b12x_layer_prefix,
51
+ get_b12x_mtp_feedback,
52
+ register_b12x_layer,
53
+ set_b12x_preparation_provider,
54
+ )
55
+ from vllm.utils.torch_utils import (
56
+ LayerNameType,
57
+ _encode_layer_name,
58
+ _resolve_layer_name,
59
+ direct_register_custom_op,
60
+ )
61
+
62
+ from ..config import Qwen4ExpTextConfig
63
+ from .backend import uses_b12x
64
+ from .hyperconnection import (
65
+ GatedResidual,
66
+ GroupedGemmaRMSNorm,
67
+ HyperConnectionConfig,
68
+ HyperConnectionWorkspace,
69
+ )
70
+ from .low_latency_gemm import enable_qwen4_exp_low_latency_gemm
71
+ from .model import (
72
+ _EXTRA_WEIGHTS_MAPPER,
73
+ _QWEN4_EXP_IGNORED_MISSING_SUFFIXES,
74
+ Qwen4ExpDecoderLayer,
75
+ Qwen4ExpMixtureOfExperts,
76
+ Qwen4ExpSparseMoeBlock,
77
+ _remap_qsa_cache_scale_name,
78
+ )
79
+
80
+
81
+ def _mtp_api() -> Any:
82
+ api = get_b12x_mtp_feedback()
83
+ if api is None:
84
+ raise ImportError(
85
+ "Qwen4Exp MTP requires b12x.sequence.mtp_feedback; "
86
+ "install the b12x serving extra"
87
+ )
88
+ return api
89
+
90
+
91
+ def _remap_ignored_layers(
92
+ ignored_layers: list[str],
93
+ mtp_start_layer_idx: int,
94
+ ) -> list[str]:
95
+ return [_remap_mtp_layer_name(name, mtp_start_layer_idx) for name in ignored_layers]
96
+
97
+
98
+ def _remap_mtp_layer_name(name: str, mtp_start_layer_idx: int) -> str:
99
+ if not name.startswith("mtp."):
100
+ return name
101
+ return re.sub(
102
+ r"(?<=\.layers\.)\d+",
103
+ lambda match: str(mtp_start_layer_idx + int(match.group(0))),
104
+ name,
105
+ )
106
+
107
+
108
+ def _remap_mtp_quantized_layers(
109
+ quantized_layers: dict[str, dict[str, Any]],
110
+ mtp_start_layer_idx: int,
111
+ ) -> dict[str, dict[str, Any]]:
112
+ return {
113
+ _remap_mtp_layer_name(name, mtp_start_layer_idx): layer_config
114
+ for name, layer_config in quantized_layers.items()
115
+ }
116
+
117
+
118
+ _remap_quantized_layers = _remap_mtp_quantized_layers
119
+
120
+
121
+ def _remap_mtp_weight_name(name: str) -> str | None:
122
+ """Map target-checkpoint names into the standalone draft model."""
123
+ for checkpoint_prefix in ("model.language_model.", "language_model."):
124
+ if name.startswith(checkpoint_prefix):
125
+ name = name.removeprefix(checkpoint_prefix)
126
+ break
127
+
128
+ if name.startswith("embed_tokens."):
129
+ name = f"model.{name}"
130
+ if name.startswith("model.mtp."):
131
+ name = name.removeprefix("model.")
132
+ for shared_head_prefix in (
133
+ "mtp.shared_head.head.",
134
+ "model.shared_head.head.",
135
+ "shared_head.head.",
136
+ ):
137
+ if name.startswith(shared_head_prefix):
138
+ return name.replace(shared_head_prefix, "lm_head.", 1)
139
+ if name.startswith("model.lm_head."):
140
+ return name.removeprefix("model.")
141
+ if name.startswith("mtp."):
142
+ return name.replace("mtp.", "model.", 1)
143
+ if name.startswith("model.embed_tokens.") or name.startswith("lm_head."):
144
+ return name
145
+ return None
146
+
147
+
148
+ def _make_draft_vllm_config(
149
+ vllm_config: VllmConfig,
150
+ mtp_start_layer_idx: int,
151
+ ) -> VllmConfig:
152
+ speculative_config = vllm_config.speculative_config
153
+ if speculative_config is None or speculative_config.draft_model_config is None:
154
+ raise ValueError("speculative_config.draft_model_config must be set")
155
+
156
+ draft_quant_config = get_draft_quant_config(vllm_config)
157
+ if draft_quant_config is not None:
158
+ configure_quant_config(draft_quant_config, Qwen4ExpMTP)
159
+ quantized_layers = getattr(draft_quant_config, "quantized_layers", None)
160
+ if quantized_layers:
161
+ draft_quant_config.quantized_layers = ( # type: ignore[attr-defined]
162
+ _remap_mtp_quantized_layers(
163
+ quantized_layers,
164
+ mtp_start_layer_idx,
165
+ )
166
+ )
167
+ for attribute in ("ignored_layers", "exclude_modules"):
168
+ names = getattr(draft_quant_config, attribute, None)
169
+ if names:
170
+ setattr(
171
+ draft_quant_config,
172
+ attribute,
173
+ _remap_ignored_layers(names, mtp_start_layer_idx),
174
+ )
175
+
176
+ cache_config = vllm_config.cache_config
177
+ if _MTP_BF16_KV and cache_config.cache_dtype in ("fp8", "fp8_e4m3"):
178
+ cache_config = replace(cache_config, cache_dtype="bfloat16")
179
+ draft_vllm_config = replace(
180
+ vllm_config,
181
+ model_config=speculative_config.draft_model_config,
182
+ cache_config=cache_config,
183
+ )
184
+ draft_vllm_config.quant_config = draft_quant_config
185
+ return draft_vllm_config
186
+
187
+
188
+ @support_torch_compile(
189
+ dynamic_arg_dims={
190
+ "input_ids": 0,
191
+ "positions": -1,
192
+ "intermediate_tensors": 0,
193
+ "inputs_embeds": 0,
194
+ "hidden_states": 0,
195
+ }
196
+ )
197
+ class Qwen4ExpMultiTokenPredictor(nn.Module):
198
+ """Shared draft stack with optional fused token/multi-stream feedback."""
199
+
200
+ hf_to_vllm_mapper = Qwen3_5Model.hf_to_vllm_mapper | _EXTRA_WEIGHTS_MAPPER
201
+
202
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
203
+ super().__init__()
204
+ config: Qwen4ExpTextConfig = vllm_config.model_config.hf_text_config
205
+ self.config = config
206
+ self._use_b12x = uses_b12x(vllm_config)
207
+ self.vocab_size = config.vocab_size
208
+ self.hidden_size = config.hidden_size
209
+ self.hc_count = config.hc_count
210
+ self.mtp_start_layer_idx = config.num_hidden_layers
211
+ self.num_mtp_layers = int(getattr(config, "mtp_num_hidden_layers", 1))
212
+ self.supports_mtp_prefill_compaction = (
213
+ self._use_b12x
214
+ and self.num_mtp_layers == 1
215
+ and envs.VLLM_QWEN3_8_FLASH_NEXT_MTP_COMPACT
216
+ and vllm_config.scheduler_config.max_num_seqs == 1
217
+ )
218
+ self._prefill_output_indices: torch.Tensor | None = None
219
+ if self.num_mtp_layers < 1:
220
+ raise ValueError("MTP requires at least one predictor layer")
221
+
222
+ self.embed_tokens = VocabParallelEmbedding(
223
+ self.vocab_size,
224
+ self.hidden_size,
225
+ prefix=maybe_prefix(prefix, "embed_tokens"),
226
+ )
227
+ draft_vllm_config = _make_draft_vllm_config(
228
+ vllm_config,
229
+ self.mtp_start_layer_idx,
230
+ )
231
+ with set_current_vllm_config(draft_vllm_config, prefix=prefix):
232
+ projection_cls = (
233
+ ReplicatedLinear if self._use_b12x else ColumnParallelLinear
234
+ )
235
+ projection_kwargs = {} if self._use_b12x else {"gather_output": True}
236
+ self.fc_embedding = projection_cls(
237
+ self.hidden_size,
238
+ self.hidden_size,
239
+ bias=False,
240
+ params_dtype=torch.bfloat16,
241
+ quant_config=None if self._use_b12x else draft_vllm_config.quant_config,
242
+ **projection_kwargs,
243
+ prefix=maybe_prefix(prefix, "fc_embedding"),
244
+ return_bias=False,
245
+ )
246
+ self.fc_hidden = projection_cls(
247
+ self.hidden_size,
248
+ self.hidden_size,
249
+ bias=False,
250
+ params_dtype=torch.bfloat16,
251
+ quant_config=None if self._use_b12x else draft_vllm_config.quant_config,
252
+ **projection_kwargs,
253
+ prefix=maybe_prefix(prefix, "fc_hidden"),
254
+ return_bias=False,
255
+ )
256
+ hc_config = HyperConnectionConfig(
257
+ hc_count=self.hc_count,
258
+ hidden_size=self.hidden_size,
259
+ params_dtype=torch.bfloat16,
260
+ hc_lowrank=config.hc_lowrank,
261
+ rms_norm_eps=config.rms_norm_eps,
262
+ hc_per_branch_norm=True,
263
+ )
264
+ max_tokens = vllm_config.scheduler_config.max_num_batched_tokens
265
+ self.hyper_connection_workspace = (
266
+ HyperConnectionWorkspace(hc_config, max_tokens)
267
+ if self._use_b12x
268
+ else None
269
+ )
270
+ self.layers = nn.ModuleList(
271
+ [
272
+ Qwen4ExpDecoderLayer(
273
+ draft_vllm_config,
274
+ layer_type="full_attention",
275
+ workspace=self.hyper_connection_workspace,
276
+ prefix=(
277
+ f"{prefix}.layers.{self.mtp_start_layer_idx + idx}"
278
+ if prefix
279
+ else f"layers.{self.mtp_start_layer_idx + idx}"
280
+ ),
281
+ )
282
+ for idx in range(self.num_mtp_layers)
283
+ ]
284
+ )
285
+ self.hyper_connection_mixer = GatedResidual(
286
+ hc_config,
287
+ workspace=self.hyper_connection_workspace,
288
+ use_combine=False,
289
+ prefix=maybe_prefix(prefix, "hyper_connection_mixer"),
290
+ )
291
+
292
+ self.is_fused_shared_expert_enabled = is_model_fused_shared_expert_compatible(
293
+ self.layers, Qwen4ExpSparseMoeBlock, "mlp"
294
+ )
295
+ self.pre_fc_norm_embedding = GroupedGemmaRMSNorm(
296
+ self.hidden_size,
297
+ eps=config.rms_norm_eps,
298
+ group_size=None,
299
+ dtype=torch.bfloat16,
300
+ )
301
+ self.pre_fc_norm_hidden = GroupedGemmaRMSNorm(
302
+ self.hidden_size * self.hc_count,
303
+ eps=config.rms_norm_eps,
304
+ group_size=None,
305
+ dtype=torch.bfloat16,
306
+ )
307
+ self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
308
+ ["hidden_states"], self.hidden_size * self.hc_count
309
+ )
310
+
311
+ device = torch.device(current_platform.current_device())
312
+ self.register_buffer(
313
+ "_decode_output_indices",
314
+ torch.zeros(1, dtype=torch.int64, device=device),
315
+ persistent=False,
316
+ )
317
+ if self.supports_mtp_prefill_compaction:
318
+ self._prefill_output_indices = self._decode_output_indices
319
+ if not self._use_b12x:
320
+ return
321
+ self._feedback_caps = _mtp_api().Caps(
322
+ device=device,
323
+ max_tokens=max_tokens,
324
+ hidden_size=self.hidden_size,
325
+ streams=self.hc_count,
326
+ dtype=torch.bfloat16,
327
+ )
328
+ self._plan = None
329
+ self._feedback_scratch_spec: tuple[tuple[int, ...], torch.dtype] | None = None
330
+ self.register_buffer("_feedback_token_embedding", None, persistent=False)
331
+ self.register_buffer("_feedback_multi_state", None, persistent=False)
332
+ self.register_buffer("_feedback_output", None, persistent=False)
333
+ compilation_config = vllm_config.compilation_config
334
+ if prefix in compilation_config.static_forward_context:
335
+ raise ValueError(f"duplicate layer name: {prefix}")
336
+ compilation_config.static_forward_context[prefix] = self
337
+ self.prefix = prefix
338
+ b12x_name = b12x_layer_prefix(self)
339
+ self._b12x_layer_name = _encode_layer_name(b12x_name)
340
+ register_b12x_layer(b12x_name, self)
341
+ if not getattr(self, "b12x_preparation_suppressed", False):
342
+ set_b12x_preparation_provider(self, self)
343
+
344
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
345
+ return self.embed_tokens(input_ids)
346
+
347
+ def set_prefill_output_indices(self, output_indices: torch.Tensor | None) -> None:
348
+ """Select request-tail outputs after populating the attention cache."""
349
+ # AOT reuses the same compiled callable for first and subsequent drafts.
350
+ # Keep the one-row selector's tensor contract stable in both phases.
351
+ self._prefill_output_indices = (
352
+ self._decode_output_indices if output_indices is None else output_indices
353
+ )
354
+
355
+ def snapshot_qsa_interval_starts(self) -> None:
356
+ for layer in self.layers:
357
+ attention = getattr(layer, "self_attn", None)
358
+ snapshot = getattr(attention, "snapshot_speculative_interval_starts", None)
359
+ if snapshot is not None:
360
+ snapshot()
361
+
362
+ def restore_qsa_interval_starts(self) -> None:
363
+ for layer in self.layers:
364
+ attention = getattr(layer, "self_attn", None)
365
+ restore = getattr(attention, "restore_speculative_interval_starts", None)
366
+ if restore is not None:
367
+ restore()
368
+
369
+ def set_skip_topk(self, skip: bool) -> None:
370
+ """Reuse each draft attention layer's anchor selection within a round."""
371
+ for layer in self.layers:
372
+ attention = getattr(layer, "self_attn", None)
373
+ setter = getattr(attention, "set_skip_topk", None)
374
+ if setter is not None:
375
+ setter(skip and self.num_mtp_layers == 1)
376
+ elif (
377
+ attention is not None
378
+ and getattr(attention, "indexer", None) is not None
379
+ ):
380
+ attention.indexer.skip_topk = skip and self.num_mtp_layers == 1
381
+
382
+ def compact_topk_indices(self, source_rows: torch.Tensor) -> None:
383
+ """Capture accepted-token-aligned draft selections in request order."""
384
+ for layer in self.layers:
385
+ attention = getattr(layer, "self_attn", None)
386
+ compact = getattr(attention, "compact_topk_indices", None)
387
+ if compact is not None:
388
+ compact(source_rows)
389
+ elif (
390
+ attention is not None
391
+ and getattr(attention, "indexer", None) is not None
392
+ ):
393
+ buffer = attention.topk_indices_buffer
394
+ buffer[: source_rows.numel()].copy_(buffer.index_select(0, source_rows))
395
+
396
+ @property
397
+ def _feedback_request_name(self) -> str:
398
+ return f"{self.prefix}.mtp.feedback"
399
+
400
+ @staticmethod
401
+ def _alignment(tensor: torch.Tensor) -> int:
402
+ pointer = tensor.data_ptr()
403
+ return min(16, pointer & -pointer) if pointer else 16
404
+
405
+ def _feedback_plan(self):
406
+ return _mtp_api().plan(
407
+ self._feedback_caps,
408
+ invocation={
409
+ "input_alignments": (
410
+ 16,
411
+ 16,
412
+ self._alignment(self.pre_fc_norm_embedding.weight),
413
+ self._alignment(self.pre_fc_norm_hidden.weight),
414
+ )
415
+ },
416
+ )
417
+
418
+ def _feedback_resources_ready(self) -> bool:
419
+ weights = (
420
+ self.pre_fc_norm_embedding.weight,
421
+ self.pre_fc_norm_hidden.weight,
422
+ self.fc_embedding.weight,
423
+ self.fc_hidden.weight,
424
+ )
425
+ return not any(weight.is_meta for weight in weights)
426
+
427
+ def get_b12x_preparation_units(
428
+ self, layer: torch.nn.Module, workload: B12xWorkload
429
+ ) -> Sequence[B12xPreparationUnit]:
430
+ if layer is not self:
431
+ raise ValueError("MTP preparation owner mismatch")
432
+ if not self._use_b12x or workload.stage != "weights":
433
+ return ()
434
+ if not self._feedback_resources_ready():
435
+ return ()
436
+ if workload.max_tokens > self._feedback_caps.max_tokens:
437
+ raise PreparationResourceUnavailableError(
438
+ f"{self.prefix} MTP feedback capacity "
439
+ f"{self._feedback_caps.max_tokens} cannot serve "
440
+ f"{workload.max_tokens} tokens"
441
+ )
442
+ plan = self._feedback_plan()
443
+ self._plan = plan
444
+ request = plan.request(
445
+ name=self._feedback_request_name,
446
+ prepare_call=self._prepare_feedback_call,
447
+ benchmark_call=self._feedback_benchmark_factory(),
448
+ )
449
+ return (
450
+ B12xPreparationUnit(
451
+ name="MTP_FEEDBACK",
452
+ key=(self.prefix, workload.max_tokens),
453
+ requests=(request,),
454
+ stage="weights",
455
+ ),
456
+ )
457
+
458
+ def _publish_feedback_storage(self, state) -> None:
459
+ """Publish fixed feedback storage while keeping arena views transient."""
460
+ (spec,) = state.layout.scratch_specs()
461
+ self._feedback_scratch_spec = (spec.shape, spec.dtype)
462
+ factory = dict(device=spec.device, dtype=torch.bfloat16)
463
+ tokens = state.layout.caps.max_tokens
464
+ self._feedback_token_embedding = torch.empty(
465
+ (tokens, self.hidden_size), **factory
466
+ )
467
+ self._feedback_multi_state = torch.empty(
468
+ (tokens, self.hc_count, self.hidden_size), **factory
469
+ )
470
+ self._feedback_output = torch.empty(
471
+ state.layout.output_storage_shape(), **factory
472
+ )
473
+
474
+ def _prepare_feedback_call(self, state):
475
+ self._publish_feedback_storage(state)
476
+ token_embedding = self._feedback_token_embedding
477
+ multi_state = self._feedback_multi_state
478
+ output = self._feedback_output
479
+ assert token_embedding is not None
480
+ assert multi_state is not None
481
+ assert output is not None
482
+ assert self._feedback_scratch_spec is not None
483
+ # Trial scratch belongs to the trial; the serving workspace is drawn
484
+ # from only inside the feedback op body.
485
+ shape, dtype = self._feedback_scratch_spec
486
+ scratch = torch.empty(shape, dtype=dtype, device=token_embedding.device)
487
+ return self._feedback_call(
488
+ state,
489
+ scratch=scratch,
490
+ token_embedding=token_embedding,
491
+ multi_state=multi_state,
492
+ output=output,
493
+ )
494
+
495
+ def _feedback_benchmark_factory(self):
496
+ shared = None
497
+
498
+ def factory(state):
499
+ nonlocal shared
500
+ tensors = None if shared is None else tuple(ref() for ref in shared)
501
+ if tensors is None or any(tensor is None for tensor in tensors):
502
+ (spec,) = state.layout.scratch_specs()
503
+ scratch = torch.empty(spec.shape, dtype=spec.dtype, device=spec.device)
504
+ tensor_factory = dict(device=spec.device, dtype=torch.bfloat16)
505
+ tokens = state.layout.caps.max_tokens
506
+ token_embedding = torch.empty(
507
+ (tokens, self.hidden_size), **tensor_factory
508
+ )
509
+ multi_state = torch.empty(
510
+ (tokens, self.hc_count, self.hidden_size), **tensor_factory
511
+ )
512
+ output = torch.empty(
513
+ state.layout.output_storage_shape(), **tensor_factory
514
+ )
515
+ tensors = scratch, token_embedding, multi_state, output
516
+ shared = tuple(weakref.ref(tensor) for tensor in tensors)
517
+ scratch, token_embedding, multi_state, output = tensors
518
+ return self._feedback_call(
519
+ state,
520
+ scratch=scratch,
521
+ token_embedding=token_embedding,
522
+ multi_state=multi_state,
523
+ output=output,
524
+ )
525
+
526
+ return factory
527
+
528
+ def _feedback_call(
529
+ self,
530
+ state,
531
+ *,
532
+ scratch,
533
+ token_embedding,
534
+ multi_state,
535
+ output,
536
+ ):
537
+ from b12x.preparation import PreparedCall
538
+
539
+ def produce() -> None:
540
+ # Prime the installed fixed route with real-shaped, nonzero
541
+ # activations while borrowing the loaded norm and FC weights.
542
+ token_embedding.fill_(0.125)
543
+ multi_state.fill_(0.25)
544
+
545
+ def reset() -> None:
546
+ scratch.zero_()
547
+ output.zero_()
548
+
549
+ def run():
550
+ return state.run_tensors(
551
+ token_embedding,
552
+ multi_state,
553
+ self.pre_fc_norm_embedding.weight,
554
+ self.pre_fc_norm_hidden.weight,
555
+ self.fc_embedding.weight,
556
+ self.fc_hidden.weight,
557
+ scratch,
558
+ output,
559
+ eps=self.config.rms_norm_eps,
560
+ )
561
+
562
+ return PreparedCall(
563
+ run=run,
564
+ produce=produce,
565
+ reset=reset,
566
+ capture_safe=False,
567
+ )
568
+
569
+ def _run_feedback(
570
+ self,
571
+ token_embedding: torch.Tensor,
572
+ multi_state: torch.Tensor,
573
+ ) -> None:
574
+ plan = self._plan
575
+ if plan is None or self._feedback_scratch_spec is None:
576
+ raise PreparationResourceUnavailableError(
577
+ f"{self.prefix} MTP feedback is not prepared"
578
+ )
579
+ from vllm.v1.worker.workspace import current_workspace_manager
580
+
581
+ (scratch,) = current_workspace_manager().get_simultaneous(
582
+ self._feedback_scratch_spec
583
+ )
584
+ num_tokens = token_embedding.shape[0]
585
+ if num_tokens > self._feedback_caps.max_tokens:
586
+ raise ValueError(
587
+ "Qwen4Exp MTP feedback capacity exceeded: "
588
+ f"{num_tokens}/{self._feedback_caps.max_tokens} tokens"
589
+ )
590
+ multi_state = multi_state.reshape(num_tokens, self.hc_count, self.hidden_size)
591
+ self._feedback_token_embedding[:num_tokens].copy_(token_embedding)
592
+ self._feedback_multi_state[:num_tokens].copy_(multi_state)
593
+ binding = _mtp_api().bind(
594
+ plan,
595
+ scratch=scratch,
596
+ token_embedding=self._feedback_token_embedding,
597
+ multi_state=self._feedback_multi_state,
598
+ token_norm_weight=self.pre_fc_norm_embedding.weight,
599
+ state_norm_weight=self.pre_fc_norm_hidden.weight,
600
+ embedding_fc_weight=self.fc_embedding.weight.data,
601
+ hidden_fc_weight=self.fc_hidden.weight.data,
602
+ output=self._feedback_output,
603
+ tokens=num_tokens,
604
+ )
605
+ _mtp_api().run(binding, eps=self.config.rms_norm_eps)
606
+
607
+ def _prepare_feedback(
608
+ self,
609
+ token_embedding: torch.Tensor,
610
+ hidden_states: torch.Tensor,
611
+ ) -> torch.Tensor:
612
+ if not self._use_b12x:
613
+ embedding = self.fc_embedding(self.pre_fc_norm_embedding(token_embedding))
614
+ multi_state = self.pre_fc_norm_hidden(hidden_states).reshape(
615
+ -1, self.hc_count, self.hidden_size
616
+ )
617
+ return (self.fc_hidden(multi_state) + embedding.unsqueeze(1)).flatten(-2)
618
+ if self._plan is None or self._feedback_output is None:
619
+ raise PreparationResourceUnavailableError(
620
+ f"{self.prefix} MTP feedback is not prepared"
621
+ )
622
+ if torch.compiler.is_compiling():
623
+ torch.ops.vllm.qwen4_exp_b12x_mtp_feedback(
624
+ token_embedding,
625
+ hidden_states,
626
+ self._feedback_output,
627
+ self._b12x_layer_name,
628
+ )
629
+ else:
630
+ self._run_feedback(token_embedding, hidden_states)
631
+ return self._feedback_output[: token_embedding.shape[0]].flatten(-2)
632
+
633
+ def forward(
634
+ self,
635
+ input_ids: torch.Tensor | None,
636
+ positions: torch.Tensor,
637
+ hidden_states: torch.Tensor | None = None,
638
+ intermediate_tensors: IntermediateTensors | None = None,
639
+ inputs_embeds: torch.Tensor | None = None,
640
+ spec_step_idx: int = 0,
641
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor] | IntermediateTensors:
642
+ if get_pp_group().is_first_rank:
643
+ if hidden_states is None:
644
+ raise ValueError("MTP requires target-model hidden states")
645
+ if inputs_embeds is None:
646
+ if input_ids is None:
647
+ raise ValueError("input_ids or inputs_embeds is required")
648
+ inputs_embeds = self.embed_input_ids(input_ids)
649
+ hidden_states = self._prepare_feedback(inputs_embeds, hidden_states)
650
+ else:
651
+ if intermediate_tensors is None:
652
+ raise ValueError("pipeline stage requires intermediate tensors")
653
+ hidden_states = intermediate_tensors["hidden_states"]
654
+
655
+ layer = self.layers[spec_step_idx % self.num_mtp_layers]
656
+ hidden_states, block_output, injection = layer(
657
+ hidden_states=hidden_states,
658
+ prev_block_output=None,
659
+ prev_injection=None,
660
+ positions=positions,
661
+ input_ids=None,
662
+ query_start_loc=None,
663
+ ngram_context=None,
664
+ output_indices=self._prefill_output_indices,
665
+ )
666
+ if not get_pp_group().is_last_rank:
667
+ hidden_states = layer.mlp_hyper_connection.combine(
668
+ hidden_states, block_output, injection
669
+ )
670
+ return IntermediateTensors({"hidden_states": hidden_states})
671
+
672
+ multi_hidden, sample_hidden_states, _ = (
673
+ self.hyper_connection_mixer.combine_and_mix(
674
+ hidden_states, block_output, injection
675
+ )
676
+ )
677
+ return sample_hidden_states, multi_hidden
678
+
679
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
680
+ weights = maybe_fuse_shared_experts(
681
+ weights,
682
+ enabled=self.is_fused_shared_expert_enabled,
683
+ n_routed_experts=getattr(self.config, "num_experts", 0) or 0,
684
+ n_shared_experts=1,
685
+ ckpt_prefix="mlp.shared_expert",
686
+ )
687
+ loader = AutoWeightsLoader(
688
+ self,
689
+ ignore_unexpected_suffixes=(_QWEN4_EXP_IGNORED_MISSING_SUFFIXES.copy()),
690
+ )
691
+ mapper = self.hf_to_vllm_mapper | WeightsMapper(
692
+ orig_to_new_substr={"hyper_connection_mixer.block_inject_weight": None}
693
+ )
694
+ return loader.load_weights(weights, mapper=mapper)
695
+
696
+
697
+ def _mtp_feedback_op(
698
+ token_embedding: torch.Tensor,
699
+ multi_state: torch.Tensor,
700
+ output: torch.Tensor,
701
+ layer_name: LayerNameType,
702
+ ) -> None:
703
+ layer = b12x_layer(_resolve_layer_name(layer_name))
704
+ layer._run_feedback(token_embedding, multi_state)
705
+
706
+
707
+ def _mtp_feedback_fake(
708
+ token_embedding: torch.Tensor,
709
+ multi_state: torch.Tensor,
710
+ output: torch.Tensor,
711
+ layer_name: LayerNameType,
712
+ ) -> None:
713
+ return
714
+
715
+
716
+ direct_register_custom_op(
717
+ op_name="qwen4_exp_b12x_mtp_feedback",
718
+ op_func=_mtp_feedback_op,
719
+ mutates_args=["output"],
720
+ fake_impl=_mtp_feedback_fake,
721
+ )
722
+
723
+
724
+ logger = init_logger(__name__)
725
+
726
+ # Reduced draft vocabulary: path to a JSON list of target token ids. The NVFP4
727
+ # draft head then holds only those rows (the 4 draft steps read ~1.4 ms of
728
+ # full-vocab head per step otherwise) and logits are scattered back to the
729
+ # full vocabulary with -inf elsewhere, so rejection sampling stays exact.
730
+ _DRAFT_VOCAB = os.environ.get("VLLM_QWEN38_DRAFT_VOCAB")
731
+ # Rowwise-FP8 decode GEMVs for the draft layer's unquantized bf16 projections
732
+ # (self-attention, shared expert, fc_embedding/fc_hidden; ~139 MB per draft step).
733
+ _MTP_FP8 = os.environ.get("VLLM_QWEN38_MTP_FP8", "0") == "1"
734
+ # Store the draft-vocabulary rows as row-major NVFP4 (0.56 vs 1.03 bytes per
735
+ # weight), requantized from the shared MXFP8 head; the stock draft head is NVFP4.
736
+ _DRAFT_NVFP4 = os.environ.get("VLLM_QWEN38_DRAFT_NVFP4", "0") == "1"
737
+ # Keep the draft layer's KV cache in bf16 while the target's stays FP8: the draft
738
+ # attends over its own cache at every draft step, so FP8 rounding there costs
739
+ # acceptance (reported ~+7.5% greedy decode on the NVFP4 checkpoint).
740
+ _MTP_BF16_KV = os.environ.get("VLLM_QWEN38_MTP_BF16_KV", "0") == "1"
741
+
742
+
743
+ @support_torch_compile(
744
+ dynamic_arg_dims={
745
+ "input_ids": 0,
746
+ "positions": -1,
747
+ "intermediate_tensors": 0,
748
+ "inputs_embeds": 0,
749
+ "hidden_states": 0,
750
+ }
751
+ )
752
+ class Qwen4ExpMTP(
753
+ LocalArgmaxMixin,
754
+ nn.Module,
755
+ SupportsPP,
756
+ Qwen4ExpMixtureOfExperts,
757
+ ):
758
+ checkpoint_weight_name_prefixes = tuple(
759
+ checkpoint_prefix + weight_prefix
760
+ for checkpoint_prefix in ("", "model.language_model.", "language_model.")
761
+ for weight_prefix in (
762
+ "mtp.",
763
+ "model.mtp.",
764
+ "embed_tokens.",
765
+ "model.embed_tokens.",
766
+ "lm_head.",
767
+ "model.lm_head.",
768
+ "shared_head.head.",
769
+ "model.shared_head.head.",
770
+ )
771
+ )
772
+
773
+ packed_modules_mapping = {
774
+ "qkv_proj": ["q_proj", "k_proj", "v_proj"],
775
+ "gate_up_proj": ["gate_proj", "up_proj"],
776
+ "in_proj_qkvz": ["in_proj_qkv", "in_proj_z"],
777
+ "in_proj_ba": ["in_proj_b", "in_proj_a"],
778
+ "input_mix_weight_down_block_inject": [
779
+ "input_mix_weight_down",
780
+ "block_inject_weight",
781
+ "_input_mix_padding",
782
+ ],
783
+ }
784
+
785
+ def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
786
+ config: Qwen4ExpTextConfig = vllm_config.model_config.hf_text_config
787
+ self.vllm_config = vllm_config
788
+ if vllm_config.cache_config.mamba_cache_mode == "all":
789
+ raise NotImplementedError("Qwen4Exp MTP requires --mamba-cache-mode=align")
790
+ self.quant_config = vllm_config.quant_config
791
+ super().__init__()
792
+ self.has_own_lm_head = envs.VLLM_MTP_NVFP4_LM_HEAD
793
+ if (
794
+ self.has_own_lm_head
795
+ and envs.is_set("VLLM_MTP_NVFP4_LM_HEAD")
796
+ and config.tie_word_embeddings
797
+ ):
798
+ raise ValueError("NVFP4 draft head requires untied word embeddings")
799
+ self.config = config
800
+ self.model = Qwen4ExpMultiTokenPredictor(
801
+ vllm_config=vllm_config,
802
+ prefix=maybe_prefix(prefix, "mtp"),
803
+ )
804
+
805
+ if get_pp_group().is_last_rank:
806
+ self.lm_head = ParallelLMHead(
807
+ config.vocab_size,
808
+ config.hidden_size,
809
+ quant_config=self.quant_config,
810
+ prefix=maybe_prefix(prefix, "lm_head"),
811
+ lm_head_quantization="nvfp4" if self.has_own_lm_head else None,
812
+ )
813
+ self.has_own_lm_head = self.lm_head.runtime_lm_head_quantization == "nvfp4"
814
+ if config.tie_word_embeddings:
815
+ self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
816
+ else:
817
+ self.lm_head = PPMissingLayer()
818
+
819
+ self.logits_processor = LogitsProcessor(
820
+ config.vocab_size,
821
+ lm_head=self.lm_head,
822
+ )
823
+ self.make_empty_intermediate_tensors = (
824
+ self.model.make_empty_intermediate_tensors
825
+ )
826
+ self.set_moe_parameters(self.model.layers)
827
+ if not uses_b12x(vllm_config):
828
+ enable_qwen4_exp_low_latency_gemm(self, vllm_config.model_config.dtype)
829
+
830
+ def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
831
+ return self.model.embed_input_ids(input_ids)
832
+
833
+ def forward(
834
+ self,
835
+ input_ids: torch.Tensor | None,
836
+ positions: torch.Tensor,
837
+ hidden_states: torch.Tensor | None = None,
838
+ intermediate_tensors: IntermediateTensors | None = None,
839
+ inputs_embeds: torch.Tensor | None = None,
840
+ spec_step_idx: int = 0,
841
+ **kwargs: object,
842
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor] | IntermediateTensors:
843
+ return self.model(
844
+ input_ids,
845
+ positions,
846
+ hidden_states,
847
+ intermediate_tensors,
848
+ inputs_embeds,
849
+ spec_step_idx=spec_step_idx,
850
+ )
851
+
852
+ def compute_logits(
853
+ self,
854
+ hidden_states: torch.Tensor,
855
+ spec_step_idx: int = 0,
856
+ ) -> torch.Tensor | None:
857
+ subset = self._draft_subset()
858
+ if subset is not None and hidden_states.dim() == 2 and hidden_states.shape[0] <= 16:
859
+ ids, *weights = subset
860
+ if len(weights) == 3:
861
+ from .ops.nvfp4_gemv import nvfp4_gemv
862
+
863
+ logits = nvfp4_gemv(hidden_states.contiguous(), *weights)
864
+ else:
865
+ from .ops.mxfp8_gemv import mxfp8_gemv
866
+
867
+ logits = mxfp8_gemv(
868
+ hidden_states.contiguous(), *weights, ids.numel(), self.config.hidden_size
869
+ )
870
+ full = logits.new_full(
871
+ (logits.shape[0], self.config.vocab_size), float("-inf"), dtype=torch.float32
872
+ )
873
+ full.index_copy_(1, ids, logits.float())
874
+ return full
875
+ return self.logits_processor(self.lm_head, hidden_states)
876
+
877
+ def _draft_subset(self):
878
+ """(ids, MXFP8 values, E8M0 scale rows) of the draft-vocabulary head rows.
879
+
880
+ Gathered once, outside graph capture, from the shared target lm_head's
881
+ b12x-packed MXFP8 weight (VLLM_MTP_NVFP4_LM_HEAD=0 makes the draft share
882
+ it). None when disabled or the head is not MXFP8-packed.
883
+ """
884
+ cached = self.__dict__.get("_draft_subset_cache")
885
+ if cached is not None or not _DRAFT_VOCAB:
886
+ return cached or None
887
+ if torch.cuda.is_current_stream_capturing():
888
+ return None
889
+ packed = getattr(self.lm_head, "b12x_mxfp8_packed_weight", None)
890
+ if packed is None:
891
+ logger.warning_once(
892
+ "VLLM_QWEN38_DRAFT_VOCAB needs a b12x MXFP8 lm_head "
893
+ "(VLLM_MXFP8_LM_HEAD=1, VLLM_MTP_NVFP4_LM_HEAD=0); using the full head."
894
+ )
895
+ self.__dict__["_draft_subset_cache"] = ()
896
+ return None
897
+ with open(_DRAFT_VOCAB) as f:
898
+ ids = sorted({int(t) for t in json.load(f)})
899
+ device = packed.weight.values.device
900
+ ids = torch.tensor(ids, dtype=torch.long, device=device)
901
+ scale_rows = packed.weight.scale_rows[0]
902
+ if scale_rows.dtype != torch.uint8:
903
+ scale_rows = scale_rows.view(torch.uint8)
904
+ if _DRAFT_NVFP4:
905
+ from .ops.nvfp4_gemv import quantize_nvfp4_rows
906
+
907
+ k = self.config.hidden_size
908
+ n = ids.numel()
909
+ nv = (
910
+ torch.empty((n, k // 2), dtype=torch.uint8, device=device),
911
+ torch.empty((n, k // 16), dtype=torch.uint8, device=device),
912
+ torch.empty(n, dtype=torch.float32, device=device),
913
+ )
914
+ for r in range(0, n, 2048): # small temporaries: this runs in the profile run
915
+ rows = ids[r : r + 2048]
916
+ v = packed.weight.values.index_select(0, rows)[:, :k].float()
917
+ e = scale_rows.index_select(0, rows)[:, : k // 32].to(torch.int32)
918
+ e = (e << 23).view(torch.float32) # E8M0 -> 2^(e-127)
919
+ w = (v.view(v.shape[0], -1, 32) * e[..., None]).view(v.shape[0], k)
920
+ for dst, src in zip(nv, quantize_nvfp4_rows(w)):
921
+ dst[r : r + 2048] = src
922
+ cache = (ids, *nv)
923
+ kind = "NVFP4 requantized from"
924
+ else:
925
+ cache = (
926
+ ids,
927
+ packed.weight.values.index_select(0, ids).contiguous(),
928
+ scale_rows.index_select(0, ids).contiguous(),
929
+ )
930
+ kind = "MXFP8 rows of"
931
+ self.__dict__["_draft_subset_cache"] = cache
932
+ logger.info(
933
+ "Qwen3.8 MTP draft head: %d-token vocabulary subset (%s the shared lm_head).",
934
+ ids.numel(), kind,
935
+ )
936
+ return cache
937
+
938
+ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
939
+ qsa_layer_ids = frozenset(range(self.model.num_mtp_layers))
940
+
941
+ def remap_weight_names():
942
+ for name, weight in weights:
943
+ remapped_name = _remap_mtp_weight_name(name)
944
+ if remapped_name is not None:
945
+ yield (
946
+ _remap_qsa_cache_scale_name(remapped_name, qsa_layer_ids),
947
+ weight,
948
+ )
949
+
950
+ loader = AutoWeightsLoader(
951
+ self,
952
+ ignore_unexpected_suffixes=(_QWEN4_EXP_IGNORED_MISSING_SUFFIXES.copy()),
953
+ )
954
+ mapper = WeightsMapper(
955
+ orig_to_new_substr={"hyper_connection_mixer.block_inject_weight": None}
956
+ )
957
+ loaded = loader.load_weights(remap_weight_names(), mapper=mapper)
958
+ if _MTP_FP8:
959
+ from .ops.hc_fp8_gemv import install_fp8_decode_linears
960
+
961
+ names = install_fp8_decode_linears(self)
962
+ logger.info(
963
+ "Qwen3.8 MTP: %d bf16 projections use rowwise-FP8 decode GEMVs.", len(names)
964
+ )
965
+ return loaded
966
+
967
+
968
+ __all__ = ["Qwen4ExpMTP", "Qwen4ExpMultiTokenPredictor"]
recipe/patches/qwen4_exp/nvidia/ops/gdn_deferred.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Deferred-commit GDN decode for speculative verify steps (Qwen3.8 GatedDeltaNet).
3
+
4
+ Stock b12x verify reads the committed fp32 state once and writes one full state per
5
+ verify token (5 x 3.1 MB per layer at MTP-4); the next step reads only the column of
6
+ the last accepted token. gdn_verify keeps the recurrence in registers and writes small
7
+ per-token records instead (decay g, delta per value row, normalized k). Once
8
+ acceptance is known, gdn_commit replays the accepted tokens from the same source state
9
+ and writes column n-1 only. Rows that end a request, cross a mamba block or take a
10
+ logits-only step get every verify column written, exactly as stock verify would have,
11
+ so prefix-cache checkpoint/align copies always find the columns they read.
12
+
13
+ Math (b12x _PackedRecurrentQwenKernel, ungrouped path), per value head h, key head
14
+ h // (VH/KH), per token t:
15
+ q^ = q * rsqrt(|q|^2 + 1e-6) * scale, k^ = k * rsqrt(|k|^2 + 1e-6)
16
+ g = exp(-exp(A_log[h]) * softplus(a + dt_bias[h])) (softplus linear above 20)
17
+ beta = bf16(sigmoid(b))
18
+ S = S * g ; delta = (v - S k^) * beta ; S = S + delta k^T ; o = S q^
19
+ """
20
+
21
+ import torch
22
+ import triton
23
+ import triton.language as tl
24
+
25
+ KEY_DIM = 128
26
+ VALUE_DIM = 128
27
+ ROWS = 32 # value rows per program (b12x uses 32 per CTA)
28
+
29
+
30
+ @triton.jit
31
+ def _gdn_verify_kernel(
32
+ qkv_ptr, a_ptr, b_ptr, alog_ptr, dtb_ptr,
33
+ state_ptr, idx_ptr, nacc_ptr, qsl_ptr, nseq_ptr, valid_ptr,
34
+ out_ptr, g_rec_ptr, d_rec_ptr, k_rec_ptr,
35
+ stride_qkv, stride_a, stride_b, stride_ot, stride_oh,
36
+ slot_stride, idx_stride_r, idx_stride_c,
37
+ scale,
38
+ KEY_HEADS: tl.constexpr, VALUE_HEADS: tl.constexpr, COLUMNS: tl.constexpr,
39
+ ROWS: tl.constexpr, KD: tl.constexpr, VD: tl.constexpr,
40
+ ):
41
+ pid = tl.program_id(0)
42
+ tiles: tl.constexpr = VD // ROWS
43
+ r = pid // (VALUE_HEADS * tiles)
44
+ vh = (pid // tiles) % VALUE_HEADS
45
+ tile = pid % tiles
46
+ if r >= tl.load(nseq_ptr):
47
+ return
48
+ kh = vh // (VALUE_HEADS // KEY_HEADS)
49
+ start = tl.load(qsl_ptr + r)
50
+ end = tl.load(qsl_ptr + r + 1)
51
+ col = tl.load(nacc_ptr + r) - 1
52
+ src = tl.load(idx_ptr + r * idx_stride_r + col * idx_stride_c).to(tl.int64)
53
+ rows = tile * ROWS + tl.arange(0, ROWS)
54
+ cols = tl.arange(0, KD)
55
+ S = tl.load(state_ptr + src * slot_stride + vh * (VD * KD) + rows[:, None] * KD + cols[None, :])
56
+ a_scale = tl.exp(tl.load(alog_ptr + vh).to(tl.float32))
57
+ dtb = tl.load(dtb_ptr + vh).to(tl.float32)
58
+ for t in tl.static_range(COLUMNS):
59
+ tok = start + t
60
+ if tok < end:
61
+ base = qkv_ptr + tok.to(tl.int64) * stride_qkv
62
+ q = tl.load(base + kh * KD + cols).to(tl.float32)
63
+ k = tl.load(base + KEY_HEADS * KD + kh * KD + cols).to(tl.float32)
64
+ v = tl.load(base + 2 * KEY_HEADS * KD + vh * VD + rows).to(tl.float32)
65
+ qn = q * (1.0 / tl.sqrt(tl.sum(q * q, axis=0) + 1e-6)) * scale
66
+ kn = k * (1.0 / tl.sqrt(tl.sum(k * k, axis=0) + 1e-6))
67
+ sp = tl.load(a_ptr + tok * stride_a + vh).to(tl.float32) + dtb
68
+ sp = tl.where(sp <= 20.0, tl.log(1.0 + tl.exp(sp)), sp)
69
+ g = tl.exp(-a_scale * sp)
70
+ beta = 1.0 / (1.0 + tl.exp(-tl.load(b_ptr + tok * stride_b + vh).to(tl.float32)))
71
+ beta = beta.to(tl.bfloat16).to(tl.float32)
72
+ S = S * g
73
+ delta = (v - tl.sum(S * kn[None, :], axis=1)) * beta
74
+ S = S + delta[:, None] * kn[None, :]
75
+ o = tl.sum(S * qn[None, :], axis=1)
76
+ tl.store(out_ptr + tok * stride_ot + vh * stride_oh + rows, o.to(tl.bfloat16))
77
+ tl.store(d_rec_ptr + (tok * VALUE_HEADS + vh) * VD + rows, delta)
78
+ if tile == 0:
79
+ tl.store(g_rec_ptr + tok * VALUE_HEADS + vh, g)
80
+ if vh % (VALUE_HEADS // KEY_HEADS) == 0:
81
+ tl.store(k_rec_ptr + (tok * KEY_HEADS + kh) * KD + cols, kn)
82
+ if (vh == 0) & (tile == 0):
83
+ tl.store(valid_ptr + r, 1)
84
+
85
+
86
+ @triton.jit
87
+ def _gdn_verify_flat_kernel(
88
+ qkv_ptr, a_ptr, b_ptr, alog_ptr, dtb_ptr,
89
+ state_ptr, idx_ptr, nacc_ptr, qsl_ptr, nseq_ptr, valid_ptr,
90
+ out_ptr, g_rec_ptr, d_rec_ptr, k_rec_ptr,
91
+ stride_qkv, stride_a, stride_b, stride_ot, stride_oh,
92
+ slot_stride, idx_stride_r, idx_stride_c,
93
+ scale,
94
+ KEY_HEADS: tl.constexpr, VALUE_HEADS: tl.constexpr, COLUMNS: tl.constexpr,
95
+ ROWS: tl.constexpr, KD: tl.constexpr, VD: tl.constexpr,
96
+ ):
97
+ """_gdn_verify_kernel without per-token branches: the state load is issued first,
98
+ every token's inputs load up front (masked), and each step's S.k and S.q are two
99
+ independent reductions over the pre-decay state:
100
+ delta = (v - g (S k^)) beta ; o = g (S q^) + delta (k^.q^) ; S = g S + delta k^T
101
+ """
102
+ pid = tl.program_id(0)
103
+ tiles: tl.constexpr = VD // ROWS
104
+ r = pid // (VALUE_HEADS * tiles)
105
+ vh = (pid // tiles) % VALUE_HEADS
106
+ tile = pid % tiles
107
+ if r >= tl.load(nseq_ptr):
108
+ return
109
+ kh = vh // (VALUE_HEADS // KEY_HEADS)
110
+ start = tl.load(qsl_ptr + r)
111
+ end = tl.load(qsl_ptr + r + 1)
112
+ col = tl.load(nacc_ptr + r) - 1
113
+ src = tl.load(idx_ptr + r * idx_stride_r + col * idx_stride_c).to(tl.int64)
114
+ rows = tile * ROWS + tl.arange(0, ROWS)
115
+ cols = tl.arange(0, KD)
116
+ S = tl.load(state_ptr + src * slot_stride + vh * (VD * KD) + rows[:, None] * KD + cols[None, :])
117
+ a_scale = tl.exp(tl.load(alog_ptr + vh).to(tl.float32))
118
+ dtb = tl.load(dtb_ptr + vh).to(tl.float32)
119
+ for t in tl.static_range(COLUMNS):
120
+ tok = start + t
121
+ live = tok < end
122
+ base = qkv_ptr + tok.to(tl.int64) * stride_qkv
123
+ q = tl.load(base + kh * KD + cols, mask=live & (cols < KD), other=0.0).to(tl.float32)
124
+ k = tl.load(base + KEY_HEADS * KD + kh * KD + cols, mask=live & (cols < KD), other=0.0).to(tl.float32)
125
+ v = tl.load(base + 2 * KEY_HEADS * KD + vh * VD + rows, mask=live & (rows < VD), other=0.0).to(tl.float32)
126
+ qn = q * (1.0 / tl.sqrt(tl.sum(q * q, axis=0) + 1e-6)) * scale
127
+ kn = k * (1.0 / tl.sqrt(tl.sum(k * k, axis=0) + 1e-6))
128
+ sp = tl.load(a_ptr + tok * stride_a + vh, mask=live, other=0.0).to(tl.float32) + dtb
129
+ sp = tl.where(sp <= 20.0, tl.log(1.0 + tl.exp(sp)), sp)
130
+ g = tl.exp(-a_scale * sp)
131
+ beta = 1.0 / (1.0 + tl.exp(-tl.load(b_ptr + tok * stride_b + vh, mask=live, other=0.0).to(tl.float32)))
132
+ beta = beta.to(tl.bfloat16).to(tl.float32)
133
+ sk = tl.sum(S * kn[None, :], axis=1)
134
+ sq = tl.sum(S * qn[None, :], axis=1)
135
+ delta = (v - g * sk) * beta
136
+ o = g * sq + delta * tl.sum(kn * qn, axis=0)
137
+ S = S * g + delta[:, None] * kn[None, :]
138
+ tl.store(out_ptr + tok * stride_ot + vh * stride_oh + rows, o.to(tl.bfloat16), mask=live & (rows < VD))
139
+ tl.store(d_rec_ptr + (tok * VALUE_HEADS + vh) * VD + rows, delta, mask=live & (rows < VD))
140
+ tl.store(g_rec_ptr + tok * VALUE_HEADS + vh, g, mask=live & (tile == 0))
141
+ tl.store(k_rec_ptr + (tok * KEY_HEADS + kh) * KD + cols, kn,
142
+ mask=live & (tile == 0) & (vh % (VALUE_HEADS // KEY_HEADS) == 0) & (cols < KD))
143
+ if (vh == 0) & (tile == 0):
144
+ tl.store(valid_ptr + r, 1)
145
+
146
+
147
+ _FLAT = __import__("os").environ.get("VLLM_QWEN38_GDN_VERIFY_FLAT", "0") == "1"
148
+ # Swept at 1 live row of 4 (36 layers): 32x8 0.905 ms, 16x2 0.838, 8x1 0.763.
149
+ _VERIFY_ROWS = 8
150
+ _VERIFY_WARPS = 1
151
+
152
+
153
+ def gdn_verify(
154
+ mixed_qkv, a, b, A_log, dt_bias, state, state_indices, num_accepted, query_start_loc,
155
+ num_seqs, valid, out, records, scale, max_seqs, key_heads=16, value_heads=48, flat=None,
156
+ rows=None, num_warps=None,
157
+ ):
158
+ """Spec-verify recurrence. ``out`` [T, VH, 128] bf16 gets the pre-norm output;
159
+ records get (g [T,VH], delta [T,VH,128], k^ [T,KH,128]) fp32; ``state`` is not
160
+ written. Marks ``valid[r] = 1`` for every live request row."""
161
+ g_rec, d_rec, k_rec = records
162
+ flat = _FLAT if flat is None else flat
163
+ rows = rows or (_VERIFY_ROWS if flat else ROWS)
164
+ num_warps = num_warps or (_VERIFY_WARPS if flat else 8)
165
+ grid = (max_seqs * value_heads * (VALUE_DIM // rows),)
166
+ kernel = _gdn_verify_flat_kernel if flat else _gdn_verify_kernel
167
+ kernel[grid](
168
+ mixed_qkv, a, b, A_log, dt_bias,
169
+ state, state_indices, num_accepted, query_start_loc, num_seqs, valid,
170
+ out, g_rec, d_rec, k_rec,
171
+ mixed_qkv.stride(0), a.stride(0), b.stride(0), out.stride(0), out.stride(1),
172
+ state.stride(0), state_indices.stride(0), state_indices.stride(1),
173
+ scale,
174
+ KEY_HEADS=key_heads, VALUE_HEADS=value_heads, COLUMNS=state_indices.shape[1],
175
+ ROWS=rows, KD=KEY_DIM, VD=VALUE_DIM,
176
+ num_warps=num_warps,
177
+ )
178
+
179
+
180
+ @triton.jit
181
+ def _gdn_commit_kernel(
182
+ state_tab, g_tab, d_tab, k_tab, idx_tab,
183
+ nacc_ptr, qsl_ptr, nseq_ptr, valid_ptr,
184
+ nsamp_ptr, req_map_ptr, ncomp_ptr, cap_ptr, cap_stride, num_rows,
185
+ slot_stride, idx_stride_r, idx_stride_c, mamba_block,
186
+ FORCE_FULL: tl.constexpr, HAS_CAPTURE: tl.constexpr, NUM_CAPTURES: tl.constexpr,
187
+ KEY_HEADS: tl.constexpr, VALUE_HEADS: tl.constexpr, COLUMNS: tl.constexpr,
188
+ ROWS: tl.constexpr, KD: tl.constexpr, VD: tl.constexpr, ROW_MODE: tl.constexpr,
189
+ ):
190
+ layer = tl.program_id(1)
191
+ pid = tl.program_id(0)
192
+ tiles: tl.constexpr = VD // ROWS
193
+ r = pid // (VALUE_HEADS * tiles)
194
+ vh = (pid // tiles) % VALUE_HEADS
195
+ tile = pid % tiles
196
+ # Only real batch rows: graph padding rows carry stale state indices that can alias a
197
+ # live request's blocks, and have no num_sampled/req_map entry.
198
+ if (r >= tl.load(nseq_ptr)) | (r >= num_rows):
199
+ return
200
+ if tl.load(valid_ptr + r) == 0:
201
+ return
202
+ req = tl.load(req_map_ptr + r)
203
+ if req < 0:
204
+ return
205
+ start = tl.load(qsl_ptr + r)
206
+ T = tl.load(qsl_ptr + r + 1) - start
207
+ n = tl.minimum(tl.maximum(tl.load(nsamp_ptr + r).to(tl.int32), 1), T)
208
+ full = FORCE_FULL
209
+ after = tl.load(ncomp_ptr + req).to(tl.int64)
210
+ full = full | ((after // mamba_block) != ((after - n) // mamba_block))
211
+ if HAS_CAPTURE:
212
+ caps = tl.load(cap_ptr + r * cap_stride + tl.arange(0, NUM_CAPTURES))
213
+ full = full | (tl.max(caps, axis=0) > 0)
214
+ # ROW_MODE 1: only rows that need every column; 2: only the others (0: all rows)
215
+ if ROW_MODE == 1:
216
+ if not full:
217
+ return
218
+ if ROW_MODE == 2:
219
+ if full:
220
+ return
221
+ last = tl.where(full, T, n)
222
+ state_ptr = tl.load(state_tab + layer).to(tl.pointer_type(tl.float32))
223
+ # Per-layer staged block ids: vLLM spreads the GDN layers over several KV-cache
224
+ # groups that alias the same memory with different block tables.
225
+ idx_ptr = tl.load(idx_tab + layer).to(tl.pointer_type(tl.int32))
226
+ g_rec = tl.load(g_tab + layer).to(tl.pointer_type(tl.float32))
227
+ d_rec = tl.load(d_tab + layer).to(tl.pointer_type(tl.float32))
228
+ k_rec = tl.load(k_tab + layer).to(tl.pointer_type(tl.float32))
229
+ kh = vh // (VALUE_HEADS // KEY_HEADS)
230
+ col = tl.load(nacc_ptr + r) - 1
231
+ src = tl.load(idx_ptr + r * idx_stride_r + col * idx_stride_c).to(tl.int64)
232
+ rows = tile * ROWS + tl.arange(0, ROWS)
233
+ cols = tl.arange(0, KD)
234
+ offs = vh * (VD * KD) + rows[:, None] * KD + cols[None, :]
235
+ S = tl.load(state_ptr + src * slot_stride + offs)
236
+ for t in tl.static_range(COLUMNS):
237
+ if t < last:
238
+ tok = start + t
239
+ g = tl.load(g_rec + tok * VALUE_HEADS + vh)
240
+ delta = tl.load(d_rec + (tok * VALUE_HEADS + vh) * VD + rows)
241
+ kn = tl.load(k_rec + (tok * KEY_HEADS + kh) * KD + cols)
242
+ S = S * g
243
+ S = S + delta[:, None] * kn[None, :]
244
+ if full | (t == n - 1):
245
+ dst = tl.load(idx_ptr + r * idx_stride_r + t * idx_stride_c).to(tl.int64)
246
+ tl.store(state_ptr + dst * slot_stride + offs, S)
247
+
248
+
249
+ def gdn_commit(
250
+ tables, state_indices, num_accepted, query_start_loc, num_seqs, valid,
251
+ num_sampled, req_map, num_computed, capture_tokens, mamba_block, slot_stride, max_seqs,
252
+ force_full=False, key_heads=16, value_heads=48, row_mode=0,
253
+ ):
254
+ """Replay each deferred row's accepted tokens in every layer of ``tables``
255
+ (int64 device tensors of per-layer state/g/delta/k^/staged-index base addresses;
256
+ ``state_indices`` supplies only the index strides and column count).
257
+
258
+ ``state_indices``/``num_accepted``/``query_start_loc``/``num_seqs`` are the
259
+ verify-time staging buffers; ``num_sampled``/``req_map`` are per batch row;
260
+ ``num_computed`` is the post-step count per request slot; ``capture_tokens`` is
261
+ the [rows, slots] boundary-capture tensor or None. ``row_mode`` 1 commits only the
262
+ rows that need every column (block crossing / capture / forced), 2 only the others."""
263
+ state_tab, g_tab, d_tab, k_tab, idx_tab = tables
264
+ grid = (max_seqs * value_heads * (VALUE_DIM // ROWS), state_tab.numel())
265
+ cap = capture_tokens if capture_tokens is not None else num_sampled
266
+ ncap = capture_tokens.shape[1] if capture_tokens is not None else 1
267
+ assert ncap & (ncap - 1) == 0, "capture slots must be a power of two"
268
+ _gdn_commit_kernel[grid](
269
+ state_tab, g_tab, d_tab, k_tab, idx_tab,
270
+ num_accepted, query_start_loc, num_seqs, valid,
271
+ num_sampled, req_map, num_computed, cap, cap.stride(0) if capture_tokens is not None else 0,
272
+ min(num_sampled.numel(), req_map.numel()),
273
+ slot_stride, state_indices.stride(0), state_indices.stride(1), mamba_block,
274
+ FORCE_FULL=bool(force_full), HAS_CAPTURE=capture_tokens is not None, NUM_CAPTURES=ncap,
275
+ KEY_HEADS=key_heads, VALUE_HEADS=value_heads, COLUMNS=state_indices.shape[1],
276
+ ROWS=ROWS, KD=KEY_DIM, VD=VALUE_DIM, ROW_MODE=row_mode,
277
+ num_warps=8,
278
+ )
279
+
280
+
281
+ @triton.jit
282
+ def _gdn_gated_rmsnorm_kernel(
283
+ output, z, norm_weight, num_tokens, eps, token_capacity,
284
+ stride_output_token: tl.int64, stride_output_head: tl.int64,
285
+ stride_z_token: tl.int64, stride_z_head: tl.int64,
286
+ VALUE_HEADS: tl.constexpr, VALUE_HEAD_DIM: tl.constexpr,
287
+ SIGMOID_GATE: tl.constexpr, NORM_WEIGHT_FP32: tl.constexpr,
288
+ ):
289
+ """Copy of b12x gdn_decode._gated_rmsnorm_kernel (non-KDA paths). b12x freezes its own
290
+ Triton kernels after preparation, so a new specialization of its copy cannot JIT."""
291
+ token_value_head = tl.program_id(0)
292
+ token = token_value_head // VALUE_HEADS
293
+ value_head = token_value_head % VALUE_HEADS
294
+ cols = tl.arange(0, VALUE_HEAD_DIM)
295
+ mask = cols < VALUE_HEAD_DIM
296
+ token_i64 = token.to(tl.int64)
297
+ output_offsets = token_i64 * stride_output_token + value_head.to(tl.int64) * stride_output_head + cols.to(tl.int64)
298
+ live_tokens = tl.load(num_tokens).to(tl.int32)
299
+ if token >= tl.maximum(0, tl.minimum(live_tokens, token_capacity)):
300
+ tl.store(output + output_offsets, 0.0, mask=mask)
301
+ return
302
+ z_offsets = token_i64 * stride_z_token + value_head.to(tl.int64) * stride_z_head + cols.to(tl.int64)
303
+ values = tl.load(output + output_offsets, mask=mask, other=0.0).to(tl.float32)
304
+ variance = tl.sum(values * values, axis=0) / VALUE_HEAD_DIM
305
+ normalized = values * tl.rsqrt(variance + eps)
306
+ if NORM_WEIGHT_FP32:
307
+ normalized = normalized.to(tl.bfloat16)
308
+ weight = tl.load(norm_weight + cols, mask=mask, other=0.0).to(tl.float32)
309
+ weighted = normalized.to(tl.float32) * weight
310
+ else:
311
+ normalized = normalized.to(tl.bfloat16)
312
+ weight = tl.load(norm_weight + cols, mask=mask, other=0.0).to(tl.bfloat16)
313
+ weighted = (normalized * weight).to(tl.bfloat16).to(tl.float32)
314
+ gate_input = tl.load(z + z_offsets, mask=mask, other=0.0).to(tl.float32)
315
+ gate = tl.sigmoid(gate_input)
316
+ if not SIGMOID_GATE:
317
+ gate *= gate_input
318
+ tl.store(output + output_offsets, weighted * gate, mask=mask)
319
+
320
+
321
+ def gdn_gated_rmsnorm(output, z, norm_weight, num_tokens, eps, value_heads, sigmoid_gate):
322
+ """In-place gated RMSNorm of ``output`` [T, VH, 128] with gate ``z`` (b12x semantics)."""
323
+ m = output.shape[0]
324
+ _gdn_gated_rmsnorm_kernel[(m * value_heads,)](
325
+ output, z, norm_weight, num_tokens, float(eps), m,
326
+ output.stride(0), output.stride(1), z.stride(0), z.stride(1),
327
+ VALUE_HEADS=value_heads, VALUE_HEAD_DIM=128,
328
+ SIGMOID_GATE=bool(sigmoid_gate), NORM_WEIGHT_FP32=norm_weight.dtype == torch.float32,
329
+ num_warps=4,
330
+ )
recipe/patches/qwen4_exp/nvidia/ops/gemma_norm.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """One-launch GemmaRMSNorm for the residual-free path.
3
+
4
+ vLLM's GemmaRMSNorm.forward_cuda is its native path: it materializes
5
+ ``weight.float() + 1.0`` and then runs ir.ops.rms_norm as separate eager torch
6
+ ops (to-fp32 copy, pow, mean, add, rsqrt, mul, mul, to-bf16). With compilation
7
+ off that is ~10 kernels per call; QSA's q_norm/k_norm alone are ~20 tiny kernels
8
+ per attention layer at decode. This kernel does the same fp32 math in one launch
9
+ and reads strided rows directly (QSA normalizes a chunk() view of q/gate).
10
+ """
11
+
12
+ import torch
13
+ import triton
14
+ import triton.language as tl
15
+
16
+
17
+ @triton.jit
18
+ def _gemma_rms_norm_kernel(
19
+ x_ptr, w_ptr, out_ptr,
20
+ H, stride_t, stride_h, D, eps,
21
+ BLOCK_D: tl.constexpr,
22
+ ):
23
+ row = tl.program_id(0)
24
+ t = row // H
25
+ h = row % H
26
+ offs = tl.arange(0, BLOCK_D)
27
+ mask = offs < D
28
+ x = tl.load(x_ptr + t * stride_t + h * stride_h + offs, mask=mask, other=0.0).to(tl.float32)
29
+ var = tl.sum(x * x, axis=0) / D
30
+ y = x * (1.0 / tl.sqrt(var + eps))
31
+ w = tl.load(w_ptr + offs, mask=mask, other=0.0).to(tl.float32) + 1.0
32
+ tl.store(out_ptr + row * D + offs, (y * w).to(out_ptr.dtype.element_ty), mask=mask)
33
+
34
+
35
+ def gemma_rms_norm(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor | None:
36
+ """``x * rsqrt(mean(x^2) + eps) * (1 + weight)`` in fp32, cast to x.dtype.
37
+
38
+ Returns None when the layout is unsupported (caller falls back)."""
39
+ if x.dtype not in (torch.bfloat16, torch.float16) or x.dim() not in (2, 3) or x.stride(-1) != 1:
40
+ return None
41
+ x3 = x.unsqueeze(1) if x.dim() == 2 else x
42
+ t, h, d = x3.shape
43
+ if d > 8192 or weight.numel() != d:
44
+ return None
45
+ out = torch.empty(x.shape, dtype=x.dtype, device=x.device)
46
+ if t * h == 0:
47
+ return out
48
+ block = triton.next_power_of_2(d)
49
+ _gemma_rms_norm_kernel[(t * h,)](
50
+ x3, weight, out, h, x3.stride(0), x3.stride(1), d, eps,
51
+ BLOCK_D=block, num_warps=1 if block <= 256 else 4,
52
+ )
53
+ return out
54
+
55
+
56
+ def install_gemma_norm() -> None:
57
+ from vllm.model_executor.layers.layernorm import GemmaRMSNorm
58
+
59
+ if getattr(GemmaRMSNorm, "_qwen38_fused", False):
60
+ return
61
+ stock = GemmaRMSNorm.forward_cuda
62
+
63
+ def forward_cuda(self, x, residual=None):
64
+ if residual is None:
65
+ out = gemma_rms_norm(x, self.weight, self.variance_epsilon)
66
+ if out is not None:
67
+ return out
68
+ return stock(self, x, residual)
69
+
70
+ GemmaRMSNorm.forward_cuda = forward_cuda
71
+ GemmaRMSNorm._qwen38_fused = True
recipe/patches/qwen4_exp/nvidia/ops/hc_fp8_gemv.py ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Rowwise-FP8 GEMV for the HyperConnection mix projections at decode.
3
+
4
+ Each GatedResidual mixer runs two bf16 projections per layer:
5
+
6
+ down (+ injection) [rows, 10240] x [336, 10240]^T (6.9 MB bf16)
7
+ up [rows, 320] x [10240, 320]^T (6.6 MB bf16)
8
+
9
+ About 100 mixers run per decode step, so these weights are ~1.3 GB of
10
+ traffic per step. cuBLAS on GB10 (sm_121) falls back to sm80 WMMA kernels for
11
+ them at 64-73% of the bf16 bandwidth floor. Storing an FP8 copy with one
12
+ scale per output row halves the bytes; this kernel streams it with every SM
13
+ busy: split-K partials for the narrow down projection, a single pass for the
14
+ wide up projection. Rows > MAX_ROWS (prefill) keep the bf16 path.
15
+
16
+ Split-K partials are reduced in a fixed order, so results are deterministic.
17
+ """
18
+
19
+ import os
20
+
21
+ import torch
22
+ import triton
23
+ import triton.language as tl
24
+
25
+ MAX_ROWS = 16
26
+ FP8_MAX = 448.0
27
+ # VLLM_QWEN38_GEMV_TUNE=1: configs re-tuned by gemv_dirty_bench.py (8 MB of dirty L2
28
+ # before every call, closer to in-model conditions than DRAM-cold back-to-back calls).
29
+ _TUNE = os.environ.get("VLLM_QWEN38_GEMV_TUNE", "0") == "1"
30
+
31
+ # (BLOCK_N, BLOCK_K, SPLIT_K, num_warps) keyed by (N >= threshold); tuned on
32
+ # GB10 with DRAM-cold weights by hc_fp8_gemv_bench.py.
33
+ _NARROW = (16, 256, 10, 4) if _TUNE else (16, 256, 8, 4) # N ~ 320-336, K = 10240; dirty 17.7 -> 14.9 us
34
+ _WIDE = (64, 64, 1, 4) # N = 10240, K = 320
35
+
36
+
37
+ def quantize_rowwise_fp8(
38
+ weight: torch.Tensor, chunk_rows: int = 1024
39
+ ) -> tuple[torch.Tensor, torch.Tensor]:
40
+ """Return (fp8 [N, K], fp32 scale [N]) with ``weight ~= fp8 * scale[:, None]``.
41
+
42
+ Rows are converted in chunks so the fp32 temporaries stay small: callers
43
+ build these copies during vLLM's memory-profiling run, whose peak sets the
44
+ KV cache size.
45
+ """
46
+ w = weight.detach()
47
+ n = w.shape[0]
48
+ w8 = torch.empty(w.shape, dtype=torch.float8_e4m3fn, device=w.device)
49
+ scale = torch.empty(n, dtype=torch.float32, device=w.device)
50
+ for r in range(0, n, chunk_rows):
51
+ rows = w[r : r + chunk_rows].float()
52
+ s = rows.abs().amax(dim=1).clamp_min(1e-12) / FP8_MAX
53
+ w8[r : r + chunk_rows] = (rows / s[:, None]).clamp_(-FP8_MAX, FP8_MAX).to(torch.float8_e4m3fn)
54
+ scale[r : r + chunk_rows] = s
55
+ return w8, scale
56
+
57
+
58
+ @triton.jit
59
+ def _fp8_gemv_kernel(
60
+ x_ptr, w_ptr, s_ptr, out_ptr, part_ptr,
61
+ M, N, K,
62
+ stride_xm, stride_wn, stride_om,
63
+ K_PER_SPLIT,
64
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
65
+ SPLIT: tl.constexpr,
66
+ ):
67
+ pid_n = tl.program_id(0)
68
+ pid_k = tl.program_id(1)
69
+ offs_m = tl.arange(0, BLOCK_M)
70
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
71
+ k_start = pid_k * K_PER_SPLIT
72
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
73
+ for kk in range(0, K_PER_SPLIT, BLOCK_K):
74
+ offs_k = k_start + kk + tl.arange(0, BLOCK_K)
75
+ x = tl.load(
76
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
77
+ mask=(offs_m[:, None] < M) & (offs_k[None, :] < K),
78
+ other=0.0,
79
+ )
80
+ w = tl.load(
81
+ w_ptr + offs_n[:, None] * stride_wn + offs_k[None, :],
82
+ mask=(offs_n[:, None] < N) & (offs_k[None, :] < K),
83
+ other=0.0,
84
+ ).to(tl.bfloat16)
85
+ acc += tl.dot(x, tl.trans(w))
86
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
87
+ if SPLIT == 1:
88
+ s = tl.load(s_ptr + offs_n, mask=offs_n < N, other=0.0)
89
+ out = acc * s[None, :]
90
+ tl.store(
91
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :],
92
+ out.to(out_ptr.dtype.element_ty),
93
+ mask=mask,
94
+ )
95
+ else:
96
+ tl.store(
97
+ part_ptr + (pid_k * M + offs_m[:, None]) * N + offs_n[None, :],
98
+ acc,
99
+ mask=mask,
100
+ )
101
+
102
+
103
+ @triton.jit
104
+ def _reduce_kernel(
105
+ part_ptr, s_ptr, out_ptr, M, N, stride_om,
106
+ SPLIT: tl.constexpr, BLOCK: tl.constexpr,
107
+ ):
108
+ offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
109
+ mask = offs < M * N
110
+ acc = tl.zeros((BLOCK,), dtype=tl.float32)
111
+ for s in tl.static_range(SPLIT):
112
+ acc += tl.load(part_ptr + s * M * N + offs, mask=mask, other=0.0)
113
+ n = offs % N
114
+ m = offs // N
115
+ scale = tl.load(s_ptr + n, mask=mask, other=0.0)
116
+ tl.store(
117
+ out_ptr + m * stride_om + n,
118
+ (acc * scale).to(out_ptr.dtype.element_ty),
119
+ mask=mask,
120
+ )
121
+
122
+
123
+ def _config_for(n: int, k: int) -> tuple[int, int, int, int]:
124
+ if k >= 8192 and n <= 512:
125
+ return _NARROW
126
+ if n >= 2048 and k <= 512:
127
+ return _WIDE
128
+ # Other shapes (MTP projections): enough N tiles x K splits to fill ~2 waves
129
+ # of GB10's 48 SMs while keeping each split at least two K tiles long.
130
+ block_n = 64 if n >= 8192 else 32
131
+ block_k = 256 if k >= 2048 else 128
132
+ tiles = triton.cdiv(n, block_n)
133
+ split = 1
134
+ while tiles * split < 96 and k // (2 * split) >= 2 * block_k:
135
+ split *= 2
136
+ return block_n, block_k, split, 4
137
+
138
+
139
+ def fp8_gemv(
140
+ x: torch.Tensor,
141
+ w8: torch.Tensor,
142
+ scale: torch.Tensor,
143
+ out_dtype: torch.dtype = torch.bfloat16,
144
+ config: tuple[int, int, int, int] | None = None,
145
+ ) -> torch.Tensor:
146
+ """Return ``x @ (w8 * scale[:, None]).T``; ``x`` [M <= 16, K] bf16 (row-strided ok)."""
147
+ m, k = x.shape
148
+ n = w8.shape[0]
149
+ assert m <= MAX_ROWS and x.dtype == torch.bfloat16 and x.stride(1) == 1
150
+ block_n, block_k, split_k, num_warps = config or _config_for(n, k)
151
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
152
+ split_k = triton.cdiv(k, k_per_split)
153
+ out = torch.empty((m, n), dtype=out_dtype, device=x.device)
154
+ part = (
155
+ torch.empty((split_k, m, n), dtype=torch.float32, device=x.device)
156
+ if split_k > 1
157
+ else out
158
+ )
159
+ grid = (triton.cdiv(n, block_n), split_k)
160
+ _fp8_gemv_kernel[grid](
161
+ x, w8, scale, out, part,
162
+ m, n, k,
163
+ x.stride(0), w8.stride(0), out.stride(0),
164
+ k_per_split,
165
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k,
166
+ SPLIT=split_k,
167
+ num_warps=num_warps,
168
+ )
169
+ if split_k > 1:
170
+ block = 1024
171
+ _reduce_kernel[(triton.cdiv(m * n, block),)](
172
+ part, scale, out, m, n, out.stride(0),
173
+ SPLIT=split_k, BLOCK=block, num_warps=4,
174
+ )
175
+ return out
176
+
177
+
178
+ @triton.jit
179
+ def _reduce_silu_kernel(
180
+ part_ptr, s_ptr, out_ptr, y_ptr, M, N, stride_om, stride_ym, SILU_N,
181
+ HC: tl.constexpr, SPLIT: tl.constexpr, BLOCK: tl.constexpr,
182
+ ):
183
+ """_reduce_kernel that also writes hc_silu of the first SILU_N columns."""
184
+ offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
185
+ mask = offs < M * N
186
+ acc = tl.zeros((BLOCK,), dtype=tl.float32)
187
+ for s in tl.static_range(SPLIT):
188
+ acc += tl.load(part_ptr + s * M * N + offs, mask=mask, other=0.0)
189
+ n = offs % N
190
+ m = offs // N
191
+ scale = tl.load(s_ptr + n, mask=mask, other=0.0)
192
+ v = (acc * scale).to(out_ptr.dtype.element_ty)
193
+ tl.store(out_ptr + m * stride_om + n, v, mask=mask)
194
+ xs = v.to(tl.float32) / HC
195
+ tl.store(
196
+ y_ptr + m * stride_ym + n,
197
+ (xs * tl.sigmoid(xs)).to(y_ptr.dtype.element_ty),
198
+ mask=mask & (n < SILU_N),
199
+ )
200
+
201
+
202
+ @triton.jit
203
+ def _fp8_gemv_down_silu_fused_kernel(
204
+ x_ptr, w_ptr, s_ptr, out_ptr, y_ptr, part_ptr, cnt_ptr,
205
+ M, N, K, stride_xm, stride_wn, stride_om, stride_ym, SILU_N, K_PER_SPLIT,
206
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
207
+ SPLIT: tl.constexpr, HC: tl.constexpr,
208
+ ):
209
+ """Split-K FP8 GEMV whose last-arriving program per N tile does the reduce, the
210
+ row scale and hc_silu (one launch instead of GEMV + _reduce_silu_kernel). That
211
+ program also resets the tile counter, so CUDA-graph replays stay valid."""
212
+ pid_n = tl.program_id(0)
213
+ pid_k = tl.program_id(1)
214
+ offs_m = tl.arange(0, BLOCK_M)
215
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
216
+ k_start = pid_k * K_PER_SPLIT
217
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
218
+ for kk in range(0, K_PER_SPLIT, BLOCK_K):
219
+ offs_k = k_start + kk + tl.arange(0, BLOCK_K)
220
+ x = tl.load(x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
221
+ mask=(offs_m[:, None] < M) & (offs_k[None, :] < K), other=0.0)
222
+ w = tl.load(w_ptr + offs_n[:, None] * stride_wn + offs_k[None, :],
223
+ mask=(offs_n[:, None] < N) & (offs_k[None, :] < K), other=0.0).to(tl.bfloat16)
224
+ acc += tl.dot(x, tl.trans(w))
225
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
226
+ tl.store(part_ptr + (pid_k * BLOCK_M + offs_m[:, None]) * N + offs_n[None, :], acc, mask=mask)
227
+ # release our partial, then count arrivals for this N tile
228
+ arrived = tl.atomic_add(cnt_ptr + pid_n, 1, sem="acq_rel")
229
+ if arrived == SPLIT - 1:
230
+ tot = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
231
+ for s in tl.static_range(SPLIT):
232
+ tot += tl.load(part_ptr + (s * BLOCK_M + offs_m[:, None]) * N + offs_n[None, :],
233
+ mask=mask, other=0.0, cache_modifier=".cg")
234
+ sc = tl.load(s_ptr + offs_n, mask=offs_n < N, other=0.0)
235
+ v = (tot * sc[None, :]).to(out_ptr.dtype.element_ty)
236
+ tl.store(out_ptr + offs_m[:, None] * stride_om + offs_n[None, :], v, mask=mask)
237
+ xs = v.to(tl.float32) / HC
238
+ tl.store(y_ptr + offs_m[:, None] * stride_ym + offs_n[None, :],
239
+ (xs * tl.sigmoid(xs)).to(y_ptr.dtype.element_ty),
240
+ mask=mask & (offs_n[None, :] < SILU_N))
241
+ tl.atomic_xchg(cnt_ptr + pid_n, 0)
242
+
243
+
244
+ _FUSED_DOWN = __import__("os").environ.get("VLLM_QWEN38_HC_DOWN_FUSED", "0") == "1"
245
+ # gate-mix tiling "BLOCK_H,BLOCK_K,num_warps" (default 16,64,4)
246
+ _MIX_CFG = tuple(int(v) for v in __import__("os").environ.get("VLLM_QWEN38_HC_MIX_CFG", "16,64,4").split(","))
247
+ _fused_scratch: dict = {}
248
+
249
+
250
+ def _down_scratch(device, split, n, tiles):
251
+ """Persistent partials + zeroed counters, created outside graph capture."""
252
+ key = (str(device), split, n, tiles)
253
+ buf = _fused_scratch.get(key)
254
+ if buf is None:
255
+ if torch.cuda.is_current_stream_capturing():
256
+ raise RuntimeError("HC fused down scratch must exist before graph capture")
257
+ buf = (torch.empty((split, MAX_ROWS, n), dtype=torch.float32, device=device),
258
+ torch.zeros((tiles,), dtype=torch.int32, device=device))
259
+ _fused_scratch[key] = buf
260
+ return buf
261
+
262
+
263
+ def fp8_gemv_down_silu_fused(x, w8, scale, silu_n, hc, config=None):
264
+ """Single-launch fp8_gemv_down_silu (same rounding points). Calls that share a
265
+ shape share one scratch, which is safe while they run on one stream in order."""
266
+ m, k = x.shape
267
+ n = w8.shape[0]
268
+ assert m <= MAX_ROWS and x.stride(1) == 1
269
+ block_n, block_k, split_k, num_warps = config or _config_for(n, k)
270
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
271
+ split_k = triton.cdiv(k, k_per_split)
272
+ tiles = triton.cdiv(n, block_n)
273
+ part, cnt = _down_scratch(x.device, split_k, n, tiles)
274
+ out = torch.empty((m, n), dtype=torch.bfloat16, device=x.device)
275
+ y = torch.empty((m, silu_n), dtype=torch.bfloat16, device=x.device)
276
+ _fp8_gemv_down_silu_fused_kernel[(tiles, split_k)](
277
+ x, w8, scale, out, y, part, cnt,
278
+ m, n, k, x.stride(0), w8.stride(0), out.stride(0), y.stride(0), silu_n, k_per_split,
279
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k, SPLIT=split_k, HC=hc,
280
+ num_warps=num_warps,
281
+ )
282
+ return out, y
283
+
284
+
285
+ def fp8_gemv_down_silu(
286
+ x: torch.Tensor, w8: torch.Tensor, scale: torch.Tensor, silu_n: int, hc: int
287
+ ) -> tuple[torch.Tensor, torch.Tensor]:
288
+ """HC down projection plus hc_silu of its first ``silu_n`` columns.
289
+
290
+ Returns (``x @ W.T`` [M, N] bf16, ``(v/hc) * sigmoid(v/hc)`` [M, silu_n] bf16) with
291
+ the stock rounding points (bf16 projection, fp32 SiLU), saving the separate
292
+ scaled-SiLU launch.
293
+ """
294
+ m, k = x.shape
295
+ n = w8.shape[0]
296
+ block_n, block_k, split_k, num_warps = _config_for(n, k)
297
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
298
+ split_k = triton.cdiv(k, k_per_split)
299
+ out = torch.empty((m, n), dtype=torch.bfloat16, device=x.device)
300
+ y = torch.empty((m, silu_n), dtype=torch.bfloat16, device=x.device)
301
+ if split_k == 1:
302
+ out = fp8_gemv(x, w8, scale)
303
+ xs = out[:, :silu_n].float() / hc
304
+ y.copy_(xs * torch.sigmoid(xs))
305
+ return out, y
306
+ part = torch.empty((split_k, m, n), dtype=torch.float32, device=x.device)
307
+ _fp8_gemv_kernel[(triton.cdiv(n, block_n), split_k)](
308
+ x, w8, scale, out, part,
309
+ m, n, k,
310
+ x.stride(0), w8.stride(0), out.stride(0),
311
+ k_per_split,
312
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k,
313
+ SPLIT=split_k,
314
+ num_warps=num_warps,
315
+ )
316
+ block = 1024
317
+ _reduce_silu_kernel[(triton.cdiv(m * n, block),)](
318
+ part, scale, out, y, m, n, out.stride(0), y.stride(0), silu_n,
319
+ HC=hc, SPLIT=split_k, BLOCK=block, num_warps=4,
320
+ )
321
+ return out, y
322
+
323
+
324
+ @triton.jit
325
+ def _fp8_gemv_gate_mix_kernel(
326
+ x_ptr, w_ptr, s_ptr, n_ptr, out_ptr,
327
+ M, K, HC_DIM,
328
+ stride_xm, stride_wn, stride_nm, stride_om,
329
+ BLOCK_M: tl.constexpr, BLOCK_H: tl.constexpr, BLOCK_K: tl.constexpr,
330
+ HC: tl.constexpr,
331
+ ):
332
+ """HC up projection fused with hc_gate_mix: each program owns BLOCK_H hidden
333
+ columns and computes the gate logits of all HC streams for them."""
334
+ pid = tl.program_id(0)
335
+ offs_m = tl.arange(0, BLOCK_M)
336
+ offs_h = pid * BLOCK_H + tl.arange(0, BLOCK_H)
337
+ m_mask = offs_m < M
338
+ h_mask = offs_h < HC_DIM
339
+ mix = tl.zeros((BLOCK_M, BLOCK_H), dtype=tl.float32)
340
+ for s in tl.static_range(HC):
341
+ rows = s * HC_DIM + offs_h
342
+ acc = tl.zeros((BLOCK_M, BLOCK_H), dtype=tl.float32)
343
+ for k0 in range(0, K, BLOCK_K):
344
+ offs_k = k0 + tl.arange(0, BLOCK_K)
345
+ x = tl.load(
346
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
347
+ mask=m_mask[:, None] & (offs_k[None, :] < K),
348
+ other=0.0,
349
+ )
350
+ w = tl.load(
351
+ w_ptr + rows[:, None] * stride_wn + offs_k[None, :],
352
+ mask=h_mask[:, None] & (offs_k[None, :] < K),
353
+ other=0.0,
354
+ ).to(tl.bfloat16)
355
+ acc += tl.dot(x, tl.trans(w))
356
+ sc = tl.load(s_ptr + rows, mask=h_mask, other=0.0)
357
+ g = (acc * sc[None, :]).to(tl.bfloat16).to(tl.float32)
358
+ xn = tl.load(
359
+ n_ptr + offs_m[:, None] * stride_nm + rows[None, :],
360
+ mask=m_mask[:, None] & h_mask[None, :],
361
+ other=0.0,
362
+ ).to(tl.float32)
363
+ mix += tl.sigmoid(g) * xn
364
+ mix = mix / HC
365
+ tl.store(
366
+ out_ptr + offs_m[:, None] * stride_om + offs_h[None, :],
367
+ mix.to(out_ptr.dtype.element_ty),
368
+ mask=m_mask[:, None] & h_mask[None, :],
369
+ )
370
+
371
+
372
+ def fp8_gemv_gate_mix(
373
+ x: torch.Tensor,
374
+ w8: torch.Tensor,
375
+ scale: torch.Tensor,
376
+ normalized: torch.Tensor,
377
+ hc: int,
378
+ block_h: int = 16,
379
+ block_k: int = 64,
380
+ num_warps: int = 4,
381
+ ) -> torch.Tensor:
382
+ """``hc_gate_mix(normalized, x @ W.T, hc)`` without materializing the gate logits.
383
+
384
+ x [M <= 16, K] bf16; W [hc * D, K] rowwise FP8; normalized [M, hc * D] bf16.
385
+ Returns [M, D] bf16: mean over streams of sigmoid(bf16 gate) * normalized.
386
+ """
387
+ m, k = x.shape
388
+ n = w8.shape[0]
389
+ assert m <= MAX_ROWS and x.stride(1) == 1 and normalized.stride(1) == 1 and n % hc == 0
390
+ d = n // hc
391
+ out = torch.empty((m, d), dtype=normalized.dtype, device=x.device)
392
+ _fp8_gemv_gate_mix_kernel[(triton.cdiv(d, block_h),)](
393
+ x, w8, scale, normalized, out,
394
+ m, k, d,
395
+ x.stride(0), w8.stride(0), normalized.stride(0), out.stride(0),
396
+ BLOCK_M=MAX_ROWS, BLOCK_H=block_h, BLOCK_K=block_k, HC=hc,
397
+ num_warps=num_warps,
398
+ )
399
+ return out
400
+
401
+
402
+ def install_fp8_decode_linears(model: torch.nn.Module) -> list[str]:
403
+ """Route decode rows (<= MAX_ROWS) of ``model``'s plain bf16 Linear layers
404
+ through ``fp8_gemv``; prefill rows keep the stock bf16 path.
405
+
406
+ Skips HyperConnection mixers (own path, VLLM_QWEN38_HC_FP8), MoE router and
407
+ shared-expert gates (their logits stay bf16) and anything biased/quantized.
408
+ b12x finishes checkpoint transfers after load_weights returns, so each FP8
409
+ copy is built on the first call outside graph capture (the profile run),
410
+ when the bf16 weight holds its final values. Returns the wrapped names.
411
+ """
412
+ from vllm.model_executor.layers.linear import LinearBase, UnquantizedLinearMethod
413
+
414
+ names = []
415
+ for name, mod in model.named_modules():
416
+ if (
417
+ not isinstance(mod, LinearBase)
418
+ or "hyper_connection" in name
419
+ or name.endswith(".gate")
420
+ or name.endswith("shared_expert_gate")
421
+ or not isinstance(getattr(mod, "quant_method", None), UnquantizedLinearMethod)
422
+ or getattr(mod, "bias", None) is not None
423
+ ):
424
+ continue
425
+ weight = getattr(mod, "weight", None)
426
+ if (
427
+ weight is None
428
+ or weight.is_meta
429
+ or weight.dtype != torch.bfloat16
430
+ or weight.dim() != 2
431
+ or weight.shape[1] % 32
432
+ ):
433
+ continue
434
+ stock = mod.forward
435
+ cache: list = []
436
+
437
+ def forward(x, _mod=mod, _cache=cache, _stock=stock):
438
+ if not _cache:
439
+ if torch.cuda.is_current_stream_capturing():
440
+ return _stock(x)
441
+ _cache.extend(quantize_rowwise_fp8(_mod.weight))
442
+ if x.dim() >= 2 and x.dtype == torch.bfloat16 and x.numel() <= MAX_ROWS * x.shape[-1]:
443
+ w8, scale = _cache
444
+ x2 = x.reshape(-1, x.shape[-1])
445
+ if x2.stride(-1) != 1:
446
+ x2 = x2.contiguous()
447
+ out = fp8_gemv(x2, w8, scale).view(*x.shape[:-1], w8.shape[0])
448
+ return (out, None) if getattr(_mod, "return_bias", True) else out
449
+ return _stock(x)
450
+
451
+ mod.forward = forward
452
+ names.append(name)
453
+ return names
recipe/patches/qwen4_exp/nvidia/ops/mxfp8_gemv.py ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Split-K MXFP8 GEMV over b12x's packed dense weights, for decode rows.
3
+
4
+ b12x's dense MXFP8 kernel launches one CTA per 64 output columns at decode, so
5
+ narrow projections leave most of GB10's 48 SMs idle (decode profile, M=5):
6
+
7
+ out_proj / o_proj N=2560 K=6144 40 CTAs 101 us (floor 58 us)
8
+ shared expert up N=1280 K=2560 20 CTAs 36 us (floor 12 us)
9
+ shared expert down N=2560 K=640 40 CTAs 16 us (floor 6 us)
10
+
11
+ b12x keeps the ModelOpt layout next to its MMA-swizzled scales: values
12
+ [N, Kp] e4m3 row-major (K padded to 128) and scale_rows [N, Kp/32] E8M0. This
13
+ kernel reads those directly (no extra memory), dequantizes a tile to bf16
14
+ (exact: e4m3 * 2^e fits bf16), and splits K so every SM streams weights.
15
+ Partials are reduced in a fixed order, so results are deterministic.
16
+ """
17
+
18
+ import os
19
+
20
+ import torch
21
+ import triton
22
+ import triton.language as tl
23
+
24
+ MAX_ROWS = 16
25
+ # VLLM_QWEN38_GEMV_TUNE=1: configs re-tuned by gemv_dirty_bench.py (8 MB of dirty L2
26
+ # before every call, closer to in-model conditions than DRAM-cold back-to-back calls).
27
+ _TUNE = os.environ.get("VLLM_QWEN38_GEMV_TUNE", "0") == "1"
28
+
29
+
30
+ @triton.jit
31
+ def _mxfp8_gemv_kernel(
32
+ x_ptr, w_ptr, s_ptr, out_ptr, part_ptr,
33
+ M, N, K,
34
+ stride_xm, stride_wn, stride_sn, stride_om,
35
+ K_PER_SPLIT,
36
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
37
+ SPLIT: tl.constexpr,
38
+ ):
39
+ pid_n = tl.program_id(0)
40
+ pid_k = tl.program_id(1)
41
+ offs_m = tl.arange(0, BLOCK_M)
42
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
43
+ offs_g = tl.arange(0, BLOCK_K // 32)
44
+ k_start = pid_k * K_PER_SPLIT
45
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
46
+ n_mask = offs_n[:, None] < N
47
+ for kk in range(0, K_PER_SPLIT, BLOCK_K):
48
+ k0 = k_start + kk
49
+ offs_k = k0 + tl.arange(0, BLOCK_K)
50
+ k_mask = offs_k[None, :] < K
51
+ x = tl.load(
52
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
53
+ mask=(offs_m[:, None] < M) & k_mask,
54
+ other=0.0,
55
+ )
56
+ w = tl.load(
57
+ w_ptr + offs_n[:, None] * stride_wn + offs_k[None, :],
58
+ mask=n_mask & k_mask,
59
+ other=0.0,
60
+ ).to(tl.float32)
61
+ s = tl.load(
62
+ s_ptr + offs_n[:, None] * stride_sn + (k0 // 32 + offs_g)[None, :],
63
+ mask=n_mask & ((k0 + offs_g * 32)[None, :] < K),
64
+ other=127,
65
+ )
66
+ # E8M0 -> 2^(s-127) by placing the byte in the fp32 exponent field.
67
+ scale = (s.to(tl.int32) << 23).to(tl.float32, bitcast=True)
68
+ w = tl.reshape(w, (BLOCK_N, BLOCK_K // 32, 32)) * scale[:, :, None]
69
+ w = tl.reshape(w, (BLOCK_N, BLOCK_K)).to(tl.bfloat16)
70
+ acc += tl.dot(x, tl.trans(w))
71
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
72
+ if SPLIT == 1:
73
+ tl.store(
74
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :],
75
+ acc.to(out_ptr.dtype.element_ty),
76
+ mask=mask,
77
+ )
78
+ else:
79
+ tl.store(
80
+ part_ptr + (pid_k * M + offs_m[:, None]) * N + offs_n[None, :],
81
+ acc,
82
+ mask=mask,
83
+ )
84
+
85
+
86
+ @triton.jit
87
+ def _reduce_kernel(part_ptr, out_ptr, M, N, stride_om, SPLIT: tl.constexpr, BLOCK: tl.constexpr):
88
+ offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
89
+ mask = offs < M * N
90
+ acc = tl.zeros((BLOCK,), dtype=tl.float32)
91
+ for s in tl.static_range(SPLIT):
92
+ acc += tl.load(part_ptr + s * M * N + offs, mask=mask, other=0.0)
93
+ tl.store(out_ptr + (offs // N) * stride_om + offs % N, acc.to(out_ptr.dtype.element_ty), mask=mask)
94
+
95
+
96
+ # (BLOCK_N, BLOCK_K, SPLIT_K, num_warps) per (N, K), tuned on GB10 with
97
+ # DRAM-cold weights by mxfp8_gemv_bench.py at M=5 (MTP-4 verify). Cold-cache us,
98
+ # b12x in-model -> this kernel: out_proj 100.9 -> 67.9, shared up 35.6 -> 16.5,
99
+ # shared down 15.6 -> 6.8, attn qkv 167 -> 147, GDN qkvz 203.5 -> 181.
100
+ # Shapes not listed use _default_config.
101
+ _CONFIGS: dict[tuple[int, int], tuple[int, int, int, int]] = {
102
+ (2560, 6144): (32, 256, 1, 4),
103
+ (1280, 2560): (64, 128, 8, 4) if _TUNE else (16, 128, 4, 4), # dirty 17.6 -> 14.8 us
104
+ (2560, 640): (32, 128, 1, 4),
105
+ (13312, 2560): (64, 256, 1, 8),
106
+ (16384, 2560): (32, 256, 1, 4) if _TUNE else (64, 256, 1, 8), # dirty 186.6 -> 178.2 us
107
+ }
108
+
109
+
110
+ def _default_config(n: int, k: int) -> tuple[int, int, int, int]:
111
+ block_n = 32
112
+ tiles = triton.cdiv(n, block_n)
113
+ split = max(1, min(16, (4 * 48) // tiles))
114
+ while split > 1 and k // split < 256:
115
+ split //= 2
116
+ return block_n, 256 if k >= 2048 else 128, split, 4
117
+
118
+
119
+ def mxfp8_gemv(
120
+ x: torch.Tensor,
121
+ values: torch.Tensor,
122
+ scale_rows: torch.Tensor,
123
+ out_features: int,
124
+ in_features: int,
125
+ out_dtype: torch.dtype = torch.bfloat16,
126
+ config: tuple[int, int, int, int] | None = None,
127
+ ) -> torch.Tensor:
128
+ """``x @ dequant(values, scale_rows)[:out_features, :in_features].T`` for M <= 16."""
129
+ m = x.shape[0]
130
+ n, k = int(out_features), int(in_features)
131
+ if scale_rows.dtype != torch.uint8: # b12x stores E8M0 as float8_e8m0fnu
132
+ scale_rows = scale_rows.view(torch.uint8)
133
+ assert m <= MAX_ROWS and x.dtype == torch.bfloat16 and x.stride(1) == 1
134
+ block_n, block_k, split_k, num_warps = config or _CONFIGS.get((n, k)) or _default_config(n, k)
135
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
136
+ split_k = triton.cdiv(k, k_per_split)
137
+ out = torch.empty((m, n), dtype=out_dtype, device=x.device)
138
+ part = (
139
+ torch.empty((split_k, m, n), dtype=torch.float32, device=x.device)
140
+ if split_k > 1
141
+ else out
142
+ )
143
+ grid = (triton.cdiv(n, block_n), split_k)
144
+ _mxfp8_gemv_kernel[grid](
145
+ x, values, scale_rows, out, part,
146
+ m, n, k,
147
+ x.stride(0), values.stride(0), scale_rows.stride(0), out.stride(0),
148
+ k_per_split,
149
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k,
150
+ SPLIT=split_k,
151
+ num_warps=num_warps,
152
+ )
153
+ if split_k > 1:
154
+ block = 1024
155
+ _reduce_kernel[(triton.cdiv(m * n, block),)](
156
+ part, out, m, n, out.stride(0), SPLIT=split_k, BLOCK=block, num_warps=4
157
+ )
158
+ return out
159
+
160
+
161
+ @triton.jit
162
+ def _mxfp8_swiglu_gate_kernel(
163
+ gu_ptr, x_ptr, wg_ptr, w_ptr, s_ptr, out_ptr,
164
+ M, N, K, H,
165
+ stride_gum, stride_xm, stride_wn, stride_sn, stride_om,
166
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
167
+ BLOCK_H: tl.constexpr,
168
+ ):
169
+ pid_n = tl.program_id(0)
170
+ offs_m = tl.arange(0, BLOCK_M)
171
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
172
+ offs_g = tl.arange(0, BLOCK_K // 32)
173
+ m_mask = offs_m[:, None] < M
174
+ n_mask = offs_n[:, None] < N
175
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
176
+ for k0 in range(0, K, BLOCK_K):
177
+ offs_k = k0 + tl.arange(0, BLOCK_K)
178
+ mk = m_mask & (offs_k[None, :] < K)
179
+ g = tl.load(gu_ptr + offs_m[:, None] * stride_gum + offs_k[None, :], mask=mk, other=0.0)
180
+ u = tl.load(gu_ptr + offs_m[:, None] * stride_gum + K + offs_k[None, :], mask=mk, other=0.0)
181
+ # vLLM act_and_mul: bf16(silu(g) in fp32), then bf16(silu * u).
182
+ g = g.to(tl.float32)
183
+ silu = (g / (1.0 + tl.exp(-g))).to(tl.bfloat16).to(tl.float32)
184
+ a = (silu * u.to(tl.float32)).to(tl.bfloat16)
185
+ w = tl.load(
186
+ w_ptr + offs_n[:, None] * stride_wn + offs_k[None, :],
187
+ mask=n_mask & (offs_k[None, :] < K),
188
+ other=0.0,
189
+ ).to(tl.float32)
190
+ s = tl.load(
191
+ s_ptr + offs_n[:, None] * stride_sn + (k0 // 32 + offs_g)[None, :],
192
+ mask=n_mask & ((k0 + offs_g * 32)[None, :] < K),
193
+ other=127,
194
+ )
195
+ scale = (s.to(tl.int32) << 23).to(tl.float32, bitcast=True)
196
+ w = tl.reshape(w, (BLOCK_N, BLOCK_K // 32, 32)) * scale[:, :, None]
197
+ w = tl.reshape(w, (BLOCK_N, BLOCK_K)).to(tl.bfloat16)
198
+ acc += tl.dot(a, tl.trans(w))
199
+ # Shared-expert gate: sigmoid(x . w_gate) per row (L2-resident x, 5 KB w_gate).
200
+ gacc = tl.zeros((BLOCK_M,), dtype=tl.float32)
201
+ for h0 in range(0, H, BLOCK_H):
202
+ offs_h = h0 + tl.arange(0, BLOCK_H)
203
+ xv = tl.load(
204
+ x_ptr + offs_m[:, None] * stride_xm + offs_h[None, :],
205
+ mask=m_mask & (offs_h[None, :] < H),
206
+ other=0.0,
207
+ ).to(tl.float32)
208
+ wv = tl.load(wg_ptr + offs_h, mask=offs_h < H, other=0.0).to(tl.float32)
209
+ gacc += tl.sum(xv * wv[None, :], axis=1)
210
+ # Stock rounding: bf16 gate logit, bf16 sigmoid, bf16 down output, bf16 product.
211
+ gl = gacc.to(tl.bfloat16).to(tl.float32)
212
+ sig = (1.0 / (1.0 + tl.exp(-gl))).to(tl.bfloat16).to(tl.float32)
213
+ out = acc.to(tl.bfloat16).to(tl.float32) * sig[:, None]
214
+ tl.store(
215
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :],
216
+ out.to(out_ptr.dtype.element_ty),
217
+ mask=m_mask & (offs_n[None, :] < N),
218
+ )
219
+
220
+
221
+ def mxfp8_gemv_swiglu_gate(
222
+ gate_up: torch.Tensor,
223
+ x: torch.Tensor,
224
+ w_gate: torch.Tensor,
225
+ values: torch.Tensor,
226
+ scale_rows: torch.Tensor,
227
+ out_features: int,
228
+ in_features: int,
229
+ config: tuple[int, int, int] = (32, 64, 4), # shared_fuse_bench.py: 32.1 -> 14.2 us cold, M=5
230
+ ) -> torch.Tensor:
231
+ """Fused shared-expert tail for decode rows:
232
+ ``sigmoid(x @ w_gate) * (silu(g) * u) @ dequant(values, scale_rows)[:N, :K].T``
233
+ with ``gate_up = [g | u]`` [M, 2K] and ``x`` [M, H] the expert input."""
234
+ m, k = gate_up.shape[0], int(in_features)
235
+ n = int(out_features)
236
+ if scale_rows.dtype != torch.uint8:
237
+ scale_rows = scale_rows.view(torch.uint8)
238
+ assert m <= MAX_ROWS and gate_up.shape[1] == 2 * k and gate_up.stride(1) == 1
239
+ assert x.shape[0] == m and x.stride(1) == 1 and w_gate.numel() == x.shape[1]
240
+ block_n, block_k, num_warps = config
241
+ out = torch.empty((m, n), dtype=torch.bfloat16, device=x.device)
242
+ _mxfp8_swiglu_gate_kernel[(triton.cdiv(n, block_n),)](
243
+ gate_up, x, w_gate, values, scale_rows, out,
244
+ m, n, k, x.shape[1],
245
+ gate_up.stride(0), x.stride(0), values.stride(0), scale_rows.stride(0), out.stride(0),
246
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k, BLOCK_H=512,
247
+ num_warps=num_warps,
248
+ )
249
+ return out
recipe/patches/qwen4_exp/nvidia/ops/nvfp4_gemv.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Split-K NVFP4 GEMV for decode rows (MTP draft vocabulary subset).
3
+
4
+ The draft head is evaluated 4x per MTP-4 step over a 32K-64K token subset.
5
+ Gathered from the target's MXFP8 head it streams 1.03 bytes per weight; this
6
+ row-major NVFP4 copy streams 0.56 (e2m1 nibbles, one e4m3 scale per 16 weights,
7
+ one fp32 scale per row), matching the precision of the draft's own NVFP4 head.
8
+
9
+ Layout (all row-major, K % 32 == 0):
10
+ packed uint8 [N, K/2] low nibble = even k, high nibble = odd k
11
+ scales uint8 [N, K/16] e4m3 bit patterns
12
+ gscale fp32 [N] weight ~= e2m1 * e4m3 * gscale
13
+ """
14
+
15
+ import torch
16
+ import triton
17
+ import triton.language as tl
18
+
19
+ MAX_ROWS = 16
20
+ E2M1_MAX = 6.0
21
+ E4M3_MAX = 448.0
22
+ # Midpoints between the e2m1 magnitudes 0, .5, 1, 1.5, 2, 3, 4, 6.
23
+ _E2M1_BOUNDS = (0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0)
24
+
25
+
26
+ def quantize_nvfp4_rows(
27
+ weight: torch.Tensor, chunk_rows: int = 2048
28
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
29
+ """Quantize ``weight`` [N, K] to (packed, scales, gscale); see module doc."""
30
+ n, k = weight.shape
31
+ if k % 32:
32
+ raise ValueError("NVFP4 rows need K % 32 == 0")
33
+ dev = weight.device
34
+ packed = torch.empty((n, k // 2), dtype=torch.uint8, device=dev)
35
+ scales = torch.empty((n, k // 16), dtype=torch.uint8, device=dev)
36
+ gscale = torch.empty(n, dtype=torch.float32, device=dev)
37
+ bounds = torch.tensor(_E2M1_BOUNDS, dtype=torch.float32, device=dev)
38
+ for r in range(0, n, chunk_rows):
39
+ rows = weight[r : r + chunk_rows].float()
40
+ g = (rows.abs().amax(dim=1) / (E2M1_MAX * E4M3_MAX)).clamp_min(1e-30)
41
+ blocks = rows.view(rows.shape[0], k // 16, 16)
42
+ s = (blocks.abs().amax(dim=2) / E2M1_MAX / g[:, None]).clamp_(max=E4M3_MAX)
43
+ s8 = s.to(torch.float8_e4m3fn)
44
+ denom = s8.float() * g[:, None]
45
+ q = torch.where(denom[..., None] > 0, blocks / denom[..., None], 0.0)
46
+ idx = torch.bucketize(q.abs(), bounds).to(torch.uint8)
47
+ nib = idx | ((q < 0).to(torch.uint8) << 3)
48
+ nib = nib.view(rows.shape[0], k)
49
+ packed[r : r + chunk_rows] = nib[:, 0::2] | (nib[:, 1::2] << 4)
50
+ scales[r : r + chunk_rows] = s8.view(torch.uint8)
51
+ gscale[r : r + chunk_rows] = g
52
+ return packed, scales, gscale
53
+
54
+
55
+ def dequantize_nvfp4_rows(packed, scales, gscale) -> torch.Tensor:
56
+ """Reference fp32 dequantization (tests)."""
57
+ lut = torch.tensor(
58
+ [0, 0.5, 1, 1.5, 2, 3, 4, 6, -0.0, -0.5, -1, -1.5, -2, -3, -4, -6],
59
+ dtype=torch.float32, device=packed.device,
60
+ )
61
+ n = packed.shape[0]
62
+ nib = torch.stack((packed & 0xF, packed >> 4), dim=2).view(n, -1).long()
63
+ v = lut[nib].view(n, -1, 16)
64
+ s = scales.view(torch.float8_e4m3fn).float()
65
+ return (v * s[..., None]).view(n, -1) * gscale[:, None]
66
+
67
+
68
+ @triton.jit
69
+ def _nvfp4_gemv_kernel(
70
+ x_ptr, w_ptr, s_ptr, g_ptr, out_ptr, part_ptr,
71
+ M, N, K,
72
+ stride_xm, stride_wn, stride_sn, stride_om,
73
+ K_PER_SPLIT,
74
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
75
+ SPLIT: tl.constexpr,
76
+ ):
77
+ pid_n = tl.program_id(0)
78
+ pid_k = tl.program_id(1)
79
+ offs_m = tl.arange(0, BLOCK_M)
80
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
81
+ offs_b = tl.arange(0, BLOCK_K // 2)
82
+ offs_g = tl.arange(0, BLOCK_K // 16)
83
+ n_mask = offs_n[:, None] < N
84
+ k_start = pid_k * K_PER_SPLIT
85
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
86
+ for kk in range(0, K_PER_SPLIT, BLOCK_K):
87
+ k0 = k_start + kk
88
+ offs_k = k0 + tl.arange(0, BLOCK_K)
89
+ x = tl.load(
90
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
91
+ mask=(offs_m[:, None] < M) & (offs_k[None, :] < K),
92
+ other=0.0,
93
+ )
94
+ b = tl.load(
95
+ w_ptr + offs_n[:, None] * stride_wn + (k0 // 2 + offs_b)[None, :],
96
+ mask=n_mask & ((k0 + 2 * offs_b)[None, :] < K),
97
+ other=0,
98
+ ).to(tl.uint32)
99
+ nib = tl.reshape(tl.join(b & 0xF, b >> 4), (BLOCK_N, BLOCK_K))
100
+ # e2m1 -> fp32 bits: e = bits 2:1, m = bit 0, sign = bit 3.
101
+ e = (nib >> 1) & 3
102
+ m = nib & 1
103
+ bits = tl.where(e > 0, ((e + 126) << 23) | (m << 22), m * 0x3F000000)
104
+ bits = bits | ((nib & 8) << 28)
105
+ v = bits.to(tl.float32, bitcast=True)
106
+ s = tl.load(
107
+ s_ptr + offs_n[:, None] * stride_sn + (k0 // 16 + offs_g)[None, :],
108
+ mask=n_mask & ((k0 + offs_g * 16)[None, :] < K),
109
+ other=0,
110
+ )
111
+ s = s.to(tl.float8e4nv, bitcast=True).to(tl.float32)
112
+ v = tl.reshape(v, (BLOCK_N, BLOCK_K // 16, 16)) * s[:, :, None]
113
+ w = tl.reshape(v, (BLOCK_N, BLOCK_K)).to(tl.bfloat16) # exact: <= 6 mantissa bits
114
+ acc += tl.dot(x, tl.trans(w))
115
+ mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
116
+ if SPLIT == 1:
117
+ g = tl.load(g_ptr + offs_n, mask=offs_n < N, other=0.0)
118
+ tl.store(
119
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :],
120
+ (acc * g[None, :]).to(out_ptr.dtype.element_ty),
121
+ mask=mask,
122
+ )
123
+ else:
124
+ tl.store(
125
+ part_ptr + (pid_k * M + offs_m[:, None]) * N + offs_n[None, :],
126
+ acc,
127
+ mask=mask,
128
+ )
129
+
130
+
131
+ @triton.jit
132
+ def _reduce_kernel(
133
+ part_ptr, g_ptr, out_ptr, M, N, stride_om, SPLIT: tl.constexpr, BLOCK: tl.constexpr
134
+ ):
135
+ offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
136
+ mask = offs < M * N
137
+ acc = tl.zeros((BLOCK,), dtype=tl.float32)
138
+ for s in tl.static_range(SPLIT):
139
+ acc += tl.load(part_ptr + s * M * N + offs, mask=mask, other=0.0)
140
+ n = offs % N
141
+ g = tl.load(g_ptr + n, mask=mask, other=0.0)
142
+ tl.store(out_ptr + (offs // N) * stride_om + n, (acc * g).to(out_ptr.dtype.element_ty), mask=mask)
143
+
144
+
145
+ # (BLOCK_N, BLOCK_K, SPLIT_K, num_warps); tuned by nvfp4_gemv_bench.py on GB10.
146
+ _DEFAULT = (64, 256, 1, 4)
147
+ _CONFIGS: dict[tuple[int, int], tuple[int, int, int, int]] = {}
148
+
149
+
150
+ def nvfp4_gemv(
151
+ x: torch.Tensor,
152
+ packed: torch.Tensor,
153
+ scales: torch.Tensor,
154
+ gscale: torch.Tensor,
155
+ out_dtype: torch.dtype = torch.bfloat16,
156
+ config: tuple[int, int, int, int] | None = None,
157
+ ) -> torch.Tensor:
158
+ """``x @ dequant(packed, scales, gscale).T`` for ``x`` [M <= 16, K] bf16."""
159
+ m, k = x.shape
160
+ n = packed.shape[0]
161
+ assert m <= MAX_ROWS and x.dtype == torch.bfloat16 and x.stride(1) == 1
162
+ assert packed.shape[1] * 2 == k and scales.shape[1] * 16 == k
163
+ block_n, block_k, split_k, num_warps = config or _CONFIGS.get((n, k), _DEFAULT)
164
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
165
+ split_k = triton.cdiv(k, k_per_split)
166
+ out = torch.empty((m, n), dtype=out_dtype, device=x.device)
167
+ part = (
168
+ torch.empty((split_k, m, n), dtype=torch.float32, device=x.device)
169
+ if split_k > 1
170
+ else out
171
+ )
172
+ grid = (triton.cdiv(n, block_n), split_k)
173
+ _nvfp4_gemv_kernel[grid](
174
+ x, packed, scales, gscale, out, part,
175
+ m, n, k,
176
+ x.stride(0), packed.stride(0), scales.stride(0), out.stride(0),
177
+ k_per_split,
178
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k,
179
+ SPLIT=split_k,
180
+ num_warps=num_warps,
181
+ )
182
+ if split_k > 1:
183
+ block = 1024
184
+ _reduce_kernel[(triton.cdiv(m * n, block),)](
185
+ part, gscale, out, m, n, out.stride(0), SPLIT=split_k, BLOCK=block, num_warps=4
186
+ )
187
+ return out
recipe/patches/qwen4_exp/nvidia/ops/small_gemv.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Split-K GEMV for small bf16 projections at decode (ported from the DeepSeek-V4 overlay).
3
+
4
+ At decode (1-16 rows) cuBLAS on GB10 (sm_121) falls back to sm80 WMMA kernels
5
+ for these [rows, 4096] x [N, 4096]^T projections and launches too few blocks
6
+ to use the memory bandwidth, e.g. 4 blocks for the N=64 indexer weights_proj.
7
+ Isolated with DRAM-cold weights the gain is ~1 ms per decode step, mostly the
8
+ N=64 projection where cuBLAS is ~5x off; the larger ones are near bandwidth:
9
+
10
+ compressor wkv+wgate N=2048 (c4 layers) / 1024 (c128 layers), fp32 out
11
+ indexer compressor N=512, fp32 out
12
+ MoE router gate N=216, fp32 out
13
+ indexer weights_proj N=64, bf16 out
14
+
15
+ This kernel splits both N and K so every SM streams weights, accumulates in
16
+ fp32 with tensor-core dots, and reduces split-K partials with fp32 atomics.
17
+ Atomic reduction order is not fixed, so results can differ from torch.mm in
18
+ the last fp32 bits. Rows > MAX_ROWS (prefill) fall back to torch.mm.
19
+
20
+ Opt-in with VLLM_QWEN38_SMALL_GEMV=1.
21
+ """
22
+
23
+ import os
24
+
25
+ import torch
26
+ import triton
27
+ import triton.language as tl
28
+
29
+ ENABLED = os.environ.get("VLLM_QWEN38_SMALL_GEMV", "0") == "1"
30
+ # VLLM_QWEN38_GEMV_TUNE=1: configs re-tuned by gemv_dirty_bench.py (8 MB of dirty L2
31
+ # before every call, closer to in-model conditions than DRAM-cold back-to-back calls).
32
+ _TUNE = os.environ.get("VLLM_QWEN38_GEMV_TUNE", "0") == "1"
33
+ MAX_ROWS = 16
34
+
35
+ # (BLOCK_N, BLOCK_K, SPLIT_K, num_warps) per N, tuned on GB10 by
36
+ # ds4_small_gemv_bench.py with DRAM-cold weights (as in the real model) at the
37
+ # DSpark verify width (6 rows). N not listed uses the nearest smaller entry.
38
+ # Cold-cache time per call vs torch.mm: N=64 27->5 us, 216 12->11, 512 24->20,
39
+ # 1024 44->37.5, 2048 82->73 (bandwidth floor 61 us). Only N=64 is a big win;
40
+ # the rest are already near the bandwidth floor.
41
+ # Qwen3.8 router (K=2560, N=512), cold: torch.mm 13.9 us -> 12.5 us (floor 9.6).
42
+ _CONFIGS: dict[int, tuple[int, int, int, int]] = {
43
+ 64: (32, 128, 32, 4),
44
+ 216: (64, 128, 32, 8),
45
+ 512: (32, 256, 10, 4) if _TUNE else (16, 256, 1, 4), # dirty 11.6 -> 9.5 us
46
+ 1024: (64, 256, 1, 8),
47
+ 2048: (64, 256, 1, 8),
48
+ }
49
+
50
+
51
+ @triton.jit
52
+ def _small_gemv_kernel(
53
+ x_ptr, w_ptr, out_ptr,
54
+ M, N, K,
55
+ stride_xm, stride_wn, stride_om,
56
+ K_PER_SPLIT,
57
+ BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
58
+ USE_ATOMIC: tl.constexpr,
59
+ ):
60
+ pid_n = tl.program_id(0)
61
+ pid_k = tl.program_id(1)
62
+ offs_m = tl.arange(0, BLOCK_M)
63
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
64
+ k_start = pid_k * K_PER_SPLIT
65
+ acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
66
+ for kk in range(0, K_PER_SPLIT, BLOCK_K):
67
+ offs_k = k_start + kk + tl.arange(0, BLOCK_K)
68
+ x = tl.load(
69
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :],
70
+ mask=(offs_m[:, None] < M) & (offs_k[None, :] < K),
71
+ other=0.0,
72
+ )
73
+ w = tl.load(
74
+ w_ptr + offs_n[:, None] * stride_wn + offs_k[None, :],
75
+ mask=(offs_n[:, None] < N) & (offs_k[None, :] < K),
76
+ other=0.0,
77
+ )
78
+ acc += tl.dot(x, tl.trans(w))
79
+ out_offs = out_ptr + offs_m[:, None] * stride_om + offs_n[None, :]
80
+ out_mask = (offs_m[:, None] < M) & (offs_n[None, :] < N)
81
+ if USE_ATOMIC:
82
+ tl.atomic_add(out_offs, acc, mask=out_mask, sem="relaxed")
83
+ else:
84
+ tl.store(out_offs, acc, mask=out_mask)
85
+
86
+
87
+ def _config_for(n: int) -> tuple[int, int, int, int]:
88
+ keys = [k for k in sorted(_CONFIGS) if k <= n]
89
+ return _CONFIGS[keys[-1] if keys else min(_CONFIGS)]
90
+
91
+
92
+ def small_mm(
93
+ x: torch.Tensor,
94
+ weight: torch.Tensor,
95
+ out_dtype: torch.dtype,
96
+ config: tuple[int, int, int, int] | None = None,
97
+ ) -> torch.Tensor:
98
+ """Return ``x @ weight.T`` as ``out_dtype``; ``weight`` is [N, K]."""
99
+ m, k = x.shape
100
+ n = weight.shape[0]
101
+ if (
102
+ m > MAX_ROWS
103
+ or m == 0
104
+ or x.dtype != torch.bfloat16
105
+ or weight.dtype != torch.bfloat16
106
+ or x.stride(1) != 1
107
+ or weight.stride(1) != 1
108
+ ):
109
+ return torch.mm(x, weight.t(), out_dtype=out_dtype)
110
+ block_n, block_k, split_k, num_warps = config or _config_for(n)
111
+ k_per_split = triton.cdiv(triton.cdiv(k, split_k), block_k) * block_k
112
+ split_k = triton.cdiv(k, k_per_split)
113
+ use_atomic = split_k > 1
114
+ out = (torch.zeros if use_atomic else torch.empty)(
115
+ (m, n), dtype=torch.float32, device=x.device
116
+ )
117
+ grid = (triton.cdiv(n, block_n), split_k)
118
+ _small_gemv_kernel[grid](
119
+ x, weight, out,
120
+ m, n, k,
121
+ x.stride(0), weight.stride(0), out.stride(0),
122
+ k_per_split,
123
+ BLOCK_M=MAX_ROWS, BLOCK_N=block_n, BLOCK_K=block_k,
124
+ USE_ATOMIC=use_atomic,
125
+ num_warps=num_warps,
126
+ )
127
+ return out if out_dtype == torch.float32 else out.to(out_dtype)
recipe/patches/vllm_core/v1/core/boundary_checkpoint.py ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+ """Exact request-boundary lookup and ownership for recurrent prefix caches."""
5
+
6
+ import hashlib
7
+ from dataclasses import dataclass, field
8
+ from typing import TYPE_CHECKING, Any, Literal
9
+
10
+ from vllm.v1.core.kv_cache_utils import BlockHash, generate_block_hash_extra_keys
11
+ from vllm.v1.request import Request
12
+
13
+ if TYPE_CHECKING:
14
+ from vllm.v1.core.block_pool import BlockPool
15
+
16
+ MAX_BOUNDARY_STOP_TOKENS = 128
17
+
18
+ # Modalities whose placeholder spans are fully identified by their content hash.
19
+ CHECKPOINT_MODALITIES = frozenset({"image", "video"})
20
+ # Placeholder positions map into [2**30, 2**31): above every vocabulary and
21
+ # inside LMCache's token wire domain. Each position carries 30 independent
22
+ # bits of the item's full content hash.
23
+ _CONTENT_TOKEN_BASE = 1 << 30
24
+ # A checkpoint may not end within an item's first positions: with fewer of
25
+ # them its key would hold under 150 bits of that item's identity.
26
+ MIN_CHECKPOINT_ITEM_TOKENS = 5
27
+
28
+
29
+ def content_token_ids(
30
+ request: Request, start: int, end: int, salt: bytes = b""
31
+ ) -> tuple[int, ...]:
32
+ """Token IDs of ``[start, end)`` with each multimodal span named by content.
33
+
34
+ Placeholder tokens are identical for every image, so a checkpoint keyed by
35
+ token IDs alone could restore one image's state for another. Positions in
36
+ a multimodal span are replaced by pseudo-IDs derived from the item's
37
+ content hash and the position inside the span. Text requests are unchanged.
38
+
39
+ Args:
40
+ request: Request whose ``all_token_ids`` and ``mm_features`` are read.
41
+ start: First position, inclusive.
42
+ end: Last position, exclusive.
43
+ salt: Extra identity (for example encoder precision) mixed into the IDs.
44
+ """
45
+ tokens = request.all_token_ids[start:end]
46
+ features = request.mm_features
47
+ if not features:
48
+ return tuple(tokens)
49
+ tokens = list(tokens)
50
+ for feature in features:
51
+ offset = feature.mm_position.offset
52
+ length = feature.mm_position.length
53
+ lo, hi = max(offset, start), min(offset + length, end)
54
+ if lo >= hi:
55
+ continue
56
+ key = hashlib.sha256(
57
+ b"vllm-mm-content-tokens-v2\0" + salt + b"\0" + feature.identifier.encode()
58
+ ).digest()
59
+ # Counter-mode BLAKE2b keyed by the full digest: 16 values per call,
60
+ # independent across positions, so a span is not one 30-bit seed.
61
+ first, last = lo - offset, hi - offset
62
+ for block in range(first // 16, (last - 1) // 16 + 1):
63
+ words = hashlib.blake2b(
64
+ block.to_bytes(8, "little"), key=key, digest_size=64
65
+ ).digest()
66
+ for index in range(max(first, block * 16), min(last, block * 16 + 16)):
67
+ word = words[(index % 16) * 4 : (index % 16) * 4 + 4]
68
+ value = int.from_bytes(word, "little") & (_CONTENT_TOKEN_BASE - 1)
69
+ tokens[offset + index - start] = _CONTENT_TOKEN_BASE + value
70
+ return tuple(tokens)
71
+
72
+
73
+ def checkpoint_end_allowed(request: Request, num_tokens: int) -> bool:
74
+ """Whether a checkpoint may end after ``num_tokens`` of this request.
75
+
76
+ It must not end within the first MIN_CHECKPOINT_ITEM_TOKENS positions of
77
+ a multimodal item: its key would identify that item by too few bits.
78
+ """
79
+ for feature in request.mm_features or ():
80
+ offset = feature.mm_position.offset
81
+ depth = min(MIN_CHECKPOINT_ITEM_TOKENS, feature.mm_position.length)
82
+ if offset < num_tokens < offset + depth:
83
+ return False
84
+ return True
85
+
86
+
87
+ # Optional checkpoints have fixed slots; absent slots contain no KV blocks.
88
+ PROMPT_CHECKPOINT_SLOT = 0
89
+ RESPONSE_CHECKPOINT_SLOT = 1
90
+ INSTRUCTION_CHECKPOINT_SLOT = 2
91
+ PREFILL_TAIL_CHECKPOINT_SLOT = 3
92
+ NUM_BOUNDARY_CHECKPOINT_SLOTS = 4
93
+
94
+ BoundaryCheckpointKind = Literal["instruction", "prompt", "response", "prefill_tail"]
95
+
96
+
97
+ import os as _os
98
+
99
+ # VLLM_QWEN38_TAIL_CKPT="GRID,SUFFIX": place the prefill-tail checkpoint on a GRID-token
100
+ # grid at least SUFFIX tokens before the prompt end, instead of one to two prefill
101
+ # chunks before it. A follow-up turn re-renders only the previous generation prompt
102
+ # (a few tokens) differently, so it can resume almost at the old prompt's end and the
103
+ # reusable prefix no longer depends on the chunk size. Costs one short extra prefill
104
+ # step per new prompt. Hits stay hash-validated, so a misplaced checkpoint only misses.
105
+ _TAIL_CKPT = tuple(
106
+ int(v) for v in _os.environ.get("VLLM_QWEN38_TAIL_CKPT", "").split(",") if v
107
+ )
108
+
109
+
110
+ def get_prefill_tail_checkpoint_position(
111
+ prompt_tokens: int, chunk_budget: int, instruction_boundary: int | None
112
+ ) -> int | None:
113
+ """Leave one to two prefill chunks after a bounded suffix-recovery point."""
114
+ if chunk_budget <= 0:
115
+ return None
116
+ if len(_TAIL_CKPT) == 2:
117
+ grid, suffix = _TAIL_CKPT
118
+ position = (prompt_tokens - suffix) // grid * grid
119
+ else:
120
+ position = (prompt_tokens // chunk_budget - 1) * chunk_budget
121
+ return position if position > (instruction_boundary or 0) else None
122
+
123
+
124
+ def boundary_checkpoint_slots(request: Request) -> tuple[int, ...]:
125
+ """Allocate only the endpoints that this request can publish."""
126
+ slots: tuple[int, ...] = (PROMPT_CHECKPOINT_SLOT, RESPONSE_CHECKPOINT_SLOT)
127
+ if request.recurrent_instruction_boundary is not None:
128
+ slots += (INSTRUCTION_CHECKPOINT_SLOT,)
129
+ if request.recurrent_prefill_tail_boundary is not None:
130
+ slots += (PREFILL_TAIL_CHECKPOINT_SLOT,)
131
+ return slots
132
+
133
+
134
+ @dataclass(frozen=True)
135
+ class BoundaryCheckpoint:
136
+ checkpoint_id: int
137
+ num_tokens: int
138
+ block_ids: tuple[tuple[int, ...], ...]
139
+ auxiliary_block_ids: tuple[int, ...] = ()
140
+ draft_prefix_len: int = 0
141
+ kind: BoundaryCheckpointKind = "prompt"
142
+
143
+ @property
144
+ def dependencies(self) -> frozenset[int]:
145
+ return frozenset(
146
+ block_id
147
+ for group in (*self.block_ids, self.auxiliary_block_ids)
148
+ for block_id in group
149
+ if block_id != 0
150
+ )
151
+
152
+
153
+ @dataclass
154
+ class _RadixNode:
155
+ tokens: tuple[int, ...] = ()
156
+ checkpoint_id: int | None = None
157
+ children: dict[int, "_RadixNode"] = field(default_factory=dict)
158
+
159
+ def insert(self, tokens: tuple[int, ...], checkpoint_id: int) -> None:
160
+ node = self
161
+ while tokens:
162
+ child = node.children.get(tokens[0])
163
+ if child is None:
164
+ node.children[tokens[0]] = _RadixNode(tokens, checkpoint_id)
165
+ return
166
+ common = 0
167
+ for a, b in zip(tokens, child.tokens):
168
+ if a != b:
169
+ break
170
+ common += 1
171
+ if common < len(child.tokens):
172
+ split = _RadixNode(child.tokens[:common])
173
+ node.children[tokens[0]] = split
174
+ child.tokens = child.tokens[common:]
175
+ split.children[child.tokens[0]] = child
176
+ child = split
177
+ node = child
178
+ tokens = tokens[common:]
179
+ node.checkpoint_id = checkpoint_id
180
+
181
+ def find(self, tokens: tuple[int, ...]) -> int | None:
182
+ node = self
183
+ found = node.checkpoint_id
184
+ offset = 0
185
+ while offset < len(tokens):
186
+ child = node.children.get(tokens[offset])
187
+ if child is None or tokens[offset : offset + len(child.tokens)] != (
188
+ child.tokens
189
+ ):
190
+ break
191
+ offset += len(child.tokens)
192
+ node = child
193
+ if node.checkpoint_id is not None:
194
+ found = node.checkpoint_id
195
+ return found
196
+
197
+ def remove(self, tokens: tuple[int, ...]) -> None:
198
+ node = self
199
+ path: list[tuple[_RadixNode, int]] = []
200
+ offset = 0
201
+ while offset < len(tokens):
202
+ key = tokens[offset]
203
+ path.append((node, key))
204
+ node = node.children[key]
205
+ offset += len(node.tokens)
206
+ node.checkpoint_id = None
207
+ for parent, key in reversed(path):
208
+ child = parent.children[key]
209
+ if child.checkpoint_id is not None:
210
+ break
211
+ if not child.children:
212
+ del parent.children[key]
213
+ elif len(child.children) == 1:
214
+ grandchild = next(iter(child.children.values()))
215
+ grandchild.tokens = child.tokens + grandchild.tokens
216
+ parent.children[key] = grandchild
217
+
218
+
219
+ # A full hash authenticates preceding blocks; the first block uses the same
220
+ # discriminators as the normal prefix cache, including cache salt and LoRA.
221
+ _RootKey = tuple[int, BlockHash | None, tuple[Any, ...] | None]
222
+
223
+
224
+ @dataclass
225
+ class _PendingCheckpoint:
226
+ checkpoint: BoundaryCheckpoint
227
+ root_key: _RootKey
228
+ tokens: tuple[int, ...]
229
+ num_ranks: int
230
+ completed_ranks: set[int] = field(default_factory=set)
231
+ invalidated: bool = False
232
+
233
+
234
+ class BoundaryCheckpointCache:
235
+ """Cache immutable bundles in the ordinary, evictable KV block pool.
236
+
237
+ Staged bundles pin every dependency until all workers acknowledge their
238
+ copies. Published bundles release those pins and participate in the pool's
239
+ LRU. Evicting any dependency invalidates the entire bundle. Readers acquire
240
+ their own pins and must copy mutable tails before running a continuation.
241
+ """
242
+
243
+ def __init__(self, block_pool: "BlockPool") -> None:
244
+ if block_pool.boundary_checkpoints is not None:
245
+ raise ValueError("Block pool already has a boundary checkpoint cache")
246
+ block_pool.boundary_checkpoints = self
247
+ self.block_pool = block_pool
248
+ self.hash_block_size = block_pool.hash_block_size
249
+ self._roots: dict[_RootKey, _RadixNode] = {}
250
+ self._entries: dict[int, _PendingCheckpoint] = {}
251
+ self._pending: dict[int, _PendingCheckpoint] = {}
252
+ self._by_block: dict[int, set[int]] = {}
253
+ self._pending_by_block: dict[int, set[int]] = {}
254
+ self._next_id = 1
255
+
256
+ def __len__(self) -> int:
257
+ return len(self._entries)
258
+
259
+ def next_id(self) -> int:
260
+ checkpoint_id = self._next_id
261
+ self._next_id += 1
262
+ return checkpoint_id
263
+
264
+ def _root_key(self, request: Request, start: int) -> _RootKey:
265
+ if start:
266
+ return start, request.block_hashes[start // self.hash_block_size - 1], None
267
+ extra_keys, _ = generate_block_hash_extra_keys(
268
+ request, 0, self.hash_block_size, 0
269
+ )
270
+ return 0, None, extra_keys
271
+
272
+ @staticmethod
273
+ def supports_request(request: Request) -> bool:
274
+ return (
275
+ request.prompt_token_ids is not None
276
+ and request.prompt_embeds is None
277
+ and all(
278
+ feature.modality in CHECKPOINT_MODALITIES and feature.identifier
279
+ for feature in request.mm_features or ()
280
+ )
281
+ and not request.resumable
282
+ and request.sampling_params is not None
283
+ and len(request.sampling_params.stop_token_ids or ())
284
+ + int(request.sampling_params.eos_token_id is not None)
285
+ <= MAX_BOUNDARY_STOP_TOKENS
286
+ )
287
+
288
+ def stage(
289
+ self,
290
+ request: Request,
291
+ checkpoint: BoundaryCheckpoint,
292
+ num_ranks: int,
293
+ ) -> None:
294
+ """Pin a complete bundle while its asynchronous copies finish."""
295
+ if not self.supports_request(request):
296
+ raise ValueError("Boundary checkpoints require a text generation request")
297
+ if not 0 < checkpoint.num_tokens <= request.num_tokens:
298
+ raise ValueError("Checkpoint must cover an existing, nonempty prefix")
299
+ if not checkpoint_end_allowed(request, checkpoint.num_tokens):
300
+ raise ValueError("Checkpoint cannot end at the start of a multimodal item")
301
+ if not 0 <= checkpoint.draft_prefix_len <= checkpoint.num_tokens:
302
+ raise ValueError("Draft prefix cannot extend beyond the target prefix")
303
+ if num_ranks < 1:
304
+ raise ValueError("Checkpoint must be acknowledged by at least one rank")
305
+ cid = checkpoint.checkpoint_id
306
+ if cid in self._entries or cid in self._pending:
307
+ raise ValueError("Checkpoint ID is already in use")
308
+ blocks = [self.block_pool.blocks[i] for i in checkpoint.dependencies]
309
+ if not blocks or any(block.ref_cnt == 0 for block in blocks):
310
+ raise ValueError("Checkpoint dependencies must still be owned by producer")
311
+ start = ((checkpoint.num_tokens - 1) // self.hash_block_size) * (
312
+ self.hash_block_size
313
+ )
314
+ pending = _PendingCheckpoint(
315
+ checkpoint,
316
+ self._root_key(request, start),
317
+ content_token_ids(request, start, checkpoint.num_tokens),
318
+ num_ranks,
319
+ )
320
+ self.block_pool.touch(blocks)
321
+ self._pending[cid] = pending
322
+ for block_id in checkpoint.dependencies:
323
+ self._pending_by_block.setdefault(block_id, set()).add(cid)
324
+
325
+ def acknowledge(self, checkpoint_id: int, rank: int) -> bool:
326
+ """Publish only after every rank's copy completion has been observed."""
327
+ pending = self._pending.get(checkpoint_id)
328
+ if pending is None:
329
+ return False
330
+ if not 0 <= rank < pending.num_ranks:
331
+ raise ValueError("Invalid checkpoint rank")
332
+ pending.completed_ranks.add(rank)
333
+ if len(pending.completed_ranks) != pending.num_ranks:
334
+ return False
335
+ if pending.invalidated:
336
+ self.discard(checkpoint_id)
337
+ return False
338
+ del self._pending[checkpoint_id]
339
+ self._forget_pending(pending.checkpoint)
340
+ root = self._roots.setdefault(pending.root_key, _RadixNode())
341
+ previous_id = root.find(pending.tokens)
342
+ if previous_id is not None:
343
+ previous = self._entries[previous_id]
344
+ if previous.tokens == pending.tokens:
345
+ # Deduplicate exact endpoints without disturbing active readers.
346
+ self.invalidate(previous_id)
347
+ root = self._roots.setdefault(pending.root_key, _RadixNode())
348
+ root.insert(pending.tokens, checkpoint_id)
349
+ self._entries[checkpoint_id] = pending
350
+ for block_id in pending.checkpoint.dependencies:
351
+ self._by_block.setdefault(block_id, set()).add(checkpoint_id)
352
+ self._release(pending.checkpoint)
353
+ return True
354
+
355
+ def is_pending(self, checkpoint_id: int) -> bool:
356
+ """Return whether a bundle still owns pins for unfinished worker copies."""
357
+ return checkpoint_id in self._pending
358
+
359
+ def discard(self, checkpoint_id: int) -> None:
360
+ """Release an unpublished bundle after its GPU writers have completed."""
361
+ pending = self._pending.pop(checkpoint_id, None)
362
+ if pending is not None:
363
+ self._forget_pending(pending.checkpoint)
364
+ self._release(pending.checkpoint)
365
+
366
+ def _forget_pending(self, checkpoint: BoundaryCheckpoint) -> None:
367
+ for block_id in checkpoint.dependencies:
368
+ ids = self._pending_by_block[block_id]
369
+ ids.remove(checkpoint.checkpoint_id)
370
+ if not ids:
371
+ del self._pending_by_block[block_id]
372
+
373
+ def find(self, request: Request, max_length: int) -> BoundaryCheckpoint | None:
374
+ if not self.supports_request(request) or request.skip_reading_prefix_cache:
375
+ return None
376
+ max_length = min(max_length, request.num_tokens)
377
+ if max_length <= 0:
378
+ return None
379
+ start = ((max_length - 1) // self.hash_block_size) * self.hash_block_size
380
+ for offset in range(start, -1, -self.hash_block_size):
381
+ root = self._roots.get(self._root_key(request, offset))
382
+ if root is None:
383
+ continue
384
+ tokens = content_token_ids(
385
+ request, offset, min(offset + self.hash_block_size, max_length)
386
+ )
387
+ checkpoint_id = root.find(tokens)
388
+ if checkpoint_id is not None:
389
+ return self._entries[checkpoint_id].checkpoint
390
+ return None
391
+
392
+ def acquire(self, checkpoint_id: int) -> BoundaryCheckpoint | None:
393
+ entry = self._entries.get(checkpoint_id)
394
+ if entry is None:
395
+ return None
396
+ checkpoint = entry.checkpoint
397
+ self.block_pool.touch(
398
+ [self.block_pool.blocks[i] for i in checkpoint.dependencies]
399
+ )
400
+ return checkpoint
401
+
402
+ def release(self, checkpoint: BoundaryCheckpoint) -> None:
403
+ self._release(checkpoint)
404
+
405
+ def _release(self, checkpoint: BoundaryCheckpoint) -> None:
406
+ self.block_pool.free_blocks(
407
+ self.block_pool.blocks[i] for i in checkpoint.dependencies
408
+ )
409
+
410
+ def contains_block(self, block_id: int) -> bool:
411
+ return block_id in self._by_block
412
+
413
+ def invalidate_block(self, block_id: int) -> None:
414
+ for checkpoint_id in self._pending_by_block.get(block_id, ()):
415
+ self._pending[checkpoint_id].invalidated = True
416
+ for checkpoint_id in tuple(self._by_block.get(block_id, ())):
417
+ self.invalidate(checkpoint_id)
418
+
419
+ def invalidate(self, checkpoint_id: int) -> None:
420
+ entry = self._entries.pop(checkpoint_id, None)
421
+ if entry is None:
422
+ return
423
+ root = self._roots[entry.root_key]
424
+ root.remove(entry.tokens)
425
+ if not root.children:
426
+ del self._roots[entry.root_key]
427
+ for block_id in entry.checkpoint.dependencies:
428
+ ids = self._by_block[block_id]
429
+ ids.remove(checkpoint_id)
430
+ if not ids:
431
+ del self._by_block[block_id]
432
+
433
+ def clear(self) -> None:
434
+ if self._pending:
435
+ raise RuntimeError("Cannot clear checkpoints with pending GPU writers")
436
+ self._entries.clear()
437
+ self._roots.clear()
438
+ self._by_block.clear()
recipe/patches/vllm_kernels/linear/mxfp8/b12x.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+ from __future__ import annotations
5
+
6
+ from collections.abc import Sequence
7
+
8
+ import torch
9
+
10
+ from vllm.model_executor.kernels.linear.b12x_blockscaled import B12xBlockscaledLinear
11
+ from vllm.model_executor.layers.quantization.utils.mxfp8_utils import (
12
+ MXFP8_BLOCK_SIZE,
13
+ MXFP8_SCALE_DTYPE,
14
+ MXFP8_VALUE_DTYPE,
15
+ )
16
+ from vllm.model_executor.utils import replace_parameter
17
+ from vllm.platforms import current_platform
18
+ from vllm.utils.b12x import (
19
+ B12xWorkload,
20
+ b12x_layer_prefix,
21
+ get_b12x_dense_activation_mode,
22
+ get_b12x_projection_workspace_sizes,
23
+ register_b12x_layer,
24
+ reuse_packed_weight_storage,
25
+ run_b12x_blockscaled_linear,
26
+ set_b12x_preparation_provider,
27
+ )
28
+ from vllm.utils.b12x import (
29
+ get_b12x_blockscaled as _import_b12x_blockscaled,
30
+ )
31
+ from vllm.utils.torch_utils import _encode_layer_name
32
+
33
+ from .Mxfp8LinearKernel import Mxfp8LinearKernel, Mxfp8LinearLayerConfig
34
+
35
+ import os as _os
36
+
37
+ from vllm.logger import init_logger as _init_logger
38
+
39
+ _logger = _init_logger(__name__)
40
+
41
+ # Decode-row split-K GEMV over the packed values/scale_rows for the listed
42
+ # "NxK" shapes, or "all" (qwen4_exp/nvidia/ops/mxfp8_gemv.py). Empty disables.
43
+ _GEMV_SPEC = _os.environ.get("VLLM_QWEN38_MXFP8_GEMV", "")
44
+ _GEMV_ALL = _GEMV_SPEC == "all"
45
+ _GEMV_SHAPES = frozenset(
46
+ tuple(int(v) for v in item.split("x"))
47
+ for item in ("" if _GEMV_ALL else _GEMV_SPEC).split(",")
48
+ if item
49
+ )
50
+ _GEMV_LOGGED: set = set()
51
+
52
+
53
+ class B12xMxfp8LinearKernel(Mxfp8LinearKernel):
54
+ """ModelOpt MXFP8 linear through a layer-held prepared b12x plan."""
55
+
56
+ @classmethod
57
+ def is_supported(cls, compute_capability=None):
58
+ del compute_capability
59
+ if not current_platform.is_cuda():
60
+ return False, "b12x MXFP8 kernels are only available on CUDA"
61
+ if not current_platform.is_device_capability_family(120):
62
+ return False, "b12x MXFP8 kernels require a Blackwell 12x device"
63
+ api = _import_b12x_blockscaled()
64
+ if api is None:
65
+ return False, "Install the B12X backend with `pip install vllm[b12x]`"
66
+ if not api.is_supported():
67
+ return False, "b12x.gemm.blockscaled is not supported"
68
+ return True, None
69
+
70
+ @classmethod
71
+ def can_implement(cls, config: Mxfp8LinearLayerConfig):
72
+ del config
73
+ return True, None
74
+
75
+ def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
76
+ weight = layer.weight.data
77
+ scales = layer.weight_scale.data
78
+ assert weight.dtype == MXFP8_VALUE_DTYPE and weight.ndim == 2
79
+ assert scales.dtype == MXFP8_SCALE_DTYPE and scales.ndim == 2
80
+ out_features, in_features = map(int, weight.shape)
81
+ assert in_features % MXFP8_BLOCK_SIZE == 0
82
+ layer.b12x_mxfp8_output_size = out_features
83
+ padded_out_features = (out_features + 7) // 8 * 8
84
+ if padded_out_features != out_features:
85
+ padded_weight = weight.new_zeros((padded_out_features, in_features))
86
+ padded_weight[:out_features].copy_(weight)
87
+ weight = padded_weight
88
+ # E8M0 byte 127 is a unit scale for the artificial zero rows.
89
+ padded_scales = scales.new_full(
90
+ (max(padded_out_features, scales.shape[0]), scales.shape[1]), 127
91
+ )
92
+ padded_scales[:out_features].copy_(scales[:out_features])
93
+ scales = padded_scales
94
+ api = _import_b12x_blockscaled()
95
+ assert api is not None
96
+ packed = api.pack_weight(
97
+ weight.detach(),
98
+ scales[:, : in_features // MXFP8_BLOCK_SIZE].detach(),
99
+ )
100
+ layer.b12x_mxfp8_packed_weight = reuse_packed_weight_storage(
101
+ getattr(layer, "b12x_mxfp8_packed_weight", None), packed
102
+ )
103
+ # A method that pre-quantizes or keeps BF16 activations sets the mode
104
+ # before weight processing; the configured default applies otherwise.
105
+ layer.b12x_activation_mode = getattr(
106
+ layer, "b12x_activation_mode", None
107
+ ) or get_b12x_dense_activation_mode("mxfp8")
108
+ layer.b12x_bf16_input_supported = (
109
+ in_features % 128 == 0 and padded_out_features % 8 == 0
110
+ )
111
+ name = b12x_layer_prefix(layer)
112
+ # A reload into the same packed storage keeps the holder and its
113
+ # prepared plan; new storage declares anew.
114
+ existing = getattr(layer, "b12x_linear", None)
115
+ if existing is None or not existing.holds(layer.b12x_mxfp8_packed_weight):
116
+ layer.b12x_linear = B12xBlockscaledLinear(
117
+ layer.b12x_mxfp8_packed_weight,
118
+ recipe="mxfp8",
119
+ activation_mode=layer.b12x_activation_mode,
120
+ layer_name=name,
121
+ )
122
+ # A layer marked b12x_prefill_a16 runs prefill-sized calls with BF16
123
+ # activations through a second plan over the same packed weight.
124
+ wants_prefill = (
125
+ getattr(layer, "b12x_prefill_a16", False)
126
+ and layer.b12x_activation_mode != "a16"
127
+ )
128
+ prefill = getattr(layer, "b12x_linear_prefill", None)
129
+ if not wants_prefill:
130
+ layer.b12x_linear_prefill = None
131
+ elif prefill is None or not prefill.holds(layer.b12x_mxfp8_packed_weight):
132
+ layer.b12x_linear_prefill = B12xBlockscaledLinear(
133
+ layer.b12x_mxfp8_packed_weight,
134
+ recipe="mxfp8",
135
+ activation_mode="a16",
136
+ layer_name=name,
137
+ )
138
+ layer.b12x_layer_name = _encode_layer_name(name)
139
+ register_b12x_layer(name, layer)
140
+ replace_parameter(layer, "weight", weight.new_empty((0,)))
141
+ replace_parameter(layer, "weight_scale", scales.new_empty((0,)))
142
+ if not getattr(layer, "b12x_preparation_suppressed", False):
143
+ set_b12x_preparation_provider(layer, self)
144
+
145
+ def get_b12x_preparation_units(
146
+ self, layer: torch.nn.Module, workload: B12xWorkload
147
+ ) -> Sequence[object]:
148
+ packed = layer.b12x_mxfp8_packed_weight
149
+ if packed.weight.values.is_meta:
150
+ return ()
151
+ linear = layer.b12x_linear
152
+ units = [linear.unit(workload, name=f"linear.mxfp8.{linear.layer_name}")]
153
+ prefill = getattr(layer, "b12x_linear_prefill", None)
154
+ if prefill is not None:
155
+ units.append(
156
+ prefill.unit(workload, name=f"linear.mxfp8.a16.{prefill.layer_name}")
157
+ )
158
+ return tuple(units)
159
+
160
+ def get_workspace_size(self, layer: torch.nn.Module, rows: int) -> int:
161
+ return get_b12x_projection_workspace_sizes(rows, layer)[0]
162
+
163
+ def apply_weights(
164
+ self,
165
+ layer: torch.nn.Module,
166
+ x: torch.Tensor,
167
+ bias: torch.Tensor | None = None,
168
+ ) -> torch.Tensor:
169
+ source = x.reshape(-1, x.shape[-1]).contiguous()
170
+ if source.dtype != torch.bfloat16:
171
+ raise ValueError("prepared vLLM MXFP8 path requires BF16 activations")
172
+ logical_out_features = layer.b12x_mxfp8_output_size
173
+ if (_GEMV_ALL or _GEMV_SHAPES) and source.shape[0] <= 16 and bias is None:
174
+ packed = layer.b12x_mxfp8_packed_weight
175
+ key = (int(logical_out_features), int(packed.in_features))
176
+ if _GEMV_ALL or key in _GEMV_SHAPES:
177
+ from vllm.models.qwen4_exp.nvidia.ops.mxfp8_gemv import mxfp8_gemv
178
+
179
+ if key not in _GEMV_LOGGED:
180
+ _GEMV_LOGGED.add(key)
181
+ _logger.info("MXFP8 decode GEMV active for N=%d K=%d", *key)
182
+ out = mxfp8_gemv(
183
+ source,
184
+ packed.weight.values,
185
+ packed.weight.scale_rows[0],
186
+ key[0],
187
+ key[1],
188
+ )
189
+ return out.view(*x.shape[:-1], key[0])
190
+ out_features = int(layer.b12x_mxfp8_packed_weight.out_features)
191
+ padded = logical_out_features != out_features
192
+ output = run_b12x_blockscaled_linear(
193
+ source, None if padded else bias, out_features, layer.b12x_layer_name
194
+ )
195
+ if padded:
196
+ output = output[:, :logical_out_features]
197
+ if bias is not None:
198
+ output.add_(bias)
199
+ return output.view(*x.shape[:-1], logical_out_features)
200
+
201
+
202
+ __all__ = ["B12xMxfp8LinearKernel"]
recipe/results/bench-sbs.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ qwen3.8-flash-next ctx=2000 conc=1: prompt_tok=2074 | prefill 1850 tok/s agg ( 1850/stream, TTFT 1.1s) | decode 51.7 tok/s agg ( 51.7/stream)
2
+ qwen3.8-flash-next ctx=2000 conc=4: prompt_tok=2063 | prefill 2230 tok/s agg ( 558/stream, TTFT 4.1s) | decode 107.0 tok/s agg ( 26.7/stream)
3
+ qwen3.8-flash-next ctx=46000 conc=1: prompt_tok=45928 | prefill 2259 tok/s agg ( 2259/stream, TTFT 20.3s) | decode 45.8 tok/s agg ( 45.8/stream)
4
+ qwen3.8-flash-next ctx=46000 conc=4: prompt_tok=45990 | prefill 4114 tok/s agg ( 1029/stream, TTFT 84.0s) | decode 37.4 tok/s agg ( 9.4/stream)
recipe/results/kl-neutral-a16-as-served.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "chunks": 87,
3
+ "positions": 178089,
4
+ "mean_kld": 0.04918030487709328,
5
+ "kld_se": 0.00030368987921901996,
6
+ "median_kld": 0.012365318834781647,
7
+ "p90_kld": 0.11989473551511765,
8
+ "p99_kld": 0.5401471257209778,
9
+ "p999_kld": 1.5018013715744019,
10
+ "same_top1_pct": 91.74120804766156,
11
+ "ppl_q": 4.076651335380382,
12
+ "ppl_base": 4.045180731278633,
13
+ "ln_ppl_ratio": 0.00774967060309506,
14
+ "rms_dp_pct": 6.86464061644188
15
+ }
recipe/results/kl-neutral-a4-prefill.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "chunks": 87,
3
+ "positions": 178089,
4
+ "mean_kld": 0.06648334885537187,
5
+ "kld_se": 0.00039647044943181815,
6
+ "median_kld": 0.017018219456076622,
7
+ "p90_kld": 0.16397449374198914,
8
+ "p99_kld": 0.7069363594055176,
9
+ "p999_kld": 1.9706969261169434,
10
+ "same_top1_pct": 90.33067735795024,
11
+ "ppl_q": 4.095423230995411,
12
+ "ppl_base": 4.045180731278633,
13
+ "ln_ppl_ratio": 0.012343835396803282,
14
+ "rms_dp_pct": 7.96968497071922
15
+ }
recipe/results/needle.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ prompt_tokens=28,842 | 17.2s (~1,672 tok/s prefill) | found=True | 'PELICAN-42'
2
+ NEEDLE OK
3
+ prompt_tokens=96,042 | 57.8s (~1,661 tok/s prefill) | found=True | 'PELICAN-42'
4
+ NEEDLE OK
5
+ prompt_tokens=124,842 | 65.5s (~1,905 tok/s prefill) | found=True | 'PELICAN-42'
6
+ NEEDLE OK
recipe/results/quality-probe.jsonl ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"task": "gsm8k", "ok": true, "pred": "18", "ref": "18", "finish": "stop", "out_tokens": 239}
2
+ {"task": "gsm8k", "ok": true, "pred": "3", "ref": "3", "finish": "stop", "out_tokens": 236}
3
+ {"task": "gsm8k", "ok": true, "pred": "70000", "ref": "70000", "finish": "stop", "out_tokens": 520}
4
+ {"task": "gsm8k", "ok": true, "pred": "540", "ref": "540", "finish": "stop", "out_tokens": 250}
5
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 282}
6
+ {"task": "gsm8k", "ok": true, "pred": "64", "ref": "64", "finish": "stop", "out_tokens": 476}
7
+ {"task": "gsm8k", "ok": true, "pred": "260", "ref": "260", "finish": "stop", "out_tokens": 235}
8
+ {"task": "gsm8k", "ok": true, "pred": "160", "ref": "160", "finish": "stop", "out_tokens": 439}
9
+ {"task": "gsm8k", "ok": true, "pred": "45", "ref": "45", "finish": "stop", "out_tokens": 653}
10
+ {"task": "gsm8k", "ok": true, "pred": "460", "ref": "460", "finish": "stop", "out_tokens": 308}
11
+ {"task": "gsm8k", "ok": true, "pred": "366", "ref": "366", "finish": "stop", "out_tokens": 449}
12
+ {"task": "gsm8k", "ok": true, "pred": "694", "ref": "694", "finish": "stop", "out_tokens": 491}
13
+ {"task": "gsm8k", "ok": false, "pred": "12", "ref": "13", "finish": "stop", "out_tokens": 1383}
14
+ {"task": "gsm8k", "ok": true, "pred": "18", "ref": "18", "finish": "stop", "out_tokens": 611}
15
+ {"task": "gsm8k", "ok": true, "pred": "60", "ref": "60", "finish": "stop", "out_tokens": 314}
16
+ {"task": "gsm8k", "ok": true, "pred": "125", "ref": "125", "finish": "stop", "out_tokens": 296}
17
+ {"task": "gsm8k", "ok": true, "pred": "230", "ref": "230", "finish": "stop", "out_tokens": 213}
18
+ {"task": "gsm8k", "ok": true, "pred": "57500", "ref": "57500", "finish": "stop", "out_tokens": 380}
19
+ {"task": "gsm8k", "ok": true, "pred": "7", "ref": "7", "finish": "stop", "out_tokens": 196}
20
+ {"task": "gsm8k", "ok": true, "pred": "6", "ref": "6", "finish": "stop", "out_tokens": 402}
21
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 577}
22
+ {"task": "gsm8k", "ok": true, "pred": "14", "ref": "14", "finish": "stop", "out_tokens": 223}
23
+ {"task": "gsm8k", "ok": true, "pred": "7", "ref": "7", "finish": "stop", "out_tokens": 204}
24
+ {"task": "gsm8k", "ok": true, "pred": "8", "ref": "8", "finish": "stop", "out_tokens": 216}
25
+ {"task": "gsm8k", "ok": true, "pred": "26", "ref": "26", "finish": "stop", "out_tokens": 255}
26
+ {"task": "gsm8k", "ok": true, "pred": "2", "ref": "2", "finish": "stop", "out_tokens": 368}
27
+ {"task": "gsm8k", "ok": true, "pred": "243", "ref": "243", "finish": "stop", "out_tokens": 375}
28
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 232}
29
+ {"task": "gsm8k", "ok": true, "pred": "25", "ref": "25", "finish": "stop", "out_tokens": 295}
30
+ {"task": "gsm8k", "ok": true, "pred": "104", "ref": "104", "finish": "stop", "out_tokens": 273}
31
+ {"task": "gsm8k", "ok": true, "pred": "109", "ref": "109", "finish": "stop", "out_tokens": 290}
32
+ {"task": "gsm8k", "ok": true, "pred": "80", "ref": "80", "finish": "stop", "out_tokens": 306}
33
+ {"task": "gsm8k", "ok": true, "pred": "35", "ref": "35", "finish": "stop", "out_tokens": 140}
34
+ {"task": "gsm8k", "ok": true, "pred": "70", "ref": "70", "finish": "stop", "out_tokens": 407}
35
+ {"task": "gsm8k", "ok": true, "pred": "23", "ref": "23", "finish": "stop", "out_tokens": 216}
36
+ {"task": "gsm8k", "ok": true, "pred": "9", "ref": "9", "finish": "stop", "out_tokens": 256}
37
+ {"task": "gsm8k", "ok": true, "pred": "75", "ref": "75", "finish": "stop", "out_tokens": 240}
38
+ {"task": "gsm8k", "ok": true, "pred": "2", "ref": "2", "finish": "stop", "out_tokens": 434}
39
+ {"task": "gsm8k", "ok": true, "pred": "10", "ref": "10", "finish": "stop", "out_tokens": 347}
40
+ {"task": "gsm8k", "ok": true, "pred": "18", "ref": "18", "finish": "stop", "out_tokens": 524}
41
+ {"task": "gsm8k", "ok": true, "pred": "8", "ref": "8", "finish": "stop", "out_tokens": 315}
42
+ {"task": "gsm8k", "ok": true, "pred": "200", "ref": "200", "finish": "stop", "out_tokens": 418}
43
+ {"task": "gsm8k", "ok": true, "pred": "26", "ref": "26", "finish": "stop", "out_tokens": 218}
44
+ {"task": "gsm8k", "ok": true, "pred": "48", "ref": "48", "finish": "stop", "out_tokens": 405}
45
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 338}
46
+ {"task": "gsm8k", "ok": true, "pred": "104", "ref": "104", "finish": "stop", "out_tokens": 461}
47
+ {"task": "gsm8k", "ok": true, "pred": "163", "ref": "163", "finish": "stop", "out_tokens": 415}
48
+ {"task": "gsm8k", "ok": true, "pred": "800", "ref": "800", "finish": "stop", "out_tokens": 423}
49
+ {"task": "gsm8k", "ok": true, "pred": "8", "ref": "8", "finish": "stop", "out_tokens": 218}
50
+ {"task": "gsm8k", "ok": true, "pred": "30", "ref": "30", "finish": "stop", "out_tokens": 343}
51
+ {"task": "gsm8k", "ok": true, "pred": "294", "ref": "294", "finish": "stop", "out_tokens": 245}
52
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 288}
53
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 346}
54
+ {"task": "gsm8k", "ok": true, "pred": "40", "ref": "40", "finish": "stop", "out_tokens": 338}
55
+ {"task": "gsm8k", "ok": true, "pred": "40", "ref": "40", "finish": "stop", "out_tokens": 322}
56
+ {"task": "gsm8k", "ok": true, "pred": "14", "ref": "14", "finish": "stop", "out_tokens": 172}
57
+ {"task": "gsm8k", "ok": true, "pred": "3", "ref": "3", "finish": "stop", "out_tokens": 208}
58
+ {"task": "gsm8k", "ok": true, "pred": "83", "ref": "83", "finish": "stop", "out_tokens": 533}
59
+ {"task": "gsm8k", "ok": true, "pred": "57", "ref": "57", "finish": "stop", "out_tokens": 901}
60
+ {"task": "gsm8k", "ok": true, "pred": "187", "ref": "187", "finish": "stop", "out_tokens": 184}
61
+ {"task": "gsm8k", "ok": true, "pred": "17", "ref": "17", "finish": "stop", "out_tokens": 330}
62
+ {"task": "gsm8k", "ok": true, "pred": "1430", "ref": "1430", "finish": "stop", "out_tokens": 247}
63
+ {"task": "gsm8k", "ok": true, "pred": "25000", "ref": "25000", "finish": "stop", "out_tokens": 396}
64
+ {"task": "gsm8k", "ok": true, "pred": "1596", "ref": "1596", "finish": "stop", "out_tokens": 432}
65
+ {"task": "gsm8k", "ok": true, "pred": "300", "ref": "300", "finish": "stop", "out_tokens": 333}
66
+ {"task": "gsm8k", "ok": true, "pred": "36", "ref": "36", "finish": "stop", "out_tokens": 309}
67
+ {"task": "gsm8k", "ok": true, "pred": "48", "ref": "48", "finish": "stop", "out_tokens": 358}
68
+ {"task": "gsm8k", "ok": true, "pred": "595", "ref": "595", "finish": "stop", "out_tokens": 367}
69
+ {"task": "gsm8k", "ok": true, "pred": "36", "ref": "36", "finish": "stop", "out_tokens": 305}
70
+ {"task": "gsm8k", "ok": true, "pred": "60", "ref": "60", "finish": "stop", "out_tokens": 232}
71
+ {"task": "gsm8k", "ok": true, "pred": "7425", "ref": "7425", "finish": "stop", "out_tokens": 285}
72
+ {"task": "gsm8k", "ok": true, "pred": "60", "ref": "60", "finish": "stop", "out_tokens": 164}
73
+ {"task": "gsm8k", "ok": true, "pred": "221", "ref": "221", "finish": "stop", "out_tokens": 268}
74
+ {"task": "gsm8k", "ok": true, "pred": "255", "ref": "255", "finish": "stop", "out_tokens": 357}
75
+ {"task": "gsm8k", "ok": true, "pred": "88", "ref": "88", "finish": "stop", "out_tokens": 555}
76
+ {"task": "gsm8k", "ok": true, "pred": "60", "ref": "60", "finish": "stop", "out_tokens": 554}
77
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 386}
78
+ {"task": "gsm8k", "ok": true, "pred": "100", "ref": "100", "finish": "stop", "out_tokens": 337}
79
+ {"task": "gsm8k", "ok": true, "pred": "6", "ref": "6", "finish": "stop", "out_tokens": 401}
80
+ {"task": "gsm8k", "ok": true, "pred": "70", "ref": "70", "finish": "stop", "out_tokens": 265}
81
+ {"task": "gsm8k", "ok": true, "pred": "10", "ref": "10", "finish": "stop", "out_tokens": 295}
82
+ {"task": "gsm8k", "ok": true, "pred": "17", "ref": "17", "finish": "stop", "out_tokens": 213}
83
+ {"task": "gsm8k", "ok": true, "pred": "623", "ref": "623", "finish": "stop", "out_tokens": 281}
84
+ {"task": "gsm8k", "ok": true, "pred": "600", "ref": "600", "finish": "stop", "out_tokens": 188}
85
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 401}
86
+ {"task": "gsm8k", "ok": false, "pred": "48", "ref": "44", "finish": "stop", "out_tokens": 332}
87
+ {"task": "gsm8k", "ok": true, "pred": "22", "ref": "22", "finish": "stop", "out_tokens": 262}
88
+ {"task": "gsm8k", "ok": true, "pred": "9360", "ref": "9360", "finish": "stop", "out_tokens": 697}
89
+ {"task": "gsm8k", "ok": true, "pred": "8000", "ref": "8000", "finish": "stop", "out_tokens": 251}
90
+ {"task": "gsm8k", "ok": true, "pred": "24", "ref": "24", "finish": "stop", "out_tokens": 418}
91
+ {"task": "gsm8k", "ok": true, "pred": "225", "ref": "225", "finish": "stop", "out_tokens": 355}
92
+ {"task": "gsm8k", "ok": true, "pred": "28", "ref": "28", "finish": "stop", "out_tokens": 232}
93
+ {"task": "gsm8k", "ok": true, "pred": "4", "ref": "4", "finish": "stop", "out_tokens": 270}
94
+ {"task": "gsm8k", "ok": false, "pred": "36.36", "ref": "36", "finish": "stop", "out_tokens": 606}
95
+ {"task": "gsm8k", "ok": true, "pred": "348", "ref": "348", "finish": "stop", "out_tokens": 375}
96
+ {"task": "gsm8k", "ok": true, "pred": "40", "ref": "40", "finish": "stop", "out_tokens": 399}
97
+ {"task": "gsm8k", "ok": true, "pred": "3", "ref": "3", "finish": "stop", "out_tokens": 196}
98
+ {"task": "gsm8k", "ok": true, "pred": "12", "ref": "12", "finish": "stop", "out_tokens": 457}
99
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 467}
100
+ {"task": "gsm8k", "ok": true, "pred": "58", "ref": "58", "finish": "stop", "out_tokens": 293}
101
+ {"task": "gsm8k", "ok": true, "pred": "175", "ref": "175", "finish": "stop", "out_tokens": 539}
102
+ {"task": "gsm8k", "ok": true, "pred": "6", "ref": "6", "finish": "stop", "out_tokens": 261}
103
+ {"task": "gsm8k", "ok": true, "pred": "26", "ref": "26", "finish": "stop", "out_tokens": 326}
104
+ {"task": "gsm8k", "ok": true, "pred": "140", "ref": "140", "finish": "stop", "out_tokens": 502}
105
+ {"task": "gsm8k", "ok": true, "pred": "500", "ref": "500", "finish": "stop", "out_tokens": 320}
106
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 135}
107
+ {"task": "gsm8k", "ok": true, "pred": "72", "ref": "72", "finish": "stop", "out_tokens": 560}
108
+ {"task": "gsm8k", "ok": true, "pred": "3", "ref": "3", "finish": "stop", "out_tokens": 511}
109
+ {"task": "gsm8k", "ok": true, "pred": "50", "ref": "50", "finish": "stop", "out_tokens": 435}
110
+ {"task": "gsm8k", "ok": true, "pred": "28", "ref": "28", "finish": "stop", "out_tokens": 299}
111
+ {"task": "gsm8k", "ok": true, "pred": "45", "ref": "45", "finish": "stop", "out_tokens": 257}
112
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 468}
113
+ {"task": "gsm8k", "ok": true, "pred": "24", "ref": "24", "finish": "stop", "out_tokens": 182}
114
+ {"task": "gsm8k", "ok": true, "pred": "25", "ref": "25", "finish": "stop", "out_tokens": 134}
115
+ {"task": "gsm8k", "ok": true, "pred": "6", "ref": "6", "finish": "stop", "out_tokens": 627}
116
+ {"task": "gsm8k", "ok": true, "pred": "90", "ref": "90", "finish": "stop", "out_tokens": 634}
117
+ {"task": "gsm8k", "ok": true, "pred": "42", "ref": "42", "finish": "stop", "out_tokens": 357}
118
+ {"task": "gsm8k", "ok": true, "pred": "360", "ref": "360", "finish": "stop", "out_tokens": 171}
119
+ {"task": "gsm8k", "ok": true, "pred": "4", "ref": "4", "finish": "stop", "out_tokens": 264}
120
+ {"task": "gsm8k", "ok": false, "pred": "99076.92", "ref": "95200", "finish": "stop", "out_tokens": 1311}
121
+ {"task": "gsm8k", "ok": true, "pred": "240", "ref": "240", "finish": "stop", "out_tokens": 201}
122
+ {"task": "gsm8k", "ok": true, "pred": "27", "ref": "27", "finish": "stop", "out_tokens": 257}
123
+ {"task": "gsm8k", "ok": true, "pred": "48", "ref": "48", "finish": "stop", "out_tokens": 413}
124
+ {"task": "gsm8k", "ok": true, "pred": "50", "ref": "50", "finish": "stop", "out_tokens": 389}
125
+ {"task": "gsm8k", "ok": true, "pred": "10", "ref": "10", "finish": "stop", "out_tokens": 249}
126
+ {"task": "gsm8k", "ok": true, "pred": "10", "ref": "10", "finish": "stop", "out_tokens": 285}
127
+ {"task": "gsm8k", "ok": true, "pred": "82", "ref": "82", "finish": "stop", "out_tokens": 150}
128
+ {"task": "gsm8k", "ok": true, "pred": "120", "ref": "120", "finish": "stop", "out_tokens": 209}
129
+ {"task": "gsm8k", "ok": true, "pred": "880", "ref": "880", "finish": "stop", "out_tokens": 450}
130
+ {"task": "gsm8k", "ok": true, "pred": "10000", "ref": "10000", "finish": "stop", "out_tokens": 493}
131
+ {"task": "gsm8k", "ok": true, "pred": "30", "ref": "30", "finish": "stop", "out_tokens": 197}
132
+ {"task": "gsm8k", "ok": false, "pred": "1040", "ref": "940", "finish": "stop", "out_tokens": 155}
133
+ {"task": "gsm8k", "ok": true, "pred": "60", "ref": "60", "finish": "stop", "out_tokens": 240}
134
+ {"task": "gsm8k", "ok": true, "pred": "13", "ref": "13", "finish": "stop", "out_tokens": 283}
135
+ {"task": "gsm8k", "ok": true, "pred": "720", "ref": "720", "finish": "stop", "out_tokens": 150}
136
+ {"task": "gsm8k", "ok": true, "pred": "40", "ref": "40", "finish": "stop", "out_tokens": 175}
137
+ {"task": "gsm8k", "ok": true, "pred": "6", "ref": "6", "finish": "stop", "out_tokens": 169}
138
+ {"task": "gsm8k", "ok": true, "pred": "29", "ref": "29", "finish": "stop", "out_tokens": 394}
139
+ {"task": "gsm8k", "ok": true, "pred": "105", "ref": "105", "finish": "stop", "out_tokens": 457}
140
+ {"task": "gsm8k", "ok": true, "pred": "70", "ref": "70", "finish": "stop", "out_tokens": 308}
141
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 310}
142
+ {"task": "gsm8k", "ok": true, "pred": "400", "ref": "400", "finish": "stop", "out_tokens": 539}
143
+ {"task": "gsm8k", "ok": true, "pred": "140", "ref": "140", "finish": "stop", "out_tokens": 176}
144
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 327}
145
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 352}
146
+ {"task": "gsm8k", "ok": true, "pred": "4000", "ref": "4000", "finish": "stop", "out_tokens": 269}
147
+ {"task": "gsm8k", "ok": true, "pred": "2125", "ref": "2125", "finish": "stop", "out_tokens": 1013}
148
+ {"task": "gsm8k", "ok": true, "pred": "75", "ref": "75", "finish": "stop", "out_tokens": 525}
149
+ {"task": "gsm8k", "ok": true, "pred": "30", "ref": "30", "finish": "stop", "out_tokens": 199}
150
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 313}
151
+ {"task": "gsm8k", "ok": true, "pred": "4", "ref": "4", "finish": "stop", "out_tokens": 528}
152
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 426}
153
+ {"task": "gsm8k", "ok": true, "pred": "4", "ref": "4", "finish": "stop", "out_tokens": 203}
154
+ {"task": "gsm8k", "ok": true, "pred": "48", "ref": "48", "finish": "stop", "out_tokens": 747}
155
+ {"task": "gsm8k", "ok": true, "pred": "272", "ref": "272", "finish": "stop", "out_tokens": 435}
156
+ {"task": "gsm8k", "ok": true, "pred": "280", "ref": "280", "finish": "stop", "out_tokens": 471}
157
+ {"task": "gsm8k", "ok": true, "pred": "1400", "ref": "1400", "finish": "stop", "out_tokens": 192}
158
+ {"task": "gsm8k", "ok": true, "pred": "80", "ref": "80", "finish": "stop", "out_tokens": 494}
159
+ {"task": "gsm8k", "ok": true, "pred": "34", "ref": "34", "finish": "stop", "out_tokens": 316}
160
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 182}
161
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 233}
162
+ {"task": "gsm8k", "ok": true, "pred": "32", "ref": "32", "finish": "stop", "out_tokens": 1283}
163
+ {"task": "gsm8k", "ok": true, "pred": "92", "ref": "92", "finish": "stop", "out_tokens": 739}
164
+ {"task": "gsm8k", "ok": false, "pred": "54", "ref": "50", "finish": "stop", "out_tokens": 320}
165
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 339}
166
+ {"task": "gsm8k", "ok": true, "pred": "77", "ref": "77", "finish": "stop", "out_tokens": 311}
167
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 196}
168
+ {"task": "gsm8k", "ok": true, "pred": "16", "ref": "16", "finish": "stop", "out_tokens": 437}
169
+ {"task": "gsm8k", "ok": true, "pred": "18", "ref": "18", "finish": "stop", "out_tokens": 171}
170
+ {"task": "gsm8k", "ok": true, "pred": "120", "ref": "120", "finish": "stop", "out_tokens": 251}
171
+ {"task": "gsm8k", "ok": true, "pred": "150", "ref": "150", "finish": "stop", "out_tokens": 378}
172
+ {"task": "gsm8k", "ok": true, "pred": "1210", "ref": "1210", "finish": "stop", "out_tokens": 320}
173
+ {"task": "gsm8k", "ok": true, "pred": "51", "ref": "51", "finish": "stop", "out_tokens": 441}
174
+ {"task": "gsm8k", "ok": true, "pred": "18000", "ref": "18000", "finish": "stop", "out_tokens": 383}
175
+ {"task": "gsm8k", "ok": true, "pred": "95", "ref": "95", "finish": "stop", "out_tokens": 305}
176
+ {"task": "gsm8k", "ok": true, "pred": "15", "ref": "15", "finish": "stop", "out_tokens": 404}
177
+ {"task": "gsm8k", "ok": true, "pred": "100", "ref": "100", "finish": "stop", "out_tokens": 222}
178
+ {"task": "gsm8k", "ok": true, "pred": "350", "ref": "350", "finish": "stop", "out_tokens": 527}
179
+ {"task": "gsm8k", "ok": true, "pred": "122", "ref": "122", "finish": "stop", "out_tokens": 288}
180
+ {"task": "gsm8k", "ok": true, "pred": "130", "ref": "130", "finish": "stop", "out_tokens": 278}
181
+ {"task": "gsm8k", "ok": true, "pred": "20", "ref": "20", "finish": "stop", "out_tokens": 300}
182
+ {"task": "gsm8k", "ok": true, "pred": "160", "ref": "160", "finish": "stop", "out_tokens": 408}
183
+ {"task": "gsm8k", "ok": true, "pred": "23", "ref": "23", "finish": "stop", "out_tokens": 272}
184
+ {"task": "gsm8k", "ok": true, "pred": "2", "ref": "2", "finish": "stop", "out_tokens": 527}
185
+ {"task": "gsm8k", "ok": false, "pred": "100", "ref": "25", "finish": "stop", "out_tokens": 746}
186
+ {"task": "gsm8k", "ok": true, "pred": "30", "ref": "30", "finish": "stop", "out_tokens": 218}
187
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 495}
188
+ {"task": "gsm8k", "ok": true, "pred": "106", "ref": "106", "finish": "stop", "out_tokens": 699}
189
+ {"task": "gsm8k", "ok": true, "pred": "50", "ref": "50", "finish": "stop", "out_tokens": 405}
190
+ {"task": "gsm8k", "ok": true, "pred": "34", "ref": "34", "finish": "stop", "out_tokens": 377}
191
+ {"task": "gsm8k", "ok": true, "pred": "360", "ref": "360", "finish": "stop", "out_tokens": 178}
192
+ {"task": "gsm8k", "ok": true, "pred": "5", "ref": "5", "finish": "stop", "out_tokens": 349}
193
+ {"task": "gsm8k", "ok": true, "pred": "91", "ref": "91", "finish": "stop", "out_tokens": 278}
194
+ {"task": "gsm8k", "ok": true, "pred": "24", "ref": "24", "finish": "stop", "out_tokens": 388}
195
+ {"task": "gsm8k", "ok": true, "pred": "10", "ref": "10", "finish": "stop", "out_tokens": 125}
196
+ {"task": "gsm8k", "ok": true, "pred": "12", "ref": "12", "finish": "stop", "out_tokens": 204}
197
+ {"task": "gsm8k", "ok": true, "pred": "120", "ref": "120", "finish": "stop", "out_tokens": 209}
198
+ {"task": "gsm8k", "ok": true, "pred": "6277", "ref": "6277", "finish": "stop", "out_tokens": 206}
199
+ {"task": "gsm8k", "ok": true, "pred": "320", "ref": "320", "finish": "stop", "out_tokens": 388}
200
+ {"task": "gsm8k", "ok": true, "pred": "7500", "ref": "7500", "finish": "stop", "out_tokens": 425}
201
+ {"task": "humaneval", "id": "HumanEval/0", "ok": true, "finish": "stop", "out_tokens": 173}
202
+ {"task": "humaneval", "id": "HumanEval/1", "ok": true, "finish": "stop", "out_tokens": 224}
203
+ {"task": "humaneval", "id": "HumanEval/2", "ok": true, "finish": "stop", "out_tokens": 97}
204
+ {"task": "humaneval", "id": "HumanEval/3", "ok": true, "finish": "stop", "out_tokens": 157}
205
+ {"task": "humaneval", "id": "HumanEval/4", "ok": true, "finish": "stop", "out_tokens": 164}
206
+ {"task": "humaneval", "id": "HumanEval/5", "ok": true, "finish": "stop", "out_tokens": 152}
207
+ {"task": "humaneval", "id": "HumanEval/6", "ok": true, "finish": "stop", "out_tokens": 216}
208
+ {"task": "humaneval", "id": "HumanEval/7", "ok": true, "finish": "stop", "out_tokens": 112}
209
+ {"task": "humaneval", "id": "HumanEval/8", "ok": true, "finish": "stop", "out_tokens": 163}
210
+ {"task": "humaneval", "id": "HumanEval/9", "ok": true, "finish": "stop", "out_tokens": 160}
211
+ {"task": "humaneval", "id": "HumanEval/10", "ok": true, "finish": "stop", "out_tokens": 270}
212
+ {"task": "humaneval", "id": "HumanEval/11", "ok": true, "finish": "stop", "out_tokens": 140}
213
+ {"task": "humaneval", "id": "HumanEval/12", "ok": true, "finish": "stop", "out_tokens": 128}
214
+ {"task": "humaneval", "id": "HumanEval/13", "ok": true, "finish": "stop", "out_tokens": 107}
215
+ {"task": "humaneval", "id": "HumanEval/14", "ok": true, "finish": "stop", "out_tokens": 84}
216
+ {"task": "humaneval", "id": "HumanEval/15", "ok": true, "finish": "stop", "out_tokens": 92}
217
+ {"task": "humaneval", "id": "HumanEval/16", "ok": true, "finish": "stop", "out_tokens": 81}
218
+ {"task": "humaneval", "id": "HumanEval/17", "ok": true, "finish": "stop", "out_tokens": 218}
219
+ {"task": "humaneval", "id": "HumanEval/18", "ok": true, "finish": "stop", "out_tokens": 161}
220
+ {"task": "humaneval", "id": "HumanEval/19", "ok": true, "finish": "stop", "out_tokens": 266}
221
+ {"task": "humaneval", "id": "HumanEval/20", "ok": true, "finish": "stop", "out_tokens": 273}
222
+ {"task": "humaneval", "id": "HumanEval/21", "ok": true, "finish": "stop", "out_tokens": 172}
223
+ {"task": "humaneval", "id": "HumanEval/22", "ok": true, "finish": "stop", "out_tokens": 119}
224
+ {"task": "humaneval", "id": "HumanEval/23", "ok": true, "finish": "stop", "out_tokens": 53}
225
+ {"task": "humaneval", "id": "HumanEval/24", "ok": true, "finish": "stop", "out_tokens": 91}
226
+ {"task": "humaneval", "id": "HumanEval/25", "ok": true, "finish": "stop", "out_tokens": 202}
227
+ {"task": "humaneval", "id": "HumanEval/26", "ok": true, "finish": "stop", "out_tokens": 119}
228
+ {"task": "humaneval", "id": "HumanEval/27", "ok": true, "finish": "stop", "out_tokens": 58}
229
+ {"task": "humaneval", "id": "HumanEval/28", "ok": true, "finish": "stop", "out_tokens": 70}
230
+ {"task": "humaneval", "id": "HumanEval/29", "ok": true, "finish": "stop", "out_tokens": 107}
231
+ {"task": "humaneval", "id": "HumanEval/30", "ok": true, "finish": "stop", "out_tokens": 139}
232
+ {"task": "humaneval", "id": "HumanEval/31", "ok": true, "finish": "stop", "out_tokens": 193}
233
+ {"task": "humaneval", "id": "HumanEval/32", "ok": false, "finish": "stop", "out_tokens": 509}
234
+ {"task": "humaneval", "id": "HumanEval/33", "ok": true, "finish": "stop", "out_tokens": 263}
235
+ {"task": "humaneval", "id": "HumanEval/34", "ok": true, "finish": "stop", "out_tokens": 87}
236
+ {"task": "humaneval", "id": "HumanEval/35", "ok": true, "finish": "stop", "out_tokens": 97}
237
+ {"task": "humaneval", "id": "HumanEval/36", "ok": true, "finish": "stop", "out_tokens": 139}
238
+ {"task": "humaneval", "id": "HumanEval/37", "ok": true, "finish": "stop", "out_tokens": 220}
239
+ {"task": "humaneval", "id": "HumanEval/38", "ok": false, "finish": "stop", "out_tokens": 144}
240
+ {"task": "humaneval", "id": "HumanEval/39", "ok": true, "finish": "stop", "out_tokens": 251}
241
+ {"task": "humaneval", "id": "HumanEval/40", "ok": true, "finish": "stop", "out_tokens": 235}
242
+ {"task": "humaneval", "id": "HumanEval/41", "ok": true, "finish": "stop", "out_tokens": 163}
243
+ {"task": "humaneval", "id": "HumanEval/42", "ok": true, "finish": "stop", "out_tokens": 132}
244
+ {"task": "humaneval", "id": "HumanEval/43", "ok": true, "finish": "stop", "out_tokens": 201}
245
+ {"task": "humaneval", "id": "HumanEval/44", "ok": true, "finish": "stop", "out_tokens": 157}
246
+ {"task": "humaneval", "id": "HumanEval/45", "ok": true, "finish": "stop", "out_tokens": 60}
247
+ {"task": "humaneval", "id": "HumanEval/46", "ok": true, "finish": "stop", "out_tokens": 293}
248
+ {"task": "humaneval", "id": "HumanEval/47", "ok": true, "finish": "stop", "out_tokens": 153}
249
+ {"task": "humaneval", "id": "HumanEval/48", "ok": true, "finish": "stop", "out_tokens": 87}
250
+ {"task": "humaneval", "id": "HumanEval/49", "ok": true, "finish": "stop", "out_tokens": 134}
251
+ {"task": "humaneval", "id": "HumanEval/50", "ok": true, "finish": "stop", "out_tokens": 133}
252
+ {"task": "humaneval", "id": "HumanEval/51", "ok": true, "finish": "stop", "out_tokens": 165}
253
+ {"task": "humaneval", "id": "HumanEval/52", "ok": true, "finish": "stop", "out_tokens": 100}
254
+ {"task": "humaneval", "id": "HumanEval/53", "ok": true, "finish": "stop", "out_tokens": 63}
255
+ {"task": "humaneval", "id": "HumanEval/54", "ok": true, "finish": "stop", "out_tokens": 161}
256
+ {"task": "humaneval", "id": "HumanEval/55", "ok": true, "finish": "stop", "out_tokens": 130}
257
+ {"task": "humaneval", "id": "HumanEval/56", "ok": true, "finish": "stop", "out_tokens": 157}
258
+ {"task": "humaneval", "id": "HumanEval/57", "ok": true, "finish": "stop", "out_tokens": 168}
259
+ {"task": "humaneval", "id": "HumanEval/58", "ok": true, "finish": "stop", "out_tokens": 142}
260
+ {"task": "humaneval", "id": "HumanEval/59", "ok": true, "finish": "stop", "out_tokens": 196}
261
+ {"task": "humaneval", "id": "HumanEval/60", "ok": true, "finish": "stop", "out_tokens": 125}
262
+ {"task": "humaneval", "id": "HumanEval/61", "ok": true, "finish": "stop", "out_tokens": 156}
263
+ {"task": "humaneval", "id": "HumanEval/62", "ok": true, "finish": "stop", "out_tokens": 136}
264
+ {"task": "humaneval", "id": "HumanEval/63", "ok": true, "finish": "stop", "out_tokens": 247}
265
+ {"task": "humaneval", "id": "HumanEval/64", "ok": true, "finish": "stop", "out_tokens": 209}
266
+ {"task": "humaneval", "id": "HumanEval/65", "ok": true, "finish": "stop", "out_tokens": 152}
267
+ {"task": "humaneval", "id": "HumanEval/66", "ok": true, "finish": "stop", "out_tokens": 140}
268
+ {"task": "humaneval", "id": "HumanEval/67", "ok": true, "finish": "stop", "out_tokens": 273}
269
+ {"task": "humaneval", "id": "HumanEval/68", "ok": true, "finish": "stop", "out_tokens": 429}
270
+ {"task": "humaneval", "id": "HumanEval/69", "ok": true, "finish": "stop", "out_tokens": 227}
271
+ {"task": "humaneval", "id": "HumanEval/70", "ok": true, "finish": "stop", "out_tokens": 237}
272
+ {"task": "humaneval", "id": "HumanEval/71", "ok": true, "finish": "stop", "out_tokens": 237}
273
+ {"task": "humaneval", "id": "HumanEval/72", "ok": true, "finish": "stop", "out_tokens": 255}
274
+ {"task": "humaneval", "id": "HumanEval/73", "ok": true, "finish": "stop", "out_tokens": 210}
275
+ {"task": "humaneval", "id": "HumanEval/74", "ok": true, "finish": "stop", "out_tokens": 264}
276
+ {"task": "humaneval", "id": "HumanEval/75", "ok": true, "finish": "stop", "out_tokens": 288}
277
+ {"task": "humaneval", "id": "HumanEval/76", "ok": true, "finish": "stop", "out_tokens": 240}
278
+ {"task": "humaneval", "id": "HumanEval/77", "ok": true, "finish": "stop", "out_tokens": 232}
279
+ {"task": "humaneval", "id": "HumanEval/78", "ok": true, "finish": "stop", "out_tokens": 383}
280
+ {"task": "humaneval", "id": "HumanEval/79", "ok": true, "finish": "stop", "out_tokens": 224}
281
+ {"task": "humaneval", "id": "HumanEval/80", "ok": true, "finish": "stop", "out_tokens": 194}
282
+ {"task": "humaneval", "id": "HumanEval/81", "ok": true, "finish": "stop", "out_tokens": 530}
283
+ {"task": "humaneval", "id": "HumanEval/82", "ok": true, "finish": "stop", "out_tokens": 169}
284
+ {"task": "humaneval", "id": "HumanEval/83", "ok": true, "finish": "stop", "out_tokens": 292}
285
+ {"task": "humaneval", "id": "HumanEval/84", "ok": true, "finish": "stop", "out_tokens": 180}
286
+ {"task": "humaneval", "id": "HumanEval/85", "ok": true, "finish": "stop", "out_tokens": 102}
287
+ {"task": "humaneval", "id": "HumanEval/86", "ok": true, "finish": "stop", "out_tokens": 184}
288
+ {"task": "humaneval", "id": "HumanEval/87", "ok": true, "finish": "stop", "out_tokens": 351}
289
+ {"task": "humaneval", "id": "HumanEval/88", "ok": true, "finish": "stop", "out_tokens": 286}
290
+ {"task": "humaneval", "id": "HumanEval/89", "ok": true, "finish": "stop", "out_tokens": 225}
291
+ {"task": "humaneval", "id": "HumanEval/90", "ok": true, "finish": "stop", "out_tokens": 158}
292
+ {"task": "humaneval", "id": "HumanEval/91", "ok": true, "finish": "stop", "out_tokens": 184}
293
+ {"task": "humaneval", "id": "HumanEval/92", "ok": true, "finish": "stop", "out_tokens": 223}
294
+ {"task": "humaneval", "id": "HumanEval/93", "ok": true, "finish": "stop", "out_tokens": 326}
295
+ {"task": "humaneval", "id": "HumanEval/94", "ok": true, "finish": "stop", "out_tokens": 468}
296
+ {"task": "humaneval", "id": "HumanEval/95", "ok": true, "finish": "stop", "out_tokens": 282}
297
+ {"task": "humaneval", "id": "HumanEval/96", "ok": true, "finish": "stop", "out_tokens": 284}
298
+ {"task": "humaneval", "id": "HumanEval/97", "ok": false, "finish": "stop", "out_tokens": 139}
299
+ {"task": "humaneval", "id": "HumanEval/98", "ok": true, "finish": "stop", "out_tokens": 119}
300
+ {"task": "humaneval", "id": "HumanEval/99", "ok": true, "finish": "stop", "out_tokens": 219}
301
+ {"task": "humaneval", "id": "HumanEval/100", "ok": true, "finish": "stop", "out_tokens": 177}
302
+ {"task": "humaneval", "id": "HumanEval/101", "ok": true, "finish": "stop", "out_tokens": 175}
303
+ {"task": "humaneval", "id": "HumanEval/102", "ok": true, "finish": "stop", "out_tokens": 185}
304
+ {"task": "humaneval", "id": "HumanEval/103", "ok": true, "finish": "stop", "out_tokens": 213}
305
+ {"task": "humaneval", "id": "HumanEval/104", "ok": true, "finish": "stop", "out_tokens": 175}
306
+ {"task": "humaneval", "id": "HumanEval/105", "ok": true, "finish": "stop", "out_tokens": 475}
307
+ {"task": "humaneval", "id": "HumanEval/106", "ok": true, "finish": "stop", "out_tokens": 234}
308
+ {"task": "humaneval", "id": "HumanEval/107", "ok": true, "finish": "stop", "out_tokens": 327}
309
+ {"task": "humaneval", "id": "HumanEval/108", "ok": true, "finish": "stop", "out_tokens": 272}
310
+ {"task": "humaneval", "id": "HumanEval/109", "ok": true, "finish": "stop", "out_tokens": 481}
311
+ {"task": "humaneval", "id": "HumanEval/110", "ok": true, "finish": "stop", "out_tokens": 313}
312
+ {"task": "humaneval", "id": "HumanEval/111", "ok": true, "finish": "stop", "out_tokens": 298}
313
+ {"task": "humaneval", "id": "HumanEval/112", "ok": true, "finish": "stop", "out_tokens": 209}
314
+ {"task": "humaneval", "id": "HumanEval/113", "ok": false, "finish": "stop", "out_tokens": 396}
315
+ {"task": "humaneval", "id": "HumanEval/114", "ok": true, "finish": "stop", "out_tokens": 166}
316
+ {"task": "humaneval", "id": "HumanEval/115", "ok": true, "finish": "stop", "out_tokens": 383}
317
+ {"task": "humaneval", "id": "HumanEval/116", "ok": true, "finish": "stop", "out_tokens": 198}
318
+ {"task": "humaneval", "id": "HumanEval/117", "ok": true, "finish": "stop", "out_tokens": 278}
319
+ {"task": "humaneval", "id": "HumanEval/118", "ok": true, "finish": "stop", "out_tokens": 298}
320
+ {"task": "humaneval", "id": "HumanEval/119", "ok": true, "finish": "stop", "out_tokens": 304}
321
+ {"task": "humaneval", "id": "HumanEval/120", "ok": true, "finish": "stop", "out_tokens": 268}
322
+ {"task": "humaneval", "id": "HumanEval/121", "ok": true, "finish": "stop", "out_tokens": 136}
323
+ {"task": "humaneval", "id": "HumanEval/122", "ok": true, "finish": "stop", "out_tokens": 186}
324
+ {"task": "humaneval", "id": "HumanEval/123", "ok": true, "finish": "stop", "out_tokens": 328}
325
+ {"task": "humaneval", "id": "HumanEval/124", "ok": true, "finish": "stop", "out_tokens": 545}
326
+ {"task": "humaneval", "id": "HumanEval/125", "ok": true, "finish": "stop", "out_tokens": 229}
327
+ {"task": "humaneval", "id": "HumanEval/126", "ok": true, "finish": "stop", "out_tokens": 345}
328
+ {"task": "humaneval", "id": "HumanEval/127", "ok": true, "finish": "stop", "out_tokens": 459}
329
+ {"task": "humaneval", "id": "HumanEval/128", "ok": true, "finish": "stop", "out_tokens": 216}
330
+ {"task": "humaneval", "id": "HumanEval/129", "ok": true, "finish": "stop", "out_tokens": 943}
331
+ {"task": "humaneval", "id": "HumanEval/130", "ok": true, "finish": "stop", "out_tokens": 717}
332
+ {"task": "humaneval", "id": "HumanEval/131", "ok": true, "finish": "stop", "out_tokens": 152}
333
+ {"task": "humaneval", "id": "HumanEval/132", "ok": true, "finish": "stop", "out_tokens": 708}
334
+ {"task": "humaneval", "id": "HumanEval/133", "ok": true, "finish": "stop", "out_tokens": 204}
335
+ {"task": "humaneval", "id": "HumanEval/134", "ok": true, "finish": "stop", "out_tokens": 282}
336
+ {"task": "humaneval", "id": "HumanEval/135", "ok": true, "finish": "stop", "out_tokens": 149}
337
+ {"task": "humaneval", "id": "HumanEval/136", "ok": true, "finish": "stop", "out_tokens": 213}
338
+ {"task": "humaneval", "id": "HumanEval/137", "ok": true, "finish": "stop", "out_tokens": 232}
339
+ {"task": "humaneval", "id": "HumanEval/138", "ok": true, "finish": "stop", "out_tokens": 166}
340
+ {"task": "humaneval", "id": "HumanEval/139", "ok": true, "finish": "stop", "out_tokens": 140}
341
+ {"task": "humaneval", "id": "HumanEval/140", "ok": false, "finish": "stop", "out_tokens": 175}
342
+ {"task": "humaneval", "id": "HumanEval/141", "ok": true, "finish": "stop", "out_tokens": 432}
343
+ {"task": "humaneval", "id": "HumanEval/142", "ok": true, "finish": "stop", "out_tokens": 240}
344
+ {"task": "humaneval", "id": "HumanEval/143", "ok": true, "finish": "stop", "out_tokens": 281}
345
+ {"task": "humaneval", "id": "HumanEval/144", "ok": true, "finish": "stop", "out_tokens": 253}
346
+ {"task": "humaneval", "id": "HumanEval/145", "ok": false, "finish": "stop", "out_tokens": 235}
347
+ {"task": "humaneval", "id": "HumanEval/146", "ok": true, "finish": "stop", "out_tokens": 266}
348
+ {"task": "humaneval", "id": "HumanEval/147", "ok": true, "finish": "stop", "out_tokens": 1157}
349
+ {"task": "humaneval", "id": "HumanEval/148", "ok": true, "finish": "stop", "out_tokens": 331}
350
+ {"task": "humaneval", "id": "HumanEval/149", "ok": true, "finish": "stop", "out_tokens": 239}
351
+ {"task": "humaneval", "id": "HumanEval/150", "ok": true, "finish": "stop", "out_tokens": 199}
352
+ {"task": "humaneval", "id": "HumanEval/151", "ok": true, "finish": "stop", "out_tokens": 190}
353
+ {"task": "humaneval", "id": "HumanEval/152", "ok": true, "finish": "stop", "out_tokens": 257}
354
+ {"task": "humaneval", "id": "HumanEval/153", "ok": true, "finish": "stop", "out_tokens": 391}
355
+ {"task": "humaneval", "id": "HumanEval/154", "ok": true, "finish": "stop", "out_tokens": 182}
356
+ {"task": "humaneval", "id": "HumanEval/155", "ok": true, "finish": "stop", "out_tokens": 138}
357
+ {"task": "humaneval", "id": "HumanEval/156", "ok": true, "finish": "stop", "out_tokens": 285}
358
+ {"task": "humaneval", "id": "HumanEval/157", "ok": true, "finish": "stop", "out_tokens": 152}
359
+ {"task": "humaneval", "id": "HumanEval/158", "ok": true, "finish": "stop", "out_tokens": 194}
360
+ {"task": "humaneval", "id": "HumanEval/159", "ok": true, "finish": "stop", "out_tokens": 341}
361
+ {"task": "humaneval", "id": "HumanEval/160", "ok": true, "finish": "stop", "out_tokens": 273}
362
+ {"task": "humaneval", "id": "HumanEval/161", "ok": true, "finish": "stop", "out_tokens": 183}
363
+ {"task": "humaneval", "id": "HumanEval/162", "ok": true, "finish": "stop", "out_tokens": 119}
364
+ {"task": "humaneval", "id": "HumanEval/163", "ok": false, "finish": "stop", "out_tokens": 170}
recipe/results/quality-probe.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ qwen3.8-flash-next GSM8K first 200: 193/200 = 96.5% (truncated 0, errors 0, 757s)
2
+ qwen3.8-flash-next HumanEval 164: 157/164 = 95.7% (truncated 0, errors 0, 317s)
3
+ wrote /workspace/hf-release/results-raw/quality-probe.jsonl | thinking False
recipe/results/toolcall.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ finish_reason: tool_calls | tool_calls: [{"id": "chatcmpl-tool-ad003c660f5c49ce", "type": "function", "function": {"name": "web_search", "arguments": "{\"query\": \"2026 Tour de France winner\"}"}}]
2
+ reasoning field present: True | content:
3
+ no-tool case -> finish: stop | tool_calls: False | reasoning: True | content: 4
4
+ TOOLCALL TEST OK
recipe/serve/draft_vocab_64k.json ADDED
The diff for this file is too large to render. See raw diff
 
recipe/serve/seccomp.json ADDED
@@ -0,0 +1,1229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "defaultAction": "SCMP_ACT_ERRNO",
3
+ "defaultErrnoRet": 1,
4
+ "archMap": [
5
+ {
6
+ "architecture": "SCMP_ARCH_X86_64",
7
+ "subArchitectures": [
8
+ "SCMP_ARCH_X86",
9
+ "SCMP_ARCH_X32"
10
+ ]
11
+ },
12
+ {
13
+ "architecture": "SCMP_ARCH_AARCH64",
14
+ "subArchitectures": [
15
+ "SCMP_ARCH_ARM"
16
+ ]
17
+ },
18
+ {
19
+ "architecture": "SCMP_ARCH_MIPS64",
20
+ "subArchitectures": [
21
+ "SCMP_ARCH_MIPS",
22
+ "SCMP_ARCH_MIPS64N32"
23
+ ]
24
+ },
25
+ {
26
+ "architecture": "SCMP_ARCH_MIPS64N32",
27
+ "subArchitectures": [
28
+ "SCMP_ARCH_MIPS",
29
+ "SCMP_ARCH_MIPS64"
30
+ ]
31
+ },
32
+ {
33
+ "architecture": "SCMP_ARCH_MIPSEL64",
34
+ "subArchitectures": [
35
+ "SCMP_ARCH_MIPSEL",
36
+ "SCMP_ARCH_MIPSEL64N32"
37
+ ]
38
+ },
39
+ {
40
+ "architecture": "SCMP_ARCH_MIPSEL64N32",
41
+ "subArchitectures": [
42
+ "SCMP_ARCH_MIPSEL",
43
+ "SCMP_ARCH_MIPSEL64"
44
+ ]
45
+ },
46
+ {
47
+ "architecture": "SCMP_ARCH_S390X",
48
+ "subArchitectures": [
49
+ "SCMP_ARCH_S390"
50
+ ]
51
+ },
52
+ {
53
+ "architecture": "SCMP_ARCH_RISCV64",
54
+ "subArchitectures": null
55
+ },
56
+ {
57
+ "architecture": "SCMP_ARCH_LOONGARCH64",
58
+ "subArchitectures": null
59
+ }
60
+ ],
61
+ "syscalls": [
62
+ {
63
+ "names": [
64
+ "_llseek",
65
+ "_newselect",
66
+ "accept",
67
+ "accept4",
68
+ "access",
69
+ "adjtimex",
70
+ "alarm",
71
+ "bind",
72
+ "brk",
73
+ "cachestat",
74
+ "capget",
75
+ "capset",
76
+ "chdir",
77
+ "chmod",
78
+ "chown",
79
+ "chown32",
80
+ "clock_adjtime",
81
+ "clock_adjtime64",
82
+ "clock_getres",
83
+ "clock_getres_time64",
84
+ "clock_gettime",
85
+ "clock_gettime64",
86
+ "clock_nanosleep",
87
+ "clock_nanosleep_time64",
88
+ "close",
89
+ "close_range",
90
+ "connect",
91
+ "copy_file_range",
92
+ "creat",
93
+ "dup",
94
+ "dup2",
95
+ "dup3",
96
+ "epoll_create",
97
+ "epoll_create1",
98
+ "epoll_ctl",
99
+ "epoll_ctl_old",
100
+ "epoll_pwait",
101
+ "epoll_pwait2",
102
+ "epoll_wait",
103
+ "epoll_wait_old",
104
+ "eventfd",
105
+ "eventfd2",
106
+ "execve",
107
+ "execveat",
108
+ "exit",
109
+ "exit_group",
110
+ "faccessat",
111
+ "faccessat2",
112
+ "fadvise64",
113
+ "fadvise64_64",
114
+ "fallocate",
115
+ "fanotify_mark",
116
+ "fchdir",
117
+ "fchmod",
118
+ "fchmodat",
119
+ "fchmodat2",
120
+ "fchown",
121
+ "fchown32",
122
+ "fchownat",
123
+ "fcntl",
124
+ "fcntl64",
125
+ "fdatasync",
126
+ "fgetxattr",
127
+ "flistxattr",
128
+ "flock",
129
+ "fork",
130
+ "fremovexattr",
131
+ "fsetxattr",
132
+ "fstat",
133
+ "fstat64",
134
+ "fstatat64",
135
+ "fstatfs",
136
+ "fstatfs64",
137
+ "fsync",
138
+ "ftruncate",
139
+ "ftruncate64",
140
+ "futex",
141
+ "futex_requeue",
142
+ "futex_time64",
143
+ "futex_wait",
144
+ "futex_waitv",
145
+ "futex_wake",
146
+ "futimesat",
147
+ "get_robust_list",
148
+ "get_thread_area",
149
+ "getcpu",
150
+ "getcwd",
151
+ "getdents",
152
+ "getdents64",
153
+ "getegid",
154
+ "getegid32",
155
+ "geteuid",
156
+ "geteuid32",
157
+ "getgid",
158
+ "getgid32",
159
+ "getgroups",
160
+ "getgroups32",
161
+ "getitimer",
162
+ "getpeername",
163
+ "getpgid",
164
+ "getpgrp",
165
+ "getpid",
166
+ "getppid",
167
+ "getpriority",
168
+ "getrandom",
169
+ "getresgid",
170
+ "getresgid32",
171
+ "getresuid",
172
+ "getresuid32",
173
+ "getrlimit",
174
+ "getrusage",
175
+ "getsid",
176
+ "getsockname",
177
+ "getsockopt",
178
+ "gettid",
179
+ "gettimeofday",
180
+ "getuid",
181
+ "getuid32",
182
+ "getxattr",
183
+ "getxattrat",
184
+ "inotify_add_watch",
185
+ "inotify_init",
186
+ "inotify_init1",
187
+ "inotify_rm_watch",
188
+ "io_cancel",
189
+ "io_destroy",
190
+ "io_getevents",
191
+ "io_pgetevents",
192
+ "io_pgetevents_time64",
193
+ "io_setup",
194
+ "io_submit",
195
+ "io_uring_enter",
196
+ "io_uring_register",
197
+ "io_uring_setup",
198
+ "ioctl",
199
+ "ioprio_get",
200
+ "ioprio_set",
201
+ "ipc",
202
+ "kill",
203
+ "landlock_add_rule",
204
+ "landlock_create_ruleset",
205
+ "landlock_restrict_self",
206
+ "lchown",
207
+ "lchown32",
208
+ "lgetxattr",
209
+ "link",
210
+ "linkat",
211
+ "listen",
212
+ "listmount",
213
+ "listxattr",
214
+ "listxattrat",
215
+ "llistxattr",
216
+ "lremovexattr",
217
+ "lseek",
218
+ "lsetxattr",
219
+ "lstat",
220
+ "lstat64",
221
+ "madvise",
222
+ "map_shadow_stack",
223
+ "membarrier",
224
+ "memfd_create",
225
+ "memfd_secret",
226
+ "mincore",
227
+ "mkdir",
228
+ "mkdirat",
229
+ "mknod",
230
+ "mknodat",
231
+ "mlock",
232
+ "mlock2",
233
+ "mlockall",
234
+ "mmap",
235
+ "mmap2",
236
+ "mprotect",
237
+ "mq_getsetattr",
238
+ "mq_notify",
239
+ "mq_open",
240
+ "mq_timedreceive",
241
+ "mq_timedreceive_time64",
242
+ "mq_timedsend",
243
+ "mq_timedsend_time64",
244
+ "mq_unlink",
245
+ "mremap",
246
+ "mseal",
247
+ "msgctl",
248
+ "msgget",
249
+ "msgrcv",
250
+ "msgsnd",
251
+ "msync",
252
+ "munlock",
253
+ "munlockall",
254
+ "munmap",
255
+ "name_to_handle_at",
256
+ "nanosleep",
257
+ "newfstatat",
258
+ "open",
259
+ "openat",
260
+ "openat2",
261
+ "pause",
262
+ "pidfd_open",
263
+ "pidfd_send_signal",
264
+ "pipe",
265
+ "pipe2",
266
+ "pkey_alloc",
267
+ "pkey_free",
268
+ "pkey_mprotect",
269
+ "poll",
270
+ "ppoll",
271
+ "ppoll_time64",
272
+ "prctl",
273
+ "pread64",
274
+ "preadv",
275
+ "preadv2",
276
+ "prlimit64",
277
+ "process_mrelease",
278
+ "pselect6",
279
+ "pselect6_time64",
280
+ "pwrite64",
281
+ "pwritev",
282
+ "pwritev2",
283
+ "read",
284
+ "readahead",
285
+ "readlink",
286
+ "readlinkat",
287
+ "readv",
288
+ "recv",
289
+ "recvfrom",
290
+ "recvmmsg",
291
+ "recvmmsg_time64",
292
+ "recvmsg",
293
+ "remap_file_pages",
294
+ "removexattr",
295
+ "removexattrat",
296
+ "rename",
297
+ "renameat",
298
+ "renameat2",
299
+ "restart_syscall",
300
+ "riscv_hwprobe",
301
+ "rmdir",
302
+ "rseq",
303
+ "rt_sigaction",
304
+ "rt_sigpending",
305
+ "rt_sigprocmask",
306
+ "rt_sigqueueinfo",
307
+ "rt_sigreturn",
308
+ "rt_sigsuspend",
309
+ "rt_sigtimedwait",
310
+ "rt_sigtimedwait_time64",
311
+ "rt_tgsigqueueinfo",
312
+ "sched_get_priority_max",
313
+ "sched_get_priority_min",
314
+ "sched_getaffinity",
315
+ "sched_getattr",
316
+ "sched_getparam",
317
+ "sched_getscheduler",
318
+ "sched_rr_get_interval",
319
+ "sched_rr_get_interval_time64",
320
+ "sched_setaffinity",
321
+ "sched_setattr",
322
+ "sched_setparam",
323
+ "sched_setscheduler",
324
+ "sched_yield",
325
+ "seccomp",
326
+ "select",
327
+ "semctl",
328
+ "semget",
329
+ "semop",
330
+ "semtimedop",
331
+ "semtimedop_time64",
332
+ "send",
333
+ "sendfile",
334
+ "sendfile64",
335
+ "sendmmsg",
336
+ "sendmsg",
337
+ "sendto",
338
+ "set_robust_list",
339
+ "set_thread_area",
340
+ "set_tid_address",
341
+ "setfsgid",
342
+ "setfsgid32",
343
+ "setfsuid",
344
+ "setfsuid32",
345
+ "setgid",
346
+ "setgid32",
347
+ "setgroups",
348
+ "setgroups32",
349
+ "setitimer",
350
+ "setpgid",
351
+ "setpriority",
352
+ "setregid",
353
+ "setregid32",
354
+ "setresgid",
355
+ "setresgid32",
356
+ "setresuid",
357
+ "setresuid32",
358
+ "setreuid",
359
+ "setreuid32",
360
+ "setrlimit",
361
+ "setsid",
362
+ "setsockopt",
363
+ "setuid",
364
+ "setuid32",
365
+ "setxattr",
366
+ "setxattrat",
367
+ "shmat",
368
+ "shmctl",
369
+ "shmdt",
370
+ "shmget",
371
+ "shutdown",
372
+ "sigaltstack",
373
+ "signalfd",
374
+ "signalfd4",
375
+ "sigprocmask",
376
+ "sigreturn",
377
+ "socketcall",
378
+ "socketpair",
379
+ "splice",
380
+ "stat",
381
+ "stat64",
382
+ "statfs",
383
+ "statfs64",
384
+ "statmount",
385
+ "statx",
386
+ "symlink",
387
+ "symlinkat",
388
+ "sync",
389
+ "sync_file_range",
390
+ "syncfs",
391
+ "sysinfo",
392
+ "tee",
393
+ "tgkill",
394
+ "time",
395
+ "timer_create",
396
+ "timer_delete",
397
+ "timer_getoverrun",
398
+ "timer_gettime",
399
+ "timer_gettime64",
400
+ "timer_settime",
401
+ "timer_settime64",
402
+ "timerfd_create",
403
+ "timerfd_gettime",
404
+ "timerfd_gettime64",
405
+ "timerfd_settime",
406
+ "timerfd_settime64",
407
+ "times",
408
+ "tkill",
409
+ "truncate",
410
+ "truncate64",
411
+ "ugetrlimit",
412
+ "umask",
413
+ "uname",
414
+ "unlink",
415
+ "unlinkat",
416
+ "uretprobe",
417
+ "utime",
418
+ "utimensat",
419
+ "utimensat_time64",
420
+ "utimes",
421
+ "vfork",
422
+ "vmsplice",
423
+ "wait4",
424
+ "waitid",
425
+ "waitpid",
426
+ "write",
427
+ "writev"
428
+ ],
429
+ "action": "SCMP_ACT_ALLOW"
430
+ },
431
+ {
432
+ "names": [
433
+ "process_vm_readv",
434
+ "process_vm_writev",
435
+ "ptrace"
436
+ ],
437
+ "action": "SCMP_ACT_ALLOW",
438
+ "includes": {
439
+ "minKernel": "4.8"
440
+ }
441
+ },
442
+ {
443
+ "names": [
444
+ "socket"
445
+ ],
446
+ "action": "SCMP_ACT_ALLOW",
447
+ "args": [
448
+ {
449
+ "index": 0,
450
+ "value": 3,
451
+ "op": "SCMP_CMP_LT"
452
+ }
453
+ ]
454
+ },
455
+ {
456
+ "names": [
457
+ "socket"
458
+ ],
459
+ "action": "SCMP_ACT_ALLOW",
460
+ "args": [
461
+ {
462
+ "index": 0,
463
+ "value": 7,
464
+ "op": "SCMP_CMP_EQ"
465
+ }
466
+ ]
467
+ },
468
+ {
469
+ "names": [
470
+ "socket"
471
+ ],
472
+ "action": "SCMP_ACT_ALLOW",
473
+ "args": [
474
+ {
475
+ "index": 0,
476
+ "value": 8,
477
+ "op": "SCMP_CMP_EQ"
478
+ }
479
+ ]
480
+ },
481
+ {
482
+ "names": [
483
+ "socket"
484
+ ],
485
+ "action": "SCMP_ACT_ALLOW",
486
+ "args": [
487
+ {
488
+ "index": 0,
489
+ "value": 9,
490
+ "op": "SCMP_CMP_EQ"
491
+ }
492
+ ]
493
+ },
494
+ {
495
+ "names": [
496
+ "socket"
497
+ ],
498
+ "action": "SCMP_ACT_ALLOW",
499
+ "args": [
500
+ {
501
+ "index": 0,
502
+ "value": 10,
503
+ "op": "SCMP_CMP_EQ"
504
+ }
505
+ ]
506
+ },
507
+ {
508
+ "names": [
509
+ "socket"
510
+ ],
511
+ "action": "SCMP_ACT_ALLOW",
512
+ "args": [
513
+ {
514
+ "index": 0,
515
+ "value": 13,
516
+ "op": "SCMP_CMP_EQ"
517
+ }
518
+ ]
519
+ },
520
+ {
521
+ "names": [
522
+ "socket"
523
+ ],
524
+ "action": "SCMP_ACT_ALLOW",
525
+ "args": [
526
+ {
527
+ "index": 0,
528
+ "value": 14,
529
+ "op": "SCMP_CMP_EQ"
530
+ }
531
+ ]
532
+ },
533
+ {
534
+ "names": [
535
+ "socket"
536
+ ],
537
+ "action": "SCMP_ACT_ALLOW",
538
+ "args": [
539
+ {
540
+ "index": 0,
541
+ "value": 15,
542
+ "op": "SCMP_CMP_EQ"
543
+ }
544
+ ]
545
+ },
546
+ {
547
+ "names": [
548
+ "socket"
549
+ ],
550
+ "action": "SCMP_ACT_ALLOW",
551
+ "args": [
552
+ {
553
+ "index": 0,
554
+ "value": 16,
555
+ "op": "SCMP_CMP_EQ"
556
+ }
557
+ ]
558
+ },
559
+ {
560
+ "names": [
561
+ "socket"
562
+ ],
563
+ "action": "SCMP_ACT_ALLOW",
564
+ "args": [
565
+ {
566
+ "index": 0,
567
+ "value": 17,
568
+ "op": "SCMP_CMP_EQ"
569
+ }
570
+ ]
571
+ },
572
+ {
573
+ "names": [
574
+ "socket"
575
+ ],
576
+ "action": "SCMP_ACT_ALLOW",
577
+ "args": [
578
+ {
579
+ "index": 0,
580
+ "value": 18,
581
+ "op": "SCMP_CMP_EQ"
582
+ }
583
+ ]
584
+ },
585
+ {
586
+ "names": [
587
+ "socket"
588
+ ],
589
+ "action": "SCMP_ACT_ALLOW",
590
+ "args": [
591
+ {
592
+ "index": 0,
593
+ "value": 21,
594
+ "op": "SCMP_CMP_EQ"
595
+ }
596
+ ]
597
+ },
598
+ {
599
+ "names": [
600
+ "socket"
601
+ ],
602
+ "action": "SCMP_ACT_ALLOW",
603
+ "args": [
604
+ {
605
+ "index": 0,
606
+ "value": 22,
607
+ "op": "SCMP_CMP_EQ"
608
+ }
609
+ ]
610
+ },
611
+ {
612
+ "names": [
613
+ "socket"
614
+ ],
615
+ "action": "SCMP_ACT_ALLOW",
616
+ "args": [
617
+ {
618
+ "index": 0,
619
+ "value": 24,
620
+ "op": "SCMP_CMP_EQ"
621
+ }
622
+ ]
623
+ },
624
+ {
625
+ "names": [
626
+ "socket"
627
+ ],
628
+ "action": "SCMP_ACT_ALLOW",
629
+ "args": [
630
+ {
631
+ "index": 0,
632
+ "value": 26,
633
+ "op": "SCMP_CMP_EQ"
634
+ }
635
+ ]
636
+ },
637
+ {
638
+ "names": [
639
+ "socket"
640
+ ],
641
+ "action": "SCMP_ACT_ALLOW",
642
+ "args": [
643
+ {
644
+ "index": 0,
645
+ "value": 27,
646
+ "op": "SCMP_CMP_EQ"
647
+ }
648
+ ]
649
+ },
650
+ {
651
+ "names": [
652
+ "socket"
653
+ ],
654
+ "action": "SCMP_ACT_ALLOW",
655
+ "args": [
656
+ {
657
+ "index": 0,
658
+ "value": 28,
659
+ "op": "SCMP_CMP_EQ"
660
+ }
661
+ ]
662
+ },
663
+ {
664
+ "names": [
665
+ "socket"
666
+ ],
667
+ "action": "SCMP_ACT_ALLOW",
668
+ "args": [
669
+ {
670
+ "index": 0,
671
+ "value": 29,
672
+ "op": "SCMP_CMP_EQ"
673
+ }
674
+ ]
675
+ },
676
+ {
677
+ "names": [
678
+ "socket"
679
+ ],
680
+ "action": "SCMP_ACT_ALLOW",
681
+ "args": [
682
+ {
683
+ "index": 0,
684
+ "value": 30,
685
+ "op": "SCMP_CMP_EQ"
686
+ }
687
+ ]
688
+ },
689
+ {
690
+ "names": [
691
+ "socket"
692
+ ],
693
+ "action": "SCMP_ACT_ALLOW",
694
+ "args": [
695
+ {
696
+ "index": 0,
697
+ "value": 31,
698
+ "op": "SCMP_CMP_EQ"
699
+ }
700
+ ]
701
+ },
702
+ {
703
+ "names": [
704
+ "socket"
705
+ ],
706
+ "action": "SCMP_ACT_ALLOW",
707
+ "args": [
708
+ {
709
+ "index": 0,
710
+ "value": 32,
711
+ "op": "SCMP_CMP_EQ"
712
+ }
713
+ ]
714
+ },
715
+ {
716
+ "names": [
717
+ "socket"
718
+ ],
719
+ "action": "SCMP_ACT_ALLOW",
720
+ "args": [
721
+ {
722
+ "index": 0,
723
+ "value": 33,
724
+ "op": "SCMP_CMP_EQ"
725
+ }
726
+ ]
727
+ },
728
+ {
729
+ "names": [
730
+ "socket"
731
+ ],
732
+ "action": "SCMP_ACT_ALLOW",
733
+ "args": [
734
+ {
735
+ "index": 0,
736
+ "value": 35,
737
+ "op": "SCMP_CMP_EQ"
738
+ }
739
+ ]
740
+ },
741
+ {
742
+ "names": [
743
+ "socket"
744
+ ],
745
+ "action": "SCMP_ACT_ALLOW",
746
+ "args": [
747
+ {
748
+ "index": 0,
749
+ "value": 36,
750
+ "op": "SCMP_CMP_EQ"
751
+ }
752
+ ]
753
+ },
754
+ {
755
+ "names": [
756
+ "socket"
757
+ ],
758
+ "action": "SCMP_ACT_ALLOW",
759
+ "args": [
760
+ {
761
+ "index": 0,
762
+ "value": 39,
763
+ "op": "SCMP_CMP_EQ"
764
+ }
765
+ ]
766
+ },
767
+ {
768
+ "names": [
769
+ "socket"
770
+ ],
771
+ "action": "SCMP_ACT_ALLOW",
772
+ "args": [
773
+ {
774
+ "index": 0,
775
+ "value": 41,
776
+ "op": "SCMP_CMP_EQ"
777
+ }
778
+ ]
779
+ },
780
+ {
781
+ "names": [
782
+ "socket"
783
+ ],
784
+ "action": "SCMP_ACT_ALLOW",
785
+ "args": [
786
+ {
787
+ "index": 0,
788
+ "value": 42,
789
+ "op": "SCMP_CMP_EQ"
790
+ }
791
+ ]
792
+ },
793
+ {
794
+ "names": [
795
+ "socket"
796
+ ],
797
+ "action": "SCMP_ACT_ALLOW",
798
+ "args": [
799
+ {
800
+ "index": 0,
801
+ "value": 43,
802
+ "op": "SCMP_CMP_EQ"
803
+ }
804
+ ]
805
+ },
806
+ {
807
+ "names": [
808
+ "socket"
809
+ ],
810
+ "action": "SCMP_ACT_ALLOW",
811
+ "args": [
812
+ {
813
+ "index": 0,
814
+ "value": 44,
815
+ "op": "SCMP_CMP_EQ"
816
+ }
817
+ ]
818
+ },
819
+ {
820
+ "names": [
821
+ "socket"
822
+ ],
823
+ "action": "SCMP_ACT_ALLOW",
824
+ "args": [
825
+ {
826
+ "index": 0,
827
+ "value": 45,
828
+ "op": "SCMP_CMP_EQ"
829
+ }
830
+ ]
831
+ },
832
+ {
833
+ "names": [
834
+ "personality"
835
+ ],
836
+ "action": "SCMP_ACT_ALLOW",
837
+ "args": [
838
+ {
839
+ "index": 0,
840
+ "value": 0,
841
+ "op": "SCMP_CMP_EQ"
842
+ }
843
+ ]
844
+ },
845
+ {
846
+ "names": [
847
+ "personality"
848
+ ],
849
+ "action": "SCMP_ACT_ALLOW",
850
+ "args": [
851
+ {
852
+ "index": 0,
853
+ "value": 8,
854
+ "op": "SCMP_CMP_EQ"
855
+ }
856
+ ]
857
+ },
858
+ {
859
+ "names": [
860
+ "personality"
861
+ ],
862
+ "action": "SCMP_ACT_ALLOW",
863
+ "args": [
864
+ {
865
+ "index": 0,
866
+ "value": 131072,
867
+ "op": "SCMP_CMP_EQ"
868
+ }
869
+ ]
870
+ },
871
+ {
872
+ "names": [
873
+ "personality"
874
+ ],
875
+ "action": "SCMP_ACT_ALLOW",
876
+ "args": [
877
+ {
878
+ "index": 0,
879
+ "value": 131080,
880
+ "op": "SCMP_CMP_EQ"
881
+ }
882
+ ]
883
+ },
884
+ {
885
+ "names": [
886
+ "personality"
887
+ ],
888
+ "action": "SCMP_ACT_ALLOW",
889
+ "args": [
890
+ {
891
+ "index": 0,
892
+ "value": 4294967295,
893
+ "op": "SCMP_CMP_EQ"
894
+ }
895
+ ]
896
+ },
897
+ {
898
+ "names": [
899
+ "sync_file_range2",
900
+ "swapcontext"
901
+ ],
902
+ "action": "SCMP_ACT_ALLOW",
903
+ "includes": {
904
+ "arches": [
905
+ "ppc64le"
906
+ ]
907
+ }
908
+ },
909
+ {
910
+ "names": [
911
+ "arm_fadvise64_64",
912
+ "arm_sync_file_range",
913
+ "sync_file_range2",
914
+ "breakpoint",
915
+ "cacheflush",
916
+ "set_tls"
917
+ ],
918
+ "action": "SCMP_ACT_ALLOW",
919
+ "includes": {
920
+ "arches": [
921
+ "arm",
922
+ "arm64"
923
+ ]
924
+ }
925
+ },
926
+ {
927
+ "names": [
928
+ "arch_prctl"
929
+ ],
930
+ "action": "SCMP_ACT_ALLOW",
931
+ "includes": {
932
+ "arches": [
933
+ "amd64",
934
+ "x32"
935
+ ]
936
+ }
937
+ },
938
+ {
939
+ "names": [
940
+ "modify_ldt"
941
+ ],
942
+ "action": "SCMP_ACT_ALLOW",
943
+ "includes": {
944
+ "arches": [
945
+ "amd64",
946
+ "x32",
947
+ "x86"
948
+ ]
949
+ }
950
+ },
951
+ {
952
+ "names": [
953
+ "s390_pci_mmio_read",
954
+ "s390_pci_mmio_write",
955
+ "s390_runtime_instr"
956
+ ],
957
+ "action": "SCMP_ACT_ALLOW",
958
+ "includes": {
959
+ "arches": [
960
+ "s390",
961
+ "s390x"
962
+ ]
963
+ }
964
+ },
965
+ {
966
+ "names": [
967
+ "riscv_flush_icache"
968
+ ],
969
+ "action": "SCMP_ACT_ALLOW",
970
+ "includes": {
971
+ "arches": [
972
+ "riscv64"
973
+ ]
974
+ }
975
+ },
976
+ {
977
+ "names": [
978
+ "open_by_handle_at"
979
+ ],
980
+ "action": "SCMP_ACT_ALLOW",
981
+ "includes": {
982
+ "caps": [
983
+ "CAP_DAC_READ_SEARCH"
984
+ ]
985
+ }
986
+ },
987
+ {
988
+ "names": [
989
+ "bpf",
990
+ "clone",
991
+ "clone3",
992
+ "fanotify_init",
993
+ "fsconfig",
994
+ "fsmount",
995
+ "fsopen",
996
+ "fspick",
997
+ "lookup_dcookie",
998
+ "lsm_get_self_attr",
999
+ "lsm_list_modules",
1000
+ "lsm_set_self_attr",
1001
+ "mount",
1002
+ "mount_setattr",
1003
+ "move_mount",
1004
+ "open_tree",
1005
+ "perf_event_open",
1006
+ "quotactl",
1007
+ "quotactl_fd",
1008
+ "setdomainname",
1009
+ "sethostname",
1010
+ "setns",
1011
+ "syslog",
1012
+ "umount",
1013
+ "umount2",
1014
+ "unshare"
1015
+ ],
1016
+ "action": "SCMP_ACT_ALLOW",
1017
+ "includes": {
1018
+ "caps": [
1019
+ "CAP_SYS_ADMIN"
1020
+ ]
1021
+ }
1022
+ },
1023
+ {
1024
+ "names": [
1025
+ "clone"
1026
+ ],
1027
+ "action": "SCMP_ACT_ALLOW",
1028
+ "args": [
1029
+ {
1030
+ "index": 0,
1031
+ "value": 2114060288,
1032
+ "op": "SCMP_CMP_MASKED_EQ"
1033
+ }
1034
+ ],
1035
+ "excludes": {
1036
+ "caps": [
1037
+ "CAP_SYS_ADMIN"
1038
+ ],
1039
+ "arches": [
1040
+ "s390",
1041
+ "s390x"
1042
+ ]
1043
+ }
1044
+ },
1045
+ {
1046
+ "names": [
1047
+ "clone"
1048
+ ],
1049
+ "action": "SCMP_ACT_ALLOW",
1050
+ "args": [
1051
+ {
1052
+ "index": 1,
1053
+ "value": 2114060288,
1054
+ "op": "SCMP_CMP_MASKED_EQ"
1055
+ }
1056
+ ],
1057
+ "comment": "s390 parameter ordering for clone is different",
1058
+ "includes": {
1059
+ "arches": [
1060
+ "s390",
1061
+ "s390x"
1062
+ ]
1063
+ },
1064
+ "excludes": {
1065
+ "caps": [
1066
+ "CAP_SYS_ADMIN"
1067
+ ]
1068
+ }
1069
+ },
1070
+ {
1071
+ "names": [
1072
+ "clone3"
1073
+ ],
1074
+ "action": "SCMP_ACT_ERRNO",
1075
+ "errnoRet": 38,
1076
+ "excludes": {
1077
+ "caps": [
1078
+ "CAP_SYS_ADMIN"
1079
+ ]
1080
+ }
1081
+ },
1082
+ {
1083
+ "names": [
1084
+ "reboot"
1085
+ ],
1086
+ "action": "SCMP_ACT_ALLOW",
1087
+ "includes": {
1088
+ "caps": [
1089
+ "CAP_SYS_BOOT"
1090
+ ]
1091
+ }
1092
+ },
1093
+ {
1094
+ "names": [
1095
+ "chroot"
1096
+ ],
1097
+ "action": "SCMP_ACT_ALLOW",
1098
+ "includes": {
1099
+ "caps": [
1100
+ "CAP_SYS_CHROOT"
1101
+ ]
1102
+ }
1103
+ },
1104
+ {
1105
+ "names": [
1106
+ "delete_module",
1107
+ "init_module",
1108
+ "finit_module"
1109
+ ],
1110
+ "action": "SCMP_ACT_ALLOW",
1111
+ "includes": {
1112
+ "caps": [
1113
+ "CAP_SYS_MODULE"
1114
+ ]
1115
+ }
1116
+ },
1117
+ {
1118
+ "names": [
1119
+ "acct"
1120
+ ],
1121
+ "action": "SCMP_ACT_ALLOW",
1122
+ "includes": {
1123
+ "caps": [
1124
+ "CAP_SYS_PACCT"
1125
+ ]
1126
+ }
1127
+ },
1128
+ {
1129
+ "names": [
1130
+ "kcmp",
1131
+ "pidfd_getfd",
1132
+ "process_madvise",
1133
+ "process_vm_readv",
1134
+ "process_vm_writev",
1135
+ "ptrace"
1136
+ ],
1137
+ "action": "SCMP_ACT_ALLOW",
1138
+ "includes": {
1139
+ "caps": [
1140
+ "CAP_SYS_PTRACE"
1141
+ ]
1142
+ }
1143
+ },
1144
+ {
1145
+ "names": [
1146
+ "iopl",
1147
+ "ioperm"
1148
+ ],
1149
+ "action": "SCMP_ACT_ALLOW",
1150
+ "includes": {
1151
+ "caps": [
1152
+ "CAP_SYS_RAWIO"
1153
+ ]
1154
+ }
1155
+ },
1156
+ {
1157
+ "names": [
1158
+ "settimeofday",
1159
+ "stime",
1160
+ "clock_settime",
1161
+ "clock_settime64"
1162
+ ],
1163
+ "action": "SCMP_ACT_ALLOW",
1164
+ "includes": {
1165
+ "caps": [
1166
+ "CAP_SYS_TIME"
1167
+ ]
1168
+ }
1169
+ },
1170
+ {
1171
+ "names": [
1172
+ "vhangup"
1173
+ ],
1174
+ "action": "SCMP_ACT_ALLOW",
1175
+ "includes": {
1176
+ "caps": [
1177
+ "CAP_SYS_TTY_CONFIG"
1178
+ ]
1179
+ }
1180
+ },
1181
+ {
1182
+ "names": [
1183
+ "get_mempolicy",
1184
+ "mbind",
1185
+ "set_mempolicy",
1186
+ "set_mempolicy_home_node"
1187
+ ],
1188
+ "action": "SCMP_ACT_ALLOW",
1189
+ "includes": {
1190
+ "caps": [
1191
+ "CAP_SYS_NICE"
1192
+ ]
1193
+ }
1194
+ },
1195
+ {
1196
+ "names": [
1197
+ "syslog"
1198
+ ],
1199
+ "action": "SCMP_ACT_ALLOW",
1200
+ "includes": {
1201
+ "caps": [
1202
+ "CAP_SYSLOG"
1203
+ ]
1204
+ }
1205
+ },
1206
+ {
1207
+ "names": [
1208
+ "bpf"
1209
+ ],
1210
+ "action": "SCMP_ACT_ALLOW",
1211
+ "includes": {
1212
+ "caps": [
1213
+ "CAP_BPF"
1214
+ ]
1215
+ }
1216
+ },
1217
+ {
1218
+ "names": [
1219
+ "perf_event_open"
1220
+ ],
1221
+ "action": "SCMP_ACT_ALLOW",
1222
+ "includes": {
1223
+ "caps": [
1224
+ "CAP_PERFMON"
1225
+ ]
1226
+ }
1227
+ }
1228
+ ]
1229
+ }
recipe/serve/serve.sh ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Qwen3.8-Flash-Next (NVIDIA FP4-Local) on one DGX Spark (GB10) with vLLM + b12x.
3
+ #
4
+ # bash serve.sh # OpenAI-compatible API on :8000 (docker logs -f qwen38-fp4)
5
+ # bash serve.sh stop
6
+ #
7
+ # Serves the weights in this repo (../.. from this script) with the patches in ../patches
8
+ # mounted over a pinned eugr/spark-vllm-b12x image. Every flag below is on in the published
9
+ # configuration; set one to 0 (or empty) to fall back to the image's stock code path.
10
+ # Optional: API_KEY=... requires a bearer token.
11
+ set -euo pipefail
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+
14
+ NAME=qwen38-fp4
15
+ IMAGE="${IMAGE:-eugr/spark-vllm-b12x@sha256:03a956544e9f9b4b7ba91690469fbe7848bdbef29a7d2e0f5788368467bb3595}"
16
+ MODEL_DIR="${MODEL_DIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
17
+ CACHE_DIR="${CACHE_DIR:-$HOME/.cache/qwen38-flash-next-spark}" # compile/autotune caches (first boot autotunes)
18
+ PORT="${PORT:-8000}"
19
+ GPU_MEMORY_UTILIZATION="${GPU_MEMORY_UTILIZATION:-0.70}"
20
+ MAX_MODEL_LEN="${MAX_MODEL_LEN:-131072}"
21
+ MAX_NUM_SEQS="${MAX_NUM_SEQS:-4}"
22
+ # Prefill chunk. The request-boundary prefix cache used to place its reusable prefill
23
+ # checkpoint one to two chunks before the prompt end, so larger chunks made every agent
24
+ # follow-up recompute more (real-session replay, 2026-10-19: follow-up TTFT 2.26 s at
25
+ # 2048, 3.53 s at 4096). TAIL_CKPT moves that checkpoint to just before the prompt end:
26
+ # follow-ups recompute only the new message (0.77 s at either size), so 4096 now costs
27
+ # nothing there and prefills cold prompts 13-15% faster (~2,140 tok/s). KV pool
28
+ # ~233K -> ~182K tokens. 8192 OOMs at 0.70 memory.
29
+ MAX_NUM_BATCHED_TOKENS="${MAX_NUM_BATCHED_TOKENS:-4096}"
30
+ MTP_TOKENS="${MTP_TOKENS:-4}" # 0 disables MTP; swept 2-6, 4 is best on real agentic turns
31
+ EXTRA_ARGS="${EXTRA_ARGS:-}" # appended verbatim to vllm serve
32
+ # Docker default seccomp + io_uring_{setup,enter,register} only: b12x reads the on-disk
33
+ # PLE tables with io_uring (B12X_DISK_BACKEND supports only io_uring or gds), which the
34
+ # default profile blocks.
35
+ SECCOMP="${SECCOMP:-$SCRIPT_DIR/seccomp.json}"
36
+ # MXFP8_LM_HEAD=1 image-native b12x MXFP8 target lm_head with bf16 activations
37
+ # (bf16 head was an sm80 WMMA fallback, ~5 ms/step); ~3% faster steps,
38
+ # +6% synthetic decode, larger KV cache. Not bitwise lossless.
39
+ # Patches from ../patches (mounted over the image). Defaults (2026-10-16,
40
+ # real agentic replay, server inter-token latency): 71.2 -> 63.3 ms per MTP verify step,
41
+ # 46.9 -> 52.2 tok/s weighted decode; logprob KL vs the unpatched launcher 7.5e-3, inside
42
+ # the 8.6e-3 run-to-run noise; tool calls 5/5. Set any flag to 0 (or empty) to disable it.
43
+ # 2026-10-19 defaults add MTP_BF16_KV, PLE_KEEPALIVE_US=2000, PLE_OVERLAP, GEMMA_NORM, HC_FUSE:
44
+ # replay 62.3 -> 60.3 ms/step, draft acceptance 55 -> 63%, 52.0 -> 59.1 tok/s weighted decode;
45
+ # 40-prompt category harness median 53.8 -> 68.1 tok/s; KL vs before 6.1e-3; tool calls 5/5.
46
+ # Cost: the bf16 draft-layer KV pads the mamba pages, KV pool ~300K -> ~220K tokens.
47
+ # + GDN_DEFER (verify records + one post-acceptance commit): replay 60.0-60.3 -> 58.4 ms/step,
48
+ # 40-prompt 59.0-59.2 -> 57.4; 34.7K-token greedy output identical to stock over 256 tokens.
49
+ # + MOE_A16 (2026-10-19 evening): routed experts with bf16 activations instead of the
50
+ # checkpoint-default FP4 activations. KLD vs BF16 (AtomicChat llama.cpp neutral set, 61K
51
+ # positions) 0.0677 -> 0.0482, same top-1 90.3 -> 91.8%, PPL +1.9% -> +0.8%; and faster:
52
+ # replay 58.72 -> 57.59 ms/step (60.6 -> 63.2 tok/s), 40-prompt 57.59 -> 56.40 ms/step;
53
+ # prefill -1..3%. 6 of 7 cold boots OK (one unexplained cudaErrorNotPermitted at init).
54
+ # Measured noise source: b12x MoE sums top-k expert outputs with bf16 atomics, so outputs
55
+ # vary run to run (MOE_DETERMINISTIC=1 fixes it for FP4-activation MoE at +4% decode and
56
+ # +28% TTFT; it has no effect with MOE_A16). FP8 KV costs no measurable KLD.
57
+ # SMALL_GEMV=1 split-K Triton GEMV for the MoE router (sm80 fallback on GB10)
58
+ # FP8_LM_HEAD=1 older rowwise-fp8 lm_head patch; superseded by MXFP8_LM_HEAD (its
59
+ # fp32 quantize temp shrinks the KV cache). Use one or the other.
60
+ # HC_FP8=1 rowwise-FP8 split-K GEMVs for the HyperConnection mix projections
61
+ # at decode (sm80 bf16 fallback otherwise); prefill keeps bf16.
62
+ # MXFP8_GEMV="2560x6144,1280x2560,..." split-K Triton GEMV over b12x's packed MXFP8
63
+ # weights for decode rows of the listed NxK projections (b12x leaves
64
+ # most SMs idle on narrow N at decode); prefill stays on b12x.
65
+ # DRAFT_VOCAB=/cache/draft_vocab_64k.json MTP draft logits over a token subset:
66
+ # MXFP8 rows gathered from the shared target lm_head (the draft's own
67
+ # NVFP4 head is dropped), scattered to the full vocab with -inf.
68
+ # DRAFT_NVFP4=1 with DRAFT_VOCAB: keep the subset rows as row-major NVFP4 (requantized
69
+ # from the MXFP8 head; the stock draft head is NVFP4 too), ~half the bytes.
70
+ # MTP_FP8=1 rowwise-FP8 decode GEMVs for the MTP draft layer's bf16 projections.
71
+ # MTP_BF16_KV=1 keep the MTP draft layer's KV cache in bf16 (the target stays FP8).
72
+ # GEMMA_NORM=1 one Triton launch per GemmaRMSNorm (QSA q/k norms etc.; stock ~10 eager ops).
73
+ # GEMV_TUNE=1 decode GEMV configs re-tuned with dirty L2 (router, HC down, qkvz, shared up).
74
+ # HC_FUSE=1 with HC_FP8: scaled SiLU and gate mix fused into the HC FP8 GEMVs (4 -> 2 launches).
75
+ # GDN_DEFER=1 GDN verify writes per-token records instead of 5 fp32 states per layer; one
76
+ # post-acceptance commit writes the column the next step reads.
77
+ # PLE_OVERLAP=1 run the disk-PLE read/lookup on a side stream; the forward waits only at the
78
+ # PLE layer (stream wait-value node), so decoder layer 0 overlaps the read.
79
+ # TARGET_FP8=1 same for the target's remaining plain bf16 projections (PLE key/value).
80
+ # SHARED_FUSE=1 shared-expert tail at decode in one kernel: SiLU*mul + MXFP8 down GEMV +
81
+ # sigmoid(shared_expert_gate) (stock runs 5-6 small kernels per MoE layer).
82
+ # PLE_PATCH=1 disk-PLE reader knobs (set via EXTRA_ENV): VLLM_PLE_QUEUE_DEPTH
83
+ # (io_uring depth, stock 64), B12X_PLE_BUFFERED=1 (page cache instead
84
+ # of O_DIRECT), VLLM_PLE_STATS=N (log reader stats every N steps).
85
+ # PLE_ASYNC=1 enqueue the per-step disk-PLE row reads on the CUDA stream as a host
86
+ # function, so the CPU launches the model graph while the rows are read
87
+ # (stock blocks the CPU on the ID copy + reads, then launches).
88
+ # PLE_KEEPALIVE_US=N with PLE_ASYNC: while PLE reads are recent (2 s), one 4 KiB read
89
+ # every N us keeps the NVMe out of its idle power states, which add
90
+ # ~1-2 ms wake-up to each step's read burst (fio p99 2.2 ms after 60 ms idle).
91
+ MXFP8_LM_HEAD="${MXFP8_LM_HEAD:-1}"
92
+ FP8_LM_HEAD="${FP8_LM_HEAD:-0}"
93
+ SMALL_GEMV="${SMALL_GEMV:-1}"
94
+ HC_FP8="${HC_FP8:-1}"
95
+ PLE_PATCH="${PLE_PATCH:-0}"
96
+ DRAFT_VOCAB="${DRAFT_VOCAB-$SCRIPT_DIR/draft_vocab_64k.json}" # set empty to disable
97
+ MXFP8_GEMV="${MXFP8_GEMV-2560x6144,1280x2560,16384x2560,640x2560}" # shapes that beat b12x in-model
98
+ MTP_FP8="${MTP_FP8:-1}"
99
+ MTP_BF16_KV="${MTP_BF16_KV:-1}"
100
+ GEMMA_NORM="${GEMMA_NORM:-1}"
101
+ GEMV_TUNE="${GEMV_TUNE:-0}"
102
+ HC_FUSE="${HC_FUSE:-1}"
103
+ PLE_OVERLAP="${PLE_OVERLAP:-1}"
104
+ GDN_DEFER="${GDN_DEFER:-1}"
105
+ GDN_ASYNC="${GDN_ASYNC:-0}" # GDN_DEFER commit on a side stream overlapping the MTP draft phase
106
+ GDN_FLAT="${GDN_FLAT:-0}" # GDN_DEFER straight-line verify kernel (8 rows x 1 warp per program)
107
+ DRAFT_NVFP4="${DRAFT_NVFP4:-0}"
108
+ TARGET_FP8="${TARGET_FP8:-1}"
109
+ SHARED_FUSE="${SHARED_FUSE:-1}"
110
+ PLE_ASYNC="${PLE_ASYNC:-1}"
111
+ PLE_KEEPALIVE_US="${PLE_KEEPALIVE_US:-2000}"
112
+ MOE_DETERMINISTIC="${MOE_DETERMINISTIC:-0}" # b12x MoE fixed-order top-k sum instead of bf16 atomic scatter (run-to-run identical)
113
+ MOE_A16="${MOE_A16:-1}" # routed experts with bf16 activations (W4A16) instead of the checkpoint default NVFP4 activations (W4A4)
114
+ A4_PREFILL="${A4_PREFILL:-0}" # FP4 activations for prefill-size MoE calls only: +6-8% prefill, but KLD 0.0492 -> 0.0665 (W4A4 level)
115
+ TAIL_CKPT="${TAIL_CKPT:-16,16}" # "GRID,SUFFIX": prefill-tail prefix-cache checkpoint just before the prompt end (decouples follow-up reuse from chunk size)
116
+ CSF_INLINE="${CSF_INLINE:-1}" # b12x W4A16 decode MoE reads NVFP4-CSF scales inline (no csf_Pair expansion pass):
117
+ # bit-identical, -5.7% replay step time (57.5 -> 54.2 ms)
118
+ LOGIT_DUMP="${LOGIT_DUMP:-}" # measurement only: dump dir under /cache for prompt logits
119
+ EXPERT_STATS="${EXPERT_STATS:-}" # measurement only: json under /cache, distinct experts per decode call
120
+ PATCH_DIR="${PATCH_DIR:-$(cd "$SCRIPT_DIR/../patches" && pwd)}"
121
+ VLLM_PKG=/usr/local/lib/python3.12/dist-packages/vllm
122
+ EXTRA_ENV="${EXTRA_ENV:-}" # extra "-e KEY=VAL" docker flags (tuning knobs)
123
+ B12X_CACHE="${B12X_CACHE:-b12x/compile}" # b12x compile/autotune cache under CACHE_DIR; point experiments with new shapes at a copy
124
+
125
+ if [ "${1:-start}" = "stop" ]; then
126
+ docker stop "$NAME" >/dev/null 2>&1 || true
127
+ docker rm "$NAME" >/dev/null 2>&1 || true
128
+ exit 0
129
+ fi
130
+
131
+ # b12x keeps compiled kernels and autotune results under B12X_COMPILE_CACHE_DIR
132
+ # (default ~/.cache/b12x/compile inside the container, lost on every restart,
133
+ # which re-ran the ~14 min gdn_prefill autotune).
134
+ mkdir -p "$CACHE_DIR"/{vllm,flashinfer,triton,b12x/compile}
135
+ patch_args=()
136
+ declare -A mounted=()
137
+ mount() { # mount <patch-relative source> <absolute target>, once per target
138
+ [ -n "${mounted[$2]:-}" ] && return
139
+ mounted[$2]=1
140
+ patch_args+=(-v "$PATCH_DIR/$1:$2:ro")
141
+ }
142
+ QWEN=$VLLM_PKG/models/qwen4_exp/nvidia
143
+ B12X=/usr/local/lib/python3.12/dist-packages/flashinfer/experimental/b12x
144
+ if [ "$FP8_LM_HEAD" = "1" ] || [ "$SMALL_GEMV" = "1" ]; then
145
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
146
+ mount qwen4_exp/nvidia/ops/small_gemv.py $QWEN/ops/small_gemv.py
147
+ fi
148
+ [ "$FP8_LM_HEAD" = "1" ] && patch_args+=(-e VLLM_QWEN38_FP8_LM_HEAD=1)
149
+ [ "$SMALL_GEMV" = "1" ] && patch_args+=(-e VLLM_QWEN38_SMALL_GEMV=1)
150
+ [ "$MXFP8_LM_HEAD" = "1" ] && patch_args+=(-e VLLM_MXFP8_LM_HEAD=1)
151
+ if [ "$HC_FP8" = "1" ]; then
152
+ mount qwen4_exp/nvidia/hyperconnection.py $QWEN/hyperconnection.py
153
+ mount qwen4_exp/nvidia/ops/hc_fp8_gemv.py $QWEN/ops/hc_fp8_gemv.py
154
+ patch_args+=(-e VLLM_QWEN38_HC_FP8=1)
155
+ fi
156
+ if [ -n "$MXFP8_GEMV" ]; then
157
+ mount vllm_kernels/linear/mxfp8/b12x.py $VLLM_PKG/model_executor/kernels/linear/mxfp8/b12x.py
158
+ mount qwen4_exp/nvidia/ops/mxfp8_gemv.py $QWEN/ops/mxfp8_gemv.py
159
+ patch_args+=(-e VLLM_QWEN38_MXFP8_GEMV="$MXFP8_GEMV")
160
+ fi
161
+ if [ -n "$DRAFT_VOCAB" ] && [ ! -f "$DRAFT_VOCAB" ]; then
162
+ echo "warning: $DRAFT_VOCAB not found; draft vocabulary subset disabled" >&2
163
+ DRAFT_VOCAB=""
164
+ fi
165
+ if [ -n "$DRAFT_VOCAB" ]; then
166
+ # The draft shares the target's MXFP8 lm_head and gathers the subset rows from it.
167
+ mount qwen4_exp/nvidia/mtp.py $QWEN/mtp.py
168
+ mount qwen4_exp/nvidia/ops/mxfp8_gemv.py $QWEN/ops/mxfp8_gemv.py
169
+ patch_args+=(-v "$DRAFT_VOCAB:/opt/draft_vocab.json:ro" -e VLLM_QWEN38_DRAFT_VOCAB=/opt/draft_vocab.json -e VLLM_MTP_NVFP4_LM_HEAD=0)
170
+ if [ "$DRAFT_NVFP4" = "1" ]; then
171
+ mount qwen4_exp/nvidia/ops/nvfp4_gemv.py $QWEN/ops/nvfp4_gemv.py
172
+ patch_args+=(-e VLLM_QWEN38_DRAFT_NVFP4=1)
173
+ fi
174
+ fi
175
+ if [ "$MTP_FP8" = "1" ]; then
176
+ mount qwen4_exp/nvidia/mtp.py $QWEN/mtp.py
177
+ mount qwen4_exp/nvidia/ops/hc_fp8_gemv.py $QWEN/ops/hc_fp8_gemv.py
178
+ patch_args+=(-e VLLM_QWEN38_MTP_FP8=1)
179
+ fi
180
+ if [ "$MTP_BF16_KV" = "1" ]; then
181
+ mount qwen4_exp/nvidia/mtp.py $QWEN/mtp.py
182
+ patch_args+=(-e VLLM_QWEN38_MTP_BF16_KV=1)
183
+ fi
184
+ if [ "$GEMV_TUNE" = "1" ]; then patch_args+=(-e VLLM_QWEN38_GEMV_TUNE=1); fi
185
+ if [ "$HC_FUSE" = "1" ] && [ "$HC_FP8" = "1" ]; then patch_args+=(-e VLLM_QWEN38_HC_FUSE=1); fi
186
+ if [ "$GEMMA_NORM" = "1" ]; then
187
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
188
+ mount qwen4_exp/nvidia/ops/gemma_norm.py $QWEN/ops/gemma_norm.py
189
+ patch_args+=(-e VLLM_QWEN38_GEMMA_NORM=1)
190
+ fi
191
+ if [ "$SHARED_FUSE" = "1" ]; then
192
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
193
+ mount qwen4_exp/nvidia/ops/small_gemv.py $QWEN/ops/small_gemv.py
194
+ mount qwen4_exp/nvidia/ops/mxfp8_gemv.py $QWEN/ops/mxfp8_gemv.py
195
+ patch_args+=(-e VLLM_QWEN38_SHARED_FUSE=1)
196
+ fi
197
+ if [ "$TARGET_FP8" = "1" ]; then
198
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
199
+ mount qwen4_exp/nvidia/ops/small_gemv.py $QWEN/ops/small_gemv.py
200
+ mount qwen4_exp/nvidia/ops/hc_fp8_gemv.py $QWEN/ops/hc_fp8_gemv.py
201
+ patch_args+=(-e VLLM_QWEN38_TARGET_FP8=1)
202
+ fi
203
+ if [ "$GDN_DEFER" = "1" ]; then
204
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
205
+ mount qwen4_exp/nvidia/gdn_defer.py $QWEN/gdn_defer.py
206
+ mount qwen4_exp/nvidia/ops/gdn_deferred.py $QWEN/ops/gdn_deferred.py
207
+ patch_args+=(-e VLLM_QWEN38_GDN_DEFER=1)
208
+ [ "$GDN_ASYNC" = "1" ] && patch_args+=(-e VLLM_QWEN38_GDN_COMMIT_ASYNC=1)
209
+ [ "$GDN_FLAT" = "1" ] && patch_args+=(-e VLLM_QWEN38_GDN_VERIFY_FLAT=1)
210
+ fi
211
+ [ "$MOE_DETERMINISTIC" = "1" ] && patch_args+=(-e B12X_DYNAMIC_DETERMINISTIC_OUTPUT=1)
212
+ [ "$MOE_A16" = "1" ] && patch_args+=(-e VLLM_B12X_MOE_FP4_FORCE_A16=1)
213
+ [ "$A4_PREFILL" = "1" ] && patch_args+=(-e B12X_W4A16_A4_PREFILL=1)
214
+ if [ "$CSF_INLINE" = "1" ]; then
215
+ mount b12x/moe/_shared/kernels/w4a16/kernel.py $B12X/moe/_shared/kernels/w4a16/kernel.py
216
+ mount b12x/moe/fused_moe/_impl.py $B12X/moe/fused_moe/_impl.py
217
+ mount b12x/moe/fused_moe/_preparation.py $B12X/moe/fused_moe/_preparation.py
218
+ patch_args+=(-e B12X_W4A16_CSF_INLINE=1)
219
+ fi
220
+ if [ -n "$TAIL_CKPT" ] && [ "$TAIL_CKPT" != off ]; then # TAIL_CKPT=off: stock checkpoint
221
+ mount vllm_core/v1/core/boundary_checkpoint.py $VLLM_PKG/v1/core/boundary_checkpoint.py
222
+ patch_args+=(-e VLLM_QWEN38_TAIL_CKPT="$TAIL_CKPT")
223
+ fi
224
+ if [ -n "$LOGIT_DUMP" ]; then
225
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
226
+ mount qwen4_exp/nvidia/logit_dump.py $QWEN/logit_dump.py
227
+ patch_args+=(-e VLLM_QWEN38_LOGIT_DUMP="/cache/$LOGIT_DUMP")
228
+ fi
229
+ if [ -n "$EXPERT_STATS" ]; then
230
+ mount qwen4_exp/nvidia/model.py $QWEN/model.py
231
+ mount qwen4_exp/nvidia/expert_stats.py $QWEN/expert_stats.py
232
+ patch_args+=(-e VLLM_QWEN38_EXPERT_STATS="/cache/$EXPERT_STATS")
233
+ fi
234
+ if [ "$PLE_OVERLAP" = "1" ]; then
235
+ mount qwen4_exp/nvidia/b12x_ple.py $QWEN/b12x_ple.py
236
+ patch_args+=(-e VLLM_QWEN38_PLE_OVERLAP=1)
237
+ fi
238
+ if [ "$PLE_PATCH" = "1" ]; then
239
+ mount qwen4_exp/nvidia/b12x_ple.py $QWEN/b12x_ple.py
240
+ mount b12x/loader/_row_plan.h $B12X/loader/_row_plan.h
241
+ fi
242
+ if [ "$PLE_ASYNC" = "1" ]; then
243
+ mount b12x/loader/_row_plan.h $B12X/loader/_row_plan.h
244
+ mount b12x/loader/_ple_reader.c $B12X/loader/_ple_reader.c
245
+ mount b12x/loader/_storage.c $B12X/loader/_storage.c
246
+ mount b12x/sequence/_shared/disk_table.py $B12X/sequence/_shared/disk_table.py
247
+ patch_args+=(-e B12X_PLE_ASYNC=1)
248
+ if [ "${PLE_KEEPALIVE_US:-0}" != "0" ]; then patch_args+=(-e B12X_PLE_KEEPALIVE_US="$PLE_KEEPALIVE_US"); fi
249
+ fi
250
+ spec=()
251
+ [ "$MTP_TOKENS" != "0" ] && spec=(--speculative-config "{\"method\":\"mtp\",\"num_speculative_tokens\":${MTP_TOKENS}}")
252
+
253
+ docker rm -f "$NAME" >/dev/null 2>&1 || true
254
+ docker run -d --name "$NAME" \
255
+ --gpus all --ipc host --network host --shm-size=16g \
256
+ --ulimit memlock=-1 --ulimit stack=67108864 \
257
+ --security-opt seccomp="$SECCOMP" \
258
+ -e VLLM_NO_USAGE_STATS=1 -e VLLM_DO_NOT_TRACK=1 -e DO_NOT_TRACK=1 \
259
+ -e HF_HUB_DISABLE_TELEMETRY=1 \
260
+ -e VLLM_PLE_TABLE_MEMORY=disk \
261
+ -e B12X_DISK_BACKEND=io_uring \
262
+ -e VLLM_USE_V2_MODEL_RUNNER=1 \
263
+ -e B12X_POLICY_MODE=auto \
264
+ -e VLLM_SSM_CONV_STATE_LAYOUT=DS \
265
+ -e VLLM_CACHE_ROOT=/cache/vllm \
266
+ -e FLASHINFER_WORKSPACE_BASE=/cache/flashinfer \
267
+ -e TRITON_CACHE_DIR=/cache/triton \
268
+ -e B12X_COMPILE_CACHE_DIR="/cache/$B12X_CACHE" \
269
+ "${patch_args[@]}" $EXTRA_ENV \
270
+ -v "$MODEL_DIR":/model:ro \
271
+ -v "$CACHE_DIR":/cache \
272
+ --entrypoint vllm \
273
+ "$IMAGE" serve /model \
274
+ --served-model-name nvidia/Qwen3.8-Flash-Next-FP4-Local qwen3.8-flash-next \
275
+ --host 0.0.0.0 --port "$PORT" \
276
+ --quantization modelopt_mixed \
277
+ --load-format b12x \
278
+ --moe-backend b12x \
279
+ --linear-backend b12x \
280
+ --kv-cache-dtype fp8 \
281
+ --max-model-len "$MAX_MODEL_LEN" \
282
+ --max-num-seqs "$MAX_NUM_SEQS" \
283
+ --max-num-batched-tokens "$MAX_NUM_BATCHED_TOKENS" \
284
+ --gpu-memory-utilization "$GPU_MEMORY_UTILIZATION" \
285
+ --enable-prefix-caching \
286
+ --reasoning-parser qwen3 \
287
+ --tool-call-parser qwen3_xml \
288
+ --enable-auto-tool-choice \
289
+ "${spec[@]}" ${API_KEY:+--api-key "$API_KEY"} $EXTRA_ARGS
290
+ echo "started $NAME on port $PORT (docker logs -f $NAME)"
tokenizer_config.json ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "248044": {
5
+ "content": "<|endoftext|>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "248045": {
13
+ "content": "<|im_start|>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "248046": {
21
+ "content": "<|im_end|>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ },
28
+ "248047": {
29
+ "content": "<|object_ref_start|>",
30
+ "lstrip": false,
31
+ "normalized": false,
32
+ "rstrip": false,
33
+ "single_word": false,
34
+ "special": true
35
+ },
36
+ "248048": {
37
+ "content": "<|object_ref_end|>",
38
+ "lstrip": false,
39
+ "normalized": false,
40
+ "rstrip": false,
41
+ "single_word": false,
42
+ "special": true
43
+ },
44
+ "248049": {
45
+ "content": "<|box_start|>",
46
+ "lstrip": false,
47
+ "normalized": false,
48
+ "rstrip": false,
49
+ "single_word": false,
50
+ "special": true
51
+ },
52
+ "248050": {
53
+ "content": "<|box_end|>",
54
+ "lstrip": false,
55
+ "normalized": false,
56
+ "rstrip": false,
57
+ "single_word": false,
58
+ "special": true
59
+ },
60
+ "248051": {
61
+ "content": "<|quad_start|>",
62
+ "lstrip": false,
63
+ "normalized": false,
64
+ "rstrip": false,
65
+ "single_word": false,
66
+ "special": true
67
+ },
68
+ "248052": {
69
+ "content": "<|quad_end|>",
70
+ "lstrip": false,
71
+ "normalized": false,
72
+ "rstrip": false,
73
+ "single_word": false,
74
+ "special": true
75
+ },
76
+ "248053": {
77
+ "content": "<|vision_start|>",
78
+ "lstrip": false,
79
+ "normalized": false,
80
+ "rstrip": false,
81
+ "single_word": false,
82
+ "special": true
83
+ },
84
+ "248054": {
85
+ "content": "<|vision_end|>",
86
+ "lstrip": false,
87
+ "normalized": false,
88
+ "rstrip": false,
89
+ "single_word": false,
90
+ "special": true
91
+ },
92
+ "248055": {
93
+ "content": "<|vision_pad|>",
94
+ "lstrip": false,
95
+ "normalized": false,
96
+ "rstrip": false,
97
+ "single_word": false,
98
+ "special": true
99
+ },
100
+ "248056": {
101
+ "content": "<|image_pad|>",
102
+ "lstrip": false,
103
+ "normalized": false,
104
+ "rstrip": false,
105
+ "single_word": false,
106
+ "special": true
107
+ },
108
+ "248057": {
109
+ "content": "<|video_pad|>",
110
+ "lstrip": false,
111
+ "normalized": false,
112
+ "rstrip": false,
113
+ "single_word": false,
114
+ "special": true
115
+ },
116
+ "248058": {
117
+ "content": "<tool_call>",
118
+ "lstrip": false,
119
+ "normalized": false,
120
+ "rstrip": false,
121
+ "single_word": false,
122
+ "special": false
123
+ },
124
+ "248059": {
125
+ "content": "</tool_call>",
126
+ "lstrip": false,
127
+ "normalized": false,
128
+ "rstrip": false,
129
+ "single_word": false,
130
+ "special": false
131
+ },
132
+ "248060": {
133
+ "content": "<|fim_prefix|>",
134
+ "lstrip": false,
135
+ "normalized": false,
136
+ "rstrip": false,
137
+ "single_word": false,
138
+ "special": false
139
+ },
140
+ "248061": {
141
+ "content": "<|fim_middle|>",
142
+ "lstrip": false,
143
+ "normalized": false,
144
+ "rstrip": false,
145
+ "single_word": false,
146
+ "special": false
147
+ },
148
+ "248062": {
149
+ "content": "<|fim_suffix|>",
150
+ "lstrip": false,
151
+ "normalized": false,
152
+ "rstrip": false,
153
+ "single_word": false,
154
+ "special": false
155
+ },
156
+ "248063": {
157
+ "content": "<|fim_pad|>",
158
+ "lstrip": false,
159
+ "normalized": false,
160
+ "rstrip": false,
161
+ "single_word": false,
162
+ "special": false
163
+ },
164
+ "248064": {
165
+ "content": "<|repo_name|>",
166
+ "lstrip": false,
167
+ "normalized": false,
168
+ "rstrip": false,
169
+ "single_word": false,
170
+ "special": false
171
+ },
172
+ "248065": {
173
+ "content": "<|file_sep|>",
174
+ "lstrip": false,
175
+ "normalized": false,
176
+ "rstrip": false,
177
+ "single_word": false,
178
+ "special": false
179
+ },
180
+ "248066": {
181
+ "content": "<tool_response>",
182
+ "lstrip": false,
183
+ "normalized": false,
184
+ "rstrip": false,
185
+ "single_word": false,
186
+ "special": false
187
+ },
188
+ "248067": {
189
+ "content": "</tool_response>",
190
+ "lstrip": false,
191
+ "normalized": false,
192
+ "rstrip": false,
193
+ "single_word": false,
194
+ "special": false
195
+ },
196
+ "248068": {
197
+ "content": "<think>",
198
+ "lstrip": false,
199
+ "normalized": false,
200
+ "rstrip": false,
201
+ "single_word": false,
202
+ "special": false
203
+ },
204
+ "248069": {
205
+ "content": "</think>",
206
+ "lstrip": false,
207
+ "normalized": false,
208
+ "rstrip": false,
209
+ "single_word": false,
210
+ "special": false
211
+ },
212
+ "248070": {
213
+ "content": "<|audio_start|>",
214
+ "lstrip": false,
215
+ "normalized": false,
216
+ "rstrip": false,
217
+ "single_word": false,
218
+ "special": true
219
+ },
220
+ "248071": {
221
+ "content": "<|audio_end|>",
222
+ "lstrip": false,
223
+ "normalized": false,
224
+ "rstrip": false,
225
+ "single_word": false,
226
+ "special": true
227
+ },
228
+ "248072": {
229
+ "content": "<tts_pad>",
230
+ "lstrip": false,
231
+ "normalized": false,
232
+ "rstrip": false,
233
+ "single_word": false,
234
+ "special": true
235
+ },
236
+ "248073": {
237
+ "content": "<tts_text_bos>",
238
+ "lstrip": false,
239
+ "normalized": false,
240
+ "rstrip": false,
241
+ "single_word": false,
242
+ "special": true
243
+ },
244
+ "248074": {
245
+ "content": "<tts_text_eod>",
246
+ "lstrip": false,
247
+ "normalized": false,
248
+ "rstrip": false,
249
+ "single_word": false,
250
+ "special": true
251
+ },
252
+ "248075": {
253
+ "content": "<tts_text_bos_single>",
254
+ "lstrip": false,
255
+ "normalized": false,
256
+ "rstrip": false,
257
+ "single_word": false,
258
+ "special": true
259
+ },
260
+ "248076": {
261
+ "content": "<|audio_pad|>",
262
+ "lstrip": false,
263
+ "normalized": false,
264
+ "rstrip": false,
265
+ "single_word": false,
266
+ "special": true
267
+ }
268
+ },
269
+ "additional_special_tokens": [
270
+ "<|im_start|>",
271
+ "<|im_end|>",
272
+ "<|object_ref_start|>",
273
+ "<|object_ref_end|>",
274
+ "<|box_start|>",
275
+ "<|box_end|>",
276
+ "<|quad_start|>",
277
+ "<|quad_end|>",
278
+ "<|vision_start|>",
279
+ "<|vision_end|>",
280
+ "<|vision_pad|>",
281
+ "<|image_pad|>",
282
+ "<|video_pad|>"
283
+ ],
284
+ "bos_token": null,
285
+ "chat_template": "{%- set image_count = namespace(value=0) %}\n{%- set video_count = namespace(value=0) %}\n{%- macro render_content(content, do_vision_count, is_system_content=false) %}\n {%- if content is string %}\n {{- content }}\n {%- elif content is iterable and content is not mapping %}\n {%- for item in content %}\n {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}\n {%- if is_system_content %}\n {{- raise_exception('System message cannot contain images.') }}\n {%- endif %}\n {%- if do_vision_count %}\n {%- set image_count.value = image_count.value + 1 %}\n {%- endif %}\n {%- if add_vision_id %}\n {{- 'Picture ' ~ image_count.value ~ ': ' }}\n {%- endif %}\n {{- '<|vision_start|><|image_pad|><|vision_end|>' }}\n {%- elif 'video' in item or item.type == 'video' %}\n {%- if is_system_content %}\n {{- raise_exception('System message cannot contain videos.') }}\n {%- endif %}\n {%- if do_vision_count %}\n {%- set video_count.value = video_count.value + 1 %}\n {%- endif %}\n {%- if add_vision_id %}\n {{- 'Video ' ~ video_count.value ~ ': ' }}\n {%- endif %}\n {{- '<|vision_start|><|video_pad|><|vision_end|>' }}\n {%- elif 'text' in item %}\n {{- item.text }}\n {%- else %}\n {{- raise_exception('Unexpected item type in content.') }}\n {%- endif %}\n {%- endfor %}\n {%- elif content is none or content is undefined %}\n {{- '' }}\n {%- else %}\n {{- raise_exception('Unexpected content type.') }}\n {%- endif %}\n{%- endmacro %}\n{%- if not messages %}\n {{- raise_exception('No messages provided.') }}\n{%- endif %}\n{%- set reasoning_instructions = '' %}\n{%- if enable_thinking is undefined or enable_thinking is true %}\n {%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}\n {%- if resolved_reasoning_effort not in ('xhigh', 'medium', 'low') %}\n {{- raise_exception('Unexpected reasoning effort ' ~ reasoning_effort ~ '. Supported types are xhigh (default), medium, and low.') }}\n {%- endif %}\n {%- if resolved_reasoning_effort == 'xhigh' %}\n {%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}\n {%- elif resolved_reasoning_effort == 'low' %}\n {%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}\n {%- endif %}\n{%- endif %}\n{%- if tools and tools is iterable and tools is not mapping %}\n {{- '<|im_start|>system\\n' }}\n {%- if reasoning_instructions %}\n {{- reasoning_instructions + '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou have access to the following functions:\\n\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\" }}\n {{- '\\n\\nIf you choose to call a function ONLY reply in the following format with NO suffix:\\n\\n<tool_call>\\n<function=example_function_name>\\n<parameter=example_parameter_1>\\nvalue_1\\n</parameter>\\n<parameter=example_parameter_2>\\nThis is the value for the second parameter\\nthat can span\\nmultiple lines\\n</parameter>\\n</function>\\n</tool_call>\\n\\n<IMPORTANT>\\nReminder:\\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\\n- Required parameters MUST be specified\\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\\n</IMPORTANT>' }}\n {%- if messages[0].role == 'system' %}\n {%- set content = render_content(messages[0].content, false, true)|trim %}\n {%- if content %}\n {{- '\\n\\n' + content }}\n {%- endif %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {%- set content = render_content(messages[0].content, false, true)|trim %}\n {%- if content %}\n {{- '<|im_start|>system\\n' + (reasoning_instructions + '\\n\\n' if reasoning_instructions else '') + content + '<|im_end|>\\n' }}\n {%- elif reasoning_instructions %}\n {{- '<|im_start|>system\\n' + reasoning_instructions + '<|im_end|>\\n' }}\n {%- endif %}\n {%- elif reasoning_instructions %}\n {{- '<|im_start|>system\\n' + reasoning_instructions + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" %}\n {%- set content = render_content(message.content, false)|trim %}\n {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if ns.multi_step_tool %}\n {{- raise_exception('No user query found in messages.') }}\n{%- endif %}\n{%- for message in messages %}\n {%- set content = render_content(message.content, true)|trim %}\n {%- if message.role == \"system\" %}\n {%- if not loop.first %}\n {{- raise_exception('System message must be at the beginning.') }}\n {%- endif %}\n {%- elif message.role == \"user\" %}\n {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is string %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- endif %}\n {%- set reasoning_content = reasoning_content|trim %}\n {%- if preserve_thinking is undefined or preserve_thinking is true or loop.index0 > ns.last_query_index %}\n {{- '<|im_start|>' + message.role + '\\n<think>\\n' + reasoning_content + '\\n</think>\\n\\n' + content }}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {%- if loop.first %}\n {%- if content|trim %}\n {{- '\\n\\n<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- else %}\n {{- '<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- endif %}\n {%- else %}\n {{- '\\n<tool_call>\\n<function=' + tool_call.name + '>\\n' }}\n {%- endif %}\n {%- if tool_call.arguments is defined and tool_call.arguments != '' %}\n {%- for args_name, args_value in tool_call.arguments|items %}\n {{- '<parameter=' + args_name + '>\\n' }}\n {%- set args_value = args_value | string if args_value is string else args_value | tojson | safe %}\n {{- args_value }}\n {{- '\\n</parameter>\\n' }}\n {%- endfor %}\n {%- endif %}\n {{- '</function>\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.previtem and loop.previtem.role != \"tool\" %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- content }}\n {{- '\\n</tool_response>' }}\n {%- if not loop.last and loop.nextitem.role != \"tool\" %}\n {{- '<|im_end|>\\n' }}\n {%- elif loop.last %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- else %}\n {{- raise_exception('Unexpected message role.') }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if enable_thinking is defined and enable_thinking is false %}\n {{- '<think>\\n\\n</think>\\n\\n' }}\n {%- else %}\n {{- '<think>\\n' }}\n {%- endif %}\n{%- endif %}",
286
+ "clean_up_tokenization_spaces": false,
287
+ "eos_token": "<|im_end|>",
288
+ "errors": "replace",
289
+ "model_max_length": 262144,
290
+ "pad_token": "<|endoftext|>",
291
+ "split_special_tokens": false,
292
+ "tokenizer_class": "Qwen2Tokenizer",
293
+ "unk_token": null,
294
+ "add_bos_token": false,
295
+ "pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
296
+ "extra_special_tokens": {
297
+ "audio_bos_token": "<|audio_start|>",
298
+ "audio_eos_token": "<|audio_end|>",
299
+ "audio_token": "<|audio_pad|>",
300
+ "image_token": "<|image_pad|>",
301
+ "video_token": "<|video_pad|>",
302
+ "vision_bos_token": "<|vision_start|>",
303
+ "vision_eos_token": "<|vision_end|>"
304
+ }
305
+ }
video_preprocessor_config.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "size": {
3
+ "longest_edge": 25165824,
4
+ "shortest_edge": 4096
5
+ },
6
+ "patch_size": 16,
7
+ "temporal_patch_size": 2,
8
+ "merge_size": 2,
9
+ "image_mean": [
10
+ 0.5,
11
+ 0.5,
12
+ 0.5
13
+ ],
14
+ "image_std": [
15
+ 0.5,
16
+ 0.5,
17
+ 0.5
18
+ ],
19
+ "processor_class": "Qwen3VLProcessor",
20
+ "video_processor_type": "Qwen3VLVideoProcessor"
21
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff