@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / e51929ebca
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 15, 14:45 UTC

Improve configs - `SchedulerConfig` (#16533)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Harry Mellor committed Apr 14, 2025 at 10:24 UTC e51929ebca5833a5e6eed484775178d33a014faa
4 files changed +283 -222
vllm/config.py
+102 -55
@@ -1522,6 +1522,9 @@ class LoadConfig:
1522 self.ignore_patterns = ["original/**/*"]
1523
1524
1525 +DistributedExecutorBackend = Literal["ray", "mp", "uni", "external_launcher"]
1526 +
1527 +
1528 @config
1529 @dataclass
1530 class ParallelConfig:
@@ -1563,7 +1566,7 @@ class ParallelConfig:
1566 placement_group: Optional["PlacementGroup"] = None
1567 """ray distributed model workers placement group."""
1568
1566 - distributed_executor_backend: Optional[Union[str,
1569 + distributed_executor_backend: Optional[Union[DistributedExecutorBackend,
1570 type["ExecutorBase"]]] = None
1571 """Backend to use for distributed model
1572 workers, either "ray" or "mp" (multiprocessing). If the product
@@ -1687,7 +1690,7 @@ class ParallelConfig:
1690 # current node and we aren't in a ray placement group.
1691
1692 from vllm.executor import ray_utils
1690 - backend = "mp"
1693 + backend: DistributedExecutorBackend = "mp"
1694 ray_found = ray_utils.ray_is_available()
1695 if current_platform.is_neuron():
1696 # neuron uses single process to control multiple devices
@@ -1755,92 +1758,124 @@ class ParallelConfig:
1758 "worker_extension_cls must be a string (qualified class name).")
1759
1760
1761 +SchedulerPolicy = Literal["fcfs", "priority"]
1762 +
1763 +
1764 +@config
1765 @dataclass
1766 class SchedulerConfig:
1767 """Scheduler configuration."""
1768
1762 - runner_type: str = "generate" # The runner type to launch for the model.
1769 + runner_type: RunnerType = "generate"
1770 + """The runner type to launch for the model."""
1771
1764 - # Maximum number of tokens to be processed in a single iteration.
1765 - max_num_batched_tokens: int = field(default=None) # type: ignore
1772 + max_num_batched_tokens: int = None # type: ignore
1773 + """Maximum number of tokens to be processed in a single iteration.
1774 +
1775 + This config has no static default. If left unspecified by the user, it will
1776 + be set in `EngineArgs.create_engine_config` based on the usage context."""
1777
1767 - # Maximum number of sequences to be processed in a single iteration.
1768 - max_num_seqs: int = 128
1778 + max_num_seqs: int = None # type: ignore
1779 + """Maximum number of sequences to be processed in a single iteration.
1780 +
1781 + This config has no static default. If left unspecified by the user, it will
1782 + be set in `EngineArgs.create_engine_config` based on the usage context."""
1783
1770 - # Maximum length of a sequence (including prompt and generated text).
1771 - max_model_len: int = 8192
1784 + max_model_len: int = None # type: ignore
1785 + """Maximum length of a sequence (including prompt and generated text). This
1786 + is primarily set in `ModelConfig` and that value should be manually
1787 + duplicated here."""
1788
1773 - # Maximum number of sequences that can be partially prefilled concurrently
1789 max_num_partial_prefills: int = 1
1790 + """For chunked prefill, the maximum number of sequences that can be
1791 + partially prefilled concurrently."""
1792
1776 - # Maximum number of "very long prompt" sequences that can be prefilled
1777 - # concurrently (long is defined by long_prefill_threshold)
1793 max_long_partial_prefills: int = 1
1794 + """For chunked prefill, the maximum number of prompts longer than
1795 + long_prefill_token_threshold that will be prefilled concurrently. Setting
1796 + this less than max_num_partial_prefills will allow shorter prompts to jump
1797 + the queue in front of longer prompts in some cases, improving latency."""
1798
1780 - # calculate context length that determines which sequences are
1781 - # considered "long"
1799 long_prefill_token_threshold: int = 0
1800 + """For chunked prefill, a request is considered long if the prompt is
1801 + longer than this number of tokens."""
1802
1784 - # The number of slots to allocate per sequence per
1785 - # step, beyond the known token ids. This is used in speculative
1786 - # decoding to store KV activations of tokens which may or may not be
1787 - # accepted.
1803 num_lookahead_slots: int = 0
1804 + """The number of slots to allocate per sequence per
1805 + step, beyond the known token ids. This is used in speculative
1806 + decoding to store KV activations of tokens which may or may not be
1807 + accepted.
1808 +
1809 + NOTE: This will be replaced by speculative config in the future; it is
1810 + present to enable correctness tests until then."""
1811
1790 - # Apply a delay (of delay factor multiplied by previous
1791 - # prompt latency) before scheduling next prompt.
1812 delay_factor: float = 0.0
1813 + """Apply a delay (of delay factor multiplied by previous
1814 + prompt latency) before scheduling next prompt."""
1815
1794 - # If True, prefill requests can be chunked based
1795 - # on the remaining max_num_batched_tokens.
1796 - enable_chunked_prefill: bool = False
1816 + enable_chunked_prefill: bool = None # type: ignore
1817 + """If True, prefill requests can be chunked based
1818 + on the remaining max_num_batched_tokens."""
1819
1820 is_multimodal_model: bool = False
1821 + """True if the model is multimodal."""
1822 +
1823 + # TODO (ywang96): Make this configurable.
1824 + max_num_encoder_input_tokens: int = field(init=False)
1825 + """Multimodal encoder compute budget, only used in V1.
1826 +
1827 + NOTE: This is not currently configurable. It will be overridden by
1828 + max_num_batched_tokens in case max multimodal embedding size is larger."""
1829 +
1830 + # TODO (ywang96): Make this configurable.
1831 + encoder_cache_size: int = field(init=False)
1832 + """Multimodal encoder cache size, only used in V1.
1833 +
1834 + NOTE: This is not currently configurable. It will be overridden by
1835 + max_num_batched_tokens in case max multimodal embedding size is larger."""
1836
1800 - # NOTE: The following multimodal encoder budget will be initialized to
1801 - # max_num_batched_tokens and overridden in case max multimodal embedding
1802 - # size is larger.
1803 - # TODO (ywang96): Make these configurable.
1804 - # Multimodal encoder compute budget, only used in V1
1805 - max_num_encoder_input_tokens: int = field(default=None) # type: ignore
1806 -
1807 - # Multimodal encoder cache size, only used in V1
1808 - encoder_cache_size: int = field(default=None) # type: ignore
1809 -
1810 - # Whether to perform preemption by swapping or
1811 - # recomputation. If not specified, we determine the mode as follows:
1812 - # We use recomputation by default since it incurs lower overhead than
1813 - # swapping. However, when the sequence group has multiple sequences
1814 - # (e.g., beam search), recomputation is not currently supported. In
1815 - # such a case, we use swapping instead.
1837 preemption_mode: Optional[str] = None
1838 + """Whether to perform preemption by swapping or
1839 + recomputation. If not specified, we determine the mode as follows:
1840 + We use recomputation by default since it incurs lower overhead than
1841 + swapping. However, when the sequence group has multiple sequences
1842 + (e.g., beam search), recomputation is not currently supported. In
1843 + such a case, we use swapping instead."""
1844
1845 num_scheduler_steps: int = 1
1846 + """Maximum number of forward steps per scheduler call."""
1847
1820 - multi_step_stream_outputs: bool = False
1848 + multi_step_stream_outputs: bool = True
1849 + """If False, then multi-step will stream outputs at the end of all steps"""
1850
1822 - # Private API. If used, scheduler sends delta data to
1823 - # workers instead of an entire data. It should be enabled only
1824 - # when SPMD worker architecture is enabled. I.e.,
1825 - # VLLM_USE_RAY_SPMD_WORKER=1
1851 send_delta_data: bool = False
1827 -
1828 - # The scheduling policy to use. "fcfs" (default) or "priority".
1829 - policy: str = "fcfs"
1852 + """Private API. If used, scheduler sends delta data to
1853 + workers instead of an entire data. It should be enabled only
1854 + when SPMD worker architecture is enabled. I.e.,
1855 + VLLM_USE_RAY_SPMD_WORKER=1"""
1856 +
1857 + policy: SchedulerPolicy = "fcfs"
1858 + """The scheduling policy to use:\n
1859 + - "fcfs" means first come first served, i.e. requests are handled in order
1860 + of arrival.\n
1861 + - "priority" means requests are handled based on given priority (lower
1862 + value means earlier handling) and time of arrival deciding any ties)."""
1863
1864 chunked_prefill_enabled: bool = field(init=False)
1865 + """True if chunked prefill is enabled."""
1866
1833 - # If set to true and chunked prefill is enabled, we do not want to
1834 - # partially schedule a multimodal item. Only used in V1
1835 - # This ensures that if a request has a mixed prompt
1836 - # (like text tokens TTTT followed by image tokens IIIIIIIIII) where only
1837 - # some image tokens can be scheduled (like TTTTIIIII, leaving IIIII),
1838 - # it will be scheduled as TTTT in one step and IIIIIIIIII in the next.
1867 disable_chunked_mm_input: bool = False
1868 + """If set to true and chunked prefill is enabled, we do not want to
1869 + partially schedule a multimodal item. Only used in V1
1870 + This ensures that if a request has a mixed prompt
1871 + (like text tokens TTTT followed by image tokens IIIIIIIIII) where only
1872 + some image tokens can be scheduled (like TTTTIIIII, leaving IIIII),
1873 + it will be scheduled as TTTT in one step and IIIIIIIIII in the next."""
1874
1841 - # scheduler class or path. "vllm.core.scheduler.Scheduler" (default)
1842 - # or "mod.custom_class".
1875 scheduler_cls: Union[str, type[object]] = "vllm.core.scheduler.Scheduler"
1876 + """The scheduler class to use. "vllm.core.scheduler.Scheduler" is the
1877 + default scheduler. Can be a class directly or the path to a class of form
1878 + "mod.custom_class"."""
1879
1880 def compute_hash(self) -> str:
1881 """
@@ -1862,6 +1897,18 @@ class SchedulerConfig:
1897 return hash_str
1898
1899 def __post_init__(self) -> None:
1900 + if self.max_model_len is None:
1901 + self.max_model_len = 8192
1902 + logger.warning(
1903 + "max_model_len was is not set. Defaulting to arbitrary value "
1904 + "of %d.", self.max_model_len)
1905 +
1906 + if self.max_num_seqs is None:
1907 + self.max_num_seqs = 128
1908 + logger.warning(
1909 + "max_num_seqs was is not set. Defaulting to arbitrary value "
1910 + "of %d.", self.max_num_seqs)
1911 +
1912 if self.max_num_batched_tokens is None:
1913 if self.enable_chunked_prefill:
1914 if self.num_scheduler_steps > 1:
vllm/engine/arg_utils.py
+167 -153
@@ -1,25 +1,30 @@
1 # SPDX-License-Identifier: Apache-2.0
2
3 +# yapf: disable
4 import argparse
5 import dataclasses
6 import json
7 import re
8 import threading
9 from dataclasses import MISSING, dataclass, fields
9 -from typing import (TYPE_CHECKING, Any, Dict, List, Literal, Mapping, Optional,
10 - Tuple, Type, Union, cast, get_args, get_origin)
10 +from typing import (TYPE_CHECKING, Any, Callable, Dict, List, Literal, Mapping,
11 + Optional, Tuple, Type, TypeVar, Union, cast, get_args,
12 + get_origin)
13
14 import torch
15 +from typing_extensions import TypeIs
16
17 import vllm.envs as envs
18 from vllm import version
19 from vllm.config import (CacheConfig, CompilationConfig, ConfigFormat,
17 - DecodingConfig, DeviceConfig, HfOverrides,
20 + DecodingConfig, DeviceConfig,
21 + DistributedExecutorBackend, HfOverrides,
22 KVTransferConfig, LoadConfig, LoadFormat, LoRAConfig,
23 ModelConfig, ModelImpl, ObservabilityConfig,
24 ParallelConfig, PoolerConfig, PromptAdapterConfig,
21 - SchedulerConfig, SpeculativeConfig, TaskOption,
22 - TokenizerPoolConfig, VllmConfig, get_attr_docs)
25 + SchedulerConfig, SchedulerPolicy, SpeculativeConfig,
26 + TaskOption, TokenizerPoolConfig, VllmConfig,
27 + get_attr_docs)
28 from vllm.executor.executor_base import ExecutorBase
29 from vllm.logger import init_logger
30 from vllm.model_executor.layers.quantization import QUANTIZATION_METHODS
@@ -28,7 +33,9 @@ from vllm.reasoning import ReasoningParserManager
33 from vllm.test_utils import MODEL_WEIGHTS_S3_BUCKET, MODELS_ON_S3
34 from vllm.transformers_utils.utils import check_gguf_file
35 from vllm.usage.usage_lib import UsageContext
31 -from vllm.utils import FlexibleArgumentParser, StoreBoolean, is_in_ray_actor
36 +from vllm.utils import FlexibleArgumentParser, is_in_ray_actor
37 +
38 +# yapf: enable
39
40 if TYPE_CHECKING:
41 from vllm.transformers_utils.tokenizer_group import BaseTokenizerGroup
@@ -47,11 +54,32 @@ DEVICE_OPTIONS = [
54 "hpu",
55 ]
56
57 +# object is used to allow for special typing forms
58 +T = TypeVar("T")
59 +TypeHint = Union[type[Any], object]
60 +TypeHintT = Union[type[T], object]
61 +
62
51 -def nullable_str(val: str):
52 - if not val or val == "None":
63 +def optional_arg(val: str, return_type: type[T]) -> Optional[T]:
64 + if val == "" or val == "None":
65 return None
54 - return val
66 + try:
67 + return cast(Callable, return_type)(val)
68 + except ValueError as e:
69 + raise argparse.ArgumentTypeError(
70 + f"Value {val} cannot be converted to {return_type}.") from e
71 +
72 +
73 +def optional_str(val: str) -> Optional[str]:
74 + return optional_arg(val, str)
75 +
76 +
77 +def optional_int(val: str) -> Optional[int]:
78 + return optional_arg(val, int)
79 +
80 +
81 +def optional_float(val: str) -> Optional[float]:
82 + return optional_arg(val, float)
83
84
85 def nullable_kvs(val: str) -> Optional[Mapping[str, int]]:
@@ -112,7 +140,8 @@ class EngineArgs:
140 # is intended for expert use only. The API may change without
141 # notice.
142 distributed_executor_backend: Optional[Union[
115 - str, Type[ExecutorBase]]] = ParallelConfig.distributed_executor_backend
143 + DistributedExecutorBackend,
144 + Type[ExecutorBase]]] = ParallelConfig.distributed_executor_backend
145 # number of P/D disaggregation (or other disaggregation) workers
146 pipeline_parallel_size: int = ParallelConfig.pipeline_parallel_size
147 tensor_parallel_size: int = ParallelConfig.tensor_parallel_size
@@ -129,11 +158,13 @@ class EngineArgs:
158 swap_space: float = 4 # GiB
159 cpu_offload_gb: float = 0 # GiB
160 gpu_memory_utilization: float = 0.90
132 - max_num_batched_tokens: Optional[int] = None
133 - max_num_partial_prefills: Optional[int] = 1
134 - max_long_partial_prefills: Optional[int] = 1
135 - long_prefill_token_threshold: Optional[int] = 0
136 - max_num_seqs: Optional[int] = None
161 + max_num_batched_tokens: Optional[
162 + int] = SchedulerConfig.max_num_batched_tokens
163 + max_num_partial_prefills: int = SchedulerConfig.max_num_partial_prefills
164 + max_long_partial_prefills: int = SchedulerConfig.max_long_partial_prefills
165 + long_prefill_token_threshold: int = \
166 + SchedulerConfig.long_prefill_token_threshold
167 + max_num_seqs: Optional[int] = SchedulerConfig.max_num_seqs
168 max_logprobs: int = 20 # Default value for OpenAI Chat Completions API
169 disable_log_stats: bool = False
170 revision: Optional[str] = None
@@ -169,20 +200,21 @@ class EngineArgs:
200 lora_dtype: Optional[Union[str, torch.dtype]] = 'auto'
201 max_cpu_loras: Optional[int] = None
202 device: str = 'auto'
172 - num_scheduler_steps: int = 1
173 - multi_step_stream_outputs: bool = True
203 + num_scheduler_steps: int = SchedulerConfig.num_scheduler_steps
204 + multi_step_stream_outputs: bool = SchedulerConfig.multi_step_stream_outputs
205 ray_workers_use_nsight: bool = ParallelConfig.ray_workers_use_nsight
206 num_gpu_blocks_override: Optional[int] = None
176 - num_lookahead_slots: int = 0
207 + num_lookahead_slots: int = SchedulerConfig.num_lookahead_slots
208 model_loader_extra_config: Optional[
209 dict] = LoadConfig.model_loader_extra_config
210 ignore_patterns: Optional[Union[str,
211 List[str]]] = LoadConfig.ignore_patterns
181 - preemption_mode: Optional[str] = None
212 + preemption_mode: Optional[str] = SchedulerConfig.preemption_mode
213
183 - scheduler_delay_factor: float = 0.0
184 - enable_chunked_prefill: Optional[bool] = None
185 - disable_chunked_mm_input: bool = False
214 + scheduler_delay_factor: float = SchedulerConfig.delay_factor
215 + enable_chunked_prefill: Optional[
216 + bool] = SchedulerConfig.enable_chunked_prefill
217 + disable_chunked_mm_input: bool = SchedulerConfig.disable_chunked_mm_input
218
219 guided_decoding_backend: str = DecodingConfig.guided_decoding_backend
220 logits_processor_pattern: Optional[str] = None
@@ -194,8 +226,8 @@ class EngineArgs:
226 otlp_traces_endpoint: Optional[str] = None
227 collect_detailed_traces: Optional[str] = None
228 disable_async_output_proc: bool = False
197 - scheduling_policy: Literal["fcfs", "priority"] = "fcfs"
198 - scheduler_cls: Union[str, Type[object]] = "vllm.core.scheduler.Scheduler"
229 + scheduling_policy: SchedulerPolicy = SchedulerConfig.policy
230 + scheduler_cls: Union[str, Type[object]] = SchedulerConfig.scheduler_cls
231
232 override_neuron_config: Optional[Dict[str, Any]] = None
233 override_pooler_config: Optional[PoolerConfig] = None
@@ -236,15 +268,33 @@ class EngineArgs:
268 def add_cli_args(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
269 """Shared CLI arguments for vLLM engine."""
270
239 - def is_type_in_union(cls: type[Any], type: type[Any]) -> bool:
271 + def is_type_in_union(cls: TypeHint, type: TypeHint) -> bool:
272 """Check if the class is a type in a union type."""
241 - return get_origin(cls) is Union and type in get_args(cls)
242 -
243 - def is_optional(cls: type[Any]) -> bool:
273 + is_union = get_origin(cls) is Union
274 + type_in_union = type in [get_origin(a) or a for a in get_args(cls)]
275 + return is_union and type_in_union
276 +
277 + def get_type_from_union(cls: TypeHint, type: TypeHintT) -> TypeHintT:
278 + """Get the type in a union type."""
279 + for arg in get_args(cls):
280 + if (get_origin(arg) or arg) is type:
281 + return arg
282 + raise ValueError(f"Type {type} not found in union type {cls}.")
283 +
284 + def is_optional(cls: TypeHint) -> TypeIs[Union[Any, None]]:
285 """Check if the class is an optional type."""
286 return is_type_in_union(cls, type(None))
287
247 - def get_kwargs(cls: type[Any]) -> Dict[str, Any]:
288 + def can_be_type(cls: TypeHint, type: TypeHintT) -> TypeIs[TypeHintT]:
289 + """Check if the class can be of type."""
290 + return cls is type or get_origin(cls) is type or is_type_in_union(
291 + cls, type)
292 +
293 + def is_custom_type(cls: TypeHint) -> bool:
294 + """Check if the class is a custom type."""
295 + return cls.__module__ != "builtins"
296 +
297 + def get_kwargs(cls: type[Any]) -> dict[str, Any]:
298 cls_docs = get_attr_docs(cls)
299 kwargs = {}
300 for field in fields(cls):
@@ -253,19 +303,41 @@ class EngineArgs:
303 default = (field.default_factory
304 if field.default is MISSING else field.default)
305 kwargs[name] = {"default": default, "help": cls_docs[name]}
256 - # When using action="store_true"
257 - # add_argument doesn't accept type
258 - if field.type is bool:
259 - continue
260 - # Handle optional fields
261 - if is_optional(field.type):
262 - kwargs[name]["type"] = nullable_str
263 - continue
264 - # Handle str in union fields
265 - if is_type_in_union(field.type, str):
266 - kwargs[name]["type"] = str
267 - continue
268 - kwargs[name]["type"] = field.type
306 +
307 + # Make note of if the field is optional and get the actual
308 + # type of the field if it is
309 + optional = is_optional(field.type)
310 + field_type = get_args(
311 + field.type)[0] if optional else field.type
312 +
313 + if can_be_type(field_type, bool):
314 + # Creates --no-<name> and --<name> flags
315 + kwargs[name]["action"] = argparse.BooleanOptionalAction
316 + kwargs[name]["type"] = bool
317 + elif can_be_type(field_type, Literal):
318 + # Creates choices from Literal arguments
319 + if is_type_in_union(field_type, Literal):
320 + field_type = get_type_from_union(field_type, Literal)
321 + choices = get_args(field_type)
322 + kwargs[name]["choices"] = choices
323 + choice_type = type(choices[0])
324 + assert all(type(c) is choice_type for c in choices), (
325 + f"All choices must be of the same type. "
326 + f"Got {choices} with types {[type(c) for c in choices]}"
327 + )
328 + kwargs[name]["type"] = choice_type
329 + elif can_be_type(field_type, int):
330 + kwargs[name]["type"] = optional_int if optional else int
331 + elif can_be_type(field_type, float):
332 + kwargs[name][
333 + "type"] = optional_float if optional else float
334 + elif (can_be_type(field_type, str)
335 + or can_be_type(field_type, dict)
336 + or is_custom_type(field_type)):
337 + kwargs[name]["type"] = optional_str if optional else str
338 + else:
339 + raise ValueError(
340 + f"Unsupported type {field.type} for argument {name}. ")
341 return kwargs
342
343 # Model arguments
@@ -285,13 +357,13 @@ class EngineArgs:
357 'which task to use.')
358 parser.add_argument(
359 '--tokenizer',
288 - type=nullable_str,
360 + type=optional_str,
361 default=EngineArgs.tokenizer,
362 help='Name or path of the huggingface tokenizer to use. '
363 'If unspecified, model name or path will be used.')
364 parser.add_argument(
365 "--hf-config-path",
294 - type=nullable_str,
366 + type=optional_str,
367 default=EngineArgs.hf_config_path,
368 help='Name or path of the huggingface config to use. '
369 'If unspecified, model name or path will be used.')
@@ -303,21 +375,21 @@ class EngineArgs:
375 'the input. The generated output will contain token ids.')
376 parser.add_argument(
377 '--revision',
306 - type=nullable_str,
378 + type=optional_str,
379 default=None,
380 help='The specific model version to use. It can be a branch '
381 'name, a tag name, or a commit id. If unspecified, will use '
382 'the default version.')
383 parser.add_argument(
384 '--code-revision',
313 - type=nullable_str,
385 + type=optional_str,
386 default=None,
387 help='The specific revision to use for the model code on '
388 'Hugging Face Hub. It can be a branch name, a tag name, or a '
389 'commit id. If unspecified, will use the default version.')
390 parser.add_argument(
391 '--tokenizer-revision',
320 - type=nullable_str,
392 + type=optional_str,
393 default=None,
394 help='Revision of the huggingface tokenizer to use. '
395 'It can be a branch name, a tag name, or a commit id. '
@@ -357,7 +429,6 @@ class EngineArgs:
429 load_group.add_argument('--model-loader-extra-config',
430 **load_kwargs["model_loader_extra_config"])
431 load_group.add_argument('--use-tqdm-on-load',
360 - action=argparse.BooleanOptionalAction,
432 **load_kwargs["use_tqdm_on_load"])
433
434 parser.add_argument(
@@ -413,7 +484,7 @@ class EngineArgs:
484 'the behavior is subject to change in each release.')
485 parser.add_argument(
486 '--logits-processor-pattern',
416 - type=nullable_str,
487 + type=optional_str,
488 default=None,
489 help='Optional regex pattern specifying valid logits processor '
490 'qualified names that can be passed with the `logits_processors` '
@@ -439,7 +510,6 @@ class EngineArgs:
510 )
511 parallel_group.add_argument(
512 '--distributed-executor-backend',
442 - choices=['ray', 'mp', 'uni', 'external_launcher'],
513 **parallel_kwargs["distributed_executor_backend"])
514 parallel_group.add_argument(
515 '--pipeline-parallel-size', '-pp',
@@ -450,18 +520,15 @@ class EngineArgs:
520 **parallel_kwargs["data_parallel_size"])
521 parallel_group.add_argument(
522 '--enable-expert-parallel',
453 - action='store_true',
523 **parallel_kwargs["enable_expert_parallel"])
524 parallel_group.add_argument(
525 '--max-parallel-loading-workers',
526 **parallel_kwargs["max_parallel_loading_workers"])
527 parallel_group.add_argument(
528 '--ray-workers-use-nsight',
460 - action='store_true',
529 **parallel_kwargs["ray_workers_use_nsight"])
530 parallel_group.add_argument(
531 '--disable-custom-all-reduce',
464 - action='store_true',
532 **parallel_kwargs["disable_custom_all_reduce"])
533 # KV cache arguments
534 parser.add_argument('--block-size',
@@ -502,14 +569,6 @@ class EngineArgs:
569 'block manager v2) is now the default. '
570 'Setting this flag to True or False'
571 ' has no effect on vLLM behavior.')
505 - parser.add_argument(
506 - '--num-lookahead-slots',
507 - type=int,
508 - default=EngineArgs.num_lookahead_slots,
509 - help='Experimental scheduling config necessary for '
510 - 'speculative decoding. This will be replaced by '
511 - 'speculative config in the future; it is present '
512 - 'to enable correctness tests until then.')
572
573 parser.add_argument('--seed',
574 type=int,
@@ -552,36 +611,6 @@ class EngineArgs:
611 default=None,
612 help='If specified, ignore GPU profiling result and use this number'
613 ' of GPU blocks. Used for testing preemption.')
555 - parser.add_argument('--max-num-batched-tokens',
556 - type=int,
557 - default=EngineArgs.max_num_batched_tokens,
558 - help='Maximum number of batched tokens per '
559 - 'iteration.')
560 - parser.add_argument(
561 - "--max-num-partial-prefills",
562 - type=int,
563 - default=EngineArgs.max_num_partial_prefills,
564 - help="For chunked prefill, the max number of concurrent \
565 - partial prefills.")
566 - parser.add_argument(
567 - "--max-long-partial-prefills",
568 - type=int,
569 - default=EngineArgs.max_long_partial_prefills,
570 - help="For chunked prefill, the maximum number of prompts longer "
571 - "than --long-prefill-token-threshold that will be prefilled "
572 - "concurrently. Setting this less than --max-num-partial-prefills "
573 - "will allow shorter prompts to jump the queue in front of longer "
574 - "prompts in some cases, improving latency.")
575 - parser.add_argument(
576 - "--long-prefill-token-threshold",
577 - type=float,
578 - default=EngineArgs.long_prefill_token_threshold,
579 - help="For chunked prefill, a request is considered long if the "
580 - "prompt is longer than this number of tokens.")
581 - parser.add_argument('--max-num-seqs',
582 - type=int,
583 - default=EngineArgs.max_num_seqs,
584 - help='Maximum number of sequences per iteration.')
614 parser.add_argument(
615 '--max-logprobs',
616 type=int,
@@ -594,7 +623,7 @@ class EngineArgs:
623 # Quantization settings.
624 parser.add_argument('--quantization',
625 '-q',
597 - type=nullable_str,
626 + type=optional_str,
627 choices=[*QUANTIZATION_METHODS, None],
628 default=EngineArgs.quantization,
629 help='Method used to quantize the weights. If '
@@ -658,7 +687,7 @@ class EngineArgs:
687 'asynchronous tokenization. Ignored '
688 'if tokenizer_pool_size is 0.')
689 parser.add_argument('--tokenizer-pool-extra-config',
661 - type=nullable_str,
690 + type=optional_str,
691 default=EngineArgs.tokenizer_pool_extra_config,
692 help='Extra config for tokenizer pool. '
693 'This should be a JSON string that will be '
@@ -721,7 +750,7 @@ class EngineArgs:
750 'base model dtype.'))
751 parser.add_argument(
752 '--long-lora-scaling-factors',
724 - type=nullable_str,
753 + type=optional_str,
754 default=EngineArgs.long_lora_scaling_factors,
755 help=('Specify multiple scaling factors (which can '
756 'be different from base model scaling factor '
@@ -766,28 +795,6 @@ class EngineArgs:
795 help=('Maximum number of forward steps per '
796 'scheduler call.'))
797
769 - parser.add_argument(
770 - '--multi-step-stream-outputs',
771 - action=StoreBoolean,
772 - default=EngineArgs.multi_step_stream_outputs,
773 - nargs="?",
774 - const="True",
775 - help='If False, then multi-step will stream outputs at the end '
776 - 'of all steps')
777 - parser.add_argument(
778 - '--scheduler-delay-factor',
779 - type=float,
780 - default=EngineArgs.scheduler_delay_factor,
781 - help='Apply a delay (of delay factor multiplied by previous '
782 - 'prompt latency) before scheduling next prompt.')
783 - parser.add_argument(
784 - '--enable-chunked-prefill',
785 - action=StoreBoolean,
786 - default=EngineArgs.enable_chunked_prefill,
787 - nargs="?",
788 - const="True",
789 - help='If set, the prefill requests can be chunked based on the '
790 - 'max_num_batched_tokens.')
798 parser.add_argument('--speculative-config',
799 type=json.loads,
800 default=None,
@@ -863,22 +870,43 @@ class EngineArgs:
870 help="Disable async output processing. This may result in "
871 "lower performance.")
872
866 - parser.add_argument(
867 - '--scheduling-policy',
868 - choices=['fcfs', 'priority'],
869 - default="fcfs",
870 - help='The scheduling policy to use. "fcfs" (first come first served'
871 - ', i.e. requests are handled in order of arrival; default) '
872 - 'or "priority" (requests are handled based on given '
873 - 'priority (lower value means earlier handling) and time of '
874 - 'arrival deciding any ties).')
875 -
876 - parser.add_argument(
877 - '--scheduler-cls',
878 - default=EngineArgs.scheduler_cls,
879 - help='The scheduler class to use. "vllm.core.scheduler.Scheduler" '
880 - 'is the default scheduler. Can be a class directly or the path to '
881 - 'a class of form "mod.custom_class".')
873 + # Scheduler arguments
874 + scheduler_kwargs = get_kwargs(SchedulerConfig)
875 + scheduler_group = parser.add_argument_group(
876 + title="SchedulerConfig",
877 + description=SchedulerConfig.__doc__,
878 + )
879 + scheduler_group.add_argument(
880 + '--max-num-batched-tokens',
881 + **scheduler_kwargs["max_num_batched_tokens"])
882 + scheduler_group.add_argument('--max-num-seqs',
883 + **scheduler_kwargs["max_num_seqs"])
884 + scheduler_group.add_argument(
885 + "--max-num-partial-prefills",
886 + **scheduler_kwargs["max_num_partial_prefills"])
887 + scheduler_group.add_argument(
888 + "--max-long-partial-prefills",
889 + **scheduler_kwargs["max_long_partial_prefills"])
890 + scheduler_group.add_argument(
891 + "--long-prefill-token-threshold",
892 + **scheduler_kwargs["long_prefill_token_threshold"])
893 + scheduler_group.add_argument('--num-lookahead-slots',
894 + **scheduler_kwargs["num_lookahead_slots"])
895 + scheduler_group.add_argument('--scheduler-delay-factor',
896 + **scheduler_kwargs["delay_factor"])
897 + scheduler_group.add_argument(
898 + '--enable-chunked-prefill',
899 + **scheduler_kwargs["enable_chunked_prefill"])
900 + scheduler_group.add_argument(
901 + '--multi-step-stream-outputs',
902 + **scheduler_kwargs["multi_step_stream_outputs"])
903 + scheduler_group.add_argument('--scheduling-policy',
904 + **scheduler_kwargs["policy"])
905 + scheduler_group.add_argument(
906 + "--disable-chunked-mm-input",
907 + **scheduler_kwargs["disable_chunked_mm_input"])
908 + parser.add_argument('--scheduler-cls',
909 + **scheduler_kwargs["scheduler_cls"])
910
911 parser.add_argument(
912 '--override-neuron-config',
@@ -930,7 +958,7 @@ class EngineArgs:
958 'class without changing the existing functions.')
959 parser.add_argument(
960 "--generation-config",
933 - type=nullable_str,
961 + type=optional_str,
962 default="auto",
963 help="The folder path to the generation config. "
964 "Defaults to 'auto', the generation config will be loaded from "
@@ -1003,20 +1031,6 @@ class EngineArgs:
1031 "Note that even if this is set to False, cascade attention will be "
1032 "only used when the heuristic tells that it's beneficial.")
1033
1006 - parser.add_argument(
1007 - "--disable-chunked-mm-input",
1008 - action=StoreBoolean,
1009 - default=EngineArgs.disable_chunked_mm_input,
1010 - nargs="?",
1011 - const="True",
1012 - help="Disable multimodal input chunking attention for V1. "
1013 - "If set to true and chunked prefill is enabled, we do not want to"
1014 - " partially schedule a multimodal item. This ensures that if a "
1015 - "request has a mixed prompt (like text tokens TTTT followed by "
1016 - "image tokens IIIIIIIIII) where only some image tokens can be "
1017 - "scheduled (like TTTTIIIII, leaving IIIII), it will be scheduled "
1018 - "as TTTT in one step and IIIIIIIIII in the next.")
1019 -
1034 return parser
1035
1036 @classmethod
@@ -1370,7 +1384,7 @@ class EngineArgs:
1384 recommend_to_remove=False)
1385 return False
1386
1373 - if self.preemption_mode != EngineArgs.preemption_mode:
1387 + if self.preemption_mode != SchedulerConfig.preemption_mode:
1388 _raise_or_fallback(feature_name="--preemption-mode",
1389 recommend_to_remove=True)
1390 return False
@@ -1381,17 +1395,17 @@ class EngineArgs:
1395 recommend_to_remove=True)
1396 return False
1397
1384 - if self.scheduling_policy != EngineArgs.scheduling_policy:
1398 + if self.scheduling_policy != SchedulerConfig.policy:
1399 _raise_or_fallback(feature_name="--scheduling-policy",
1400 recommend_to_remove=False)
1401 return False
1402
1389 - if self.num_scheduler_steps != EngineArgs.num_scheduler_steps:
1403 + if self.num_scheduler_steps != SchedulerConfig.num_scheduler_steps:
1404 _raise_or_fallback(feature_name="--num-scheduler-steps",
1405 recommend_to_remove=True)
1406 return False
1407
1394 - if self.scheduler_delay_factor != EngineArgs.scheduler_delay_factor:
1408 + if self.scheduler_delay_factor != SchedulerConfig.delay_factor:
1409 _raise_or_fallback(feature_name="--scheduler-delay-factor",
1410 recommend_to_remove=True)
1411 return False
@@ -1475,9 +1489,9 @@ class EngineArgs:
1489
1490 # No Concurrent Partial Prefills so far.
1491 if (self.max_num_partial_prefills
1478 - != EngineArgs.max_num_partial_prefills
1492 + != SchedulerConfig.max_num_partial_prefills
1493 or self.max_long_partial_prefills
1480 - != EngineArgs.max_long_partial_prefills):
1494 + != SchedulerConfig.max_long_partial_prefills):
1495 _raise_or_fallback(feature_name="Concurrent Partial Prefill",
1496 recommend_to_remove=False)
1497 return False
vllm/entrypoints/openai/cli_args.py
+12 -12
@@ -11,7 +11,7 @@ import ssl
11 from collections.abc import Sequence
12 from typing import Optional, Union, get_args
13
14 -from vllm.engine.arg_utils import AsyncEngineArgs, nullable_str
14 +from vllm.engine.arg_utils import AsyncEngineArgs, optional_str
15 from vllm.entrypoints.chat_utils import (ChatTemplateContentFormatOption,
16 validate_chat_template)
17 from vllm.entrypoints.openai.serving_models import (LoRAModulePath,
@@ -79,7 +79,7 @@ class PromptAdapterParserAction(argparse.Action):
79
80 def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
81 parser.add_argument("--host",
82 - type=nullable_str,
82 + type=optional_str,
83 default=None,
84 help="Host name.")
85 parser.add_argument("--port", type=int, default=8000, help="Port number.")
@@ -108,13 +108,13 @@ def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
108 default=["*"],
109 help="Allowed headers.")
110 parser.add_argument("--api-key",
111 - type=nullable_str,
111 + type=optional_str,
112 default=None,
113 help="If provided, the server will require this key "
114 "to be presented in the header.")
115 parser.add_argument(
116 "--lora-modules",
117 - type=nullable_str,
117 + type=optional_str,
118 default=None,
119 nargs='+',
120 action=LoRAParserAction,
@@ -126,14 +126,14 @@ def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
126 "\"base_model_name\": \"id\"}``")
127 parser.add_argument(
128 "--prompt-adapters",
129 - type=nullable_str,
129 + type=optional_str,
130 default=None,
131 nargs='+',
132 action=PromptAdapterParserAction,
133 help="Prompt adapter configurations in the format name=path. "
134 "Multiple adapters can be specified.")
135 parser.add_argument("--chat-template",
136 - type=nullable_str,
136 + type=optional_str,
137 default=None,
138 help="The file path to the chat template, "
139 "or the template in single-line form "
@@ -151,20 +151,20 @@ def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
151 'similar to OpenAI schema. '
152 'Example: ``[{"type": "text", "text": "Hello world!"}]``')
153 parser.add_argument("--response-role",
154 - type=nullable_str,
154 + type=optional_str,
155 default="assistant",
156 help="The role name to return if "
157 "``request.add_generation_prompt=true``.")
158 parser.add_argument("--ssl-keyfile",
159 - type=nullable_str,
159 + type=optional_str,
160 default=None,
161 help="The file path to the SSL key file.")
162 parser.add_argument("--ssl-certfile",
163 - type=nullable_str,
163 + type=optional_str,
164 default=None,
165 help="The file path to the SSL cert file.")
166 parser.add_argument("--ssl-ca-certs",
167 - type=nullable_str,
167 + type=optional_str,
168 default=None,
169 help="The CA certificates file.")
170 parser.add_argument(
@@ -180,13 +180,13 @@ def make_arg_parser(parser: FlexibleArgumentParser) -> FlexibleArgumentParser:
180 )
181 parser.add_argument(
182 "--root-path",
183 - type=nullable_str,
183 + type=optional_str,
184 default=None,
185 help="FastAPI root_path when app is behind a path based routing proxy."
186 )
187 parser.add_argument(
188 "--middleware",
189 - type=nullable_str,
189 + type=optional_str,
190 action="append",
191 default=[],
192 help="Additional ASGI middleware to apply to the app. "
vllm/entrypoints/openai/run_batch.py
+2 -2
@@ -12,7 +12,7 @@ import torch
12 from prometheus_client import start_http_server
13 from tqdm import tqdm
14
15 -from vllm.engine.arg_utils import AsyncEngineArgs, nullable_str
15 +from vllm.engine.arg_utils import AsyncEngineArgs, optional_str
16 from vllm.engine.async_llm_engine import AsyncLLMEngine
17 from vllm.entrypoints.logger import RequestLogger, logger
18 # yapf: disable
@@ -61,7 +61,7 @@ def parse_args():
61 "to the output URL.",
62 )
63 parser.add_argument("--response-role",
64 - type=nullable_str,
64 + type=optional_str,
65 default="assistant",
66 help="The role name to return if "
67 "`request.add_generation_prompt=True`.")