Repository navigation
[None][feat] Initial Qwen3.5 text model support for PyT backend (BF16/FP8) - #12242
Conversation
📝 WalkthroughWalkthroughAdds support for Qwen3.5 MoE model architecture including new model class Qwen3_5MoeForCausalLM, specialized weight mapper with FP8 dequantization and projection tensor packing, dynamic layer type selection for hybrid models, and chat template utilities for template argument handling. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment Tip CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required. |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tensorrt_llm/_torch/pyexecutor/_util.py (1)
940-980:⚠️ Potential issue | 🟠 MajorPreserve caller-supplied masks for Qwen3 hybrid KV caches.
Unlike the Nemotron branch just above, this path ignores
layer_maskand never extends the masks for appended speculative layers. In one-engine speculative decoding, or when building a separate draft KV cache, the Qwen3 hybrid manager will still size/cache only the base model layers and miss the draft-only layers.Suggested direction
- hybrid_layer_mask, mamba_layer_mask = get_qwen3_hybrid_layer_masks( - config) - num_layers = sum(hybrid_layer_mask) - num_mamba_layers = sum(mamba_layer_mask) + base_hybrid_mask, base_mamba_mask = get_qwen3_hybrid_layer_masks( + config) + if layer_mask is not None: + hybrid_layer_mask = [] + mamba_layer_mask = [] + for i, include in enumerate(layer_mask): + is_attention = base_hybrid_mask[i] if i < len(base_hybrid_mask) else True + is_mamba = base_mamba_mask[i] if i < len(base_mamba_mask) else False + hybrid_layer_mask.append(is_attention and include) + mamba_layer_mask.append(is_mamba and include) + else: + hybrid_layer_mask = list(base_hybrid_mask) + mamba_layer_mask = list(base_mamba_mask) + if spec_config is not None: + num_spec_layers = get_num_spec_layers(spec_config) + hybrid_layer_mask.extend([True] * num_spec_layers) + mamba_layer_mask.extend([False] * num_spec_layers) + num_layers = sum(hybrid_layer_mask) + num_mamba_layers = sum(mamba_layer_mask)
🧹 Nitpick comments (2)
tensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.py (1)
46-47: Make_SUPPORTED_SUFFIXESimmutable.This is acting as a constant, but a mutable class-level
setis shared across all instances and is what Ruff is warning about here.frozenset(...)keeps the intent and removes the shared-mutation footgun.♻️ Proposed fix
- _SUPPORTED_SUFFIXES = {"weight", "bias", "weight_scale_inv"} + _SUPPORTED_SUFFIXES = frozenset({"weight", "bias", "weight_scale_inv"})🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.py` around lines 46 - 47, The class-level constant _SUPPORTED_SUFFIXES is defined as a mutable set which can be accidentally changed at runtime; change its definition to an immutable frozenset to preserve constant semantics and avoid shared-mutation issues (replace the set literal in _SUPPORTED_SUFFIXES with frozenset({"weight","bias","weight_scale_inv"}) so code referencing _SUPPORTED_SUFFIXES continues to work).tensorrt_llm/_torch/models/modeling_qwen3_5.py (1)
11-61: Consider reusing the existingQwen3NextForCausalLMwrapper instead of copying its setup.This class appears to differ mainly by registration name, but
__init__,load_weights(), andpost_load_weights()now need to stay in sync with the Qwen3Next wrapper intensorrt_llm/_torch/models/modeling_qwen3_next.py. A thin subclass or shared helper would reduce drift.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tensorrt_llm/_torch/models/modeling_qwen3_5.py` around lines 11 - 61, The Qwen3_5MoeForCausalLM class duplicates logic from Qwen3NextForCausalLM; replace the copy with reuse by making Qwen3_5MoeForCausalLM a thin subclass or delegating to Qwen3NextForCausalLM so __init__, load_weights, and post_load_weights remain shared; specifically, have Qwen3_5MoeForCausalLM inherit from Qwen3NextForCausalLM (or call its methods) and only override what’s necessary (the registration/name), keeping references to Qwen3_5MoeForCausalLM, Qwen3NextForCausalLM, __init__, load_weights, and post_load_weights to locate the code to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.py`:
- Around line 1-13: This file is missing the required NVIDIA Apache-2.0 header
and uses symbol-level imports that violate the repo namespace-import rule; add
the standard NVIDIA Apache-2.0 license header with the current year at the top
of tensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.py, then
convert the symbol imports to module-scoped imports (e.g., import
tensorrt_llm._torch.models.checkpoints.hf.qwen3_next_weight_mapper as
qwen3_next_weight_mapper and import tensorrt_llm._torch.models.modeling_utils as
modeling_utils and import tensorrt_llm._torch.modules.fused_moe.interface as
fused_moe_interface) and update usages of Qwen3NextHfWeightMapper,
register_mapper, MoE, and MoEWeightLoadingMode to reference the module-qualified
names (qwen3_next_weight_mapper.Qwen3NextHfWeightMapper,
modeling_utils.register_mapper, fused_moe_interface.MoE,
fused_moe_interface.MoEWeightLoadingMode).
In `@tensorrt_llm/_torch/models/modeling_qwen3_5.py`:
- Around line 1-8: Add the required NVIDIA Apache-2.0 license header (with the
current year) at the top of the file, then convert all symbol-level imports to
module-scoped imports: import the modules that contain Qwen3NextConfig,
BaseWeightMapper, ModelConfig, Qwen3NextModel, SpecDecOneEngineForCausalLM, and
register_auto_model instead of importing those symbols directly, and update any
usages in this file to reference the symbols via their module namespace (e.g.,
module.Qwen3NextConfig, module.BaseWeightMapper, module.ModelConfig,
module.Qwen3NextModel, module.SpecDecOneEngineForCausalLM,
module.register_auto_model).
In `@tensorrt_llm/_torch/modules/fused_moe/quantization.py`:
- Around line 1113-1118: Avoid caching CUDA-resident presmoothed tensors in the
global weights dict: resmooth_to_fp8_e8m0 (referenced here) moves outputs to
CUDA, and assigning them into weights[weight_name]/weights[name] holds full
experts on GPU until super().load_weights() completes and can OOM; instead
perform resmooth_to_fp8_e8m0 lazily inside the per-expert load path (where each
expert shard is copied), copy the resulting shard out to the target storage,
then immediately discard or move the temporary presmoothed tensors back to CPU
(or do not reassign them into weights) so no full expert remains resident on
CUDA between load steps.
- Around line 1102-1118: The SM100f branch is performing a full resmooth via
resmooth_to_fp8_e8m0, causing a second resmooth later when
resmooth_and_transform_fp8_scale runs; change the flow so that when is_sm_100f()
triggers and you call resmooth_to_fp8_e8m0 (in the block using
module.initial_local_expert_ids and need_load_shared_weights), you mark that the
weights were already resmoothed (e.g., set a boolean flag on the module or add a
sentinel entry to the weights dict) and then update the later post_load_weights
step that calls resmooth_and_transform_fp8_scale to check that marker and only
run the transform portion (not the resmooth) when the marker is present;
reference the functions is_sm_100f(), need_load_shared_weights(),
resmooth_to_fp8_e8m0(), resmooth_and_transform_fp8_scale(), and the
post_load_weights flow to implement the conditional skip.
In `@tensorrt_llm/_torch/pyexecutor/config_utils.py`:
- Around line 241-255: The compat branch for Qwen3.5 only matches model_type ==
"qwen3_5_moe" or architectures[0] == "Qwen3_5MoeForCausalLM", but downstream
expects flattened text configs (e.g. "qwen3_5_moe_text") and the wrapper variant
"Qwen3_5MoeForConditionalGeneration"; update the conditional in the factory that
chooses transformers.Qwen3NextConfig.from_dict(...) to also match model_type ==
"qwen3_5_moe_text" and the wrapper architecture name (e.g.
"Qwen3_5MoeForConditionalGeneration"), so that
_Qwen35ConfigCompat.normalize(config_dict) is applied for those IDs as well
before falling back to AutoConfig/other _CONFIG_REGISTRY paths (refer to
model_type, architectures, _Qwen35ConfigCompat.normalize, and
Qwen3NextConfig.from_dict).
In `@tensorrt_llm/_torch/pyexecutor/py_executor_creator.py`:
- Around line 36-39: The file currently imports is_mla twice (once from ._util
and again from .config_utils), causing a duplicate import (Flake8 F811); remove
the redundant is_mla entry from the second import line that lists is_mla,
is_nemotron_hybrid, is_qwen3_hybrid so that is_mla remains only imported from
._util while preserving imports for is_nemotron_hybrid and is_qwen3_hybrid.
- Around line 525-529: When you disable kv_cache_config.enable_block_reuse for
hybrid models in the conditional that checks is_nemotron_hybrid(config) or
is_qwen3_hybrid(config), also update the already-constructed
AttentionRuntimeFeatures passed into engines by setting its cache_reuse to match
the new value; specifically, after setting kv_cache_config.enable_block_reuse =
False, iterate the created model engines (or the single model_engine) and assign
model_engine.attn_runtime_features.cache_reuse = False so runtime features are
in sync with kv_cache_config and avoid stale behavior.
In `@tensorrt_llm/evaluate/lm_eval.py`:
- Around line 277-282: The chat-template kwargs are being filtered against
self.llm.tokenizer before the actual template/processor is chosen, which can
drop processor-specific (e.g., Qwen) args; update the code so
get_chat_template_kwargs is applied against the resolved template/processor (the
final template returned/used by inputs.utils.apply_chat_template) instead of
self.llm.tokenizer, or modify get_chat_template_kwargs to accept the resolved
template/processor and perform filtering there; locate uses around
apply_chat_template and get_chat_template_kwargs and move or extend the helper
so multimodal/processor-owned kwargs are preserved.
---
Nitpick comments:
In `@tensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.py`:
- Around line 46-47: The class-level constant _SUPPORTED_SUFFIXES is defined as
a mutable set which can be accidentally changed at runtime; change its
definition to an immutable frozenset to preserve constant semantics and avoid
shared-mutation issues (replace the set literal in _SUPPORTED_SUFFIXES with
frozenset({"weight","bias","weight_scale_inv"}) so code referencing
_SUPPORTED_SUFFIXES continues to work).
In `@tensorrt_llm/_torch/models/modeling_qwen3_5.py`:
- Around line 11-61: The Qwen3_5MoeForCausalLM class duplicates logic from
Qwen3NextForCausalLM; replace the copy with reuse by making
Qwen3_5MoeForCausalLM a thin subclass or delegating to Qwen3NextForCausalLM so
__init__, load_weights, and post_load_weights remain shared; specifically, have
Qwen3_5MoeForCausalLM inherit from Qwen3NextForCausalLM (or call its methods)
and only override what’s necessary (the registration/name), keeping references
to Qwen3_5MoeForCausalLM, Qwen3NextForCausalLM, __init__, load_weights, and
post_load_weights to locate the code to change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 66b42b7e-9154-420f-8255-50258441c259
📒 Files selected for processing (13)
tensorrt_llm/_torch/model_config.pytensorrt_llm/_torch/models/__init__.pytensorrt_llm/_torch/models/checkpoints/__init__.pytensorrt_llm/_torch/models/checkpoints/hf/qwen3_5_weight_mapper.pytensorrt_llm/_torch/models/modeling_qwen3_5.pytensorrt_llm/_torch/models/modeling_qwen3_next.pytensorrt_llm/_torch/modules/fused_moe/fused_moe_trtllm_gen.pytensorrt_llm/_torch/modules/fused_moe/quantization.pytensorrt_llm/_torch/pyexecutor/_util.pytensorrt_llm/_torch/pyexecutor/config_utils.pytensorrt_llm/_torch/pyexecutor/py_executor_creator.pytensorrt_llm/evaluate/interface.pytensorrt_llm/evaluate/lm_eval.py
💤 Files with no reviewable changes (1)
- tensorrt_llm/_torch/modules/fused_moe/fused_moe_trtllm_gen.py
ba5480a to
14567d2
Compare
|
/bot run |
|
PR_Github #39178 [ run ] triggered by Bot. Commit: |
|
PR_Github #39178 [ run ] completed with state
|
291d846 to
f9608c5
Compare
|
/bot run |
|
PR_Github #39271 [ run ] triggered by Bot. Commit: |
|
PR_Github #39271 [ run ] completed with state
|
Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
…review feedback Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
f9608c5 to
39706df
Compare
|
/bot run |
|
PR_Github #39347 [ run ] triggered by Bot. Commit: |
|
The PR replaces Qwen3.5 models will:
Suggested fix: Update |
Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
Added fix in 19ef3b6 |
Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
579fc4d to
344ffbb
Compare
|
PR_Github #39542 [ run ] completed with state |
|
/bot run |
|
PR_Github #39602 [ run ] triggered by Bot. Commit: |
|
PR_Github #39602 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #39616 [ run ] triggered by Bot. Commit: |
|
PR_Github #39616 [ run ] completed with state
|
|
/bot run |
|
PR_Github #39662 [ run ] triggered by Bot. Commit: |
|
PR_Github #39662 [ run ] completed with state |
_get_model_kv_cache_manager_cls() was added by PR NVIDIA#12242 but bypassed the V2→V1 fallback logic (beam width > 1, kv_connector, etc.). Move the fallback into that method so all callers get consistent behavior. Signed-off-by: Yi Zhang <187001205+yizhang-nv@users.noreply.github.com>
_get_model_kv_cache_manager_cls() was added by PR NVIDIA#12242 but bypassed the V2→V1 fallback logic (beam width > 1, kv_connector, etc.). Move the fallback into that method so all callers get consistent behavior. Signed-off-by: Yi Zhang <187001205+yizhang-nv@users.noreply.github.com>
_get_model_kv_cache_manager_cls() was added by PR NVIDIA#12242 but bypassed the V2→V1 fallback logic (beam width > 1, kv_connector, etc.). Move the fallback into that method so all callers get consistent behavior. Signed-off-by: Yi Zhang <187001205+yizhang-nv@users.noreply.github.com>
…/FP8) (NVIDIA#12242) Signed-off-by: Anthony Chang <27950904+rosenrodt@users.noreply.github.com>
_get_model_kv_cache_manager_cls() was added by PR NVIDIA#12242 but bypassed the V2→V1 fallback logic (beam width > 1, kv_connector, etc.). Move the fallback into that method so all callers get consistent behavior. Signed-off-by: Yi Zhang <187001205+yizhang-nv@users.noreply.github.com>
_get_model_kv_cache_manager_cls() was added by PR NVIDIA#12242 but bypassed the V2→V1 fallback logic (beam width > 1, kv_connector, etc.). Move the fallback into that method so all callers get consistent behavior. Signed-off-by: Yi Zhang <187001205+yizhang-nv@users.noreply.github.com>
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Description
This adds a thin wrapper for Qwen3.5 text model over the Qwen3-next implementation in order to reuse as much components as possible.
Verified on B200 BF16 and FP8 (deepgemm MoE backend)
Test Coverage
PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.