@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 92be3f3517
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 14, 05:30 UTC

[Feature] Use pydantic validation in parallel.py config (#26417)

Signed-off-by: simondanielsson <simon.danielsson99@hotmail.com> Co-authored-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Simon Danielsson committed Oct 9, 2025 at 14:41 UTC 92be3f351748964f160e19d9c2a805b59fbaaf99
1 file changed +54 -55
vllm/config/parallel.py
+54 -55
@@ -3,11 +3,10 @@
3
4 import hashlib
5 import os
6 -from dataclasses import field
6 from typing import TYPE_CHECKING, Any, Literal, Optional, Union
7
8 import torch
10 -from pydantic import model_validator
9 +from pydantic import Field, model_validator
10 from pydantic.dataclasses import dataclass
11 from torch.distributed import ProcessGroup, ReduceOp
12 from typing_extensions import Self
@@ -32,6 +31,7 @@ logger = init_logger(__name__)
31
32 ExpertPlacementStrategy = Literal["linear", "round_robin"]
33 DistributedExecutorBackend = Literal["ray", "mp", "uni", "external_launcher"]
34 +DataParallelBackend = Literal["ray", "mp"]
35
36
37 @config
@@ -49,7 +49,7 @@ class EPLBConfig:
49 of the last `lb_window_size` steps will be used for rearranging experts.
50 """
51
52 - num_redundant_experts: int = 0
52 + num_redundant_experts: int = Field(default=0, ge=0)
53 """Number of redundant experts to use for expert parallelism."""
54
55 log_balancedness: bool = False
@@ -84,7 +84,7 @@ class ParallelConfig:
84 """Port for data parallel messaging."""
85 data_parallel_master_port: int = 29500
86 """Port of the data parallel master."""
87 - data_parallel_backend: str = "mp"
87 + data_parallel_backend: DataParallelBackend = "mp"
88 """Backend to use for data parallel, either "mp" or "ray"."""
89 data_parallel_external_lb: bool = False
90 """Whether to use "external" DP LB mode. Applies only to online serving
@@ -102,7 +102,7 @@ class ParallelConfig:
102 """Use expert parallelism instead of tensor parallelism for MoE layers."""
103 enable_eplb: bool = False
104 """Enable expert parallelism load balancing for MoE layers."""
105 - eplb_config: EPLBConfig = field(default_factory=EPLBConfig)
105 + eplb_config: EPLBConfig = Field(default_factory=EPLBConfig)
106 """Expert parallelism configuration."""
107 expert_placement_strategy: ExpertPlacementStrategy = "linear"
108 """The expert placement strategy for MoE layers:\n
@@ -188,13 +188,13 @@ class ParallelConfig:
188 new attributes and methods to the worker class for use in collective_rpc
189 calls."""
190
191 - world_size: int = field(init=False)
191 + world_size: int = Field(init=False)
192 """world_size is TPxPP, it affects the number of workers we create."""
193
194 rank: int = 0
195 """Global rank in distributed setup."""
196
197 - _data_parallel_master_port_list: list[int] = field(default_factory=list)
197 + _data_parallel_master_port_list: list[int] = Field(default_factory=list)
198 """List of open port auto-queried for data parallel messaging.
199 Set to be private as it's not intended to be configured by users.
200 """
@@ -204,7 +204,7 @@ class ParallelConfig:
204 not change by dcp, it simply reuse the GPUs of TP group, and tp_size
205 needs to be divisible by dcp_size."""
206
207 - _api_process_count: int = 1
207 + _api_process_count: int = Field(default=1, gt=0)
208 """
209 The number of API processes initialized.
210
@@ -213,7 +213,7 @@ class ParallelConfig:
213 should only be set by API server scale-out.
214 """
215
216 - _api_process_rank: int = 0
216 + _api_process_rank: int = Field(default=0, ge=-1)
217 """
218 The rank of this API process, or `-1` for engine core processes
219 under API server scale-out.
@@ -223,6 +223,51 @@ class ParallelConfig:
223 should only be set by API server scale-out.
224 """
225
226 + @model_validator(mode="after")
227 + def _validate_parallel_config(self) -> Self:
228 + if self._api_process_rank >= self._api_process_count:
229 + raise ValueError(
230 + "Invalid value of `_api_process_rank`. "
231 + f"Expected to be `-1` or `[0, {self._api_process_count})`, "
232 + f"but found: {self._api_process_rank}"
233 + )
234 +
235 + if self.data_parallel_size_local > self.data_parallel_size:
236 + raise ValueError(
237 + f"data_parallel_size_local ({self.data_parallel_size_local}) "
238 + f"must be <= data_parallel_size ({self.data_parallel_size})"
239 + )
240 +
241 + if self.data_parallel_size <= 1 and self.data_parallel_external_lb:
242 + raise ValueError(
243 + "data_parallel_external_lb can only be set when data_parallel_size > 1"
244 + )
245 +
246 + if self.enable_eplb:
247 + if not current_platform.is_cuda():
248 + raise ValueError(
249 + "Expert parallelism load balancing is only supported on "
250 + "CUDA devices now."
251 + )
252 + if not self.enable_expert_parallel:
253 + raise ValueError("enable_expert_parallel must be True to use EPLB.")
254 + if self.tensor_parallel_size * self.data_parallel_size <= 1:
255 + raise ValueError(
256 + "EPLB requires tensor_parallel_size or data_parallel_size "
257 + f"to be greater than 1, but got "
258 + f"TP={self.tensor_parallel_size},DP={self.data_parallel_size}."
259 + )
260 + else:
261 + if self.eplb_config.num_redundant_experts != 0:
262 + raise ValueError(
263 + "num_redundant_experts is set to "
264 + f"{self.eplb_config.num_redundant_experts} but EPLB is not "
265 + "enabled. Either enable EPLB or unset "
266 + "num_redundant_experts."
267 + )
268 +
269 + return self
270 +
271 @property
272 def world_size_across_dp(self) -> int:
273 """world_size_across_dp is TPxPPxDP, it is the size of the world
@@ -396,12 +441,6 @@ class ParallelConfig:
441 logger.info("Using external launcher for distributed inference.")
442 self.world_size *= self.data_parallel_size
443
399 - if self.data_parallel_size_local > self.data_parallel_size:
400 - raise ValueError(
401 - f"data_parallel_size_local ({self.data_parallel_size_local}) "
402 - f"must be <= data_parallel_size ({self.data_parallel_size})"
403 - )
404 -
444 if self.data_parallel_size > 1 or self.data_parallel_size_local == 0:
445 # Data parallel was specified in the engine args.
446 if self.distributed_executor_backend == "external_launcher":
@@ -431,43 +470,10 @@ class ParallelConfig:
470 self.data_parallel_master_ip = envs.VLLM_DP_MASTER_IP
471 self.data_parallel_master_port = envs.VLLM_DP_MASTER_PORT
472
434 - if self.data_parallel_external_lb:
435 - raise ValueError(
436 - "data_parallel_external_lb can only "
437 - "be set when data_parallel_size > 1"
438 - )
439 -
473 if self.distributed_executor_backend == "external_launcher":
474 os.environ["VLLM_ENABLE_V1_MULTIPROCESSING"] = "0"
475 logger.info("Disabling V1 multiprocessing for external launcher.")
476
444 - if self.enable_eplb:
445 - if not current_platform.is_cuda():
446 - raise ValueError(
447 - "Expert parallelism load balancing is only supported on "
448 - "CUDA devices now."
449 - )
450 - if self.eplb_config.num_redundant_experts < 0:
451 - raise ValueError(
452 - "num_redundant_experts must be non-negative, but got "
453 - f"{self.eplb_config.num_redundant_experts}."
454 - )
455 - if not self.enable_expert_parallel:
456 - raise ValueError("enable_expert_parallel must be True to use EPLB.")
457 - if self.tensor_parallel_size * self.data_parallel_size <= 1:
458 - raise ValueError(
459 - "EPLB requires tensor_parallel_size or data_parallel_size "
460 - f"to be greater than 1, but got "
461 - f"TP={self.tensor_parallel_size},DP={self.data_parallel_size}."
462 - )
463 - else:
464 - if self.eplb_config.num_redundant_experts != 0:
465 - raise ValueError(
466 - "num_redundant_experts is set to "
467 - f"{self.eplb_config.num_redundant_experts} but EPLB is not "
468 - "enabled. Either enable EPLB or unset "
469 - "num_redundant_experts."
470 - )
477 if self.distributed_executor_backend is None and self.world_size > 1:
478 # We use multiprocessing by default if world_size fits on the
479 # current node and we aren't in a ray placement group.
@@ -514,13 +520,6 @@ class ParallelConfig:
520 if self.distributed_executor_backend is None and self.world_size == 1:
521 self.distributed_executor_backend = "uni"
522
517 - if not -1 <= self._api_process_rank < self._api_process_count:
518 - raise ValueError(
519 - "Invalid value of `_api_process_rank`. "
520 - f"Expected to be `-1` or `[0, {self._api_process_count})`, "
521 - f"but found: {self._api_process_rank}"
522 - )
523 -
523 @property
524 def use_ray(self) -> bool:
525 return self.distributed_executor_backend == "ray" or (