[Model] Migrate FlexOlmo, Olmo3 and Hunyuan V1/VL to the Transformers modeling backend (#53615)
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Signed-off-by: Cyrus Leung <tlleungac@connect.ust.hk> Co-authored-by: Xianbao QIAN <xianbao.qian@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Cyrus Leung <tlleungac@connect.ust.hk>
Harry Mellor committed
Aug 24, 2026 at 22:39 UTC
48d7132962ef3e2e6982a316013294c0029e3a64
14 files changed
+47
-3028
docs/models/supported_models.md
+5
-5
@@ -384,7 +384,6 @@ th {
384
| `FalconForCausalLM` | Falcon | `tiiuae/falcon-7b`, `tiiuae/falcon-40b`, `tiiuae/falcon-rw-7b`, etc. | | ✅︎ |
385
| `FalconMambaForCausalLM` | FalconMamba | `tiiuae/falcon-mamba-7b`, `tiiuae/falcon-mamba-7b-instruct`, etc. | | ✅︎ |
386
| `FalconH1ForCausalLM` | Falcon-H1 | `tiiuae/Falcon-H1-34B-Base`, `tiiuae/Falcon-H1-34B-Instruct`, etc. | ✅︎ | ✅︎ |
387
-| `FlexOlmoForCausalLM` | FlexOlmo | `allenai/FlexOlmo-7x7B-1T`, `allenai/FlexOlmo-7x7B-1T-RT`, etc. | | ✅︎ |
387
| `GemmaForCausalLM` | Gemma | `google/gemma-2b`, `google/gemma-1.1-2b-it`, etc. | ✅︎ | ✅︎ |
388
| `Gemma2ForCausalLM` | Gemma 2 | `google/gemma-2-9b`, `google/gemma-2-27b`, etc. | ✅︎ | ✅︎ |
389
| `Gemma3ForCausalLM` | Gemma 3 | `google/gemma-3-1b-it`, etc. | ✅︎ | ✅︎ |
@@ -406,8 +405,6 @@ th {
405
| `GraniteMoeSWAForCausalLM` | Granite MoE SWA | `ibm-granite/granite-swash-3b-a600m` | ✅︎ | ✅︎ |
406
| `GraniteSWAForCausalLM` | Granite SWA | `ibm-granite/granite-swash-2b` | ✅︎ | ✅︎ |
407
| `HrmTextForCausalLM` | HRM-Text | `sapientinc/HRM-Text-1B`, etc. | | |
409
-| `HunYuanDenseV1ForCausalLM` | Hunyuan Dense | `tencent/Hunyuan-7B-Instruct` | ✅︎ | ✅︎ |
410
-| `HunYuanMoEV1ForCausalLM` | Hunyuan-A13B | `tencent/Hunyuan-A13B-Instruct`, `tencent/Hunyuan-A13B-Pretrain`, `tencent/Hunyuan-A13B-Instruct-FP8`, etc. | ✅︎ | ✅︎ |
408
| `HYV3ForCausalLM` | HY3 | `tencent/Hy3-preview-Base`, `tencent/Hy3-preview` | ✅︎ | ✅︎ |
409
| `HyperCLOVAXForCausalLM` | HyperCLOVAX-SEED-Think-14B | `naver-hyperclovax/HyperCLOVAX-SEED-Think-14B` | ✅︎ | ✅︎ |
410
| `InternLM2ForCausalLM` | InternLM2 | `internlm/internlm2-7b`, `internlm/internlm2-chat-7b`, etc. | ✅︎ | ✅︎ |
@@ -436,7 +433,6 @@ th {
433
| `MixtralForCausalLM` | Mixtral-8x7B, Mixtral-8x7B-Instruct | `mistralai/Mixtral-8x7B-v0.1`, `mistralai/Mixtral-8x7B-Instruct-v0.1`, `mistral-community/Mixtral-8x22B-v0.1`, etc. | ✅︎ | ✅︎ |
434
| `NemotronForCausalLM` | Nemotron-3, Nemotron-4, Minitron | `nvidia/Minitron-8B-Base`, `mgoin/Nemotron-4-340B-Base-hf-FP8`, etc. | ✅︎ | ✅︎ |
435
| `NemotronHForCausalLM` | Nemotron-H | `nvidia/Nemotron-H-8B-Base-8K`, `nvidia/Nemotron-H-47B-Base-8K`, `nvidia/Nemotron-H-56B-Base-8K`, etc. | ✅︎ | ✅︎ |
439
-| `Olmo3ForCausalLM` | OLMo3 | `allenai/Olmo-3-7B-Instruct`, `allenai/Olmo-3-32B-Think`, etc. | ✅︎ | ✅︎ |
436
| `OlmoHybridForCausalLM` | OLMo Hybrid | `allenai/Olmo-Hybrid-7B` | ✅︎ | ✅︎ |
437
| `OlmoeForCausalLM` | OLMoE | `allenai/OLMoE-1B-7B-0924`, `allenai/OLMoE-1B-7B-0924-Instruct`, etc. | | ✅︎ |
438
| `OPTForCausalLM` | OPT, OPT-IML | `facebook/opt-66b`, `facebook/opt-iml-max-30b`, etc. | ✅︎ | ✅︎ |
@@ -471,9 +467,13 @@ Some models are supported only via the [Transformers modeling backend](#transfor
467
468
| Architecture | Models | Example HF Models | [LoRA](../features/lora.md) | [PP](../serving/parallelism_scaling.md) |
469
| ------------ | ------ | ----------------- | -------------------- | ------------------------- |
470
+| `FlexOlmoForCausalLM` | FlexOlmo | `allenai/FlexOlmo-7x7B-1T`, `allenai/FlexOlmo-7x7B-1T-RT`, etc. | | ✅︎ |
471
| `GPTBigCodeForCausalLM` | StarCoder, SantaCoder, WizardCoder | `bigcode/starcoder`, `bigcode/gpt_bigcode-santacoder`, `WizardLM/WizardCoder-15B-V1.0`, etc. | ✅︎ | |
472
+| `HunYuanDenseV1ForCausalLM` | Hunyuan Dense | `tencent/Hunyuan-7B-Instruct` | ✅︎ | ✅︎ |
473
+| `HunYuanMoEV1ForCausalLM` | Hunyuan-A13B | `tencent/Hunyuan-A13B-Instruct`, `tencent/Hunyuan-A13B-Pretrain`, `tencent/Hunyuan-A13B-Instruct-FP8`, etc. | ✅︎ | ✅︎ |
474
| `OlmoForCausalLM` | OLMo | `allenai/OLMo-1B-hf`, `allenai/OLMo-7B-hf`, etc. | ✅︎ | ✅︎ |
475
| `Olmo2ForCausalLM` | OLMo2 | `allenai/OLMo-2-0425-1B`, etc. | ✅︎ | ✅︎ |
476
+| `Olmo3ForCausalLM` | OLMo3 | `allenai/Olmo-3-7B-Instruct`, `allenai/Olmo-3-32B-Think`, etc. | ✅︎ | ✅︎ |
477
| `SmolLM3ForCausalLM` | SmolLM3 | `HuggingFaceTB/SmolLM3-3B` | ✅︎ | ✅︎ |
478
| `Starcoder2ForCausalLM` | Starcoder2 | `bigcode/starcoder2-3b`, `bigcode/starcoder2-7b`, `bigcode/starcoder2-15b`, etc. | ✅︎ | ✅︎ |
479
| `VaultGemmaForCausalLM` | VaultGemma | `google/vaultgemma-1b` | ✅︎ | ✅︎ |
@@ -543,7 +543,6 @@ These models primarily accept the [`LLM.generate`](./generative_models.md#llmgen
543
| `GraniteSpeechPlusForConditionalGeneration` | Granite Speech Plus | T + A | `ibm-granite/granite-speech-4.1-2b-plus` | ✅︎ | ✅︎ |
544
| `HCXVisionV2ForCausalLM` | HyperCLOVAX-SEED-Think-32B | T + I<sup>+</sup> + V<sup>+</sup> | `naver-hyperclovax/HyperCLOVAX-SEED-Think-32B` | | |
545
| `H2OVLChatModel` | H2OVL | T + I<sup>E+</sup> | `h2oai/h2ovl-mississippi-800m`, `h2oai/h2ovl-mississippi-2b`, etc. | ✅︎ | ✅︎ |
546
-| `HunYuanVLForConditionalGeneration` | HunyuanOCR | T + I<sup>E+</sup> | `tencent/HunyuanOCR`, etc. | ✅︎ | ✅︎ |
546
| `Idefics3ForConditionalGeneration` | Idefics3 | T + I | `HuggingFaceM4/Idefics3-8B-Llama3`, etc. | ✅︎ | |
547
| `IsaacForConditionalGeneration` | Isaac | T + I<sup>+</sup> | `PerceptronAI/Isaac-0.1` | ✅︎ | ✅︎ |
548
| `InternS1ForConditionalGeneration` | Intern-S1 | T + I<sup>E+</sup> + V<sup>E+</sup> | `internlm/Intern-S1`, `internlm/Intern-S1-mini`, etc. | ✅︎ | ✅︎ |
@@ -620,6 +619,7 @@ Some models are supported only via the [Transformers modeling backend](#transfor
619
| Architecture | Models | Inputs | Example HF Models | [LoRA](../features/lora.md) | [PP](../serving/parallelism_scaling.md) |
620
| ------------ | ------ | ------ | ----------------- | --------------------------- | --------------------------------------- |
621
| `Emu3ForConditionalGeneration` | Emu3 | T + I<sup>+</sup> | `BAAI/Emu3-Chat-hf` | ✅︎ | ✅︎ |
622
+| `HunYuanVLForConditionalGeneration` | HunyuanOCR | T + I<sup>E+</sup> | `tencent/HunyuanOCR`, etc. | ✅︎ | ✅︎ |
623
| `VibeVoiceAsrForConditionalGeneration` | VibeVoice-ASR | T + A<sup>+</sup> | `microsoft/VibeVoice-ASR-HF` | ✅︎ | ✅︎ |
624
625
<sup>^</sup> You need to set the architecture name via `--hf-overrides` to match the one in vLLM.</br>
tests/models/registry.py
+1
-4
@@ -329,9 +329,7 @@ _TEXT_GENERATION_EXAMPLE_MODELS = {
329
min_transformers_version="5.9.0",
330
),
331
"HunYuanDenseV1ForCausalLM": _HfExamplesInfo("tencent/Hunyuan-7B-Instruct"),
332
- "HunYuanMoEV1ForCausalLM": _HfExamplesInfo(
333
- "tencent/Hunyuan-A13B-Instruct", trust_remote_code=True
334
- ),
332
+ "HunYuanMoEV1ForCausalLM": _HfExamplesInfo("tencent/Hunyuan-A13B-Instruct"),
333
"HYV3ForCausalLM": _HfExamplesInfo("tencent/Hy3-preview", trust_remote_code=True),
334
"HyperCLOVAXForCausalLM": _HfExamplesInfo(
335
"naver-hyperclovax/HyperCLOVAX-SEED-Think-14B",
@@ -938,7 +936,6 @@ _MULTIMODAL_EXAMPLE_MODELS = {
936
),
937
"HunYuanVLForConditionalGeneration": _HfExamplesInfo(
938
"tencent/HunyuanOCR",
941
- hf_overrides={"num_experts": 0},
939
min_transformers_version="5.13",
940
),
941
"Idefics3ForConditionalGeneration": _HfExamplesInfo(
vllm/model_executor/models/flex_olmo.py
deleted
-156
@@ -1,156 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-# Licensed under the Apache License, Version 2.0 (the "License");
5
-# you may not use this file except in compliance with the License.
6
-# You may obtain a copy of the License at
7
-#
8
-# http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-# Unless required by applicable law or agreed to in writing, software
11
-# distributed under the License is distributed on an "AS IS" BASIS,
12
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
-# See the License for the specific language governing permissions and
14
-# limitations under the License.
15
-"""Inference-only FlexOlmo model compatible with HuggingFace weights."""
16
-
17
-import torch
18
-from torch import nn
19
-
20
-from vllm.config import VllmConfig
21
-from vllm.distributed import get_tensor_model_parallel_world_size
22
-from vllm.logger import init_logger
23
-from vllm.model_executor.layers.fused_moe import (
24
- FusedMoEFactory,
25
-)
26
-from vllm.model_executor.layers.layernorm import RMSNorm
27
-from vllm.model_executor.layers.linear import ReplicatedLinear
28
-from vllm.model_executor.models.olmoe import OlmoeAttention, OlmoeForCausalLM
29
-from vllm.transformers_utils.configs.flex_olmo import FlexOlmoConfig
30
-
31
-logger = init_logger(__name__)
32
-
33
-
34
-class FlexOlmoAttention(OlmoeAttention):
35
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
36
- super().__init__(vllm_config=vllm_config, prefix=prefix)
37
-
38
- hf_config = vllm_config.model_config.hf_config
39
- assert isinstance(hf_config, FlexOlmoConfig)
40
-
41
- self.k_norm = RMSNorm(
42
- self.total_num_kv_heads * self.head_dim, eps=hf_config.rms_norm_eps
43
- )
44
- self.q_norm = RMSNorm(
45
- self.total_num_heads * self.head_dim, eps=hf_config.rms_norm_eps
46
- )
47
-
48
-
49
-class FlexOlmoMoE(nn.Module):
50
- """A tensor-parallel MoE implementation for FlexOlmo that shards each expert
51
- across all ranks.
52
-
53
- Each expert's weights are sharded across all ranks and a fused MoE
54
- kernel is used for the forward pass, and finally we reduce the outputs
55
- across ranks.
56
- """
57
-
58
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
59
- super().__init__()
60
-
61
- hf_config = vllm_config.model_config.hf_config
62
- assert isinstance(hf_config, FlexOlmoConfig)
63
-
64
- tp_size = get_tensor_model_parallel_world_size()
65
-
66
- # Gate always runs at half / full precision for now.
67
- self.gate = ReplicatedLinear(
68
- hf_config.hidden_size,
69
- hf_config.num_experts,
70
- bias=False,
71
- return_bias=False,
72
- quant_config=None,
73
- prefix=f"{prefix}.gate",
74
- )
75
-
76
- self.experts = FusedMoEFactory(
77
- num_experts=hf_config.num_experts,
78
- top_k=hf_config.num_experts_per_tok,
79
- hidden_size=hf_config.hidden_size,
80
- intermediate_size=hf_config.intermediate_size,
81
- renormalize=False,
82
- quant_config=None,
83
- tp_size=tp_size,
84
- prefix=f"{prefix}.experts",
85
- router_logits_dtype=torch.float32,
86
- )
87
-
88
- self.top_k = hf_config.num_experts_per_tok
89
-
90
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
91
- # NOTE: hidden_states can have either 1D or 2D shape.
92
- orig_shape = hidden_states.shape
93
- hidden_dim = hidden_states.shape[-1]
94
- hidden_states = hidden_states.view(-1, hidden_dim)
95
-
96
- # router_logits: (num_tokens, n_experts)
97
- router_logits = self.gate(hidden_states)
98
- # Warning: The experts mutate the hidden state input! This messes up
99
- # basic things like the residual stream.
100
- final_hidden_states = self.experts(
101
- hidden_states=hidden_states.detach().clone(),
102
- router_logits=router_logits.float(),
103
- )
104
-
105
- return final_hidden_states.view(orig_shape)
106
-
107
-
108
-class FlexOlmoDecoderLayer(nn.Module):
109
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
110
- super().__init__()
111
- hf_config = vllm_config.model_config.hf_config
112
- assert isinstance(hf_config, FlexOlmoConfig)
113
-
114
- self.self_attn = FlexOlmoAttention(
115
- vllm_config=vllm_config, prefix=f"{prefix}.self_attn"
116
- )
117
- self.post_attention_layernorm = RMSNorm(
118
- hf_config.hidden_size, eps=hf_config.rms_norm_eps
119
- )
120
- self.post_feedforward_layernorm = RMSNorm(
121
- hf_config.hidden_size, eps=hf_config.rms_norm_eps
122
- )
123
-
124
- self.mlp = FlexOlmoMoE(vllm_config=vllm_config, prefix=f"{prefix}.mlp")
125
-
126
- def forward(
127
- self,
128
- positions: torch.Tensor,
129
- hidden_states: torch.Tensor,
130
- residual: torch.Tensor | None,
131
- ) -> tuple[torch.Tensor, torch.Tensor | None]:
132
- # Attention block.
133
- residual = hidden_states
134
- hidden_states = self.self_attn(positions, hidden_states)
135
- hidden_states = self.post_attention_layernorm(hidden_states)
136
- hidden_states = hidden_states + residual
137
-
138
- # MLP block.
139
- residual = hidden_states
140
- hidden_states = self.mlp(hidden_states)
141
- hidden_states = self.post_feedforward_layernorm(hidden_states)
142
- hidden_states = residual + hidden_states
143
- return hidden_states, None
144
-
145
-
146
-class FlexOlmoForCausalLM(OlmoeForCausalLM):
147
- fall_back_to_pt_during_load = False
148
-
149
- def __init__(
150
- self,
151
- *,
152
- vllm_config: VllmConfig,
153
- prefix: str = "",
154
- layer_type: type[nn.Module] = FlexOlmoDecoderLayer,
155
- ):
156
- super().__init__(vllm_config=vllm_config, prefix=prefix, layer_type=layer_type)
vllm/model_executor/models/hunyuan_v1.py
deleted
-1038
@@ -1,1038 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-# coding=utf-8
5
-# Copyright 2024 The HunYuan team.
6
-# Copyright 2023 The vLLM team.
7
-# Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
8
-#
9
-# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
10
-# and OPT implementations in this library. It has been modified from its
11
-# original forms to accommodate minor architectural differences compared
12
-# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
13
-#
14
-# Licensed under the Apache License, Version 2.0 (the "License");
15
-# you may not use this file except in compliance with the License.
16
-# You may obtain a copy of the License at
17
-#
18
-# http://www.apache.org/licenses/LICENSE-2.0
19
-#
20
-# Unless required by applicable law or agreed to in writing, software
21
-# distributed under the License is distributed on an "AS IS" BASIS,
22
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
-# See the License for the specific language governing permissions and
24
-# limitations under the License.
25
-"""Inference-only HunYuan model compatible with HuggingFace weights."""
26
-
27
-import typing
28
-from collections.abc import Callable, Iterable
29
-from itertools import islice
30
-
31
-import regex as re
32
-import torch
33
-from torch import nn
34
-from transformers import PretrainedConfig
35
-
36
-from vllm.compilation.decorators import support_torch_compile
37
-from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config
38
-from vllm.distributed import (
39
- get_ep_group,
40
- get_pp_group,
41
- get_tensor_model_parallel_world_size,
42
-)
43
-from vllm.model_executor.layers.activation import SiluAndMul
44
-from vllm.model_executor.layers.attention import Attention
45
-from vllm.model_executor.layers.fused_moe import (
46
- FusedMoEFactory,
47
- fused_moe_make_expert_params_mapping,
48
-)
49
-from vllm.model_executor.layers.layernorm import RMSNorm
50
-from vllm.model_executor.layers.linear import (
51
- ColumnParallelLinear,
52
- MergedColumnParallelLinear,
53
- QKVParallelLinear,
54
- ReplicatedLinear,
55
- RowParallelLinear,
56
-)
57
-from vllm.model_executor.layers.logits_processor import LogitsProcessor
58
-from vllm.model_executor.layers.quantization import QuantizationConfig
59
-from vllm.model_executor.layers.rotary_embedding import get_rope
60
-from vllm.model_executor.layers.vocab_parallel_embedding import (
61
- ParallelLMHead,
62
- VocabParallelEmbedding,
63
-)
64
-from vllm.model_executor.model_loader.weight_utils import (
65
- default_weight_loader,
66
- maybe_remap_kv_scale_name,
67
-)
68
-from vllm.sequence import IntermediateTensors
69
-from vllm.v1.attention.backend import AttentionType
70
-
71
-from .interfaces import (
72
- EagleModelMixin,
73
- MixtureOfExperts,
74
- SupportsEagle,
75
- SupportsEagle3,
76
- SupportsLoRA,
77
- SupportsPP,
78
-)
79
-from .utils import (
80
- AutoWeightsLoader,
81
- PPMissingLayer,
82
- is_pp_missing_parameter,
83
- make_layers,
84
- maybe_prefix,
85
-)
86
-
87
-
88
-def _is_moe(config: PretrainedConfig) -> bool:
89
- num_experts = getattr(config, "num_experts", None)
90
- if isinstance(num_experts, int):
91
- return num_experts > 1
92
- if isinstance(num_experts, list) and num_experts:
93
- # Ensure all elements are integers before calling max.
94
- if all(isinstance(e, int) for e in num_experts):
95
- return max(num_experts) > 1
96
- else:
97
- return False
98
- return False
99
-
100
-
101
-def _get_cla_factor(config: PretrainedConfig) -> int:
102
- if not getattr(config, "use_cla", False):
103
- return 1
104
- return getattr(config, "cla_share_factor", 1)
105
-
106
-
107
-class HunYuanMLP(nn.Module):
108
- def __init__(
109
- self,
110
- hidden_size: int,
111
- intermediate_size: int,
112
- hidden_act: str,
113
- quant_config: QuantizationConfig | None = None,
114
- bias: bool = False,
115
- prefix: str = "",
116
- reduce_results: bool = True,
117
- ) -> None:
118
- super().__init__()
119
- self.gate_up_proj = MergedColumnParallelLinear(
120
- input_size=hidden_size,
121
- output_sizes=[intermediate_size] * 2,
122
- bias=bias,
123
- quant_config=quant_config,
124
- prefix=f"{prefix}.gate_up_proj",
125
- )
126
- self.down_proj = RowParallelLinear(
127
- input_size=intermediate_size,
128
- output_size=hidden_size,
129
- bias=bias,
130
- quant_config=quant_config,
131
- prefix=f"{prefix}.down_proj",
132
- reduce_results=reduce_results,
133
- )
134
- if hidden_act != "silu":
135
- raise ValueError(
136
- f"Unsupported activation: {hidden_act}. Only silu is supported for now."
137
- )
138
- self.act_fn = SiluAndMul()
139
-
140
- def forward(self, x):
141
- gate_up, _ = self.gate_up_proj(x)
142
- x = self.act_fn(gate_up)
143
- x, _ = self.down_proj(x)
144
- return x
145
-
146
-
147
-class HunYuanAttention(nn.Module):
148
- def __init__(
149
- self,
150
- config: PretrainedConfig,
151
- hidden_size: int,
152
- num_heads: int,
153
- num_kv_heads: int,
154
- max_position_embeddings: int = 8192,
155
- quant_config: QuantizationConfig | None = None,
156
- bias: bool = False,
157
- cache_config: CacheConfig | None = None,
158
- prefix: str = "",
159
- layer_id: int = -1,
160
- ) -> None:
161
- super().__init__()
162
- self.hidden_size = hidden_size
163
- tp_size = get_tensor_model_parallel_world_size()
164
- self.total_num_heads = num_heads
165
- assert self.total_num_heads % tp_size == 0
166
- self.num_heads = self.total_num_heads // tp_size
167
- self.total_num_kv_heads = num_kv_heads
168
- if self.total_num_kv_heads >= tp_size:
169
- # Number of KV heads is greater than TP size, so we partition
170
- # the KV heads across multiple tensor parallel GPUs.
171
- assert self.total_num_kv_heads % tp_size == 0
172
- else:
173
- # Number of KV heads is less than TP size, so we replicate
174
- # the KV heads across multiple tensor parallel GPUs.
175
- assert tp_size % self.total_num_kv_heads == 0
176
- self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
177
-
178
- if hasattr(config, "head_dim") and config.head_dim:
179
- self.head_dim = config.head_dim
180
- elif hasattr(config, "attention_head_dim"):
181
- self.head_dim = config.attention_head_dim
182
- else:
183
- self.head_dim = self.hidden_size // self.total_num_heads
184
- self.q_size = self.num_heads * self.head_dim
185
- self.kv_size = self.num_kv_heads * self.head_dim
186
- self.scaling = self.head_dim**-0.5
187
- self.max_position_embeddings = max_position_embeddings
188
- self.use_qk_norm = getattr(config, "use_qk_norm", False)
189
- self.layer_id = layer_id
190
-
191
- self.qkv_proj = QKVParallelLinear(
192
- hidden_size=hidden_size,
193
- head_size=self.head_dim,
194
- total_num_heads=self.total_num_heads,
195
- total_num_kv_heads=self.total_num_kv_heads,
196
- bias=bias,
197
- quant_config=quant_config,
198
- prefix=f"{prefix}.qkv_proj",
199
- )
200
-
201
- self.o_proj = RowParallelLinear(
202
- input_size=self.total_num_heads * self.head_dim,
203
- output_size=hidden_size,
204
- bias=bias,
205
- quant_config=quant_config,
206
- prefix=f"{prefix}.o_proj",
207
- )
208
-
209
- self.rotary_emb = get_rope(
210
- self.head_dim,
211
- max_position=max_position_embeddings,
212
- rope_parameters=config.rope_parameters,
213
- is_neox_style=True,
214
- )
215
- self.attn = Attention(
216
- self.num_heads,
217
- self.head_dim,
218
- self.scaling,
219
- num_kv_heads=self.num_kv_heads,
220
- cache_config=cache_config,
221
- quant_config=quant_config,
222
- prefix=f"{prefix}.attn",
223
- )
224
-
225
- if self.use_qk_norm:
226
- self.query_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
227
- self.key_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
228
-
229
- def forward(
230
- self,
231
- positions: torch.Tensor,
232
- hidden_states: torch.Tensor,
233
- kv_states: tuple[torch.Tensor] | None = None,
234
- ) -> torch.Tensor:
235
- qkv, _ = self.qkv_proj(hidden_states)
236
- q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
237
- q, k = self.rotary_emb(positions, q, k)
238
- ori_k = k
239
- if self.use_qk_norm:
240
- q = self.query_layernorm(
241
- q.view(-1, self.num_heads, self.head_dim),
242
- )
243
- k = self.key_layernorm(
244
- k.view(-1, self.num_kv_heads, self.head_dim),
245
- )
246
-
247
- attn_output = self.attn(q, k, v)
248
- # For o_proj
249
- attn_output = attn_output.view(q.shape[0], -1)
250
- output, _ = self.o_proj(attn_output)
251
- return output, (ori_k, v)
252
-
253
-
254
-class HunYuanCrossAttention(nn.Module):
255
- def __init__(
256
- self,
257
- config: PretrainedConfig,
258
- hidden_size: int,
259
- num_heads: int,
260
- num_kv_heads: int,
261
- max_position_embeddings: int = 8192,
262
- quant_config: QuantizationConfig | None = None,
263
- bias: bool = False,
264
- cache_config: CacheConfig | None = None,
265
- prefix: str = "",
266
- layer_id: int = -1,
267
- ) -> None:
268
- super().__init__()
269
- self.hidden_size = hidden_size
270
- tp_size = get_tensor_model_parallel_world_size()
271
- self.total_num_heads = num_heads
272
- assert self.total_num_heads % tp_size == 0
273
- self.num_heads = self.total_num_heads // tp_size
274
- self.total_num_kv_heads = num_kv_heads
275
- if self.total_num_kv_heads >= tp_size:
276
- # Number of KV heads is greater than TP size, so we partition
277
- # the KV heads across multiple tensor parallel GPUs.
278
- assert self.total_num_kv_heads % tp_size == 0
279
- else:
280
- # Number of KV heads is less than TP size, so we replicate
281
- # the KV heads across multiple tensor parallel GPUs.
282
- assert tp_size % self.total_num_kv_heads == 0
283
- self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
284
- # MistralConfig has an optional head_dim introduced by Mistral-Nemo
285
- if hasattr(config, "head_dim"):
286
- self.head_dim = config.head_dim
287
- elif hasattr(config, "attention_head_dim"):
288
- self.head_dim = config.attention_head_dim
289
- else:
290
- self.head_dim = self.hidden_size // self.total_num_heads
291
- self.q_size = self.num_heads * self.head_dim
292
- self.kv_size = self.num_kv_heads * self.head_dim
293
- self.scaling = self.head_dim**-0.5
294
- self.max_position_embeddings = max_position_embeddings
295
- self.use_qk_norm = getattr(config, "use_qk_norm", False)
296
- self.layer_id = layer_id
297
-
298
- self.q_proj = ColumnParallelLinear(
299
- hidden_size,
300
- hidden_size,
301
- bias=bias,
302
- quant_config=quant_config,
303
- prefix=f"{prefix}.q_proj",
304
- )
305
-
306
- self.o_proj = RowParallelLinear(
307
- input_size=self.total_num_heads * self.head_dim,
308
- output_size=hidden_size,
309
- bias=bias,
310
- quant_config=quant_config,
311
- prefix=f"{prefix}.o_proj",
312
- )
313
-
314
- self.rotary_emb = get_rope(
315
- self.head_dim,
316
- max_position=max_position_embeddings,
317
- rope_parameters=config.rope_parameters,
318
- is_neox_style=True,
319
- )
320
- self.attn = Attention(
321
- self.num_heads,
322
- self.head_dim,
323
- self.scaling,
324
- num_kv_heads=self.num_kv_heads,
325
- cache_config=cache_config,
326
- quant_config=quant_config,
327
- prefix=f"{prefix}.attn",
328
- attn_type=AttentionType.ENCODER_DECODER,
329
- )
330
-
331
- if self.use_qk_norm:
332
- self.query_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
333
- self.key_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
334
-
335
- def forward(
336
- self,
337
- positions: torch.Tensor,
338
- hidden_states: torch.Tensor,
339
- kv_states: tuple[torch.Tensor] | None = None,
340
- ) -> torch.Tensor:
341
- assert kv_states is not None
342
- ori_k, v = kv_states # use last layer kv,
343
- k = ori_k
344
- q, _ = self.q_proj(hidden_states)
345
- k_tmp = torch.empty_like(k) # Todo: reduant rotary embedding
346
- q, _ = self.rotary_emb(positions, q, k_tmp)
347
- if self.use_qk_norm:
348
- q = self.query_layernorm(
349
- q.view(-1, self.num_heads, self.head_dim),
350
- )
351
- k = self.key_layernorm(
352
- k.view(-1, self.num_kv_heads, self.head_dim),
353
- )
354
-
355
- attn_output = self.attn(q, k, v)
356
- # For o_proj
357
- attn_output = attn_output.view(q.shape[0], -1)
358
- output, _ = self.o_proj(attn_output)
359
- return output, (ori_k, v)
360
-
361
-
362
-class HunYuanSparseMoeBlock(nn.Module):
363
- def __init__(
364
- self,
365
- config: PretrainedConfig,
366
- quant_config: QuantizationConfig | None = None,
367
- layer_id: int = -1,
368
- prefix: str = "",
369
- enable_eplb: bool = False,
370
- ):
371
- super().__init__()
372
- self.tp_size = get_tensor_model_parallel_world_size()
373
-
374
- self.ep_group = get_ep_group().device_group
375
- self.ep_size = self.ep_group.size()
376
- self.n_routed_experts = config.num_experts
377
-
378
- if self.tp_size > config.num_experts:
379
- raise ValueError(
380
- f"Tensor parallel size {self.tp_size} is greater than "
381
- f"the number of experts {config.num_experts}."
382
- )
383
-
384
- # Get layer_id topk if config.moe_topk is a list
385
- if isinstance(config.moe_topk, list):
386
- assert layer_id >= 0
387
- assert len(config.moe_topk) > layer_id
388
- top_k = config.moe_topk[layer_id]
389
- else:
390
- top_k = config.moe_topk
391
-
392
- # If it is moe, moe_intermediate_size is preferred
393
- intermediate_size = config.intermediate_size
394
- if config.moe_intermediate_size is not None:
395
- intermediate_size = (
396
- config.moe_intermediate_size
397
- if isinstance(config.moe_intermediate_size, int)
398
- else config.moe_intermediate_size[layer_id]
399
- )
400
-
401
- # Load balancing settings.
402
- vllm_config = get_current_vllm_config()
403
- eplb_config = vllm_config.parallel_config.eplb_config
404
- self.enable_eplb = enable_eplb
405
-
406
- self.n_logical_experts = self.n_routed_experts
407
- self.n_redundant_experts = eplb_config.num_redundant_experts
408
- self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
409
- self.n_local_physical_experts = self.n_physical_experts // self.ep_size
410
- self.gate = ReplicatedLinear(
411
- config.hidden_size,
412
- config.num_experts,
413
- bias=False,
414
- quant_config=None,
415
- prefix=f"{prefix}.gate",
416
- )
417
- if config.use_mixed_mlp_moe > 0:
418
- # Get layer_id num_shared_expert if config.num_shared_expert is
419
- # a list.
420
- if isinstance(config.num_shared_expert, list):
421
- assert layer_id >= 0
422
- assert len(config.num_shared_expert) > layer_id
423
- num_shared_expert = config.num_shared_expert[layer_id]
424
- else:
425
- num_shared_expert = config.num_shared_expert
426
-
427
- self.shared_mlp = HunYuanMLP(
428
- hidden_size=config.hidden_size,
429
- intermediate_size=config.intermediate_size * num_shared_expert,
430
- hidden_act=config.hidden_act,
431
- quant_config=quant_config,
432
- reduce_results=False,
433
- prefix=f"{prefix}.shared_mlp",
434
- )
435
- else:
436
- self.shared_mlp = None
437
-
438
- self.experts = FusedMoEFactory(
439
- shared_experts=self.shared_mlp,
440
- num_experts=self.n_routed_experts,
441
- top_k=top_k,
442
- hidden_size=config.hidden_size,
443
- intermediate_size=intermediate_size,
444
- renormalize=top_k > 1,
445
- quant_config=quant_config,
446
- prefix=f"{prefix}.experts",
447
- enable_eplb=self.enable_eplb,
448
- num_redundant_experts=self.n_redundant_experts,
449
- )
450
-
451
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
452
- # NOTE: hidden_states can have either 1D or 2D shape.
453
- orig_shape = hidden_states.shape
454
- hidden_dim = hidden_states.shape[-1]
455
- hidden_states = hidden_states.view(-1, hidden_dim)
456
-
457
- # router_logits: (num_tokens, n_experts)
458
- router_logits, _ = self.gate(hidden_states)
459
- final_hidden_states = self.experts(
460
- hidden_states=hidden_states, router_logits=router_logits
461
- )
462
-
463
- return final_hidden_states.view(orig_shape)
464
-
465
-
466
-class HunYuanDecoderLayer(nn.Module):
467
- def __init__(
468
- self,
469
- config: PretrainedConfig,
470
- cache_config: CacheConfig | None = None,
471
- quant_config: QuantizationConfig | None = None,
472
- prefix: str = "",
473
- layer_id: int = -1,
474
- enable_eplb: bool = False,
475
- ) -> None:
476
- super().__init__()
477
- assert layer_id >= 0
478
- self.layer_id = layer_id
479
- self.hidden_size = config.hidden_size
480
- self.intermediate_size = (
481
- config.intermediate_size
482
- if isinstance(config.intermediate_size, int)
483
- else config.intermediate_size[layer_id]
484
- )
485
- max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
486
- attention_bias = getattr(config, "attention_bias", False) or getattr(
487
- config, "bias", False
488
- )
489
- cla_factor = _get_cla_factor(config)
490
- attention_type = (
491
- AttentionType.ENCODER_DECODER
492
- if layer_id >= 0 and layer_id % cla_factor != 0
493
- else AttentionType.DECODER
494
- )
495
- if attention_type == AttentionType.DECODER:
496
- self.self_attn = HunYuanAttention(
497
- config=config,
498
- hidden_size=self.hidden_size,
499
- num_heads=config.num_attention_heads,
500
- num_kv_heads=getattr(
501
- config, "num_key_value_heads", config.num_attention_heads
502
- ),
503
- max_position_embeddings=max_position_embeddings,
504
- quant_config=quant_config,
505
- bias=attention_bias,
506
- cache_config=cache_config,
507
- prefix=f"{prefix}.self_attn",
508
- layer_id=layer_id,
509
- )
510
- elif attention_type == AttentionType.ENCODER_DECODER:
511
- self.self_attn = HunYuanCrossAttention(
512
- config=config,
513
- hidden_size=self.hidden_size,
514
- num_heads=config.num_attention_heads,
515
- num_kv_heads=getattr(
516
- config, "num_key_value_heads", config.num_attention_heads
517
- ),
518
- max_position_embeddings=max_position_embeddings,
519
- quant_config=quant_config,
520
- bias=attention_bias,
521
- cache_config=cache_config,
522
- prefix=f"{prefix}.self_attn",
523
- layer_id=layer_id,
524
- )
525
- else:
526
- raise RuntimeError(f"Unsupported attention type: {attention_type}")
527
-
528
- if _is_moe(config):
529
- self.mlp = HunYuanSparseMoeBlock(
530
- config=config,
531
- quant_config=quant_config,
532
- layer_id=layer_id,
533
- prefix=f"{prefix}.mlp",
534
- enable_eplb=enable_eplb,
535
- )
536
- else:
537
- self.mlp = HunYuanMLP(
538
- hidden_size=self.hidden_size,
539
- intermediate_size=self.intermediate_size,
540
- hidden_act=config.hidden_act,
541
- quant_config=quant_config,
542
- bias=getattr(config, "mlp_bias", False),
543
- prefix=f"{prefix}.mlp",
544
- )
545
-
546
- self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
547
- self.post_attention_layernorm = RMSNorm(
548
- config.hidden_size, eps=config.rms_norm_eps
549
- )
550
-
551
- def forward(
552
- self,
553
- positions: torch.Tensor,
554
- hidden_states: torch.Tensor,
555
- residual: torch.Tensor | None,
556
- kv_states: tuple[torch.Tensor] | None = None,
557
- ) -> tuple[torch.Tensor, torch.Tensor]:
558
- # Self Attention
559
- if residual is None:
560
- residual = hidden_states
561
- hidden_states = self.input_layernorm(hidden_states)
562
- else:
563
- hidden_states, residual = self.input_layernorm(hidden_states, residual)
564
- hidden_states, ori_kv_states = self.self_attn(
565
- positions=positions,
566
- hidden_states=hidden_states,
567
- kv_states=kv_states,
568
- )
569
-
570
- # Fully Connected
571
- hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
572
- hidden_states = self.mlp(hidden_states)
573
- return hidden_states, residual, ori_kv_states
574
-
575
-
576
-@support_torch_compile(
577
- dynamic_arg_dims={
578
- "input_ids": 0,
579
- # positions is of shape (xd, seq_len) if xdrope is enabled for hunyuan-vl,
580
- # otherwise (seq_len, ).
581
- "positions": -1,
582
- "intermediate_tensors": 0,
583
- "inputs_embeds": 0,
584
- }
585
-)
586
-class HunYuanModel(nn.Module, EagleModelMixin):
587
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
588
- super().__init__()
589
-
590
- config = vllm_config.model_config.hf_config
591
- cache_config = vllm_config.cache_config
592
- quant_config = vllm_config.quant_config
593
-
594
- eplb_config = vllm_config.parallel_config.eplb_config
595
- enable_eplb = vllm_config.parallel_config.enable_eplb
596
- self.num_redundant_experts = eplb_config.num_redundant_experts
597
-
598
- self.config = config
599
- self.quant_config = quant_config
600
-
601
- self.vocab_size = config.vocab_size
602
-
603
- if get_pp_group().is_first_rank or (
604
- config.tie_word_embeddings and get_pp_group().is_last_rank
605
- ):
606
- self.embed_tokens = VocabParallelEmbedding(
607
- self.vocab_size,
608
- config.hidden_size,
609
- quant_config=quant_config,
610
- )
611
- else:
612
- self.embed_tokens = PPMissingLayer()
613
- self.start_layer, self.end_layer, self.layers = make_layers(
614
- config.num_hidden_layers,
615
- lambda prefix: HunYuanDecoderLayer(
616
- config=config,
617
- layer_id=int(prefix.split(".")[-1]),
618
- cache_config=cache_config,
619
- quant_config=quant_config,
620
- prefix=prefix,
621
- enable_eplb=enable_eplb,
622
- ),
623
- prefix=f"{prefix}.layers",
624
- )
625
- if get_pp_group().is_last_rank:
626
- self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
627
- else:
628
- self.norm = PPMissingLayer()
629
-
630
- def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
631
- return self.embed_tokens(input_ids)
632
-
633
- def forward(
634
- self,
635
- input_ids: torch.Tensor | None,
636
- positions: torch.Tensor,
637
- intermediate_tensors: IntermediateTensors | None,
638
- inputs_embeds: torch.Tensor | None = None,
639
- ) -> torch.Tensor | IntermediateTensors:
640
- if get_pp_group().is_first_rank:
641
- if inputs_embeds is not None:
642
- hidden_states = inputs_embeds
643
- else:
644
- hidden_states = self.embed_input_ids(input_ids)
645
- residual = None
646
- else:
647
- assert intermediate_tensors is not None
648
- hidden_states = intermediate_tensors["hidden_states"]
649
- residual = intermediate_tensors["residual"]
650
-
651
- cla_factor = _get_cla_factor(self.config)
652
- prev_kv_states = None
653
- aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual)
654
- for i, layer in enumerate(
655
- islice(self.layers, self.start_layer, self.end_layer)
656
- ):
657
- hidden_states, residual, kv_states = layer(
658
- positions,
659
- hidden_states,
660
- residual,
661
- prev_kv_states,
662
- )
663
-
664
- if getattr(self.config, "use_cla", False) and i % cla_factor == 0:
665
- prev_kv_states = kv_states
666
- else:
667
- prev_kv_states = None
668
-
669
- self._maybe_add_hidden_state(
670
- aux_hidden_states, i + 1, hidden_states, residual
671
- )
672
-
673
- if not get_pp_group().is_last_rank:
674
- return IntermediateTensors(
675
- {"hidden_states": hidden_states, "residual": residual}
676
- )
677
-
678
- hidden_states, _ = self.norm(hidden_states, residual)
679
-
680
- if len(aux_hidden_states) > 0:
681
- return hidden_states, aux_hidden_states
682
- return hidden_states
683
-
684
- def _split_qkv_weight(self, qkv: torch.Tensor):
685
- num_attention_heads = self.config.num_attention_heads
686
- num_kv_heads = getattr(
687
- self.config, "num_key_value_heads", self.config.num_attention_heads
688
- )
689
- num_key_value_groups = num_attention_heads // num_kv_heads
690
- hidden_size = self.config.hidden_size
691
-
692
- if hasattr(self.config, "head_dim"):
693
- attention_head_dim = self.config.head_dim
694
- elif hasattr(self.config, "attention_head_dim"):
695
- attention_head_dim = self.config.attention_head_dim
696
- else:
697
- attention_head_dim = self.config.hidden_size // num_attention_heads
698
-
699
- qkv = qkv.reshape(
700
- num_kv_heads, num_key_value_groups + 2, attention_head_dim, hidden_size
701
- )
702
- q, k, v = torch.split(qkv, (num_key_value_groups, 1, 1), dim=1)
703
- q = q.reshape(-1, hidden_size)
704
- k = k.reshape(-1, hidden_size)
705
- v = v.reshape(-1, hidden_size)
706
- return torch.concat((q, k, v))
707
-
708
- def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
709
- if _is_moe(self.config):
710
- # Params for weights, fp8 weight scales, fp8 activation scales
711
- # (param_name, weight_name, expert_id, shard_id)
712
- return fused_moe_make_expert_params_mapping(
713
- self,
714
- ckpt_gate_proj_name="gate_proj",
715
- ckpt_down_proj_name="down_proj",
716
- ckpt_up_proj_name="up_proj",
717
- num_experts=self.config.num_experts,
718
- num_redundant_experts=self.num_redundant_experts,
719
- )
720
- else:
721
- return []
722
-
723
- def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
724
- cla_factor = _get_cla_factor(self.config)
725
- stacked_params_mapping = [
726
- # (param_name, shard_name, shard_id)
727
- (".qkv_proj", ".q_proj", "q"),
728
- (".qkv_proj", ".k_proj", "k"),
729
- (".qkv_proj", ".v_proj", "v"),
730
- (".gate_up_proj", ".gate_proj", 0),
731
- (".gate_up_proj", ".up_proj", 1),
732
- ]
733
-
734
- num_attention_heads = self.config.num_attention_heads
735
- num_kv_heads = getattr(
736
- self.config, "num_key_value_heads", self.config.num_attention_heads
737
- )
738
- split_params_mapping = [
739
- (".gate_up_proj", ".gate_and_up_proj", 2, [(1, 1), (0, 1)], None),
740
- (
741
- ".qkv_proj",
742
- ".qkv_proj",
743
- num_attention_heads + num_kv_heads * 2,
744
- [("q", num_attention_heads), ("k", num_kv_heads), ("v", num_kv_heads)],
745
- self._split_qkv_weight,
746
- ),
747
- ]
748
-
749
- params_dict = dict(self.named_parameters())
750
- loaded_params: set[str] = set()
751
- expert_params_mapping = self.get_expert_mapping()
752
- for name, loaded_weight in weights:
753
- if "rotary_emb.inv_freq" in name:
754
- continue
755
- if "gate_proj_bias" in name:
756
- name = name.replace("gate_proj_bias", "gate_proj.bias")
757
- if "up_proj_bias" in name:
758
- name = name.replace("up_proj_bias", "up_proj.bias")
759
- if "rotary_emb.cos_cached" in name or "rotary_emb.sin_cached" in name:
760
- # Models trained using ColossalAI may include these tensors in
761
- # the checkpoint. Skip them.
762
- continue
763
- # With tie_word_embeddings, we can skip lm_head.weight
764
- # The weight might appear unnecessarily in the files if the model is
765
- # processed with quantization, LoRA, fine-tuning, etc.
766
- if self.config.tie_word_embeddings and "lm_head.weight" in name:
767
- continue
768
-
769
- is_found = False
770
- for param_name, weight_name, shard_id in stacked_params_mapping:
771
- if weight_name not in name:
772
- continue
773
- if "mlp.experts" in name:
774
- continue
775
- # cross layer only have q_proj, skip qkv pack
776
- if weight_name == ".q_proj":
777
- match = re.search(r"layers\.\d+", name)
778
- if match:
779
- layer_id = int(match.group(0).split(".")[-1])
780
- if cla_factor > 1 and layer_id % cla_factor != 0:
781
- continue
782
- name = name.replace(weight_name, param_name)
783
- # Skip loading extra bias for GPTQ models.
784
- if name.endswith(".bias") and name not in params_dict:
785
- continue
786
-
787
- if is_pp_missing_parameter(name, self):
788
- continue
789
-
790
- param = params_dict[name]
791
- weight_loader = param.weight_loader
792
- weight_loader(param, loaded_weight, shard_id)
793
- loaded_params.add(name)
794
- is_found = True
795
- break
796
- if is_found:
797
- continue
798
-
799
- for (
800
- param_name,
801
- weight_name,
802
- den,
803
- split_param,
804
- func,
805
- ) in split_params_mapping:
806
- if weight_name not in name:
807
- continue
808
- name = name.replace(weight_name, param_name)
809
- # Skip loading extra bias for GPTQ models.
810
- if name.endswith(".bias") and name not in params_dict:
811
- continue
812
-
813
- if is_pp_missing_parameter(name, self):
814
- continue
815
-
816
- assert loaded_weight.shape[0] % den == 0
817
- units = loaded_weight.shape[0] // den
818
-
819
- param = params_dict[name]
820
- weight_loader = param.weight_loader
821
- offset = 0
822
- for shard_id, num in split_param:
823
- new_offset = offset + num * units
824
- if func:
825
- weight_loader(
826
- param, func(loaded_weight)[offset:new_offset], shard_id
827
- )
828
- else:
829
- weight_loader(param, loaded_weight[offset:new_offset], shard_id)
830
- offset = new_offset
831
-
832
- break
833
- else:
834
- # Skip loading extra bias for GPTQ models.
835
- if name.endswith(".bias") and name not in params_dict:
836
- continue
837
- is_expert_weight = False
838
- for mapping in expert_params_mapping:
839
- param_name, weight_name, expert_id, shard_id = mapping
840
- if weight_name not in name:
841
- continue
842
- # this is an expert weight and should not be
843
- # attempted to load as other weights later
844
- is_expert_weight = True
845
-
846
- # Do not modify `name` since the loop may continue here
847
- # Instead, create a new variable
848
- name_mapped = name.replace(weight_name, param_name)
849
- if is_pp_missing_parameter(name_mapped, self):
850
- continue
851
- param = params_dict[name_mapped]
852
- # We should ask the weight loader to return success or not
853
- # here since otherwise we may skip experts with other
854
- # available replicas.
855
- weight_loader = typing.cast(
856
- Callable[..., bool], param.weight_loader
857
- )
858
- success = weight_loader(
859
- param,
860
- loaded_weight,
861
- name_mapped,
862
- shard_id=shard_id,
863
- expert_id=expert_id,
864
- return_success=True,
865
- )
866
- if success:
867
- name = name_mapped
868
- break
869
- else:
870
- if is_expert_weight:
871
- # We've checked that this is an expert weight
872
- # However it's not mapped locally to this rank
873
- # So we simply skip it
874
- continue
875
- # Remapping the name of FP8 kv-scale.
876
- name = maybe_remap_kv_scale_name(name, params_dict)
877
- if name is None:
878
- continue
879
-
880
- if is_pp_missing_parameter(name, self):
881
- continue
882
-
883
- if "mlp.gate.wg." in name:
884
- name = name.replace("wg.", "")
885
-
886
- param = params_dict[name]
887
- weight_loader = getattr(
888
- param, "weight_loader", default_weight_loader
889
- )
890
- weight_loader(param, loaded_weight)
891
- loaded_params.add(name)
892
- return loaded_params
893
-
894
-
895
-class HunyuanV1ModelBase(
896
- nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3
897
-):
898
- packed_modules_mapping = {
899
- "qkv_proj": [
900
- "q_proj",
901
- "k_proj",
902
- "v_proj",
903
- ],
904
- "gate_up_proj": [
905
- "gate_proj",
906
- "up_proj",
907
- ],
908
- }
909
-
910
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
911
- super().__init__()
912
-
913
- config = vllm_config.model_config.hf_config
914
- quant_config = vllm_config.quant_config
915
- self.config = config
916
- self.quant_config = quant_config
917
-
918
- self.model = HunYuanModel(
919
- vllm_config=vllm_config,
920
- prefix=maybe_prefix(prefix, "model"),
921
- )
922
- if get_pp_group().is_last_rank:
923
- self.lm_head = ParallelLMHead(
924
- config.vocab_size,
925
- config.hidden_size,
926
- quant_config=quant_config,
927
- prefix=maybe_prefix(prefix, "lm_head"),
928
- )
929
- if config.tie_word_embeddings:
930
- self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
931
-
932
- logit_scale = getattr(config, "logit_scale", 1.0)
933
- self.logits_processor = LogitsProcessor(
934
- config.vocab_size, scale=logit_scale
935
- )
936
- else:
937
- self.lm_head = PPMissingLayer()
938
-
939
- def forward(
940
- self,
941
- input_ids: torch.Tensor | None,
942
- positions: torch.Tensor,
943
- intermediate_tensors: IntermediateTensors | None = None,
944
- inputs_embeds: torch.Tensor | None = None,
945
- ) -> torch.Tensor | IntermediateTensors:
946
- model_output = self.model(
947
- input_ids, positions, intermediate_tensors, inputs_embeds
948
- )
949
- return model_output
950
-
951
- def compute_logits(
952
- self,
953
- hidden_states: torch.Tensor,
954
- ) -> torch.Tensor | None:
955
- logits = self.logits_processor(self.lm_head, hidden_states)
956
- return logits
957
-
958
- def make_empty_intermediate_tensors(
959
- self, batch_size: int, dtype: torch.dtype, device: torch.device
960
- ) -> IntermediateTensors:
961
- return IntermediateTensors(
962
- {
963
- "hidden_states": torch.zeros(
964
- (batch_size, self.config.hidden_size), dtype=dtype, device=device
965
- ),
966
- "residual": torch.zeros(
967
- (batch_size, self.config.hidden_size), dtype=dtype, device=device
968
- ),
969
- }
970
- )
971
-
972
- def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
973
- loader = AutoWeightsLoader(self)
974
- return loader.load_weights(weights)
975
-
976
- def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
977
- return self.model.embed_input_ids(input_ids)
978
-
979
-
980
-class HunYuanMoEV1Base(HunyuanV1ModelBase, MixtureOfExperts):
981
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
982
- super().__init__(vllm_config=vllm_config, prefix=prefix)
983
-
984
- # Set MoE hyperparameters
985
- self.num_expert_groups = 1
986
- self.moe_layers = []
987
- example_layer = None
988
- for layer in self.model.layers:
989
- if isinstance(layer, PPMissingLayer):
990
- continue
991
-
992
- assert isinstance(layer, HunYuanDecoderLayer)
993
- if isinstance(layer.mlp, HunYuanSparseMoeBlock):
994
- example_layer = layer.mlp
995
- self.moe_layers.append(layer.mlp.experts)
996
-
997
- if example_layer is None:
998
- raise RuntimeError("No HunYuanMoE layer found in model.layers.")
999
-
1000
- self.num_moe_layers = len(self.moe_layers)
1001
- self.num_logical_experts = example_layer.n_logical_experts
1002
- self.num_physical_experts = example_layer.n_physical_experts
1003
- self.num_local_physical_experts = example_layer.n_local_physical_experts
1004
- self.num_routed_experts = example_layer.n_routed_experts
1005
- self.num_redundant_experts = example_layer.n_redundant_experts
1006
-
1007
- def update_physical_experts_metadata(
1008
- self,
1009
- num_physical_experts: int,
1010
- num_local_physical_experts: int,
1011
- ) -> None:
1012
- assert self.num_local_physical_experts == num_local_physical_experts
1013
- self.num_physical_experts = num_physical_experts
1014
- self.num_local_physical_experts = num_local_physical_experts
1015
- self.num_redundant_experts = num_physical_experts - self.num_logical_experts
1016
- for layer in self.model.layers:
1017
- if isinstance(layer.mlp, HunYuanSparseMoeBlock):
1018
- moe = layer.mlp
1019
- moe.n_local_physical_experts = num_local_physical_experts
1020
- moe.n_physical_experts = num_physical_experts
1021
- moe.n_redundant_experts = self.num_redundant_experts
1022
- moe.experts.update_expert_map()
1023
-
1024
- def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1025
- return self.model.get_expert_mapping()
1026
-
1027
-
1028
-class HunYuanDenseV1Base(HunyuanV1ModelBase):
1029
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1030
- super().__init__(vllm_config=vllm_config, prefix=prefix)
1031
-
1032
-
1033
-class HunYuanDenseV1ForCausalLM(HunYuanDenseV1Base):
1034
- pass
1035
-
1036
-
1037
-class HunYuanMoEV1ForCausalLM(HunYuanMoEV1Base):
1038
- pass
vllm/model_executor/models/hunyuan_vision.py
deleted
-1025
@@ -1,1025 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-# coding=utf-8
5
-# Copyright 2025 The HunYuan team.
6
-# Copyright 2025 The vLLM team.
7
-# Copyright 2025 EleutherAI and the HuggingFace Inc. team. All rights reserved.
8
-#
9
-# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
10
-# and OPT implementations in this library. It has been modified from its
11
-# original forms to accommodate minor architectural differences compared
12
-# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
13
-#
14
-# Licensed under the Apache License, Version 2.0 (the "License");
15
-# you may not use this file except in compliance with the License.
16
-# You may obtain a copy of the License at
17
-#
18
-# http://www.apache.org/licenses/LICENSE-2.0
19
-#
20
-# Unless required by applicable law or agreed to in writing, software
21
-# distributed under the License is distributed on an "AS IS" BASIS,
22
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
-# See the License for the specific language governing permissions and
24
-# limitations under the License.
25
-"""Inference-only HunYuan-VL model compatible with HuggingFace weights."""
26
-
27
-from collections.abc import Callable, Iterable, Mapping, Sequence
28
-from functools import partial
29
-from typing import Annotated, Any, Literal, TypeAlias
30
-
31
-import torch
32
-import torch.nn as nn
33
-import torch.nn.functional as F
34
-from transformers import BatchFeature, HunYuanVLProcessor
35
-from transformers.models.hunyuan_vl.image_processing_hunyuan_vl import (
36
- HunYuanVLImageProcessor,
37
- smart_resize,
38
-)
39
-
40
-from vllm.config import VllmConfig
41
-from vllm.config.multimodal import BaseDummyOptions
42
-from vllm.distributed import parallel_state
43
-from vllm.distributed import utils as dist_utils
44
-from vllm.inputs import ModalityData, MultiModalDataDict
45
-from vllm.logger import init_logger
46
-from vllm.model_executor.layers.activation import get_act_fn
47
-from vllm.model_executor.layers.attention import MMEncoderAttention
48
-from vllm.model_executor.layers.layernorm import RMSNorm
49
-from vllm.model_executor.layers.linear import (
50
- ColumnParallelLinear,
51
- QKVParallelLinear,
52
- RowParallelLinear,
53
-)
54
-from vllm.model_executor.layers.quantization import QuantizationConfig
55
-from vllm.model_executor.models.module_mapping import MultiModelKeys
56
-from vllm.multimodal import MULTIMODAL_REGISTRY
57
-from vllm.multimodal.inputs import (
58
- ImageItem,
59
- MultiModalFeatureSpec,
60
- MultiModalFieldConfig,
61
- MultiModalKwargsItems,
62
-)
63
-from vllm.multimodal.parse import (
64
- DictEmbeddingItems,
65
- ImageSize,
66
- ModalityDataItems,
67
- MultiModalDataItems,
68
- MultiModalDataParser,
69
-)
70
-from vllm.multimodal.processing import (
71
- BaseDummyInputsBuilder,
72
- BaseMultiModalProcessor,
73
- BaseProcessingInfo,
74
- PromptReplacement,
75
- PromptUpdate,
76
- PromptUpdateDetails,
77
-)
78
-from vllm.sequence import IntermediateTensors
79
-from vllm.transformers_utils.configs.hunyuan_vl import (
80
- HunYuanVLConfig,
81
- HunYuanVLVisionConfig,
82
-)
83
-from vllm.utils.tensor_schema import TensorSchema, TensorShape
84
-
85
-from .interfaces import (
86
- MultiModalEmbeddings,
87
- SupportsEagle,
88
- SupportsEagle3,
89
- SupportsLoRA,
90
- SupportsMultiModal,
91
- SupportsPP,
92
- SupportsQuant,
93
- SupportsXDRoPE,
94
-)
95
-from .utils import (
96
- AutoWeightsLoader,
97
- WeightsMapper,
98
- init_vllm_registered_model,
99
- maybe_prefix,
100
-)
101
-from .vision import is_vit_use_data_parallel
102
-
103
-logger = init_logger(__name__)
104
-
105
-# === Vision Inputs === #
106
-
107
-
108
-class HunYuanVLImagePixelInputs(TensorSchema):
109
- """
110
- Dimensions:
111
- - np: Number of patches
112
- - ni: Number of images
113
- - cps: Number of channels * patch_size * patch_size
114
- """
115
-
116
- type: Literal["pixel_values"]
117
-
118
- pixel_values: Annotated[
119
- torch.Tensor,
120
- TensorShape("np", "cps"),
121
- ]
122
-
123
- image_grid_thw: Annotated[
124
- torch.Tensor,
125
- TensorShape("ni", 3),
126
- ]
127
-
128
-
129
-class HunYuanVLImageEmbeddingInputs(TensorSchema):
130
- """
131
- Dimensions:
132
- - nf: Number of image features
133
- - hs: Hidden size
134
- - ni: Number of images
135
- """
136
-
137
- type: Literal["image_embeds"]
138
-
139
- image_embeds: Annotated[
140
- torch.Tensor,
141
- TensorShape("nf", "hs"),
142
- ]
143
-
144
- image_grid_thw: Annotated[
145
- torch.Tensor,
146
- TensorShape("ni", 3),
147
- ]
148
-
149
-
150
-HunYuanVLImageInputs: TypeAlias = (
151
- HunYuanVLImagePixelInputs | HunYuanVLImageEmbeddingInputs
152
-)
153
-
154
-# === Vision Encoder === #
155
-
156
-
157
-class HunYuanVisionMLP(nn.Module):
158
- def __init__(
159
- self,
160
- in_features: int,
161
- hidden_features: int,
162
- bias: bool = True,
163
- act_fn: Callable[[torch.Tensor], torch.Tensor] = F.gelu,
164
- quant_config: QuantizationConfig | None = None,
165
- prefix: str = "",
166
- ):
167
- super().__init__()
168
- use_data_parallel = is_vit_use_data_parallel()
169
- self.dense_h_to_4h = ColumnParallelLinear(
170
- in_features,
171
- hidden_features,
172
- bias=bias,
173
- quant_config=quant_config,
174
- prefix=f"{prefix}.dense_h_to_4h",
175
- disable_tp=use_data_parallel,
176
- )
177
- self.dense_4h_to_h = RowParallelLinear(
178
- hidden_features,
179
- in_features,
180
- bias=bias,
181
- quant_config=quant_config,
182
- prefix=f"{prefix}.dense_4h_to_h",
183
- disable_tp=use_data_parallel,
184
- )
185
- self.act_fn = act_fn
186
-
187
- def forward(self, x: torch.Tensor):
188
- x_up, _ = self.dense_h_to_4h(x)
189
- x_down, _ = self.dense_4h_to_h(self.act_fn(x_up))
190
- return x_down
191
-
192
-
193
-class HunYuanVisionAttention(nn.Module):
194
- def __init__(
195
- self,
196
- embed_dim: int,
197
- num_heads: int,
198
- projection_size: int,
199
- quant_config: QuantizationConfig | None = None,
200
- prefix: str = "",
201
- ) -> None:
202
- super().__init__()
203
- # Per attention head and per partition values.
204
- use_data_parallel = is_vit_use_data_parallel()
205
- self.tp_size = (
206
- 1
207
- if use_data_parallel
208
- else parallel_state.get_tensor_model_parallel_world_size()
209
- )
210
- self.hidden_size_per_attention_head = dist_utils.divide(
211
- projection_size, num_heads
212
- )
213
- self.num_attention_heads_per_partition = dist_utils.divide(
214
- num_heads, self.tp_size
215
- )
216
-
217
- self.qkv = QKVParallelLinear(
218
- hidden_size=embed_dim,
219
- head_size=self.hidden_size_per_attention_head,
220
- total_num_heads=num_heads,
221
- total_num_kv_heads=num_heads,
222
- bias=True,
223
- quant_config=quant_config,
224
- prefix=f"{prefix}.qkv",
225
- disable_tp=use_data_parallel,
226
- )
227
-
228
- self.o_proj = RowParallelLinear(
229
- input_size=projection_size,
230
- output_size=embed_dim,
231
- quant_config=quant_config,
232
- prefix=f"{prefix}.o_proj",
233
- disable_tp=use_data_parallel,
234
- )
235
-
236
- self.scale = self.hidden_size_per_attention_head**-0.5
237
- self.attn = MMEncoderAttention(
238
- self.num_attention_heads_per_partition,
239
- self.hidden_size_per_attention_head,
240
- self.scale,
241
- prefix=f"{prefix}.attn",
242
- )
243
-
244
- def forward(
245
- self,
246
- x: torch.Tensor,
247
- ) -> torch.Tensor:
248
- qkv, _ = self.qkv(x)
249
- q, k, v = qkv.chunk(3, dim=-1)
250
- out = self.attn(q, k, v)
251
- output, _ = self.o_proj(out)
252
- return output
253
-
254
-
255
-class HunYuanVisionBlock(nn.Module):
256
- def __init__(
257
- self,
258
- dim: int,
259
- num_heads: int,
260
- mlp_hidden_dim: int,
261
- act_fn: Callable[[torch.Tensor], torch.Tensor] = F.gelu,
262
- norm_layer: Callable[[int], nn.Module] | None = None,
263
- quant_config: QuantizationConfig | None = None,
264
- prefix: str = "",
265
- ) -> None:
266
- super().__init__()
267
- if norm_layer is None:
268
- norm_layer = partial(nn.LayerNorm, eps=1e-6)
269
- self.input_layernorm = norm_layer(dim)
270
- self.post_attention_layernorm = norm_layer(dim)
271
- self.self_attn = HunYuanVisionAttention(
272
- embed_dim=dim,
273
- num_heads=num_heads,
274
- projection_size=dim,
275
- quant_config=quant_config,
276
- prefix=f"{prefix}.self_attn",
277
- )
278
- self.mlp = HunYuanVisionMLP(
279
- dim,
280
- mlp_hidden_dim,
281
- act_fn=act_fn,
282
- bias=True,
283
- quant_config=quant_config,
284
- prefix=f"{prefix}.mlp",
285
- )
286
-
287
- def forward(
288
- self,
289
- x: torch.Tensor,
290
- ) -> torch.Tensor:
291
- x = x + self.self_attn(self.input_layernorm(x))
292
- x = x + self.mlp(self.post_attention_layernorm(x))
293
- return x
294
-
295
-
296
-class HunYuanVisionPatchEmbed(nn.Module):
297
- def __init__(self, config: HunYuanVLVisionConfig):
298
- super().__init__()
299
-
300
- self.config = config
301
- self.embed_dim = config.hidden_size
302
- self.patch_size = config.patch_size
303
- self.num_channels = config.num_channels
304
- self.spatial_merge_size = config.spatial_merge_size
305
- self.interpolate_mode = config.interpolate_mode
306
-
307
- self.patch_embedding = nn.Conv2d(
308
- in_channels=config.num_channels,
309
- out_channels=self.embed_dim,
310
- kernel_size=self.patch_size,
311
- stride=self.patch_size,
312
- bias=True,
313
- )
314
-
315
- self.max_num_patches = (config.max_image_size // self.patch_size) ** 2
316
-
317
- self.num_positions = self.max_num_patches + 1
318
- self.position_edge = int(self.num_positions**0.5)
319
- # first token is cls token, skip it
320
- self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
321
-
322
- self.patch_pos_embed = None
323
-
324
- def forward(
325
- self, pixel_values: torch.Tensor, grid_thw: list[list[int]]
326
- ) -> torch.Tensor:
327
- num_patches = pixel_values.size(0)
328
- pixel_values = pixel_values.reshape(
329
- num_patches, self.num_channels, self.patch_size, self.patch_size
330
- )
331
-
332
- patch_embeds = self.patch_embedding(pixel_values)
333
- patch_embeds = patch_embeds.squeeze(-1).squeeze(-1).unsqueeze(0)
334
-
335
- if self.patch_pos_embed is None:
336
- patch_pos_shape = (
337
- 1,
338
- self.position_edge,
339
- self.position_edge,
340
- self.embed_dim,
341
- )
342
- self.patch_pos_embed = (
343
- self.position_embedding.weight[1:, :]
344
- .reshape(patch_pos_shape)
345
- .permute(0, 3, 1, 2)
346
- .float()
347
- )
348
-
349
- patch_pos_embed_list = []
350
- for grid in grid_thw:
351
- _, h0, w0 = grid
352
- # we add a small number to avoid floating point error in the interpolation
353
- # see discussion at https://github.com/facebookresearch/dino/issues/8
354
- h0, w0 = h0 + 0.1, w0 + 0.1
355
- patch_pos_embed = nn.functional.interpolate(
356
- self.patch_pos_embed,
357
- scale_factor=(h0 / self.position_edge, w0 / self.position_edge),
358
- mode=self.interpolate_mode,
359
- align_corners=False,
360
- )
361
-
362
- patch_pos_embed = (
363
- patch_pos_embed.reshape(self.embed_dim, -1)
364
- .transpose(0, 1)
365
- .unsqueeze(0)
366
- .to(patch_embeds.dtype)
367
- )
368
- patch_pos_embed_list.append(patch_pos_embed)
369
-
370
- patch_pos_embed = torch.cat(patch_pos_embed_list, dim=1)
371
- embeddings = patch_embeds + patch_pos_embed
372
-
373
- return embeddings
374
-
375
-
376
-class HunYuanVisionPatchMerger(nn.Module):
377
- def __init__(
378
- self,
379
- in_channels,
380
- out_channels,
381
- spatial_merge_size=2,
382
- rms_norm_eps=1e-5,
383
- prefix="",
384
- ):
385
- super().__init__()
386
- self.spatial_merge_size = spatial_merge_size
387
- embed_std = out_channels**-0.5
388
-
389
- self.proj = nn.Sequential(
390
- nn.Conv2d(
391
- in_channels,
392
- in_channels * 2,
393
- kernel_size=spatial_merge_size,
394
- stride=spatial_merge_size,
395
- ),
396
- nn.GELU(),
397
- nn.Conv2d(in_channels * 2, in_channels * 4, kernel_size=1),
398
- )
399
- self.mlp = nn.Linear(in_channels * 4, out_channels)
400
-
401
- self.image_newline = nn.Parameter(torch.randn(in_channels * 4) * embed_std)
402
- self.image_begin = nn.Parameter(torch.randn(out_channels) * embed_std)
403
- self.image_end = nn.Parameter(torch.randn(out_channels) * embed_std)
404
- self.image_sep = nn.Parameter(torch.randn(out_channels) * embed_std)
405
-
406
- self.before_rms = RMSNorm(in_channels, eps=rms_norm_eps)
407
- self.after_rms = RMSNorm(out_channels, eps=rms_norm_eps)
408
-
409
- def forward(self, x, size=(16, 16)):
410
- x = self.before_rms(x)
411
-
412
- h, w = size
413
- dtype = x.dtype
414
- x = x.permute(0, 2, 1).reshape(x.shape[0], -1, h, w)
415
-
416
- x = self.proj(x) # b,c,h,w
417
- b, c, h, w = x.shape
418
- x = torch.cat(
419
- [x, self.image_newline.reshape(1, c, 1, 1).expand(b, c, h, 1).to(dtype)],
420
- dim=-1,
421
- )
422
- x = x.reshape(b, c, -1).permute(0, 2, 1)
423
- x = self.mlp(x)
424
-
425
- begin = self.image_begin.reshape(1, 1, -1).expand(b, 1, x.shape[-1]).to(dtype)
426
- end = self.image_end.reshape(1, 1, -1).expand(b, 1, x.shape[-1]).to(dtype)
427
- x = torch.cat([begin, x, end], dim=1)
428
-
429
- return self.after_rms(x)
430
-
431
-
432
-class HunYuanVisionTransformer(nn.Module):
433
- hf_to_vllm_mapper = WeightsMapper(
434
- orig_to_new_stacked={
435
- ".q_proj": (".qkv", "q"),
436
- ".k_proj": (".qkv", "k"),
437
- ".v_proj": (".qkv", "v"),
438
- }
439
- )
440
-
441
- def __init__(
442
- self,
443
- vision_config: HunYuanVLVisionConfig,
444
- quant_config: QuantizationConfig | None = None,
445
- prefix: str = "",
446
- ) -> None:
447
- super().__init__()
448
-
449
- num_hidden_layers = vision_config.num_hidden_layers
450
- self.hidden_size = vision_config.hidden_size
451
- self.num_heads = vision_config.num_attention_heads
452
- self.spatial_merge_size = vision_config.spatial_merge_size
453
-
454
- from vllm.compilation.backends import set_model_tag
455
-
456
- with set_model_tag("HunYuanVisionPatchEmbed"):
457
- self.embeddings = HunYuanVisionPatchEmbed(vision_config)
458
-
459
- norm_layer = partial(nn.LayerNorm, eps=vision_config.rms_norm_eps)
460
-
461
- with set_model_tag("HunYuanVisionBlock"):
462
- self.layers = nn.ModuleList(
463
- [
464
- HunYuanVisionBlock(
465
- dim=vision_config.hidden_size,
466
- num_heads=vision_config.num_attention_heads,
467
- mlp_hidden_dim=vision_config.intermediate_size,
468
- act_fn=get_act_fn(vision_config.hidden_act),
469
- norm_layer=norm_layer,
470
- quant_config=quant_config,
471
- prefix=f"{prefix}.layers.{layer_idx}",
472
- )
473
- for layer_idx in range(num_hidden_layers)
474
- ]
475
- )
476
-
477
- with set_model_tag("HunYuanVisionPatchMerger"):
478
- self.perceive = HunYuanVisionPatchMerger(
479
- vision_config.hidden_size,
480
- vision_config.out_hidden_size,
481
- spatial_merge_size=vision_config.spatial_merge_size,
482
- rms_norm_eps=vision_config.rms_norm_eps,
483
- prefix=f"{prefix}.perceive",
484
- )
485
-
486
- @property
487
- def dtype(self) -> torch.dtype:
488
- return self.embeddings.patch_embedding.weight.dtype
489
-
490
- @property
491
- def device(self) -> torch.device:
492
- return self.embeddings.patch_embedding.weight.device
493
-
494
- def forward(
495
- self,
496
- x: torch.Tensor,
497
- grid_thw: list[list[int]],
498
- ) -> torch.Tensor:
499
- # patchify
500
- seq_len = x.size(0)
501
- cu_seqlens: list = [0]
502
-
503
- hidden_states = x.to(device=self.device, dtype=self.dtype)
504
- # embeddings = patch_embeds + patch_pos_embed
505
- hidden_states = self.embeddings(hidden_states, grid_thw)
506
-
507
- for t, h, w in grid_thw:
508
- t, h, w = int(t), int(h), int(w)
509
- cu_seqlens.append(h * w)
510
-
511
- cu_seqlens = torch.tensor(cu_seqlens, dtype=torch.int32)
512
- cu_seqlens = torch.cumsum(cu_seqlens, dim=0, dtype=torch.int32)
513
-
514
- cu_seqlens = cu_seqlens.to(device=self.device, non_blocking=True)
515
-
516
- hidden_states = hidden_states.reshape(seq_len, -1)
517
- hidden_states = hidden_states.unsqueeze(0)
518
-
519
- # build per-image lengths once
520
- split_lengths = [int(h) * int(w) for (_, h, w) in grid_thw]
521
- for layer in self.layers:
522
- # hidden_states: (1, T_total, D)
523
- parts = hidden_states.split(split_lengths, dim=1) # list of (1, L_i, D)
524
- parts = [layer(p) for p in parts]
525
- hidden_states = torch.cat(parts, dim=1)
526
-
527
- # adapter
528
- split_lengths = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist()
529
- split_items = hidden_states.split(split_lengths, dim=1)
530
- image_embeds_list = []
531
- for grid, split_item in zip(grid_thw, split_items):
532
- image_embeds_list.append(
533
- self.perceive(split_item.contiguous(), size=grid[1:]).squeeze(0)
534
- )
535
-
536
- return image_embeds_list
537
-
538
- def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
539
- loader = AutoWeightsLoader(self)
540
- return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
541
-
542
-
543
-def _hunyuan_vl_field_config(hf_inputs: Mapping[str, torch.Tensor]):
544
- image_grid_thw = hf_inputs.get("image_grid_thw", torch.empty((0, 3)))
545
- image_grid_sizes = image_grid_thw.prod(-1)
546
- return dict(
547
- pixel_values=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes),
548
- image_embeds=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes),
549
- image_grid_thw=MultiModalFieldConfig.batched("image", keep_on_cpu=True),
550
- )
551
-
552
-
553
-class HunYuanVLMultiModalDataParser(MultiModalDataParser):
554
- # The patch grid is what sizes the placeholder range.
555
- embedding_fields = {
556
- "image": {"image_embeds": "values", "image_grid_thw": "metadata"},
557
- }
558
-
559
- def _parse_image_data(
560
- self,
561
- data: dict[str, torch.Tensor] | ModalityData[ImageItem],
562
- ) -> ModalityDataItems[Any, Any] | None:
563
- if isinstance(data, dict):
564
- required, optional = self.embedding_field_sets("image")
565
- return DictEmbeddingItems(
566
- data,
567
- modality="image",
568
- required_fields=required,
569
- optional_fields=optional,
570
- fields_factory=_hunyuan_vl_field_config,
571
- )
572
-
573
- return super()._parse_image_data(data)
574
-
575
-
576
-class HunYuanVLProcessingInfo(BaseProcessingInfo):
577
- def get_hf_config(self):
578
- return self.ctx.get_hf_config(HunYuanVLConfig)
579
-
580
- def get_hf_processor(
581
- self,
582
- **kwargs: object,
583
- ) -> HunYuanVLProcessor:
584
- # transformers>=5.13 replaced `use_fast` with `backend`; pin the
585
- # PIL backend to match the released HunyuanOCR checkpoint packing.
586
- kwargs.pop("use_fast", None)
587
- kwargs.setdefault("backend", "pil")
588
- return self.ctx.get_hf_processor(
589
- HunYuanVLProcessor,
590
- **kwargs,
591
- )
592
-
593
- def get_image_processor(
594
- self,
595
- **kwargs: object,
596
- ) -> HunYuanVLImageProcessor:
597
- return self.get_hf_processor(**kwargs).image_processor
598
-
599
- def get_data_parser(self):
600
- return HunYuanVLMultiModalDataParser(
601
- expected_hidden_size=self._get_expected_hidden_size(),
602
- allow_missing_mm_embeddings=self.allow_missing_mm_embeddings,
603
- )
604
-
605
- def get_supported_mm_limits(self) -> Mapping[str, int | None]:
606
- return {"image": None}
607
-
608
- def get_mm_max_tokens_per_item(
609
- self,
610
- seq_len: int,
611
- mm_counts: Mapping[str, int],
612
- ) -> Mapping[str, int]:
613
- max_image_tokens = self.get_max_image_tokens()
614
- # TODO: support video
615
- max_video_tokens = 0
616
- return {"image": max_image_tokens, "video": max_video_tokens}
617
-
618
- def _get_vision_info(
619
- self,
620
- *,
621
- image_width: int,
622
- image_height: int,
623
- num_frames: int = 1,
624
- do_resize: bool = True,
625
- image_processor: HunYuanVLImageProcessor,
626
- mm_kwargs: Mapping[str, object],
627
- ) -> tuple[ImageSize, int]:
628
- hf_config = self.get_hf_config()
629
- vision_config = hf_config.vision_config
630
- patch_size = vision_config.patch_size
631
- spatial_merge_size = vision_config.spatial_merge_size
632
-
633
- mm_kwargs = self.ctx.get_merged_mm_kwargs(mm_kwargs)
634
- size = image_processor.size
635
- if override_size := mm_kwargs.get("size"):
636
- size = size | override_size
637
- if (override_min_pixels := mm_kwargs.get("min_pixels")) is not None:
638
- size = size | {"shortest_edge": override_min_pixels}
639
- if (override_max_pixels := mm_kwargs.get("max_pixels")) is not None:
640
- size = size | {"longest_edge": override_max_pixels}
641
-
642
- if do_resize:
643
- resized_height, resized_width = smart_resize(
644
- height=image_height,
645
- width=image_width,
646
- factor=patch_size * spatial_merge_size,
647
- min_pixels=size["shortest_edge"],
648
- max_pixels=size["longest_edge"],
649
- )
650
- preprocessed_size = ImageSize(width=resized_width, height=resized_height)
651
- else:
652
- preprocessed_size = ImageSize(width=image_width, height=image_height)
653
-
654
- grid_t = 1
655
- grid_h = preprocessed_size.height // patch_size
656
- grid_w = preprocessed_size.width // patch_size
657
-
658
- num_vision_tokens = (
659
- grid_t * grid_h // spatial_merge_size * (grid_w // spatial_merge_size + 1)
660
- + 2
661
- )
662
-
663
- return preprocessed_size, num_vision_tokens
664
-
665
- def get_num_image_tokens(
666
- self,
667
- *,
668
- image_width: int,
669
- image_height: int,
670
- image_processor: HunYuanVLImageProcessor,
671
- mm_kwargs: Mapping[str, object],
672
- ) -> int:
673
- _, num_image_tokens = self._get_vision_info(
674
- image_width=image_width,
675
- image_height=image_height,
676
- image_processor=image_processor,
677
- mm_kwargs=mm_kwargs,
678
- )
679
- return num_image_tokens
680
-
681
- def get_image_size_with_most_features(self) -> ImageSize:
682
- image_processor = self.get_image_processor()
683
-
684
- max_image_size, _ = self._get_vision_info(
685
- image_width=512,
686
- image_height=8192,
687
- image_processor=image_processor,
688
- mm_kwargs={},
689
- )
690
- return max_image_size
691
-
692
- def get_max_image_tokens(self) -> int:
693
- image_processor = self.get_image_processor()
694
- target_width, target_height = self.get_image_size_with_most_features()
695
-
696
- return self.get_num_image_tokens(
697
- image_width=target_width,
698
- image_height=target_height,
699
- image_processor=image_processor,
700
- mm_kwargs={},
701
- )
702
-
703
-
704
-class HunYuanVLDummyInputsBuilder(BaseDummyInputsBuilder[HunYuanVLProcessingInfo]):
705
- def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
706
- num_images = mm_counts.get("image", 0)
707
-
708
- hf_processor = self.info.get_hf_processor(typ=HunYuanVLProcessor)
709
- image_placeholder = (
710
- f"{hf_processor.image_start_token}{hf_processor.image_token}"
711
- f"{hf_processor.image_end_token}"
712
- )
713
-
714
- return image_placeholder * num_images
715
-
716
- def get_dummy_mm_data(
717
- self,
718
- seq_len: int,
719
- mm_counts: Mapping[str, int],
720
- mm_options: Mapping[str, BaseDummyOptions],
721
- ) -> MultiModalDataDict:
722
- num_images = mm_counts.get("image", 1)
723
-
724
- target_width, target_height = self.info.get_image_size_with_most_features()
725
-
726
- return {
727
- "image": self._get_dummy_images(
728
- width=target_width, height=target_height, num_images=num_images
729
- ),
730
- }
731
-
732
-
733
-class HunYuanVLMultiModalProcessor(BaseMultiModalProcessor[HunYuanVLProcessingInfo]):
734
- def _get_prompt_updates(
735
- self,
736
- mm_items: MultiModalDataItems,
737
- hf_processor_mm_kwargs: Mapping[str, Any],
738
- out_mm_kwargs: MultiModalKwargsItems,
739
- ) -> Sequence[PromptUpdate]:
740
- hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
741
- image_processor = self.info.get_image_processor(**hf_processor_mm_kwargs)
742
-
743
- token_ids = {
744
- "image": hf_processor.image_token_id,
745
- "image_start": hf_processor.image_start_token_id,
746
- "image_end": hf_processor.image_end_token_id,
747
- }
748
-
749
- merge_size = image_processor.merge_size
750
-
751
- def get_replacement_hunyuan_vl(item_idx: int, modality: str):
752
- out_item = out_mm_kwargs[modality][item_idx]
753
- grid_thw = out_item[f"{modality}_grid_thw"].data
754
- assert isinstance(grid_thw, torch.Tensor)
755
-
756
- _, grid_h, grid_w = grid_thw
757
- num_tokens = (int(grid_h) // merge_size) * (
758
- int(grid_w) // merge_size + 1
759
- ) + 2
760
- tokens = (
761
- [token_ids[f"{modality}_start"]]
762
- + [token_ids[modality]] * num_tokens
763
- + [token_ids[f"{modality}_end"]]
764
- )
765
- return PromptUpdateDetails.select_token_id(tokens, token_ids[modality])
766
-
767
- return [
768
- PromptReplacement(
769
- modality=modality,
770
- target=[
771
- token_ids[f"{modality}_start"],
772
- token_ids[modality],
773
- token_ids[f"{modality}_end"],
774
- ],
775
- replacement=partial(get_replacement_hunyuan_vl, modality=modality),
776
- )
777
- for modality in ("image",)
778
- ]
779
-
780
- def _get_mm_fields_config(
781
- self,
782
- hf_inputs: BatchFeature,
783
- hf_processor_mm_kwargs: Mapping[str, object],
784
- ) -> Mapping[str, MultiModalFieldConfig]:
785
- return _hunyuan_vl_field_config(hf_inputs)
786
-
787
-
788
-@MULTIMODAL_REGISTRY.register_processor(
789
- HunYuanVLMultiModalProcessor,
790
- info=HunYuanVLProcessingInfo,
791
- dummy_inputs=HunYuanVLDummyInputsBuilder,
792
-)
793
-class HunYuanVLForConditionalGeneration(
794
- nn.Module,
795
- SupportsMultiModal,
796
- SupportsLoRA,
797
- SupportsPP,
798
- SupportsQuant,
799
- SupportsXDRoPE,
800
- SupportsEagle,
801
- SupportsEagle3,
802
-):
803
- # To ensure correct weight loading and mapping.
804
- hf_to_vllm_mapper = WeightsMapper(
805
- orig_to_new_prefix={
806
- # mapping for new names in checkpoint saved after transformers v4.52
807
- "vit.vit.": "visual.",
808
- "vit.": "visual.",
809
- "model.": "language_model.model.",
810
- }
811
- )
812
-
813
- supports_encoder_tp_data = True
814
-
815
- def get_xdrope_input_positions(
816
- self,
817
- input_tokens: list[int],
818
- mm_features: list[MultiModalFeatureSpec],
819
- ) -> torch.Tensor:
820
- kwargs = MultiModalFeatureSpec.gather_kwargs(
821
- mm_features,
822
- {"image_grid_thw"},
823
- )
824
- image_grid_thw = [item.tolist() for item in kwargs.get("image_grid_thw", [])]
825
-
826
- hf_config = self.config
827
- image_start_token_id = hf_config.image_start_token_id
828
- spatial_merge_size = hf_config.vision_config.spatial_merge_size
829
- xd_num = len(hf_config.rope_scaling["xdrope_section"])
830
-
831
- input_tokens_tensor = torch.tensor(input_tokens)
832
- image_start_indices = torch.argwhere(
833
- input_tokens_tensor == image_start_token_id
834
- ).squeeze(1)
835
-
836
- p_index = torch.arange(len(input_tokens_tensor))
837
- w_index = torch.arange(len(input_tokens_tensor))
838
- h_index = torch.arange(len(input_tokens_tensor))
839
- t_index = torch.arange(len(input_tokens_tensor))
840
- for image_index in range(len(image_start_indices)):
841
- # +1 : first image_token, +2: for xdrope positions
842
- pos = image_start_indices[image_index] + 2
843
- t, h, w = image_grid_thw[image_index]
844
- _, llm_grid_h, llm_grid_w = (
845
- t,
846
- h // spatial_merge_size,
847
- w // spatial_merge_size,
848
- )
849
-
850
- token_num = (llm_grid_w + 1) * llm_grid_h
851
- w_index[pos : pos + token_num].copy_(
852
- torch.arange(0, llm_grid_w + 1)
853
- .reshape(1, -1)
854
- .expand(llm_grid_h, -1)
855
- .reshape(-1)
856
- )
857
- h_index[pos : pos + token_num].copy_(
858
- torch.arange(0, llm_grid_h)
859
- .reshape(-1, 1)
860
- .expand(-1, llm_grid_w + 1)
861
- .reshape(-1)
862
- )
863
- t_index[pos : pos + token_num] = image_index
864
-
865
- if xd_num == 4:
866
- llm_positions = torch.stack([p_index, w_index, h_index, t_index])
867
- elif xd_num == 3:
868
- llm_positions = torch.stack([w_index, h_index, t_index])
869
-
870
- return llm_positions
871
-
872
- @classmethod
873
- def get_placeholder_str(cls, modality: str, i: int) -> str | None:
874
- if modality.startswith("image"):
875
- return "<|hy_place▁holder▁no▁100|><|hy_place▁holder▁no▁102|><|hy_place▁holder▁no▁101|>" # noqa: E501
876
-
877
- raise ValueError("Only image modality is supported")
878
-
879
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
880
- super().__init__()
881
- config: HunYuanVLConfig = vllm_config.model_config.hf_config
882
-
883
- self.config = config
884
-
885
- with self._mark_tower_model(vllm_config, {"image"}):
886
- self.visual = HunYuanVisionTransformer(
887
- config.vision_config,
888
- quant_config=vllm_config.quant_config,
889
- prefix=maybe_prefix(prefix, "visual"),
890
- )
891
-
892
- with self._mark_language_model(vllm_config):
893
- self.language_model = init_vllm_registered_model(
894
- vllm_config=vllm_config,
895
- prefix=maybe_prefix(prefix, "language_model.model"),
896
- architectures=[
897
- "HunYuanDenseV1ForCausalLM",
898
- "HunYuanMoEV1ForCausalLM",
899
- ],
900
- )
901
-
902
- self.make_empty_intermediate_tensors = (
903
- self.language_model.make_empty_intermediate_tensors
904
- )
905
-
906
- def _parse_and_validate_image_input(
907
- self, **kwargs: object
908
- ) -> HunYuanVLImageInputs | None:
909
- pixel_values = kwargs.pop("pixel_values", None)
910
- image_embeds = kwargs.pop("image_embeds", None)
911
- image_grid_thw = kwargs.pop("image_grid_thw", None)
912
-
913
- if pixel_values is None and image_embeds is None:
914
- return None
915
-
916
- # TODO: refine
917
- if isinstance(pixel_values, list):
918
- pixel_values = torch.cat(pixel_values, dim=0)
919
- if len(pixel_values.shape) == 3:
920
- last_dim = pixel_values.shape[-1]
921
- pixel_values = pixel_values.reshape(-1, last_dim)
922
- image_grid_thw = image_grid_thw.reshape(-1, 3)
923
-
924
- if pixel_values is not None:
925
- return HunYuanVLImagePixelInputs(
926
- type="pixel_values",
927
- pixel_values=pixel_values,
928
- image_grid_thw=image_grid_thw,
929
- )
930
-
931
- if image_embeds is not None:
932
- return HunYuanVLImageEmbeddingInputs(
933
- type="image_embeds",
934
- image_embeds=image_embeds,
935
- image_grid_thw=image_grid_thw,
936
- )
937
-
938
- def _process_image_input(
939
- self, image_input: HunYuanVLImageInputs
940
- ) -> tuple[torch.Tensor, ...]:
941
- grid_thw = image_input["image_grid_thw"]
942
- assert grid_thw.ndim == 2
943
- grid_thw_list = grid_thw.tolist()
944
-
945
- if image_input["type"] == "image_embeds":
946
- image_embeds = image_input["image_embeds"].type(self.visual.dtype)
947
- else:
948
- pixel_values = image_input["pixel_values"]
949
-
950
- # TODO: use_data_parallel (split image_embeds in visual)
951
- image_embeds = self.visual(pixel_values, grid_thw=grid_thw_list)
952
-
953
- return image_embeds
954
-
955
- def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict:
956
- mm_input_by_modality = {}
957
-
958
- # Preserve the order of modalities if there are multiple of them
959
- # from the order of kwargs.
960
- for input_key in kwargs:
961
- if (
962
- input_key in ("pixel_values", "image_embeds")
963
- and "image" not in mm_input_by_modality
964
- ):
965
- mm_input_by_modality["image"] = self._parse_and_validate_image_input(
966
- **kwargs
967
- )
968
- return mm_input_by_modality
969
-
970
- def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
971
- mm_input_by_modality = self._parse_and_validate_multimodal_inputs(**kwargs)
972
- if not mm_input_by_modality:
973
- return []
974
-
975
- # The result multimodal_embeddings is tuple of tensors, with each
976
- # tensor correspoending to a multimodal data item (image or video).
977
- multimodal_embeddings: tuple[torch.Tensor, ...] = ()
978
-
979
- # NOTE: It is important to iterate over the keys in this dictionary
980
- # to preserve the order of the modalities.
981
- for modality in mm_input_by_modality:
982
- multimodal_input = mm_input_by_modality[modality]
983
- if modality == "image":
984
- image_embeddings = self._process_image_input(multimodal_input)
985
- multimodal_embeddings += tuple(image_embeddings)
986
- return multimodal_embeddings
987
-
988
- def forward(
989
- self,
990
- input_ids: torch.Tensor | None,
991
- positions: torch.Tensor,
992
- intermediate_tensors: IntermediateTensors | None,
993
- inputs_embeds: torch.Tensor | None,
994
- **kwargs: object,
995
- ) -> torch.Tensor | IntermediateTensors:
996
- if intermediate_tensors is not None:
997
- inputs_embeds = None
998
-
999
- hidden_states = self.language_model(
1000
- input_ids=input_ids,
1001
- positions=positions,
1002
- intermediate_tensors=intermediate_tensors,
1003
- inputs_embeds=inputs_embeds,
1004
- )
1005
- return hidden_states
1006
-
1007
- def compute_logits(
1008
- self,
1009
- hidden_states: torch.Tensor,
1010
- ) -> torch.Tensor | None:
1011
- return self.language_model.compute_logits(hidden_states)
1012
-
1013
- def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1014
- loader = AutoWeightsLoader(self)
1015
- return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
1016
-
1017
- def get_mm_mapping(self) -> MultiModelKeys:
1018
- """
1019
- Get the module prefix in multimodal models
1020
- """
1021
- return MultiModelKeys.from_string_field(
1022
- language_model="language_model.model",
1023
- connector="visual.perceive",
1024
- tower_model="visual",
1025
- )
vllm/model_executor/models/olmo3.py
deleted
-412
@@ -1,412 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-# Adapted from
5
-# https://github.com/huggingface/transformers/blob/main/src/transformers/models/olmo3/modeling_olmo3.py
6
-# Copyright 2024 The vLLM team.
7
-# Copyright 2024 EleutherAI and the HuggingFace Inc. team. All rights reserved.
8
-#
9
-# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
10
-# and OPT implementations in this library. It has been modified from its
11
-# original forms to accommodate minor architectural differences compared
12
-# to GPT-NeoX and OPT used by the Meta AI team that trained the model.
13
-#
14
-# Licensed under the Apache License, Version 2.0 (the "License");
15
-# you may not use this file except in compliance with the License.
16
-# You may obtain a copy of the License at
17
-#
18
-# http://www.apache.org/licenses/LICENSE-2.0
19
-#
20
-# Unless required by applicable law or agreed to in writing, software
21
-# distributed under the License is distributed on an "AS IS" BASIS,
22
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
-# See the License for the specific language governing permissions and
24
-# limitations under the License.
25
-"""Inference-only OLMo3 model compatible with HuggingFace weights."""
26
-
27
-from collections.abc import Iterable
28
-from functools import partial
29
-from itertools import islice
30
-
31
-import torch
32
-from torch import nn
33
-from transformers import Olmo3Config
34
-
35
-from vllm.compilation.decorators import support_torch_compile
36
-from vllm.config import VllmConfig
37
-from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
38
-from vllm.distributed.communication_op import tensor_model_parallel_all_gather
39
-from vllm.distributed.parallel_state import get_tensor_model_parallel_rank
40
-from vllm.distributed.utils import split_tensor_along_last_dim
41
-from vllm.model_executor.layers.activation import SiluAndMul
42
-from vllm.model_executor.layers.attention import Attention
43
-from vllm.model_executor.layers.layernorm import RMSNorm
44
-from vllm.model_executor.layers.linear import (
45
- MergedColumnParallelLinear,
46
- QKVParallelLinear,
47
- RowParallelLinear,
48
-)
49
-from vllm.model_executor.layers.logits_processor import LogitsProcessor
50
-from vllm.model_executor.layers.rotary_embedding import get_rope
51
-from vllm.model_executor.layers.vocab_parallel_embedding import (
52
- ParallelLMHead,
53
- VocabParallelEmbedding,
54
-)
55
-from vllm.model_executor.models.interfaces import SupportsLoRA, SupportsPP
56
-from vllm.model_executor.models.utils import (
57
- AutoWeightsLoader,
58
- WeightsMapper,
59
- extract_layer_index,
60
- make_empty_intermediate_tensors_factory,
61
- make_layers,
62
- maybe_prefix,
63
-)
64
-from vllm.sequence import IntermediateTensors
65
-
66
-
67
-class Olmo3Attention(nn.Module):
68
- """
69
- This is the attention block where the output is computed as
70
- `Attention(LN(x))` in `MLP(LN(x + Attention(LN(x))))`
71
- (plus another skip connection).
72
- """
73
-
74
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
75
- super().__init__()
76
- self.config = vllm_config.model_config.hf_config
77
- assert isinstance(self.config, Olmo3Config)
78
-
79
- hidden_size = self.config.hidden_size
80
- self.tp_size = get_tensor_model_parallel_world_size()
81
- self.total_num_heads = self.config.num_attention_heads
82
-
83
- assert hidden_size % self.total_num_heads == 0
84
- assert self.total_num_heads % self.tp_size == 0
85
-
86
- self.num_heads = self.total_num_heads // self.tp_size
87
- self.total_num_kv_heads = (
88
- self.config.num_key_value_heads or self.total_num_heads
89
- )
90
- if self.total_num_kv_heads >= self.tp_size:
91
- assert self.total_num_kv_heads % self.tp_size == 0
92
- else:
93
- assert self.tp_size % self.total_num_kv_heads == 0
94
-
95
- self.num_kv_heads = max(1, self.total_num_kv_heads // self.tp_size)
96
- self.head_dim = hidden_size // self.total_num_heads
97
- self.q_size = self.num_heads * self.head_dim
98
- self.kv_size = self.num_kv_heads * self.head_dim
99
- self.max_position_embeddings = self.config.max_position_embeddings
100
-
101
- # Attention input projection. Projects x -> (q, k, v)
102
- self.qkv_proj = QKVParallelLinear(
103
- hidden_size,
104
- self.head_dim,
105
- self.total_num_heads,
106
- self.total_num_kv_heads,
107
- bias=False,
108
- quant_config=vllm_config.quant_config,
109
- prefix=f"{prefix}.qkv_proj",
110
- )
111
-
112
- self.tp_rank = get_tensor_model_parallel_rank()
113
- self.k_norm = RMSNorm(
114
- self.total_num_kv_heads * self.head_dim,
115
- eps=self.config.rms_norm_eps,
116
- )
117
- self.q_norm = RMSNorm(self.config.hidden_size, eps=self.config.rms_norm_eps)
118
-
119
- self.scaling = self.head_dim**-0.5
120
-
121
- layer_idx = extract_layer_index(prefix)
122
- sliding_window = None
123
- if (
124
- layer_types := getattr(self.config, "layer_types", None)
125
- ) is not None and layer_types[layer_idx] == "sliding_attention":
126
- sliding_window = self.config.sliding_window
127
-
128
- self.attn = Attention(
129
- self.num_heads,
130
- self.head_dim,
131
- self.scaling,
132
- num_kv_heads=self.num_kv_heads,
133
- cache_config=vllm_config.cache_config,
134
- quant_config=vllm_config.quant_config,
135
- per_layer_sliding_window=sliding_window,
136
- prefix=f"{prefix}.attn",
137
- )
138
-
139
- # Rotary embeddings. Rope scaling is only applied on full attention layers.
140
- rope_parameters = self.config.rope_parameters
141
- attn_type = "full_attention" if sliding_window is None else "sliding_attention"
142
- rope_parameters = rope_parameters.get(attn_type, rope_parameters)
143
- self.rotary_emb = get_rope(
144
- self.head_dim,
145
- max_position=self.max_position_embeddings,
146
- rope_parameters=rope_parameters,
147
- )
148
-
149
- # Attention output projection.
150
- self.o_proj = RowParallelLinear(
151
- self.total_num_heads * self.head_dim,
152
- hidden_size,
153
- bias=False,
154
- quant_config=vllm_config.quant_config,
155
- prefix=f"{prefix}.o_proj",
156
- )
157
-
158
- def _apply_qk_norm(
159
- self, q: torch.Tensor, k: torch.Tensor
160
- ) -> tuple[torch.Tensor, torch.Tensor]:
161
- if self.tp_size > 1:
162
- q = tensor_model_parallel_all_gather(q.contiguous())
163
- k = tensor_model_parallel_all_gather(k.contiguous())
164
- q = self.q_norm(q)
165
- k = self.k_norm(k)
166
- if self.tp_size > 1:
167
- splitter = partial(split_tensor_along_last_dim, num_partitions=self.tp_size)
168
- q = splitter(q)[self.tp_rank]
169
- k = splitter(k)[self.tp_rank]
170
- return q, k
171
-
172
- def forward(
173
- self,
174
- positions: torch.Tensor,
175
- hidden_states: torch.Tensor,
176
- ) -> torch.Tensor:
177
- qkv, _ = self.qkv_proj(hidden_states)
178
- q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
179
- q, k = self._apply_qk_norm(q, k)
180
- q, k = self.rotary_emb(positions, q, k)
181
- attn_output = self.attn(q, k, v)
182
- output, _ = self.o_proj(attn_output)
183
- return output
184
-
185
-
186
-class Olmo3MLP(nn.Module):
187
- """
188
- This is the MLP block where the output is computed as
189
- `MLP(x)` in `LN(MLP(x + LN(Attention(x))))`
190
- (plus another skip connection).
191
- """
192
-
193
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
194
- super().__init__()
195
- config = vllm_config.model_config.hf_config
196
- assert isinstance(config, Olmo3Config)
197
- hidden_size = config.hidden_size
198
- intermediate_size = config.intermediate_size
199
-
200
- # Feed-forward input projection.
201
- self.gate_up_proj = MergedColumnParallelLinear(
202
- hidden_size,
203
- [intermediate_size] * 2,
204
- bias=False,
205
- quant_config=vllm_config.quant_config,
206
- prefix=f"{prefix}.gate_up_proj",
207
- )
208
-
209
- # Activation function.
210
- self.act_fn = SiluAndMul()
211
-
212
- # Feed-forward output projection.
213
- self.down_proj = RowParallelLinear(
214
- intermediate_size,
215
- hidden_size,
216
- bias=False,
217
- quant_config=vllm_config.quant_config,
218
- prefix=f"{prefix}.down_proj",
219
- )
220
-
221
- def forward(
222
- self,
223
- x: torch.Tensor,
224
- ) -> torch.Tensor:
225
- gate_up, _ = self.gate_up_proj(x)
226
- x = self.act_fn(gate_up)
227
- x, _ = self.down_proj(x)
228
- return x
229
-
230
-
231
-class Olmo3DecoderLayer(nn.Module):
232
- """
233
- This is a typical transformer block where the output is
234
- computed as `MLP(LN(x + Attention(LN(x))))`
235
- (plus another skip connection).
236
- """
237
-
238
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
239
- super().__init__()
240
- config = vllm_config.model_config.hf_config
241
- assert isinstance(config, Olmo3Config)
242
- # Attention block.
243
- self.self_attn = Olmo3Attention(
244
- vllm_config=vllm_config, prefix=f"{prefix}.self_attn"
245
- )
246
-
247
- # MLP block.
248
- self.mlp = Olmo3MLP(vllm_config=vllm_config, prefix=f"{prefix}.mlp")
249
-
250
- # LayerNorm
251
- self.post_attention_layernorm = RMSNorm(
252
- config.hidden_size, eps=config.rms_norm_eps
253
- )
254
-
255
- self.post_feedforward_layernorm = RMSNorm(
256
- config.hidden_size, eps=config.rms_norm_eps
257
- )
258
-
259
- def forward(
260
- self,
261
- positions: torch.Tensor,
262
- hidden_states: torch.Tensor,
263
- ) -> torch.Tensor:
264
- # Attention block.
265
- residual = hidden_states
266
- hidden_states = self.self_attn(positions, hidden_states)
267
- hidden_states = self.post_attention_layernorm(hidden_states)
268
- hidden_states = hidden_states + residual
269
-
270
- # MLP block.
271
- residual = hidden_states
272
- hidden_states = self.mlp(hidden_states)
273
- hidden_states = self.post_feedforward_layernorm(hidden_states)
274
- hidden_states = residual + hidden_states
275
- return hidden_states
276
-
277
-
278
-@support_torch_compile
279
-class Olmo3Model(nn.Module):
280
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
281
- super().__init__()
282
- self.config = vllm_config.model_config.hf_config
283
- assert isinstance(self.config, Olmo3Config)
284
-
285
- self.embed_tokens = VocabParallelEmbedding(
286
- self.config.vocab_size,
287
- self.config.hidden_size,
288
- prefix=f"{prefix}.embed_tokens",
289
- )
290
- self.start_layer, self.end_layer, self.layers = make_layers(
291
- self.config.num_hidden_layers,
292
- lambda prefix: Olmo3DecoderLayer(vllm_config=vllm_config, prefix=prefix),
293
- prefix=f"{prefix}.layers",
294
- )
295
- self.norm = RMSNorm(
296
- self.config.hidden_size,
297
- eps=self.config.rms_norm_eps,
298
- )
299
- self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
300
- ["hidden_states"], self.config.hidden_size
301
- )
302
-
303
- def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
304
- return self.embed_tokens(input_ids)
305
-
306
- def forward(
307
- self,
308
- input_ids: torch.Tensor | None,
309
- positions: torch.Tensor,
310
- intermediate_tensors: IntermediateTensors | None,
311
- inputs_embeds: torch.Tensor | None = None,
312
- ) -> torch.Tensor | IntermediateTensors:
313
- """
314
- Args:
315
- input_ids: A tensor of shape `(batch_size, seq_len)`.
316
- """
317
- if get_pp_group().is_first_rank:
318
- if inputs_embeds is not None:
319
- hidden_states = inputs_embeds
320
- # Get embeddings of input.
321
- # shape: (batch_size, seq_len, d_model)
322
- else:
323
- hidden_states = self.embed_tokens(input_ids)
324
-
325
- else:
326
- assert intermediate_tensors is not None
327
- hidden_states = intermediate_tensors["hidden_states"]
328
- assert isinstance(hidden_states, torch.Tensor)
329
-
330
- # Apply blocks one-by-one.
331
- for layer in islice(self.layers, self.start_layer, self.end_layer):
332
- # shape: (batch_size, seq_len, d_model)
333
- hidden_states = layer(positions, hidden_states)
334
-
335
- if not get_pp_group().is_last_rank:
336
- return IntermediateTensors({"hidden_states": hidden_states})
337
-
338
- # Apply final layer norm.
339
- # shape: (batch_size, seq_len or 1, d_model)
340
- hidden_states = self.norm(hidden_states)
341
- return hidden_states
342
-
343
-
344
-class Olmo3ForCausalLM(nn.Module, SupportsPP, SupportsLoRA):
345
- """
346
- Extremely barebones HF model wrapper.
347
- """
348
-
349
- hf_to_vllm_mapper = WeightsMapper(
350
- orig_to_new_stacked={
351
- # weight_name: (param_name, shard_id)
352
- ".q_proj": (".qkv_proj", "q"),
353
- ".k_proj": (".qkv_proj", "k"),
354
- ".v_proj": (".qkv_proj", "v"),
355
- ".gate_proj": (".gate_up_proj", 0),
356
- ".up_proj": (".gate_up_proj", 1),
357
- }
358
- )
359
- packed_modules_mapping = {
360
- "qkv_proj": ["q_proj", "k_proj", "v_proj"],
361
- "gate_up_proj": ["gate_proj", "up_proj"],
362
- }
363
-
364
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
365
- super().__init__()
366
- config = vllm_config.model_config.hf_config
367
- assert isinstance(config, Olmo3Config)
368
- self.config = config
369
- self.model = Olmo3Model(
370
- vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
371
- )
372
- self.lm_head = ParallelLMHead(
373
- config.vocab_size,
374
- config.hidden_size,
375
- quant_config=vllm_config.quant_config,
376
- prefix=maybe_prefix(prefix, "lm_head"),
377
- )
378
- if config.tie_word_embeddings:
379
- self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
380
- self.logits_processor = LogitsProcessor(config.vocab_size)
381
- self.make_empty_intermediate_tensors = (
382
- self.model.make_empty_intermediate_tensors
383
- )
384
-
385
- def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
386
- return self.model.embed_input_ids(input_ids)
387
-
388
- def forward(
389
- self,
390
- input_ids: torch.Tensor | None,
391
- positions: torch.Tensor,
392
- intermediate_tensors: IntermediateTensors | None = None,
393
- inputs_embeds: torch.Tensor | None = None,
394
- ) -> torch.Tensor | IntermediateTensors:
395
- hidden_states = self.model(
396
- input_ids=input_ids,
397
- positions=positions,
398
- intermediate_tensors=intermediate_tensors,
399
- inputs_embeds=inputs_embeds,
400
- )
401
- return hidden_states
402
-
403
- def compute_logits(
404
- self,
405
- hidden_states: torch.Tensor,
406
- ) -> torch.Tensor | None:
407
- logits = self.logits_processor(self.lm_head, hidden_states)
408
- return logits
409
-
410
- def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
411
- loader = AutoWeightsLoader(self)
412
- return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
vllm/model_executor/models/registry.py
+8
-8
@@ -104,7 +104,6 @@ _TEXT_GENERATION_MODELS = {
104
"FalconForCausalLM": ("falcon", "FalconForCausalLM"),
105
"FalconMambaForCausalLM": ("mamba", "MambaForCausalLM"),
106
"FalconH1ForCausalLM": ("falcon_h1", "FalconH1ForCausalLM"),
107
- "FlexOlmoForCausalLM": ("flex_olmo", "FlexOlmoForCausalLM"),
107
"GemmaForCausalLM": ("gemma", "GemmaForCausalLM"),
108
"Gemma2ForCausalLM": ("gemma2", "Gemma2ForCausalLM"),
109
"Gemma3ForCausalLM": ("gemma3", "Gemma3ForCausalLM"),
@@ -128,8 +127,6 @@ _TEXT_GENERATION_MODELS = {
127
"GraniteMoeSWAForCausalLM": ("granitemoeshared", "GraniteMoeSharedForCausalLM"),
128
"GraniteSWAForCausalLM": ("granite", "GraniteForCausalLM"),
129
"HrmTextForCausalLM": ("hrm_text", "HrmTextForCausalLM"),
131
- "HunYuanMoEV1ForCausalLM": ("hunyuan_v1", "HunYuanMoEV1ForCausalLM"),
132
- "HunYuanDenseV1ForCausalLM": ("hunyuan_v1", "HunYuanDenseV1ForCausalLM"),
130
"HYV3ForCausalLM": ("hy_v3", "HYV3ForCausalLM"),
131
"HCXVisionV2ForCausalLM": ("hyperclovax_vision_v2", "HCXVisionV2ForCausalLM"),
132
"HyperCLOVAXForCausalLM": ("hyperclovax", "HyperCLOVAXForCausalLM"),
@@ -180,7 +177,6 @@ _TEXT_GENERATION_MODELS = {
177
"NemotronForCausalLM": ("nemotron", "NemotronForCausalLM"),
178
"NemotronHForCausalLM": ("nemotron_h", "NemotronHForCausalLM"),
179
"NemotronHPuzzleForCausalLM": ("nemotron_h", "NemotronHForCausalLM"),
183
- "Olmo3ForCausalLM": ("olmo3", "Olmo3ForCausalLM"),
180
"MuseGlimmerForCausalLM": ("muse_glimmer", "MuseGlimmerForCausalLM"),
181
"OlmoHybridForCausalLM": ("olmo_hybrid", "OlmoHybridForCausalLM"),
182
"OlmoeForCausalLM": ("olmoe", "OlmoeForCausalLM"),
@@ -420,10 +416,6 @@ _MULTIMODAL_MODELS = {
416
"Granite4VisionForConditionalGeneration",
417
),
418
"H2OVLChatModel": ("h2ovl", "H2OVLChatModel"),
423
- "HunYuanVLForConditionalGeneration": (
424
- "hunyuan_vision",
425
- "HunYuanVLForConditionalGeneration",
426
- ),
419
"InternVLChatModel": ("internvl", "InternVLChatModel"),
420
"InternS1ForConditionalGeneration": (
421
"interns1",
@@ -679,9 +671,13 @@ _SPECULATIVE_DECODING_MODELS = {
671
672
_TRANSFORMERS_SUPPORTED_MODELS = {
673
# Text generation models
674
+ "FlexOlmoForCausalLM": ("transformers", "TransformersMoEForCausalLM"),
675
"GPTBigCodeForCausalLM": ("transformers", "TransformersForCausalLM"),
676
+ "HunYuanDenseV1ForCausalLM": ("transformers", "TransformersForCausalLM"),
677
+ "HunYuanMoEV1ForCausalLM": ("transformers", "TransformersMoEForCausalLM"),
678
"OlmoForCausalLM": ("transformers", "TransformersForCausalLM"),
679
"Olmo2ForCausalLM": ("transformers", "TransformersForCausalLM"),
680
+ "Olmo3ForCausalLM": ("transformers", "TransformersForCausalLM"),
681
"SmolLM3ForCausalLM": ("transformers", "TransformersForCausalLM"),
682
"Starcoder2ForCausalLM": ("transformers", "TransformersForCausalLM"),
683
"VaultGemmaForCausalLM": ("transformers", "TransformersForCausalLM"),
@@ -690,6 +686,10 @@ _TRANSFORMERS_SUPPORTED_MODELS = {
686
"transformers",
687
"TransformersMultiModalForCausalLM",
688
),
689
+ "HunYuanVLForConditionalGeneration": (
690
+ "transformers",
691
+ "TransformersMultiModalForCausalLM",
692
+ ),
693
"VibeVoiceAsrForConditionalGeneration": (
694
"transformers",
695
"TransformersMultiModalForCausalLM",
vllm/model_executor/models/transformers/moe.py
+19
-6
@@ -37,10 +37,10 @@ from vllm.model_executor.layers.fused_moe import (
37
from vllm.model_executor.models.interfaces import MixtureOfExperts
38
from vllm.model_executor.models.transformers.fuser import get_fuser
39
from vllm.model_executor.models.transformers.fusers.moe import MoEBlockFuser
40
-from vllm.model_executor.models.utils import maybe_prefix
40
+from vllm.model_executor.models.utils import extract_layer_index, maybe_prefix
41
from vllm.utils.torch_utils import STR_DTYPE_TO_TORCH_DTYPE, direct_register_custom_op
42
43
-from .utils import log_replacement
43
+from .utils import log_replacement, maybe_per_layer
44
45
if TYPE_CHECKING:
46
from vllm.config import VllmConfig
@@ -183,7 +183,7 @@ class MoEMixin(MixtureOfExperts):
183
)
184
185
# Common kwargs
186
- renormalize = getattr(text_config, "norm_topk_prob", top_k > 1)
186
+ norm_topk_prob = getattr(text_config, "norm_topk_prob", None)
187
188
# Routed scaling factor kwargs
189
routed_scaling_factor = getattr(text_config, "routed_scaling_factor", 1.0)
@@ -266,12 +266,25 @@ class MoEMixin(MixtureOfExperts):
266
self.num_shared_experts = 1
267
break
268
269
+ # Only pay for the layer index if something is per-layer;
270
+ # `extract_layer_index` raises for unnumbered module paths.
271
+ per_layer = (top_k, intermediate_size, norm_topk_prob)
272
+ layer_idx = (
273
+ extract_layer_index(qual_name)
274
+ if any(isinstance(v, list) for v in per_layer)
275
+ else 0
276
+ )
277
+ layer_top_k = maybe_per_layer(top_k, layer_idx)
278
+ layer_renormalize = maybe_per_layer(norm_topk_prob, layer_idx)
279
+ if layer_renormalize is None:
280
+ layer_renormalize = layer_top_k > 1
281
+
282
kwargs: dict[str, Any] = dict(
283
num_experts=num_experts,
271
- top_k=top_k,
284
+ top_k=layer_top_k,
285
hidden_size=hidden_size,
273
- intermediate_size=intermediate_size,
274
- renormalize=renormalize,
286
+ intermediate_size=maybe_per_layer(intermediate_size, layer_idx),
287
+ renormalize=layer_renormalize,
288
quant_config=self.quant_config,
289
prefix=qual_name,
290
activation=activation,
vllm/model_executor/models/transformers/multimodal.py
+5
@@ -173,6 +173,11 @@ class MultiModalDummyInputsBuilder(BaseDummyInputsBuilder[MultiModalProcessingIn
173
image_token = processor.boi_token
174
else:
175
image_token = getattr(processor, "image_token", "")
176
+ # Some processors (e.g. HunYuanVL) reject a bare image token and
177
+ # require each one to be wrapped in its start/end markers.
178
+ start_token = getattr(processor, "image_start_token", "")
179
+ end_token = getattr(processor, "image_end_token", "")
180
+ image_token = f"{start_token}{image_token}{end_token}"
181
text += image_token * num_images
182
return text
183
vllm/model_executor/models/transformers/utils.py
+9
-1
@@ -22,7 +22,7 @@ from functools import lru_cache
22
from itertools import chain
23
from operator import attrgetter
24
from pathlib import Path
25
-from typing import TYPE_CHECKING, Literal
25
+from typing import TYPE_CHECKING, Literal, TypeVar
26
27
import torch
28
from torch import nn
@@ -306,6 +306,14 @@ def recursive_replace_linear(
306
_recursive_replace(model, prefix=prefix)
307
308
309
+T = TypeVar("T")
310
+
311
+
312
+def maybe_per_layer(value: T | list[T], layer_idx: int) -> T:
313
+ """Pick `layer_idx`'s entry from a config field that may be sized per layer."""
314
+ return value[layer_idx] if isinstance(value, list) else value
315
+
316
+
317
def named_state(module: nn.Module) -> Iterator[tuple[str, torch.Tensor]]:
318
"""`module`'s own state (i.e. named parameters and buffers)."""
319
return chain(module.named_parameters(), module.named_buffers())
vllm/transformers_utils/config.py
-2
@@ -87,11 +87,9 @@ _CONFIG_REGISTRY: dict[str, type[PretrainedConfig]] = LazyConfigDict(
87
deepseek_v4="DeepseekV4Config",
88
dots3_note="Dots3NoteConfig",
89
k3_dspark="K3DSparkConfig",
90
- flex_olmo="FlexOlmoConfig",
90
funaudiochat="FunAudioChatConfig",
91
granite4_vision="Granite4VisionConfig",
92
hyperclovax="HyperCLOVAXConfig",
94
- hunyuan_vl="HunYuanVLConfig",
93
hy_v3="HYV3Config",
94
isaac="IsaacConfig",
95
kimi_k2="DeepseekV3Config", # Kimi K2 uses same architecture as DeepSeek V3
vllm/transformers_utils/configs/__init__.py
-8
@@ -37,13 +37,9 @@ _CLASS_TO_MODULE: dict[str, str] = {
37
"K3DSparkConfig": "vllm.transformers_utils.configs.k3_dspark",
38
"DotsOCRConfig": "vllm.transformers_utils.configs.dotsocr",
39
"EAGLEConfig": "vllm.transformers_utils.configs.eagle",
40
- "FlexOlmoConfig": "vllm.transformers_utils.configs.flex_olmo",
40
"FunAudioChatConfig": "vllm.transformers_utils.configs.funaudiochat",
41
"FunAudioChatAudioEncoderConfig": "vllm.transformers_utils.configs.funaudiochat",
42
"Granite4VisionConfig": "vllm.transformers_utils.configs.granite4_vision",
44
- "HunYuanVLConfig": "vllm.transformers_utils.configs.hunyuan_vl",
45
- "HunYuanVLTextConfig": "vllm.transformers_utils.configs.hunyuan_vl",
46
- "HunYuanVLVisionConfig": "vllm.transformers_utils.configs.hunyuan_vl",
43
"HYV3Config": "vllm.transformers_utils.configs.hy_v3",
44
"HyperCLOVAXConfig": "vllm.transformers_utils.configs.hyperclovax",
45
"IsaacConfig": "vllm.transformers_utils.configs.isaac",
@@ -130,13 +126,9 @@ __all__ = [
126
"K3DSparkConfig",
127
"DotsOCRConfig",
128
"EAGLEConfig",
133
- "FlexOlmoConfig",
129
"FunAudioChatConfig",
130
"FunAudioChatAudioEncoderConfig",
131
"Granite4VisionConfig",
137
- "HunYuanVLConfig",
138
- "HunYuanVLTextConfig",
139
- "HunYuanVLVisionConfig",
132
"HYV3Config",
133
"HyperCLOVAXConfig",
134
"IsaacConfig",
vllm/transformers_utils/configs/flex_olmo.py
deleted
-82
@@ -1,82 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-from typing import Any
4
-
5
-from transformers.configuration_utils import PretrainedConfig
6
-
7
-
8
-class FlexOlmoConfig(PretrainedConfig):
9
- model_type = "flex_olmo"
10
- keys_to_ignore_at_inference = ["past_key_values"]
11
-
12
- def __init__(
13
- self,
14
- vocab_size=100352,
15
- hidden_size=4096,
16
- intermediate_size=11008,
17
- num_hidden_layers=32,
18
- num_attention_heads=32,
19
- num_key_value_heads=None,
20
- hidden_act="silu",
21
- max_position_embeddings=4096,
22
- initializer_range=0.02,
23
- rms_norm_eps=1e-06,
24
- use_cache=True,
25
- pad_token_id=100277,
26
- bos_token_id=None,
27
- eos_token_id=100257,
28
- tie_word_embeddings=False,
29
- rope_parameters: dict[str, Any] | None = None,
30
- attention_bias=False,
31
- attention_dropout=0.0,
32
- num_experts_per_tok=5,
33
- num_experts=7,
34
- output_router_logits=False,
35
- router_aux_loss_coef=0.01,
36
- norm_topk_prob=False,
37
- **kwargs,
38
- ):
39
- if "architectures" not in kwargs:
40
- kwargs["architectures"] = ["FlexOlmoForCausalLM"]
41
-
42
- self.vocab_size = vocab_size
43
- self.max_position_embeddings = max_position_embeddings
44
- self.hidden_size = hidden_size
45
- self.intermediate_size = intermediate_size
46
- self.num_hidden_layers = num_hidden_layers
47
- self.num_attention_heads = num_attention_heads
48
-
49
- # for backward compatibility
50
- if num_key_value_heads is None:
51
- num_key_value_heads = num_attention_heads
52
-
53
- self.num_key_value_heads = num_key_value_heads
54
- self.hidden_act = hidden_act
55
- self.initializer_range = initializer_range
56
- self.rms_norm_eps = rms_norm_eps
57
- self.use_cache = use_cache
58
- # Try to set `rope_scaling` if available, otherwise use `rope_parameters`
59
- rope_scaling = kwargs.pop("rope_scaling", None)
60
- rope_parameters = rope_scaling or rope_parameters or {"rope_type": "default"}
61
- rope_theta = kwargs.pop("rope_theta", 500000.0)
62
- if "rope_theta" not in rope_parameters:
63
- rope_parameters["rope_theta"] = rope_theta
64
- self.rope_parameters = rope_parameters
65
- self.attention_bias = attention_bias
66
- self.attention_dropout = attention_dropout
67
- self.num_experts_per_tok = num_experts_per_tok
68
- self.num_experts = num_experts
69
- self.output_router_logits = output_router_logits
70
- self.router_aux_loss_coef = router_aux_loss_coef
71
- self.norm_topk_prob = norm_topk_prob
72
- # Validate the correctness of rotary position embeddings parameters
73
- # BC: if there is a 'type' field, move it to 'rope_type'.
74
- if self.rope_parameters is not None and "type" in self.rope_parameters:
75
- self.rope_parameters["rope_type"] = self.rope_parameters["type"]
76
- super().__init__(
77
- pad_token_id=pad_token_id,
78
- bos_token_id=bos_token_id,
79
- eos_token_id=eos_token_id,
80
- tie_word_embeddings=tie_word_embeddings,
81
- **kwargs,
82
- )
vllm/transformers_utils/configs/hunyuan_vl.py
deleted
-281
@@ -1,281 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-# adapted from https://github.com/ManaEstras/transformers/blob/v4.57.1.hyvl/src/transformers/models/hunyuan_vl/configuration_hunyuan_vl.py
5
-
6
-from transformers import PretrainedConfig
7
-
8
-
9
-class HunYuanVLVisionConfig(PretrainedConfig):
10
- model_type = "hunyuan_vl"
11
- base_config_key = "vision_config"
12
-
13
- def __init__(
14
- self,
15
- hidden_act="gelu",
16
- hidden_size=1152,
17
- intermediate_size=4304,
18
- interpolate_mode="bilinear",
19
- rms_norm_eps=1e-05,
20
- learnable_mlp_pooling_size=0,
21
- num_attention_heads=16,
22
- num_key_value_heads=None,
23
- num_channels=3,
24
- num_hidden_layers=27,
25
- out_hidden_size=4096,
26
- patch_size=16,
27
- remove_prenorm=True,
28
- spatial_merge_size=2,
29
- temporal_patch_size=1,
30
- resize_resolution=2048,
31
- img_max_token_num=4096,
32
- max_image_size=2048,
33
- video_max_image_size=768,
34
- video_min_image_size=256,
35
- min_image_size=512,
36
- anyres_vit_max_image_size=2048,
37
- max_vit_seq_len=16384,
38
- text_hidden_size=3072,
39
- **kwargs,
40
- ):
41
- super().__init__(**kwargs)
42
-
43
- self.hidden_act = hidden_act
44
- self.hidden_size = hidden_size
45
- self.intermediate_size = intermediate_size
46
- self.interpolate_mode = interpolate_mode
47
- self.learnable_mlp_pooling_size = learnable_mlp_pooling_size
48
- self.num_attention_heads = num_attention_heads
49
- if not num_key_value_heads:
50
- self.num_key_value_heads = num_attention_heads
51
- else:
52
- self.num_key_value_heads = num_key_value_heads
53
- self.num_channels = num_channels
54
- self.num_hidden_layers = num_hidden_layers
55
- self.out_hidden_size = out_hidden_size
56
- self.patch_size = patch_size
57
- self.remove_prenorm = remove_prenorm
58
- self.spatial_merge_size = spatial_merge_size
59
- self.temporal_patch_size = temporal_patch_size
60
- self.rms_norm_eps = rms_norm_eps
61
-
62
- self.resize_resolution = resize_resolution
63
- self.img_max_token_num = img_max_token_num
64
- self.max_image_size = max_image_size
65
- self.min_image_size = min_image_size
66
- self.video_max_image_size = video_max_image_size
67
- self.video_min_image_size = video_min_image_size
68
- self.anyres_vit_max_image_size = anyres_vit_max_image_size
69
- self.max_vit_seq_len = max_vit_seq_len
70
- self.text_hidden_size = text_hidden_size
71
-
72
-
73
-class HunYuanVLTextConfig(PretrainedConfig):
74
- r"""
75
- This is the configuration class to store the configuration of a [`HunYuanVLTextConfig`]. It is used to instantiate an
76
- HunYuan model according to the specified arguments, defining the model architecture. Instantiating a configuration
77
- with the defaults will yield a similar configuration to that of the HunYuan-7B.
78
- Hunyuan-7B-Instruct [tencent/Hunyuan-7B-Instruct](https://huggingface.co/tencent/Hunyuan-7B-Instruct).
79
-
80
- Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
81
- documentation from [`PretrainedConfig`] for more information.
82
-
83
-
84
- Args:
85
- vocab_size (`int`, *optional*, defaults to 290943):
86
- Vocabulary size of the HunYuan model. Defines the number of different tokens that can be represented by the
87
- `inputs_ids` passed when calling [`HunYuanVLTextConfig`]
88
- hidden_size (`int`, *optional*, defaults to 4096):
89
- Dimension of the hidden representations.
90
- intermediate_size (`int`, *optional*, defaults to 11008):
91
- Dimension of the MLP representations or shared MLP representations.
92
- num_hidden_layers (`int`, *optional*, defaults to 32):
93
- Number of hidden layers in the Transformer decoder.
94
- num_attention_heads (`int`, *optional*, defaults to 32):
95
- Number of attention heads for each attention layer in the Transformer decoder.
96
- num_key_value_heads (`int`, *optional*):
97
- This is the number of key_value heads that should be used to implement Grouped Query Attention. If
98
- `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
99
- `num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When
100
- converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
101
- by meanpooling all the original heads within that group. For more details checkout [this
102
- paper](https://huggingface.co/papers/2305.13245). If it is not specified, will default to
103
- `num_attention_heads`.
104
- hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
105
- The non-linear activation function (function or string) in the decoder.
106
- max_position_embeddings (`int`, *optional*, defaults to 2048):
107
- The maximum sequence length that this model might ever be used with.
108
- initializer_range (`float`, *optional*, defaults to 0.02):
109
- The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
110
- rms_norm_eps (`float`, *optional*, defaults to 1e-05):
111
- The epsilon used by the rms normalization layers.
112
- use_cache (`bool`, *optional*, defaults to `True`):
113
- Whether or not the model should return the last key/values attentions (not used by all models). Only
114
- relevant if `config.is_decoder=True`.
115
- pad_token_id (`int`, *optional*, defaults to 0):
116
- Padding token id.
117
- bos_token_id (`int`, *optional*, defaults to 1):
118
- Beginning of stream token id.
119
- eos_token_id (`int`, *optional*, defaults to 2):
120
- End of stream token id.
121
- eod_token_id (int, *optional*, defaults to 3):
122
- Token ID representing the end-of-document marker. Used to indicate the termination of a text sequence.
123
- Example: In multi-document processing, this token helps the model distinguish between separate documents.
124
- pretraining_tp (`int`, *optional*, defaults to 1):
125
- Experimental feature. Tensor parallelism rank used during pretraining. Please refer to [this
126
- document](https://huggingface.co/docs/transformers/parallelism) to understand more about it. This value is
127
- necessary to ensure exact reproducibility of the pretraining results. Please refer to [this
128
- issue](https://github.com/pytorch/pytorch/issues/76232).
129
- tie_word_embeddings (`bool`, *optional*, defaults to `False`):
130
- Whether to tie weight embeddings
131
- rope_theta (`float`, *optional*, defaults to 10000.0):
132
- The base period of the RoPE embeddings.
133
- rope_scaling (`Dict`, *optional*):
134
- Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling
135
- strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is
136
- `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update
137
- `max_position_embeddings` to the expected new maximum. See the following thread for more information on how
138
- these scaling strategies behave:
139
- https://www.reddit.com/r/LocalLLaMA/comments/14mrgpr/dynamically_scaled_rope_further_increases/. This is an
140
- experimental feature, subject to breaking API changes in future versions.
141
- attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
142
- Whether to use a bias in the query, key, value and output projection layers during self-attention.
143
- attention_dropout (`float`, *optional*, defaults to 0.0):
144
- The dropout ratio for the attention probabilities.
145
- head_dim (`int`, *optional*, defaults to 128):
146
- The attention head dimension.
147
- """ # noqa: E501
148
-
149
- model_type = "hunyuan_vl_text"
150
- keys_to_ignore_at_inference = ["past_key_values"]
151
-
152
- def __init__(
153
- self,
154
- vocab_size=290943,
155
- hidden_size=4096,
156
- intermediate_size: int = 11008,
157
- num_hidden_layers=32,
158
- num_attention_heads=32,
159
- num_key_value_heads=None,
160
- hidden_act="silu",
161
- max_position_embeddings=2048,
162
- initializer_range=0.02,
163
- rms_norm_eps=1e-5,
164
- use_cache=True,
165
- pad_token_id=0,
166
- bos_token_id=1,
167
- eos_token_id=2,
168
- eod_token_id=3,
169
- pretraining_tp=1,
170
- tie_word_embeddings=False,
171
- rope_theta=10000.0,
172
- rope_scaling=None,
173
- attention_bias=False,
174
- attention_dropout=0.0,
175
- head_dim=None,
176
- **kwargs,
177
- ):
178
- self.vocab_size = vocab_size
179
- self.max_position_embeddings = max_position_embeddings
180
- self.hidden_size = hidden_size
181
- self.intermediate_size = intermediate_size
182
- self.num_hidden_layers = num_hidden_layers
183
- self.num_attention_heads = num_attention_heads
184
- self.head_dim = head_dim
185
- # for backward compatibility
186
- if num_key_value_heads is None:
187
- num_key_value_heads = num_attention_heads
188
-
189
- self.num_key_value_heads = num_key_value_heads
190
- self.hidden_act = hidden_act
191
- self.initializer_range = initializer_range
192
- self.rms_norm_eps = rms_norm_eps
193
- self.pretraining_tp = pretraining_tp
194
- self.use_cache = use_cache
195
- self.rope_theta = rope_theta
196
- self.rope_scaling = rope_scaling
197
- self.attention_bias = attention_bias
198
- self.attention_dropout = attention_dropout
199
-
200
- super().__init__(
201
- pad_token_id=pad_token_id,
202
- bos_token_id=bos_token_id,
203
- eos_token_id=eos_token_id,
204
- tie_word_embeddings=tie_word_embeddings,
205
- **kwargs,
206
- )
207
-
208
-
209
-class HunYuanVLConfig(PretrainedConfig):
210
- model_type = "hunyuan_vl"
211
- sub_configs = {
212
- "vision_config": HunYuanVLVisionConfig,
213
- "text_config": HunYuanVLTextConfig,
214
- }
215
- keys_to_ignore_at_inference = ["past_key_values"]
216
-
217
- def __init__(
218
- self,
219
- text_config=None,
220
- vision_config=None,
221
- im_start_id=120118,
222
- im_end_id=120119,
223
- image_token_id=120120,
224
- im_newline_id=120121,
225
- video_start_id=120122,
226
- video_end_id=120123,
227
- **kwargs,
228
- ):
229
- # We need to init super() here so that it does not reset values
230
- # that are in text config to the BaseClass defaults. The Base
231
- # config has many text related defaults and not all defaults are
232
- # same as for `HunYuanVLTextConfig`.
233
- super().__init__(**kwargs)
234
-
235
- if isinstance(vision_config, dict):
236
- self.vision_config = self.sub_configs["vision_config"](**vision_config)
237
- elif vision_config is None:
238
- self.vision_config = self.sub_configs["vision_config"]()
239
-
240
- if isinstance(text_config, dict):
241
- self.text_config = self.sub_configs["text_config"](**text_config)
242
- elif text_config is None:
243
- # For BC use all kwargs to init `TextConfig`
244
- self.text_config = self.sub_configs["text_config"](**kwargs)
245
-
246
- self.image_token_id = image_token_id
247
- self.im_start_id = im_start_id
248
- self.im_end_id = im_end_id
249
- self.im_newline_id = im_newline_id
250
- self.video_start_id = video_start_id
251
- self.video_end_id = video_end_id
252
-
253
- self.vision_config.text_hidden_size = self.text_config.hidden_size
254
-
255
- # Attention implementation to use. It sets it recursively on sub-configs
256
- # so we call it again in the end.
257
- self._attn_implementation = kwargs.pop("attn_implementation", None)
258
-
259
- def __setattr__(self, key, value):
260
- if (
261
- (text_config := super().__getattribute__("__dict__").get("text_config"))
262
- is not None
263
- and key not in ["dtype", "_attn_implementation_internal"]
264
- and key in text_config.__dict__
265
- ):
266
- setattr(text_config, key, value)
267
- else:
268
- super().__setattr__(key, value)
269
-
270
- def __getattribute__(self, key):
271
- if "text_config" in super().__getattribute__("__dict__") and key not in [
272
- "_name_or_path",
273
- "model_type",
274
- "dtype",
275
- "_attn_implementation_internal",
276
- ]:
277
- text_config = super().__getattribute__("text_config")
278
- if key in text_config.__dict__:
279
- return getattr(text_config, key)
280
-
281
- return super().__getattribute__(key)