@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 287471b994
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 3, 09:15 UTC

[Model Refactoring] Migrate DeepSeek V4 to vllm/models/ [1/N] (#43004)

Signed-off-by: Woosuk Kwon <woosuk@inferact.ai>

Woosuk Kwon committed May 18, 2026 at 19:50 UTC 287471b99442b44c5a16c4d70b0f3e178dd52732
12 files changed +189 -126
tests/models/test_deepseek_v4_mega_moe.py
+1 -1
@@ -6,7 +6,7 @@ from types import SimpleNamespace
6 import pytest
7 import torch
8
9 -from vllm.model_executor.models.deepseek_v4 import (
9 +from vllm.models.deepseek_v4.nvidia.deepseek_v4 import (
10 DeepseekV4MegaMoEExperts,
11 _stage_deepseek_v4_mega_moe_inputs,
12 make_deepseek_v4_expert_params_mapping,
vllm/model_executor/layers/quantization/__init__.py
+1 -1
@@ -113,7 +113,7 @@ def get_quantization_config(quantization: str) -> type[QuantizationConfig]:
113 # lazy import to avoid triggering `torch.compile` too early
114 from vllm.config.quantization import _ONLINE_SHORTHANDS
115 from vllm.model_executor.layers.quantization.quark.quark import QuarkConfig
116 - from vllm.model_executor.models.deepseek_v4 import DeepseekV4FP8Config
116 + from vllm.models.deepseek_v4 import DeepseekV4FP8Config
117
118 from .auto_gptq import AutoGPTQConfig
119 from .awq import AWQConfig
vllm/model_executor/models/registry.py
+26 -5
@@ -6,6 +6,7 @@ Whenever you add an architecture to this page, please also update
6 """
7
8 import importlib
9 +import importlib.util
10 import json
11 import os
12 import pickle
@@ -97,7 +98,7 @@ _TEXT_GENERATION_MODELS = {
98 "DeepseekV2ForCausalLM": ("deepseek_v2", "DeepseekV2ForCausalLM"),
99 "DeepseekV3ForCausalLM": ("deepseek_v2", "DeepseekV3ForCausalLM"),
100 "DeepseekV32ForCausalLM": ("deepseek_v2", "DeepseekV3ForCausalLM"),
100 - "DeepseekV4ForCausalLM": ("deepseek_v4", "DeepseekV4ForCausalLM"),
101 + "DeepseekV4ForCausalLM": ("vllm.models.deepseek_v4", "DeepseekV4ForCausalLM"),
102 "Dots1ForCausalLM": ("dots1", "Dots1ForCausalLM"),
103 "Ernie4_5ForCausalLM": ("ernie45", "Ernie4_5ForCausalLM"),
104 "Ernie4_5_MoeForCausalLM": ("ernie45_moe", "Ernie4_5_MoeForCausalLM"),
@@ -611,7 +612,7 @@ _SPECULATIVE_DECODING_MODELS = {
612 "Eagle3DeepseekV3ForCausalLM": ("deepseek_eagle3", "Eagle3DeepseekV2ForCausalLM"),
613 "EagleDeepSeekMTPModel": ("deepseek_eagle", "EagleDeepseekV3ForCausalLM"),
614 "DeepSeekMTPModel": ("deepseek_mtp", "DeepSeekMTP"),
614 - "DeepSeekV4MTPModel": ("deepseek_v4_mtp", "DeepSeekV4MTP"),
615 + "DeepSeekV4MTPModel": ("vllm.models.deepseek_v4", "DeepSeekV4MTP"),
616 "Gemma4MTPModel": ("gemma4_mtp", "Gemma4MTP"),
617 "ErnieMTPModel": ("ernie_mtp", "ErnieMTP"),
618 "ExaoneMoeMTP": ("exaone_moe_mtp", "ExaoneMoeMTP"),
@@ -870,10 +871,21 @@ class _LazyRegisteredModel(_BaseRegisteredModel):
871
872 @logtime(logger=logger, msg="Registry inspect model class")
873 def inspect_model_cls(self) -> _ModelInfo:
873 - model_path = Path(__file__).parent / f"{self.module_name.split('.')[-1]}.py"
874 + # Modules registered with a non-default location (e.g. the
875 + # hardware-isolated ``vllm.models.<name>`` layout) live outside
876 + # ``vllm/model_executor/models``. Resolve the module spec directly
877 + # so the file-hash cache stays warm for them.
878 + if self.module_name.startswith("vllm.model_executor.models."):
879 + model_path = Path(__file__).parent / f"{self.module_name.split('.')[-1]}.py"
880 + else:
881 + try:
882 + spec = importlib.util.find_spec(self.module_name)
883 + except (ImportError, ValueError):
884 + spec = None
885 + model_path = Path(spec.origin) if spec is not None and spec.origin else None
886 module_hash = None
887
876 - if model_path.exists():
888 + if model_path is not None and model_path.exists():
889 with open(model_path, "rb") as f:
890 module_hash = safe_hash(f.read(), usedforsecurity=False).hexdigest()
891
@@ -1328,10 +1340,19 @@ class _ModelRegistry:
1340 return model_cls.supports_transcription_only
1341
1342
1343 +def _resolve_module_name(mod_relname: str) -> str:
1344 + # Allow registry entries to point at fully-qualified module paths (e.g.
1345 + # ``vllm.models.deepseek_v4``) for models that live outside the legacy
1346 + # ``vllm.model_executor.models`` flat layout.
1347 + if mod_relname.startswith("vllm."):
1348 + return mod_relname
1349 + return f"vllm.model_executor.models.{mod_relname}"
1350 +
1351 +
1352 ModelRegistry = _ModelRegistry(
1353 {
1354 model_arch: _LazyRegisteredModel(
1334 - module_name=f"vllm.model_executor.models.{mod_relname}",
1355 + module_name=_resolve_module_name(mod_relname),
1356 class_name=cls_name,
1357 )
1358 for model_arch, (mod_relname, cls_name) in _VLLM_MODELS.items()
vllm/models/__init__.py new
+2
@@ -0,0 +1,2 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
vllm/models/deepseek_v4/__init__.py new
+30
@@ -0,0 +1,30 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""DeepSeek V4 model — hardware-isolated entry point.
4 +
5 +The actual implementation lives under ``nvidia/`` and ``amd/``; this module
6 +picks the right one for the current platform and re-exports the public
7 +classes used by the model registry and quantization config lookup.
8 +"""
9 +
10 +from typing import TYPE_CHECKING
11 +
12 +from vllm.platforms import current_platform
13 +
14 +from .quant_config import DeepseekV4FP8Config
15 +
16 +# Pick the per-platform implementation. The NVIDIA branch is the static
17 +# default that mypy sees; the ROCm branch overrides it at runtime and is
18 +# kept type-compatible via ``# type: ignore[assignment]``.
19 +if TYPE_CHECKING or not current_platform.is_rocm():
20 + from .nvidia.deepseek_v4 import DeepseekV4ForCausalLM
21 + from .nvidia.deepseek_v4_mtp import DeepSeekV4MTP
22 +else:
23 + from .amd.deepseek_v4 import DeepseekV4ForCausalLM # type: ignore[assignment]
24 + from .amd.deepseek_v4_mtp import DeepSeekV4MTP # type: ignore[assignment]
25 +
26 +__all__ = [
27 + "DeepSeekV4MTP",
28 + "DeepseekV4FP8Config",
29 + "DeepseekV4ForCausalLM",
30 +]
vllm/models/deepseek_v4/amd/__init__.py new
+2
@@ -0,0 +1,2 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
vllm/models/deepseek_v4/amd/deepseek_v4.py new
+1
@@ -0,0 +1 @@
1 +../nvidia/deepseek_v4.py
\ No newline at end of file
vllm/models/deepseek_v4/amd/deepseek_v4_mtp.py new
+1
@@ -0,0 +1 @@
1 +../nvidia/deepseek_v4_mtp.py
\ No newline at end of file
vllm/models/deepseek_v4/nvidia/__init__.py new
+2
@@ -0,0 +1,2 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
vllm/models/deepseek_v4/nvidia/deepseek_v4.py renamed
+11 -114
@@ -9,7 +9,7 @@ import torch
9 import torch.nn as nn
10
11 from vllm.compilation.decorators import support_torch_compile
12 -from vllm.config import VllmConfig, get_current_vllm_config
12 +from vllm.config import VllmConfig
13 from vllm.distributed import (
14 get_ep_group,
15 get_pp_group,
@@ -24,7 +24,6 @@ from vllm.model_executor.layers.deepseek_v4_attention import (
24 DeepseekV4MultiHeadLatentAttentionWrapper,
25 )
26 from vllm.model_executor.layers.fused_moe import FusedMoE
27 -from vllm.model_executor.layers.fused_moe.layer import UnquantizedFusedMoEMethod
27 from vllm.model_executor.layers.fused_moe.router.fused_topk_bias_router import (
28 fused_topk_bias,
29 )
@@ -44,15 +43,7 @@ from vllm.model_executor.layers.mhc import (
43 MHCPostOp,
44 MHCPreOp,
45 )
47 -from vllm.model_executor.layers.quantization import (
48 - QuantizationConfig,
49 - QuantizationMethods,
50 -)
51 -from vllm.model_executor.layers.quantization.fp8 import Fp8Config
52 -from vllm.model_executor.layers.quantization.mxfp4 import Mxfp4MoEMethod
53 -from vllm.model_executor.layers.quantization.utils.quant_utils import (
54 - is_layer_skipped,
55 -)
46 +from vllm.model_executor.layers.quantization import QuantizationConfig
47 from vllm.model_executor.layers.rotary_embedding import get_rope
48 from vllm.model_executor.layers.vocab_parallel_embedding import (
49 ParallelLMHead,
@@ -60,13 +51,7 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
51 )
52 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
53 from vllm.model_executor.models.interfaces import SupportsPP
63 -from vllm.model_executor.utils import set_weight_attrs
64 -from vllm.platforms import current_platform
65 -from vllm.sequence import IntermediateTensors
66 -from vllm.triton_utils import tl, triton
67 -from vllm.utils.torch_utils import direct_register_custom_op
68 -
69 -from .utils import (
54 +from vllm.model_executor.models.utils import (
55 AutoWeightsLoader,
56 PPMissingLayer,
57 WeightsMapper,
@@ -75,8 +60,11 @@ from .utils import (
60 make_layers,
61 maybe_prefix,
62 )
78 -
79 -_DEEPSEEK_V4_EXPERT_DTYPES = ("fp4", "fp8")
63 +from vllm.model_executor.utils import set_weight_attrs
64 +from vllm.platforms import current_platform
65 +from vllm.sequence import IntermediateTensors
66 +from vllm.triton_utils import tl, triton
67 +from vllm.utils.torch_utils import direct_register_custom_op
68
69
70 class DeepseekV4MLP(nn.Module):
@@ -130,97 +118,6 @@ class DeepseekV4MLP(nn.Module):
118 return x
119
120
133 -class DeepseekV4FP8Config(Fp8Config):
134 - """FP8 config for DeepSeek V4 with expert-dtype-aware MoE dispatch.
135 -
136 - DeepSeek V4 checkpoints always use FP8 block quantization for
137 - linear/attention layers. The MoE expert weights vary by checkpoint:
138 - - ``expert_dtype="fp4"`` (e.g. DeepSeek-V4-Flash): MXFP4 experts
139 - with ue8m0 (e8m0fnu) FP8 linear scales.
140 - - ``expert_dtype="fp8"`` (e.g. DeepSeek-V4-Flash-Base): FP8 block
141 - experts with float32 FP8 linear scales.
142 -
143 - The dispatch and the linear scale dtype are both keyed off
144 - ``expert_dtype`` from the model's hf_config; missing values default
145 - to ``"fp4"`` so existing FP4 checkpoints stay unchanged.
146 -
147 - NOTE: ``expert_dtype`` is resolved lazily because this config is
148 - constructed during VllmConfig setup, before ``set_current_vllm_config``
149 - is active. Reading hf_config eagerly in ``__init__`` would always see
150 - the default ``"fp4"`` and silently misroute Flash-Base checkpoints.
151 - """
152 -
153 - def __init__(self, *args, **kwargs):
154 - super().__init__(*args, **kwargs)
155 - self._resolved_expert_dtype: str | None = None
156 - # ``is_scale_e8m0`` is a property that resolves on first read,
157 - # by which time the current vllm_config has been set.
158 -
159 - @property
160 - def expert_dtype(self) -> str:
161 - if self._resolved_expert_dtype is None:
162 - try:
163 - hf_config = get_current_vllm_config().model_config.hf_config
164 - except Exception:
165 - # vllm_config not yet set; defer the decision until a
166 - # later call lands inside set_current_vllm_config.
167 - return "fp4"
168 - expert_dtype = getattr(hf_config, "expert_dtype", "fp4")
169 - if expert_dtype not in _DEEPSEEK_V4_EXPERT_DTYPES:
170 - raise ValueError(
171 - f"Unsupported DeepSeek V4 expert_dtype={expert_dtype!r}; "
172 - f"expected one of {_DEEPSEEK_V4_EXPERT_DTYPES}."
173 - )
174 - self._resolved_expert_dtype = expert_dtype
175 - from vllm.logger import init_logger
176 -
177 - init_logger(__name__).info_once(
178 - "DeepSeek V4 expert_dtype resolved to %r", expert_dtype
179 - )
180 - return self._resolved_expert_dtype
181 -
182 - @property
183 - def is_scale_e8m0(self) -> bool:
184 - # FP4 checkpoints store FP8 linear scales as e8m0fnu; FP8 expert
185 - # checkpoints (Flash-Base) store them as float32.
186 - return self.expert_dtype == "fp4"
187 -
188 - @classmethod
189 - def get_name(cls) -> QuantizationMethods:
190 - return "deepseek_v4_fp8"
191 -
192 - @classmethod
193 - def override_quantization_method(
194 - cls, hf_quant_cfg, user_quant, hf_config=None
195 - ) -> QuantizationMethods | None:
196 - if not (
197 - isinstance(hf_quant_cfg, dict)
198 - and hf_quant_cfg.get("quant_method") in ("fp8", "deepseek_v4_fp8")
199 - ):
200 - return None
201 - model_type = getattr(hf_config, "model_type", None)
202 - if model_type == "deepseek_v4" or user_quant == "deepseek_v4_fp8":
203 - return "deepseek_v4_fp8"
204 - return None
205 -
206 - def get_quant_method(self, layer, prefix):
207 - if isinstance(layer, FusedMoE):
208 - if is_layer_skipped(
209 - prefix=prefix,
210 - ignored_layers=self.ignored_layers,
211 - fused_mapping=self.packed_modules_mapping,
212 - ):
213 - return UnquantizedFusedMoEMethod(layer.moe_config)
214 - if self.expert_dtype == "fp4":
215 - return Mxfp4MoEMethod(layer.moe_config)
216 - # expert_dtype == "fp8": fall through to Fp8Config which
217 - # returns Fp8MoEMethod with block-wise float32 scales.
218 - return super().get_quant_method(layer, prefix)
219 -
220 - def is_mxfp4_quant(self, prefix, layer):
221 - return isinstance(layer, FusedMoE) and self.expert_dtype == "fp4"
222 -
223 -
121 @triton.jit
122 def _deepseek_v4_stage_mega_moe_inputs_kernel(
123 hidden_states,
@@ -1535,7 +1432,7 @@ class DeepseekV4Model(nn.Module):
1432 ):
1433 loaded_weight = loaded_weight.view(torch.uint8)
1434 for mapping in expert_mapping:
1538 - param_name, weight_name, expert_id, shard_id = mapping
1435 + param_name, weight_name, expert_id, expert_shard_id = mapping
1436 if weight_name not in name:
1437 continue
1438 name_mapped = name.replace(weight_name, param_name)
@@ -1552,7 +1449,7 @@ class DeepseekV4Model(nn.Module):
1449 param,
1450 loaded_weight,
1451 name_mapped,
1555 - shard_id=shard_id,
1452 + shard_id=expert_shard_id,
1453 expert_id=expert_id,
1454 return_success=True,
1455 )
@@ -1673,7 +1570,7 @@ class DeepseekV4ForCausalLM(nn.Module, SupportsPP):
1570 else:
1571 self.lm_head = PPMissingLayer()
1572 self.logits_processor = LogitsProcessor(config.vocab_size)
1676 - self.make_empty_intermediate_tensors = (
1573 + self.make_empty_intermediate_tensors = ( # type: ignore[method-assign]
1574 self.model.make_empty_intermediate_tensors
1575 )
1576
vllm/models/deepseek_v4/nvidia/deepseek_v4_mtp.py renamed
+6 -5
@@ -34,16 +34,16 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
34 VocabParallelEmbedding,
35 )
36 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
37 +from vllm.model_executor.models.deepseek_mtp import SharedHead
38 +from vllm.model_executor.models.deepseek_v2 import get_spec_layer_idx_from_weight_name
39 +from vllm.model_executor.models.utils import maybe_prefix
40 from vllm.platforms import current_platform
41 from vllm.sequence import IntermediateTensors
42
40 -from .deepseek_mtp import SharedHead
41 -from .deepseek_v2 import get_spec_layer_idx_from_weight_name
43 from .deepseek_v4 import (
44 DeepseekV4DecoderLayer,
45 make_deepseek_v4_expert_params_mapping,
46 )
46 -from .utils import maybe_prefix
47
48 logger = init_logger(__name__)
49
@@ -68,6 +68,7 @@ class DeepSeekV4MultiTokenPredictorLayer(nn.Module):
68 ) -> None:
69 super().__init__()
70
71 + assert vllm_config.speculative_config is not None
72 config = vllm_config.speculative_config.draft_model_config.hf_config
73 self.config = config
74 quant_config = vllm_config.quant_config
@@ -407,7 +408,7 @@ class DeepSeekV4MTP(nn.Module):
408 ):
409 loaded_weight = loaded_weight.view(torch.uint8)
410 for mapping in expert_mapping:
410 - param_name, weight_name, expert_id, shard_id = mapping
411 + param_name, weight_name, expert_id, expert_shard_id = mapping
412 if weight_name not in name:
413 continue
414 name_mapped = name.replace(weight_name, param_name)
@@ -422,7 +423,7 @@ class DeepSeekV4MTP(nn.Module):
423 param,
424 loaded_weight,
425 name_mapped,
425 - shard_id=shard_id,
426 + shard_id=expert_shard_id,
427 expert_id=expert_id,
428 return_success=True,
429 )
vllm/models/deepseek_v4/quant_config.py new
+106
@@ -0,0 +1,106 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""Quantization config for DeepSeek V4."""
4 +
5 +from vllm.config import get_current_vllm_config
6 +from vllm.model_executor.layers.fused_moe import FusedMoE
7 +from vllm.model_executor.layers.fused_moe.layer import UnquantizedFusedMoEMethod
8 +from vllm.model_executor.layers.quantization import QuantizationMethods
9 +from vllm.model_executor.layers.quantization.fp8 import Fp8Config
10 +from vllm.model_executor.layers.quantization.mxfp4 import Mxfp4MoEMethod
11 +from vllm.model_executor.layers.quantization.utils.quant_utils import (
12 + is_layer_skipped,
13 +)
14 +
15 +_DEEPSEEK_V4_EXPERT_DTYPES = ("fp4", "fp8")
16 +
17 +
18 +class DeepseekV4FP8Config(Fp8Config):
19 + """FP8 config for DeepSeek V4 with expert-dtype-aware MoE dispatch.
20 +
21 + DeepSeek V4 checkpoints always use FP8 block quantization for
22 + linear/attention layers. The MoE expert weights vary by checkpoint:
23 + - ``expert_dtype="fp4"`` (e.g. DeepSeek-V4-Flash): MXFP4 experts
24 + with ue8m0 (e8m0fnu) FP8 linear scales.
25 + - ``expert_dtype="fp8"`` (e.g. DeepSeek-V4-Flash-Base): FP8 block
26 + experts with float32 FP8 linear scales.
27 +
28 + The dispatch and the linear scale dtype are both keyed off
29 + ``expert_dtype`` from the model's hf_config; missing values default
30 + to ``"fp4"`` so existing FP4 checkpoints stay unchanged.
31 +
32 + NOTE: ``expert_dtype`` is resolved lazily because this config is
33 + constructed during VllmConfig setup, before ``set_current_vllm_config``
34 + is active. Reading hf_config eagerly in ``__init__`` would always see
35 + the default ``"fp4"`` and silently misroute Flash-Base checkpoints.
36 + """
37 +
38 + def __init__(self, *args, **kwargs):
39 + super().__init__(*args, **kwargs)
40 + self._resolved_expert_dtype: str | None = None
41 + # ``is_scale_e8m0`` is a property that resolves on first read,
42 + # by which time the current vllm_config has been set.
43 +
44 + @property
45 + def expert_dtype(self) -> str:
46 + if self._resolved_expert_dtype is None:
47 + try:
48 + hf_config = get_current_vllm_config().model_config.hf_config
49 + except Exception:
50 + # vllm_config not yet set; defer the decision until a
51 + # later call lands inside set_current_vllm_config.
52 + return "fp4"
53 + expert_dtype = getattr(hf_config, "expert_dtype", "fp4")
54 + if expert_dtype not in _DEEPSEEK_V4_EXPERT_DTYPES:
55 + raise ValueError(
56 + f"Unsupported DeepSeek V4 expert_dtype={expert_dtype!r}; "
57 + f"expected one of {_DEEPSEEK_V4_EXPERT_DTYPES}."
58 + )
59 + self._resolved_expert_dtype = expert_dtype
60 + from vllm.logger import init_logger
61 +
62 + init_logger(__name__).info_once(
63 + "DeepSeek V4 expert_dtype resolved to %r", expert_dtype
64 + )
65 + return self._resolved_expert_dtype
66 +
67 + @property
68 + def is_scale_e8m0(self) -> bool:
69 + # FP4 checkpoints store FP8 linear scales as e8m0fnu; FP8 expert
70 + # checkpoints (Flash-Base) store them as float32.
71 + return self.expert_dtype == "fp4"
72 +
73 + @classmethod
74 + def get_name(cls) -> QuantizationMethods:
75 + return "deepseek_v4_fp8"
76 +
77 + @classmethod
78 + def override_quantization_method(
79 + cls, hf_quant_cfg, user_quant, hf_config=None
80 + ) -> QuantizationMethods | None:
81 + if not (
82 + isinstance(hf_quant_cfg, dict)
83 + and hf_quant_cfg.get("quant_method") in ("fp8", "deepseek_v4_fp8")
84 + ):
85 + return None
86 + model_type = getattr(hf_config, "model_type", None)
87 + if model_type == "deepseek_v4" or user_quant == "deepseek_v4_fp8":
88 + return "deepseek_v4_fp8"
89 + return None
90 +
91 + def get_quant_method(self, layer, prefix):
92 + if isinstance(layer, FusedMoE):
93 + if is_layer_skipped(
94 + prefix=prefix,
95 + ignored_layers=self.ignored_layers,
96 + fused_mapping=self.packed_modules_mapping,
97 + ):
98 + return UnquantizedFusedMoEMethod(layer.moe_config)
99 + if self.expert_dtype == "fp4":
100 + return Mxfp4MoEMethod(layer.moe_config)
101 + # expert_dtype == "fp8": fall through to Fp8Config which
102 + # returns Fp8MoEMethod with block-wise float32 scales.
103 + return super().get_quant_method(layer, prefix)
104 +
105 + def is_mxfp4_quant(self, prefix, layer):
106 + return isinstance(layer, FusedMoE) and self.expert_dtype == "fp4"