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 (