Compare commits

...
Author SHA1 Message Date
NickLucche e0dd86f6c3 deprecate timeout
Signed-off-by: NickLucche <nlucches@redhat.com>
2026-04-28 09:03:27 +02:00
NickLucche 01f19dee7f deprecate timeout
Signed-off-by: NickLucche <nlucches@redhat.com>
2026-04-28 09:01:01 +02:00
Moritz SanftandGitHub 2c06cf3486 [Bugfix] use served_model_name for multimodal error message (#41003)
Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
2026-04-27 08:22:35 -07:00
Harry MellorGitHubmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
e6f710a87f Deprecate support for Transformers v4 (#40389)
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2026-04-27 08:19:57 -07:00
c245d35ff4 [Model] Add MiMo-V2.5 support (#40967)
Signed-off-by: Jee Jee Li <pandaleefree@gmail.com>
Signed-off-by: Isotr0py <mozf@mail2.sysu.edu.cn>
Signed-off-by: Isotr0py <Isotr0py@outlook.com>
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
Co-authored-by: Jee Jee Li <pandaleefree@gmail.com>
Co-authored-by: zjy0516 <riverclouds.zhu@qq.com>
Co-authored-by: zjy0516 <zhujiangyun@inferact.ai>
Co-authored-by: yasong <yasong.wang@inferact.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 13:26:51 +00:00
Xiaoshuang WangGitHubmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
f8ac0c7cf0 [Bugfix] Fix k_norm weight sharding in MiniMaxM2Attention when total_num_kv_heads < tp_size (#38191)
Signed-off-by: wxsIcey <1790571317@qq.com>
Signed-off-by: Icey <1790571317@qq.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2026-04-27 05:57:13 -07:00
22 changed files with 4813 additions and 18 deletions
+2
View File
@@ -439,6 +439,7 @@ th {
| `Mamba2ForCausalLM` | Mamba2 | `mistralai/Mamba-Codestral-7B-v0.1`, etc. | | ✅︎ |
| `MiMoForCausalLM` | MiMo | `XiaomiMiMo/MiMo-7B-RL`, etc. | ✅︎ | ✅︎ |
| `MiMoV2FlashForCausalLM` | MiMoV2Flash | `XiaomiMiMo/MiMo-V2-Flash`, etc. | | ✅︎ |
| `MiMoV2ProForCausalLM` | MiMoV2Pro | `XiaomiMiMo/MiMo-V2.5-Pro`, etc. | | ✅︎ |
| `MiniCPMForCausalLM` | MiniCPM | `openbmb/MiniCPM-2B-sft-bf16`, `openbmb/MiniCPM-2B-dpo-bf16`, `openbmb/MiniCPM-S-1B-sft`, etc. | ✅︎ | ✅︎ |
| `MiniCPM3ForCausalLM` | MiniCPM3 | `openbmb/MiniCPM3-4B`, etc. | ✅︎ | ✅︎ |
| `MiniMaxForCausalLM` | MiniMax-Text | `MiniMaxAI/MiniMax-Text-01-hf`, etc. | | |
@@ -590,6 +591,7 @@ These models primarily accept the [`LLM.generate`](./generative_models.md#llmgen
| `LlavaNextVideoForConditionalGeneration` | LLaVA-NeXT-Video | T + V | `llava-hf/LLaVA-NeXT-Video-7B-hf`, etc. | | ✅︎ |
| `LlavaOnevisionForConditionalGeneration` | LLaVA-Onevision | T + I<sup>+</sup> + V<sup>+</sup> | `llava-hf/llava-onevision-qwen2-7b-ov-hf`, `llava-hf/llava-onevision-qwen2-0.5b-ov-hf`, etc. | | ✅︎ |
| `MiDashengLMModel` | MiDashengLM | T + A<sup>+</sup> | `mispeech/midashenglm-7b` | | ✅︎ |
| `MiMoV2OmniForCausalLM` | MiMo-V2.5-Omni | T + I<sup>E+</sup> + V<sup>E+</sup> + A<sup>+</sup> | `XiaomiMiMo/MiMo-V2.5-Omni` | | ✅︎ |
| `MiniCPMO` | MiniCPM-O | T + I<sup>E+</sup> + V<sup>E+</sup> + A<sup>E+</sup> | `openbmb/MiniCPM-o-2_6`, etc. | ✅︎ | ✅︎ |
| `MiniCPMV` | MiniCPM-V | T + I<sup>E+</sup> + V<sup>E+</sup> | `openbmb/MiniCPM-V-2` (see note), `openbmb/MiniCPM-Llama3-V-2_5`, `openbmb/MiniCPM-V-2_6`, `openbmb/MiniCPM-V-4`, `openbmb/MiniCPM-V-4_5`, etc. | ✅︎ | |
| `MiniMaxVL01ForConditionalGeneration` | MiniMax-VL | T + I<sup>E+</sup> | `MiniMaxAI/MiniMax-VL-01`, etc. | | ✅︎ |
+18
View File
@@ -594,6 +594,9 @@ _TEXT_GENERATION_EXAMPLE_MODELS = {
"MiMoV2FlashForCausalLM": _HfExamplesInfo(
"XiaomiMiMo/MiMo-V2-Flash", trust_remote_code=True
),
"MiMoV2ProForCausalLM": _HfExamplesInfo(
"XiaomiMiMo/MiMo-V2.5-Pro", trust_remote_code=True, is_available_online=False
),
"Dots1ForCausalLM": _HfExamplesInfo("rednote-hilab/dots.llm1.inst"),
}
@@ -1069,6 +1072,9 @@ _MULTIMODAL_EXAMPLE_MODELS = {
"MiDashengLMModel": _HfExamplesInfo(
"mispeech/midashenglm-7b", trust_remote_code=True
),
"MiMoV2OmniForCausalLM": _HfExamplesInfo(
"XiaomiMiMo/MiMo-V2.5-Omni", trust_remote_code=True, is_available_online=False
),
"MiniCPMO": _HfExamplesInfo(
"openbmb/MiniCPM-o-2_6",
trust_remote_code=True,
@@ -1552,6 +1558,18 @@ _SPECULATIVE_DECODING_EXAMPLE_MODELS = {
trust_remote_code=True,
speculative_model="XiaomiMiMo/MiMo-7B-RL",
),
"MiMoV2MTPModel": _HfExamplesInfo(
"XiaomiMiMo/MiMo-V2.5-Pro",
trust_remote_code=True,
speculative_model="XiaomiMiMo/MiMo-V2.5-Pro",
is_available_online=False,
),
"MiMoV2OmniMTPModel": _HfExamplesInfo(
"XiaomiMiMo/MiMo-V2.5-Omni",
trust_remote_code=True,
speculative_model="XiaomiMiMo/MiMo-V2.5-Omni",
is_available_online=False,
),
"NemotronHMTPModel": _HfExamplesInfo(
"nvidia/Nemotron-Super-Placeholder",
speculative_model="nvidia/Nemotron-Super-Placeholder",
+16
View File
@@ -5,6 +5,8 @@ Unit tests for MultiModalRegistry.supports_multimodal_inputs and
Qwen2.5-VL visual component loading behavior.
"""
from types import SimpleNamespace
import pytest
from vllm.multimodal import MULTIMODAL_REGISTRY
@@ -32,3 +34,17 @@ def test_supports_multimodal_inputs(model_id, limit_mm_per_prompt, expected):
limit_mm_per_prompt=limit_mm_per_prompt,
)
assert MULTIMODAL_REGISTRY.supports_multimodal_inputs(ctx.model_config) is expected
def test_create_processor_error_uses_served_model_name():
model_config = SimpleNamespace(
is_multimodal_model=False,
model="/path/to/model/weights",
served_model_name="friendly-model-name",
)
with pytest.raises(
ValueError,
match="friendly-model-name is not a multimodal model",
):
MULTIMODAL_REGISTRY.create_processor(model_config)
+1 -1
View File
@@ -521,6 +521,7 @@ class ModelConfig:
if dict_overrides:
self._apply_dict_overrides(hf_config, dict_overrides)
self.hf_text_config = get_hf_text_config(self.hf_config)
self.model_arch_config = self.get_model_arch_config()
self.attention_chunk_size = getattr(
self.hf_text_config, "attention_chunk_size", None
)
@@ -528,7 +529,6 @@ class ModelConfig:
self.hf_image_processor_config = get_hf_image_processor_config(
self.model, hf_token=self.hf_token, revision=self.revision
)
self.model_arch_config = self.get_model_arch_config()
architectures = self.architectures
registry = self.registry
+43
View File
@@ -34,6 +34,7 @@ logger = init_logger(__name__)
MTPModelTypes = Literal[
"deepseek_mtp",
"mimo_mtp",
"mimo_v2_mtp",
"glm4_moe_mtp",
"glm4_moe_lite_mtp",
"glm_ocr_mtp",
@@ -332,6 +333,48 @@ class SpeculativeConfig:
}
)
if (arch := hf_config.architectures[0]) in (
"MiMoV2ProForCausalLM",
"MiMoV2OmniForCausalLM",
):
from vllm.model_executor.models.mimo_v2_mtp import (
_MIMO_V2_PRO_NUM_MTP_LAYERS,
)
mtp_arch_maps = {
"MiMoV2ProForCausalLM": "MiMoV2MTPModel",
"MiMoV2OmniForCausalLM": "MiMoV2OmniMTPModel",
}
hf_config.model_type = "mimo_v2_mtp"
# vLLM currently supports only the first MiMo-V2 MTP layer.
n_predict = _MIMO_V2_PRO_NUM_MTP_LAYERS
hf_config.update(
{
"num_hidden_layers": 0,
"n_predict": n_predict,
"num_nextn_predict_layers": n_predict,
"architectures": [mtp_arch_maps[arch]],
}
)
if hf_config.architectures[0] == "MiMoV2FlashForCausalLM":
from vllm.model_executor.models.mimo_v2_mtp import (
_MIMO_V2_FLASH_NUM_MTP_LAYERS,
)
hf_config.model_type = "mimo_v2_mtp"
# vLLM currently supports only the first MiMo-V2 MTP layer.
n_predict = _MIMO_V2_FLASH_NUM_MTP_LAYERS
hf_config.update(
{
"num_hidden_layers": 0,
"n_predict": n_predict,
"num_nextn_predict_layers": n_predict,
"architectures": ["MiMoV2MTPModel"],
}
)
if hf_config.architectures[0] == "Glm4MoeForCausalLM":
hf_config.model_type = "glm4_moe_mtp"
n_predict = getattr(hf_config, "num_nextn_predict_layers", None)
@@ -2,6 +2,7 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Scheduler-side logic for the NIXL connector."""
import os
import threading
import time
from typing import TYPE_CHECKING, Any
@@ -88,6 +89,12 @@ class NixlConnectorScheduler:
if vllm_config.scheduler_config.disable_hybrid_kv_cache_manager:
logger.info("Hybrid Memory Allocator is enabled with NIXL")
if os.environ.get("VLLM_NIXL_ABORT_REQUEST_TIMEOUT") is not None:
logger.warning(
"VLLM_NIXL_ABORT_REQUEST_TIMEOUT is deprecated and will be "
"removed in release 0.22.0."
)
# Background thread for handling new handshake requests.
self._nixl_handshake_listener_t: threading.Thread | None = None
self._stop_event = threading.Event()
+6 -1
View File
@@ -1120,7 +1120,12 @@ class QKVParallelLinear(ColumnParallelLinear):
# Special case for Quantization.
# If quantized, we need to adjust the offset and size to account
# for the packing.
if (
if isinstance(param, BlockQuantScaleParameter):
weight_block_size = getattr(self, "weight_block_size", None)
shard_size, shard_offset = adjust_block_scale_shard(
weight_block_size, shard_size, shard_offset
)
elif (
isinstance(param, (PackedColumnParameter, PackedvLLMParameter))
and param.packed_dim == param.output_dim
):
@@ -3,6 +3,7 @@
import math
from collections.abc import Callable
from functools import partial
import torch
import torch.nn.functional as F
@@ -33,28 +34,46 @@ from vllm.v1.attention.backend import AttentionMetadata
from vllm.v1.attention.backends.linear_attn import LinearAttentionMetadata
@CustomOp.register("minimax_text01_rmsnorm_tp")
class MiniMaxText01RMSNormTP(CustomOp):
name = "MiniMaxText01RMSNormTP"
def __init__(self, hidden_size: int, eps: float = 1e-6) -> None:
def __init__(
self,
hidden_size: int,
eps: float = 1e-6,
*,
weight_shard_world_size: int | None = None,
weight_shard_rank: int | None = None,
) -> None:
super().__init__()
self.tp_world = get_tensor_model_parallel_world_size()
self.tp_rank = get_tensor_model_parallel_rank()
self.weight = nn.Parameter(torch.ones(int(hidden_size / self.tp_world)))
self.weight_shard_world = weight_shard_world_size or self.tp_world
self.weight_shard_rank = (
self.tp_rank if weight_shard_rank is None else weight_shard_rank
)
self.weight.weight_loader = self.weight_loader
self.weight = nn.Parameter(torch.ones(hidden_size // self.weight_shard_world))
self.weight.weight_loader = partial(
self.weight_loader,
shard_world_size=self.weight_shard_world,
shard_rank=self.weight_shard_rank,
)
self.variance_epsilon = eps
@staticmethod
def weight_loader(
param: nn.Parameter,
loaded_weight: torch.Tensor,
shard_world_size: int | None = None,
shard_rank: int | None = None,
) -> None:
tp_world = get_tensor_model_parallel_world_size()
tp_rank = get_tensor_model_parallel_rank()
if shard_world_size is None:
shard_world_size = get_tensor_model_parallel_world_size()
if shard_rank is None:
shard_rank = get_tensor_model_parallel_rank()
shard_size = loaded_weight.shape[0] // tp_world
shard = slice(tp_rank * shard_size, (tp_rank + 1) * shard_size)
shard_size = loaded_weight.shape[0] // shard_world_size
shard = slice(shard_rank * shard_size, (shard_rank + 1) * shard_size)
param.data.copy_(loaded_weight[shard])
def _forward(
File diff suppressed because it is too large Load Diff
@@ -6,6 +6,7 @@ from itertools import islice
import torch
from torch import nn
from vllm.compilation.decorators import support_torch_compile
from vllm.config import (
CacheConfig,
VllmConfig,
@@ -268,7 +269,7 @@ class MiMoV2Attention(nn.Module):
self.total_num_heads * self.v_head_dim,
hidden_size,
bias=False,
quant_config=quant_config,
quant_config=quant_config if "mtp.layers" not in prefix else None,
reduce_results=True,
prefix=f"{prefix}.o_proj",
)
@@ -440,6 +441,7 @@ class MiMoV2FlashDecoderLayer(nn.Module):
return self.config.hybrid_layer_pattern[self.layer_id] == 1
@support_torch_compile
class MiMoV2Model(nn.Module):
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
super().__init__()
@@ -603,7 +605,13 @@ class MiMoV2Model(nn.Module):
if expert_matched:
continue
# Support fused qkv_proj checkpoint (Pro format)
if "qkv_proj" in name:
if name in params_dict:
param = params_dict[name]
loaded_weight = loaded_weight.chunk(tp_size, dim=0)[tp_rank]
default_weight_loader(param, loaded_weight)
continue
stacked_matched = False
for param_name, weight_name, shard_id in stacked_params_mapping:
if weight_name not in name:
@@ -662,6 +670,11 @@ class MiMoV2Model(nn.Module):
class MiMoV2FlashForCausalLM(nn.Module, SupportsPP, MixtureOfExperts):
packed_modules_mapping = {
"qkv_proj": ["q_proj", "k_proj", "v_proj"],
"gate_up_proj": ["gate_proj", "up_proj"],
}
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
super().__init__()
config = vllm_config.model_config.hf_config
@@ -718,3 +731,10 @@ class MiMoV2FlashForCausalLM(nn.Module, SupportsPP, MixtureOfExperts):
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
loader = AutoWeightsLoader(self)
return loader.load_weights(weights)
class MiMoV2ProForCausalLM(MiMoV2FlashForCausalLM):
packed_modules_mapping = {
"qkv_proj": ["qkv_proj"],
"gate_up_proj": ["gate_proj", "up_proj"],
}
+373
View File
@@ -0,0 +1,373 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Inference-only MiMo-V2 MTP (Multi-Token Prediction) draft model.
Supports both MiMo-V2-Pro and MiMo-V2-Flash checkpoints.
Checkpoint weight layout (model.mtp.layers.{idx}.*):
enorm - RMSNorm for token embeddings
hnorm - RMSNorm for previous hidden states
eh_proj - ReplicatedLinear(hidden*2 -> hidden)
input_layernorm - pre-attention RMSNorm
self_attn.* - attention weights; format differs by variant:
Pro: fused qkv_proj [Q;K;V] concatenated
Flash: separate q_proj, k_proj, v_proj
pre_mlp_layernorm - post-attention / pre-MLP RMSNorm
mlp.* - dense MLP (gate_proj / up_proj / down_proj)
final_layernorm - norm applied before logit computation
"""
from collections.abc import Iterable
import torch
import torch.nn as nn
from transformers import PretrainedConfig
from vllm.config import VllmConfig
from vllm.distributed import (
get_tensor_model_parallel_rank,
get_tensor_model_parallel_world_size,
)
from vllm.model_executor.layers.layernorm import RMSNorm
from vllm.model_executor.layers.linear import ReplicatedLinear
from vllm.model_executor.layers.logits_processor import LogitsProcessor
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.layers.vocab_parallel_embedding import (
ParallelLMHead,
VocabParallelEmbedding,
)
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm.sequence import IntermediateTensors
from .interfaces import (
MultiModalEmbeddings,
SupportsMultiModal,
_require_is_multimodal,
)
from .mimo_v2 import MiMoV2Attention, MiMoV2MLP
from .utils import _merge_multimodal_embeddings, maybe_prefix
# MiMo-V2 checkpoints contain multiple MTP layers, but vLLM currently supports
# only the first layer and only one speculative token.
_MIMO_V2_PRO_NUM_MTP_LAYERS = 1
_MIMO_V2_FLASH_NUM_MTP_LAYERS = 1
class MiMoV2MTPLayer(nn.Module):
"""Single MTP predictor layer for MiMo-V2 (Pro and Flash).
Mirrors the single-layer MiMo-V2 nextn reference implementation.
"""
def __init__(
self,
config: PretrainedConfig,
prefix: str,
quant_config: QuantizationConfig | None = None,
) -> None:
super().__init__()
# Predictor head components
self.enorm = RMSNorm(config.hidden_size, eps=config.layernorm_epsilon)
self.hnorm = RMSNorm(config.hidden_size, eps=config.layernorm_epsilon)
self.eh_proj = ReplicatedLinear(
config.hidden_size * 2, config.hidden_size, bias=False
)
# MTP uses the SWA attention configuration
# implementation.
swa_rope_theta = getattr(
config,
"swa_rope_theta",
getattr(config, "rope_theta", 1000000),
)
sliding_window_size = getattr(config, "sliding_window_size", -1)
self.input_layernorm = RMSNorm(config.hidden_size, eps=config.layernorm_epsilon)
self.self_attn = MiMoV2Attention(
hidden_size=config.hidden_size,
num_heads=config.swa_num_attention_heads,
num_kv_heads=config.swa_num_key_value_heads,
head_dim=config.swa_head_dim,
v_head_dim=getattr(config, "swa_v_head_dim", None),
v_scale=getattr(config, "attention_value_scale", None),
sliding_window_size=sliding_window_size,
attention_bias=config.attention_bias,
add_swa_attention_sink_bias=getattr(
config, "add_swa_attention_sink_bias", False
),
layer_id=0,
rope_theta=swa_rope_theta,
max_position_embeddings=getattr(config, "max_position_embeddings", 32768),
quant_config=quant_config,
partial_rotary_factor=getattr(config, "partial_rotary_factor", 1.0),
prefix=f"{prefix}.self_attn",
)
self.pre_mlp_layernorm = RMSNorm(
config.hidden_size, eps=config.layernorm_epsilon
)
self.mlp = MiMoV2MLP(
hidden_size=config.hidden_size,
intermediate_size=config.intermediate_size,
hidden_act=config.hidden_act,
quant_config=quant_config,
prefix=f"{prefix}.mlp",
)
self.final_layernorm = RMSNorm(config.hidden_size, eps=config.layernorm_epsilon)
def forward(
self,
inputs_embeds: torch.Tensor,
positions: torch.Tensor,
previous_hidden_states: torch.Tensor,
) -> torch.Tensor:
# Combine token embedding and previous hidden state
h, _ = self.eh_proj(
torch.cat(
[self.enorm(inputs_embeds), self.hnorm(previous_hidden_states)], dim=-1
)
)
# Transformer block with fused residual norms
residual = h
h = self.input_layernorm(h)
h = self.self_attn(positions=positions, hidden_states=h)
h, residual = self.pre_mlp_layernorm(h, residual)
h = self.mlp(h)
h = h + residual
return self.final_layernorm(h)
class _MiMoV2MTPLayers(nn.Module):
"""Thin wrapper so parameter paths match checkpoint: model.mtp.layers.*"""
def __init__(
self,
config: PretrainedConfig,
num_mtp_layers: int,
quant_config: QuantizationConfig | None,
prefix: str,
) -> None:
super().__init__()
self.layers = nn.ModuleDict(
{
str(i): MiMoV2MTPLayer(
config=config,
prefix=f"{prefix}.{i}",
quant_config=quant_config,
)
for i in range(num_mtp_layers)
}
)
class MiMoV2MultiTokenPredictor(nn.Module):
def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
super().__init__()
config = vllm_config.model_config.hf_config
spec_cfg = vllm_config.speculative_config
assert spec_cfg is not None
if spec_cfg.num_speculative_tokens != 1:
raise ValueError(
"MiMo-V2 MTP in vLLM only supports num_speculative_tokens=1."
)
num_mtp_layers = 1
self.num_mtp_layers = num_mtp_layers
self.embed_tokens = VocabParallelEmbedding(
config.vocab_size,
config.hidden_size,
)
self.mtp = _MiMoV2MTPLayers(
config=config,
num_mtp_layers=num_mtp_layers,
quant_config=vllm_config.quant_config,
prefix=maybe_prefix(prefix, "mtp.layers"),
)
self.logits_processor = LogitsProcessor(config.vocab_size)
def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
return self.embed_tokens(input_ids)
def forward(
self,
input_ids: torch.Tensor,
positions: torch.Tensor,
previous_hidden_states: torch.Tensor,
inputs_embeds: torch.Tensor | None = None,
spec_step_idx: int = 0,
) -> torch.Tensor:
assert spec_step_idx == 0, "MiMo-V2 MTP only supports one speculative token."
if inputs_embeds is None:
inputs_embeds = self.embed_input_ids(input_ids)
return self.mtp.layers[str(spec_step_idx)](
inputs_embeds, positions, previous_hidden_states
)
def compute_logits(
self,
hidden_states: torch.Tensor,
lm_head: ParallelLMHead,
spec_step_idx: int = 0,
) -> torch.Tensor:
assert spec_step_idx == 0, "MiMo-V2 MTP only supports one speculative token."
return self.logits_processor(lm_head, hidden_states)
class MiMoV2MTP(nn.Module):
def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
super().__init__()
self.config = vllm_config.model_config.hf_config
self.model = MiMoV2MultiTokenPredictor(
vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
)
self.lm_head = ParallelLMHead(
self.config.vocab_size,
self.config.hidden_size,
prefix=maybe_prefix(prefix, "lm_head"),
)
def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
return self.model.embed_input_ids(input_ids)
def forward(
self,
input_ids: torch.Tensor | None,
positions: torch.Tensor,
hidden_states: torch.Tensor,
intermediate_tensors: IntermediateTensors | None = None,
inputs_embeds: torch.Tensor | None = None,
spec_step_idx: int = 0,
) -> torch.Tensor:
assert spec_step_idx == 0, "MiMo-V2 MTP only supports one speculative token."
return self.model(
input_ids, positions, hidden_states, inputs_embeds, spec_step_idx
)
def compute_logits(
self,
hidden_states: torch.Tensor,
spec_step_idx: int = 0,
) -> torch.Tensor | None:
assert spec_step_idx == 0, "MiMo-V2 MTP only supports one speculative token."
return self.model.compute_logits(hidden_states, self.lm_head, spec_step_idx)
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
tp_rank = get_tensor_model_parallel_rank()
tp_size = get_tensor_model_parallel_world_size()
stacked_params_mapping = [
("gate_up_proj", "gate_proj", 0),
("gate_up_proj", "up_proj", 1),
# Flash format: separate projections → fused qkv_proj
("qkv_proj", "q_proj", "q"),
("qkv_proj", "k_proj", "k"),
("qkv_proj", "v_proj", "v"),
]
params_dict = dict(self.named_parameters())
loaded_params: set[str] = set()
for name, loaded_weight in weights:
if "rotary_emb.inv_freq" in name:
continue
# Only load MTP-related weights, shared embeddings, and lm_head
if (
"model.mtp" not in name
and "model.embed_tokens" not in name
and not name.startswith("lm_head")
):
continue
# Support fused qkv_proj checkpoint (Pro format).
# The checkpoint is stored pre-sharded for TP=8 as
# [Q_rank0, K_rank0, V_rank0, Q_rank1, ...], so splitting along
# dim 0 with chunk(tp_size) gives each rank its Q+K+V slice for
# both the FP8 weight and the block weight_scale_inv. This matches
# how the main model loads the same layout.
if "qkv_proj" in name:
if name in params_dict:
param = params_dict[name]
loaded_weight = loaded_weight.chunk(tp_size, dim=0)[tp_rank]
default_weight_loader(param, loaded_weight)
loaded_params.add(name)
continue
# gate_proj/up_proj → gate_up_proj stacking (both formats);
# Flash: q_proj/k_proj/v_proj → qkv_proj merging.
stacked_matched = False
for param_name, weight_name, shard_id in stacked_params_mapping:
if weight_name not in name:
continue
name_rewritten = name.replace(weight_name, param_name)
if (
name_rewritten.endswith(".bias")
and name_rewritten not in params_dict
):
continue
if name_rewritten not in params_dict:
continue
param = params_dict[name_rewritten]
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight, shard_id)
loaded_params.add(name_rewritten)
stacked_matched = True
break
if stacked_matched:
continue
if name.endswith(".bias") and name not in params_dict:
continue
if name not in params_dict:
continue
param = params_dict[name]
# attention_sink_bias is head-parallel; slice by tp
if "attention_sink_bias" in name:
total_heads = loaded_weight.shape[0]
heads_per_rank = total_heads // tp_size
loaded_weight = loaded_weight.narrow(
0, tp_rank * heads_per_rank, heads_per_rank
)
weight_loader = getattr(param, "weight_loader", default_weight_loader)
weight_loader(param, loaded_weight)
loaded_params.add(name)
return loaded_params
class MiMoV2OmniMTP(MiMoV2MTP, SupportsMultiModal):
def embed_input_ids(
self,
input_ids: torch.Tensor,
multimodal_embeddings: MultiModalEmbeddings | None = None,
*,
is_multimodal: torch.Tensor | None = None,
) -> torch.Tensor:
inputs_embeds = self._embed_text_input_ids(
input_ids,
self.model.embed_input_ids,
is_multimodal=is_multimodal,
)
if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
return inputs_embeds
is_multimodal = _require_is_multimodal(is_multimodal)
inputs_embeds = _merge_multimodal_embeddings(
inputs_embeds=inputs_embeds,
multimodal_embeddings=multimodal_embeddings,
is_multimodal=is_multimodal,
)
return inputs_embeds
File diff suppressed because it is too large Load Diff
+16 -3
View File
@@ -35,6 +35,7 @@ from vllm.compilation.decorators import support_torch_compile
from vllm.config import CacheConfig, ModelConfig, VllmConfig
from vllm.distributed import (
get_pp_group,
get_tensor_model_parallel_rank,
get_tensor_model_parallel_world_size,
)
from vllm.model_executor.layers.attention import Attention
@@ -220,9 +221,21 @@ class MiniMaxM2Attention(nn.Module):
self.q_norm = MiniMaxText01RMSNormTP(
self.head_dim * self.total_num_heads, eps=rms_norm_eps
)
self.k_norm = MiniMaxText01RMSNormTP(
self.head_dim * self.total_num_kv_heads, eps=rms_norm_eps
)
if self.total_num_kv_heads >= tp_size:
self.k_norm = MiniMaxText01RMSNormTP(
self.head_dim * self.total_num_kv_heads, eps=rms_norm_eps
)
else:
# KV heads are replicated across TP ranks; shard k_norm weight by
# total_num_kv_heads rather than tp_size to avoid incorrect sharding.
num_kv_head_replicas = tp_size // self.total_num_kv_heads
self.k_norm = MiniMaxText01RMSNormTP(
self.head_dim * self.total_num_kv_heads,
eps=rms_norm_eps,
weight_shard_world_size=self.total_num_kv_heads,
weight_shard_rank=get_tensor_model_parallel_rank()
// num_kv_head_replicas,
)
def forward(
self,
+5 -1
View File
@@ -171,7 +171,8 @@ _TEXT_GENERATION_MODELS = {
"MptForCausalLM": ("mpt", "MPTForCausalLM"),
"MPTForCausalLM": ("mpt", "MPTForCausalLM"),
"MiMoForCausalLM": ("mimo", "MiMoForCausalLM"),
"MiMoV2FlashForCausalLM": ("mimo_v2_flash", "MiMoV2FlashForCausalLM"),
"MiMoV2FlashForCausalLM": ("mimo_v2", "MiMoV2FlashForCausalLM"),
"MiMoV2ProForCausalLM": ("mimo_v2", "MiMoV2ProForCausalLM"),
"NemotronForCausalLM": ("nemotron", "NemotronForCausalLM"),
"NemotronHForCausalLM": ("nemotron_h", "NemotronHForCausalLM"),
"NemotronHPuzzleForCausalLM": ("nemotron_h", "NemotronHForCausalLM"),
@@ -468,6 +469,7 @@ _MULTIMODAL_MODELS = {
),
"MantisForConditionalGeneration": ("llava", "MantisForConditionalGeneration"),
"MiDashengLMModel": ("midashenglm", "MiDashengLMModel"),
"MiMoV2OmniForCausalLM": ("mimo_v2_omni", "MiMoV2OmniForCausalLM"),
"MiniMaxVL01ForConditionalGeneration": (
"minimax_vl_01",
"MiniMaxVL01ForConditionalGeneration",
@@ -570,6 +572,8 @@ _MULTIMODAL_MODELS = {
_SPECULATIVE_DECODING_MODELS = {
"ExtractHiddenStatesModel": ("extract_hidden_states", "ExtractHiddenStatesModel"),
"MiMoMTPModel": ("mimo_mtp", "MiMoMTP"),
"MiMoV2MTPModel": ("mimo_v2_mtp", "MiMoV2MTP"),
"MiMoV2OmniMTPModel": ("mimo_v2_mtp", "MiMoV2OmniMTP"),
"EagleLlamaForCausalLM": ("llama_eagle", "EagleLlamaForCausalLM"),
"EagleLlama4ForCausalLM": ("llama4_eagle", "EagleLlama4ForCausalLM"),
"EagleMiniCPMForCausalLM": ("minicpm_eagle", "EagleMiniCPMForCausalLM"),
+2 -1
View File
@@ -207,7 +207,8 @@ class MultiModalRegistry:
Create a multi-modal processor for a specific model and tokenizer.
"""
if not model_config.is_multimodal_model:
raise ValueError(f"{model_config.model} is not a multimodal model")
model_name = model_config.served_model_name or model_config.model
raise ValueError(f"{model_name} is not a multimodal model")
model_cls = self._get_model_cls(model_config)
factories = model_cls._processor_factory
+7
View File
@@ -66,6 +66,13 @@ MISTRAL_CONFIG_NAME = "params.json"
logger = init_logger(__name__)
if Version(version("transformers")) < Version("5.0.0"):
logger.warning(
"Support for Transformers v4 is deprecated. The Transformers v4 codepath will "
"become unmaintained in vLLM v0.22.0 and will be removed in vLLM v0.24.0. "
"Please upgrade to Transformers v5: pip install --upgrade transformers"
)
class LazyConfigDict(dict):
def __getitem__(self, key):
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from transformers import PretrainedConfig
class Mimo_VLVisionConfig(PretrainedConfig):
model_type = "mimovl"
base_config_key = "vision_config"
def __init__(
self,
depth=28,
hidden_size=1280,
hidden_act="silu",
intermediate_size=4608,
num_heads=32,
in_channels=3,
patch_size=16,
spatial_merge_size=2,
temporal_patch_size=2,
tokens_per_second=2,
window_size=128,
out_hidden_size=2048,
fullatt_block_indexes=None,
initializer_range=0.02,
kv_channels=64, # HACK
qk_channels=64,
num_query_groups=4,
num_key_value_heads=8,
vit_window_attn_types=None,
visual_token_window_size=64,
**kwargs,
):
super().__init__(**kwargs)
self.depth = depth
self.hidden_size = hidden_size
self.hidden_act = hidden_act
self.intermediate_size = intermediate_size
self.num_heads = num_heads
# Support GQA: if num_key_value_heads is not provided,
# default to num_heads (MHA)
if num_key_value_heads is None:
num_key_value_heads = num_heads
self.num_key_value_heads = num_key_value_heads
self.in_channels = in_channels
self.patch_size = patch_size
self.spatial_merge_size = spatial_merge_size
self.temporal_patch_size = temporal_patch_size
self.tokens_per_second = tokens_per_second
self.window_size = window_size
self.fullatt_block_indexes = (
fullatt_block_indexes
if fullatt_block_indexes is not None
else [7, 15, 23, 31]
)
self.out_hidden_size = out_hidden_size
self.initializer_range = initializer_range
self.kv_channels = kv_channels
self.qk_channels = qk_channels
self.num_query_groups = num_query_groups
self.vit_window_attn_types = vit_window_attn_types or [-1] * depth
self.visual_token_window_size = visual_token_window_size
@@ -445,6 +445,37 @@ class MimoMTPModelArchConfigConvertor(ModelArchConfigConvertorBase):
return getattr(self.hf_text_config, "num_nextn_predict_layers", 0)
def _strip_mimo_v2_attention_chunk_size(
hf_config: PretrainedConfig, hf_text_config: PretrainedConfig
) -> None:
# MiMo-V2-Flash's config.json sets `attention_chunk_size=128` but the
# architecture does not actually use chunked local attention. Leaving it
# set makes vLLM disable the hybrid KV cache manager
for cfg in (hf_text_config, hf_config):
if cfg is not None and hasattr(cfg, "attention_chunk_size"):
delattr(cfg, "attention_chunk_size")
class MimoV2ModelArchConfigConvertor(ModelArchConfigConvertorBase):
def __init__(self, hf_config: PretrainedConfig, hf_text_config: PretrainedConfig):
super().__init__(hf_config, hf_text_config)
_strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config)
class MimoV2MTPModelArchConfigConvertor(ModelArchConfigConvertorBase):
def __init__(self, hf_config: PretrainedConfig, hf_text_config: PretrainedConfig):
super().__init__(hf_config, hf_text_config)
_strip_mimo_v2_attention_chunk_size(hf_config, hf_text_config)
def get_num_hidden_layers(self) -> int:
n = getattr(self.hf_text_config, "num_nextn_predict_layers", None)
if n is not None:
return n
# Fall back to n_predict set by hf_config_override
n = getattr(self.hf_text_config, "n_predict", None)
return n if n is not None else 0
class GLM4MoeMTPModelArchConfigConvertor(ModelArchConfigConvertorBase):
def get_num_hidden_layers(self) -> int:
return getattr(self.hf_text_config, "num_nextn_predict_layers", 0)
@@ -511,6 +542,10 @@ MODEL_ARCH_CONFIG_CONVERTORS = {
"qwen3_next_mtp": Qwen3NextMTPModelArchConfigConvertor,
"qwen3_5_mtp": Qwen3_5MTPModelArchConfigConvertor,
"mimo_mtp": MimoMTPModelArchConfigConvertor,
"mimo_v2_pro": MimoV2ModelArchConfigConvertor,
"mimo_v2_flash": MimoV2ModelArchConfigConvertor,
"mimo_v2_mtp": MimoV2MTPModelArchConfigConvertor,
"mimo_v2_omni_mtp": MimoV2MTPModelArchConfigConvertor,
"glm4_moe_mtp": GLM4MoeMTPModelArchConfigConvertor,
"glm_ocr_mtp": GLM4MoeMTPModelArchConfigConvertor,
"ernie_mtp": ErnieMTPModelArchConfigConvertor,
@@ -27,6 +27,7 @@ __all__ = [
"IsaacProcessor",
"KimiAudioProcessor",
"KimiK25Processor",
"MiMoOmniProcessor",
"MistralCommonPixtralProcessor",
"MistralCommonVoxtralProcessor",
"NanoNemotronVLProcessor",
@@ -57,6 +58,7 @@ _CLASS_TO_MODULE: dict[str, str] = {
"IsaacProcessor": "vllm.transformers_utils.processors.isaac",
"KimiAudioProcessor": "vllm.transformers_utils.processors.kimi_audio",
"KimiK25Processor": "vllm.transformers_utils.processors.kimi_k25",
"MiMoOmniProcessor": "vllm.transformers_utils.processors.mimo_v2_omni",
"MistralCommonPixtralProcessor": "vllm.transformers_utils.processors.pixtral",
"MistralCommonVoxtralProcessor": "vllm.transformers_utils.processors.voxtral",
"NanoNemotronVLProcessor": "vllm.transformers_utils.processors.nano_nemotron_vl",
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1381,7 +1381,9 @@ def unify_hybrid_kv_cache_specs(kv_cache_spec: dict[str, KVCacheSpec]):
block_size=spec.block_size,
num_kv_heads=spec.num_kv_heads,
head_size=spec.head_size,
head_size_v=spec.head_size_v,
dtype=spec.dtype,
kv_quant_mode=spec.kv_quant_mode,
sliding_window=spec.sliding_window,
page_size_padded=spec.page_size_padded,
)
+1
View File
@@ -1344,6 +1344,7 @@ class SpecDecodeBaseProposer:
"Exaone4_5_ForConditionalGeneration",
"GlmOcrForConditionalGeneration",
"HunYuanVLForConditionalGeneration",
"MiMoV2OmniForCausalLM",
"Qwen2_5_VLForConditionalGeneration",
"Qwen3_5ForConditionalGeneration",
"Qwen3_5MoeForConditionalGeneration",