Add files using upload-large-folder tool
Browse files- LICENSE +61 -0
- LICENSE.Qwen +16 -0
- NOTICE +13 -0
- NVIDIA_MODEL_CARD.md +414 -0
- README.md +161 -0
- chat_template.jinja +170 -0
- config.json +2402 -0
- generation_config.json +12 -0
- merges.txt +0 -0
- preprocessor_config.json +21 -0
- recipe/LICENSE +21 -0
- recipe/README.md +75 -0
- recipe/THIRD_PARTY_NOTICES.md +50 -0
- recipe/bench/bench-sbs.py +39 -0
- recipe/bench/fetch-evaldata.sh +20 -0
- recipe/bench/needle-test.py +16 -0
- recipe/bench/quality-probe.py +91 -0
- recipe/bench/test-toolcall.py +21 -0
- recipe/kl/atomic_kld.py +136 -0
- recipe/patches/b12x/loader/_ple_reader.c +614 -0
- recipe/patches/b12x/loader/_row_plan.h +314 -0
- recipe/patches/b12x/loader/_storage.c +107 -0
- recipe/patches/qwen4_exp/nvidia/b12x_ple.py +1064 -0
- recipe/patches/qwen4_exp/nvidia/expert_stats.py +68 -0
- recipe/patches/qwen4_exp/nvidia/gdn_defer.py +402 -0
- recipe/patches/qwen4_exp/nvidia/hyperconnection.py +782 -0
- recipe/patches/qwen4_exp/nvidia/logit_dump.py +123 -0
- recipe/patches/qwen4_exp/nvidia/model.py +1310 -0
- recipe/patches/qwen4_exp/nvidia/mtp.py +968 -0
- recipe/patches/qwen4_exp/nvidia/ops/gdn_deferred.py +330 -0
- recipe/patches/qwen4_exp/nvidia/ops/gemma_norm.py +71 -0
- recipe/patches/qwen4_exp/nvidia/ops/hc_fp8_gemv.py +453 -0
- recipe/patches/qwen4_exp/nvidia/ops/mxfp8_gemv.py +249 -0
- recipe/patches/qwen4_exp/nvidia/ops/nvfp4_gemv.py +187 -0
- recipe/patches/qwen4_exp/nvidia/ops/small_gemv.py +127 -0
- recipe/patches/vllm_core/v1/core/boundary_checkpoint.py +438 -0
- recipe/patches/vllm_kernels/linear/mxfp8/b12x.py +202 -0
- recipe/results/bench-sbs.txt +4 -0
- recipe/results/kl-neutral-a16-as-served.json +15 -0
- recipe/results/kl-neutral-a4-prefill.json +15 -0
- recipe/results/needle.txt +6 -0
- recipe/results/quality-probe.jsonl +364 -0
- recipe/results/quality-probe.txt +3 -0
- recipe/results/toolcall.txt +4 -0
- recipe/serve/draft_vocab_64k.json +0 -0
- recipe/serve/seccomp.json +1229 -0
- recipe/serve/serve.sh +290 -0
- tokenizer_config.json +305 -0
- video_preprocessor_config.json +21 -0
- 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
|
|
|