Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/models/supported-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Note: Support for other models may vary. Features marked "N/A" are not applicabl
| `Step3p7ForConditionalGeneration` | Yes | Yes | Untested | Yes | Untested | Untested | Untested | Untested | L + I |
| `MiniMaxM3SparseForConditionalGeneration` [^12] | Yes | Yes | Untested | Yes | Untested | No | Untested | Untested | L + I + V |
| `Cosmos3ForConditionalGeneration` [^13] | Yes | Yes | Yes | Yes | Yes | Yes | Untested | Untested | L + I + V |
| `Qwen3_5MoeForConditionalGeneration` | Yes | Yes | Untested | Yes | Yes | No | Untested | Yes | L + I + V |

Note:
- L: Language
Expand Down
4 changes: 3 additions & 1 deletion tensorrt_llm/_torch/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
Qwen2ForRewardModel)
from .modeling_qwen2vl import Qwen2_5_VLModel, Qwen2VLModel
from .modeling_qwen3 import Qwen3ForCausalLM
from .modeling_qwen3_5 import Qwen3_5ForCausalLM, Qwen3_5MoeForCausalLM
from .modeling_qwen3_5 import (Qwen3_5ForCausalLM, Qwen3_5MoeForCausalLM,
Qwen3_5MoeVLModel)
from .modeling_qwen3_moe import Qwen3MoeForCausalLM
from .modeling_qwen3_next import Qwen3NextForCausalLM
from .modeling_qwen3vl import Qwen3VLModel
Expand Down Expand Up @@ -108,6 +109,7 @@
"Qwen3_5ForCausalLM",
"Qwen3_5MoeForCausalLM",
"QwenImageBenchModel",
"Qwen3_5MoeVLModel",
"Qwen3NextForCausalLM",
"Qwen3MoeVLModel",
"GptOssForCausalLM",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@register_mapper("HF", "Qwen3_5ForConditionalGeneration")
@register_mapper("HF", "QwenImageBenchForConditionalGeneration")
@register_mapper("HF", "Qwen3_5MoeForCausalLM")
@register_mapper("HF", "Qwen3_5MoeForConditionalGeneration")
@register_mapper("HF", "Qwen3_5ForCausalLM")
class Qwen3_5MoeHfWeightMapper(Qwen3NextHfWeightMapper):
"""Weight mapper for Qwen3.5 MoE text checkpoints.
Expand Down
348 changes: 346 additions & 2 deletions tensorrt_llm/_torch/models/modeling_qwen3_5.py

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions tensorrt_llm/_torch/models/modeling_qwen3_next.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,9 +976,18 @@ def get_model_defaults(cls, llm_args: 'TorchLlmArgs') -> dict:
# is supported for Mamba/SSM-based models
return {"kv_cache_config": {"enable_block_reuse": False}}

def load_weights(self, weights: dict, weight_mapper: BaseWeightMapper):
def load_weights(self,
weights: dict,
weight_mapper: BaseWeightMapper,
params_map: Optional[Dict[str, str]] = None,
allow_partial_loading: bool = False):
new_weights = weight_mapper.preprocess_weights(weights)
super().load_weights(new_weights, weight_mapper)
super().load_weights(
new_weights,
weight_mapper=weight_mapper,
params_map=params_map,
allow_partial_loading=allow_partial_loading,
)

def setup_aliases(self) -> None:
for idx, layer in enumerate(
Expand Down
21 changes: 19 additions & 2 deletions tensorrt_llm/_torch/models/modeling_qwen3vl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,7 @@ def __init__(
"Qwen3VLMoeForConditionalGeneration": "Qwen3MoeForCausalLM",
"QwenImageBenchForConditionalGeneration": "Qwen3_5ForCausalLM",
"Cosmos3ForConditionalGeneration": "Qwen3ForCausalLM",
"Qwen3_5MoeForConditionalGeneration": "Qwen3_5MoeForCausalLM",
}
llm_arch = vlm_to_llm_arch.get(self.original_arch)
if llm_arch is None:
Expand Down Expand Up @@ -1238,9 +1239,12 @@ def init_mrope_embedding(self, model_config: ModelConfig[PretrainedConfig]):
mrope_section=config.rope_scaling.get("mrope_section", None),
mrope_interleaved=config.rope_scaling.get("mrope_interleaved", False),
)
head_dim = getattr(config, "head_dim", None)
if not isinstance(head_dim, int):
head_dim = config.hidden_size // config.num_attention_heads
self.rotary_emb = MRotaryEmbedding(
pos_embd_params.rope,
head_dim=config.hidden_size // config.num_attention_heads,
head_dim=head_dim,
is_neox=pos_embd_params.is_neox,
mrope_section=pos_embd_params.mrope_section,
mrope_interleaved=pos_embd_params.mrope_interleaved,
Expand Down Expand Up @@ -1370,6 +1374,13 @@ def forward(
)
deepstack_embeds = list(deepstack_buffer.unbind(0))

# Preserve the pre-fusion token IDs. `fuse_input_embeds` collapses
# input_ids -> None when MM embeddings are fused in, but spec
# decoding (MTP / Eagle) still needs the original prompt token
# IDs for drafter context preparation; pass them through as a
# dedicated kwarg consumed by `SpecDecOneEngineForCausalLM.forward`.
orig_input_ids = input_ids

input_ids, input_embeds = fuse_input_embeds(
self.llm.model.embed_tokens,
input_ids,
Expand All @@ -1386,8 +1397,14 @@ def forward(
return_context_logits=return_context_logits,
deepstack_embeds=deepstack_embeds,
mrope_config=mrope_config,
spec_metadata=kwargs.get("spec_metadata"),
resource_manager=kwargs.get("resource_manager"),
orig_input_ids=orig_input_ids,
)
logger.debug(f"output shape: {output_prob.shape}")
# Spec-decoding (MTP / Eagle) returns a dict (accepted tokens,
# draft tokens, logits); plain forward returns a tensor.
if hasattr(output_prob, "shape"):
logger.debug(f"output shape: {output_prob.shape}")
return output_prob

def _get_requests_with_mm_data(self, multimodal_params):
Expand Down
11 changes: 8 additions & 3 deletions tensorrt_llm/_torch/models/modeling_speculative.py
Original file line number Diff line number Diff line change
Expand Up @@ -1794,12 +1794,17 @@ def forward(
True,
)

spec_input_ids = input_ids
# VLM wrappers (e.g. Qwen3VLModelBase) replace input_ids with
# fused inputs_embeds; fall back to the pre-fusion token IDs
# they forward via `orig_input_ids` so MTP / Eagle drafters
# can still access the prompt tokens.
spec_input_ids = input_ids if input_ids is not None else kwargs.get(
"orig_input_ids")
spec_position_ids = position_ids
if attn_metadata.padded_num_tokens is not None:
if input_ids is not None:
if spec_input_ids is not None:
# Slice along the first dimension
spec_input_ids = input_ids[:attn_metadata.num_tokens]
spec_input_ids = spec_input_ids[:attn_metadata.num_tokens]
if position_ids is not None:
spec_position_ids = _slice_spec_position_ids(
position_ids, attn_metadata.num_tokens)
Expand Down
8 changes: 8 additions & 0 deletions tensorrt_llm/_torch/modules/fused_moe/moe_load_balancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,14 @@ def __exit__(self, exc_type, exc_val, exc_tb):
'Qwen2MoeForCausalLM',
'Qwen3MoeForCausalLM',
'Qwen3_5MoeForCausalLM',
# Composite MoE VLMs are routed through their top-level
# ConditionalGeneration arch, so EPLB setup must recognize those here (the
# text-only arches above never match for the VLM wrapper). The inner MoE LM
# (Qwen3MoeForCausalLM / Qwen3_5MoeForCausalLM) inherits the setup() state
# via the wrapper's deepcopy of model_config. Same pattern as
# Llama4ForConditionalGeneration.
'Qwen3VLMoeForConditionalGeneration',
'Qwen3_5MoeForConditionalGeneration',
]


Expand Down
Loading
Loading