config/vllm.py: VllmConfig, __post_init__ and compute_hash
Code version: vLLM @ 3e44c0d · 2026-10-22
Purpose#
vllm/config/vllm.py defines the VllmConfig: one object that holds every top-level config of the engine. Its __post_init__ turns a set of separate sub-configs into one consistent configuration.
| File | vllm/config/vllm.py, 3,617 lines of Python. One class, VllmConfig, of about 3,160 lines; an enum, four level tables, eight fusion helpers, and the "current config" functions. |
|---|---|
| Main class | VllmConfig (vllm/config/vllm.py:347-348). |
| Runs in | __post_init__ runs where a VllmConfig is made: the API server process of vllm serve, and again in each engine core process after the handshake. The properties and compute_hash run in every process that holds a copy. |
| Called by | EngineArgs.create_engine_config (construction); the engine core, the workers, the model runner and the compile backend (properties, compute_hash, get_current_vllm_config). |
| Calls | The platform hooks, Executor.get_class, the model registry and config classes, the KV connector factory (class lookup only). |
| Not in this file | The sub-config classes (vllm/config/*.py), the flag parser and the batch defaults (engine/arg_utils.py: EngineArgs, the flags and create_engine_config), the compile backend that uses the hash (vllm/compilation/). |
Where it sits#
Panel 2 of the diagram on How configuration works shows VllmConfig(...) at the end of create_engine_config, and the phases of __post_init__. Panel 3 shows the copies in the other processes.
Who calls it#
EngineArgs.create_engine_configmakes the object (vllm/engine/arg_utils.py:2870-2903).- The engine core calls
vllm_config.__post_init__()a second time after the handshake (vllm/v1/engine/core.py:1336-1337). - vLLM's
replace()helper makes a new object with the constructor, so__post_init__runs again (vllm/config/utils.py:121-129). - The compile backend calls
compute_hash()for its cache folder (vllm/compilation/backends.py:1062). - Model code reads the active config with
get_current_vllm_config()(vllm/config/vllm.py:3576-3586).
What it calls#
- The platform hooks
apply_config_platform_defaultsandcheck_and_update_config(vllm/config/vllm.py:1804,vllm/config/vllm.py:2046). Executor.get_class(self), to ask whether the executor supports async scheduling (vllm/config/vllm.py:1622-1623).- The per-architecture config hooks in
MODELS_CONFIG_MAP(vllm/config/vllm.py:2727-2743).
Key classes and functions#
| Lines | Name | Role |
|---|---|---|
vllm/config/vllm.py:75-128 | DEFAULT_BREAKABLE_CUDAGRAPH_ARCHITECTURES | Model classes that get breakable CUDA graphs by default. |
vllm/config/vllm.py:131-143 | OptimizationLevel | O0 to O3. |
vllm/config/vllm.py:156-251 | enable_*_fusion | Callables that the level tables use to decide a fusion pass from the config. |
vllm/config/vllm.py:254-344 | OPTIMIZATION_LEVEL_00 ... _03 | The defaults of each level. |
vllm/config/vllm.py:347-457 | VllmConfig fields | 34 attributes. |
vllm/config/vllm.py:459-575 | compute_hash | The 10-digit config hash. |
vllm/config/vllm.py:586-597 | max_concurrent_batches | Size of the engine core batch queue. |
vllm/config/vllm.py:712-749 | use_v2_model_runner | Model Runner V2 or V1. |
vllm/config/vllm.py:854-874 | needs_dp_coordinator | Whether a DP coordinator process starts. |
vllm/config/vllm.py:1005-1049 | _set_config_default, _apply_optimization_level_defaults | Fill only fields that are None. |
vllm/config/vllm.py:1492-2318 | __post_init__ | Cross-checks and late defaults. |
vllm/config/vllm.py:2377-2636 | _set_cudagraph_sizes | CUDA graph capture sizes. |
vllm/config/vllm.py:2637-2713 | _set_compile_ranges | Token ranges for compiled graphs. |
vllm/config/vllm.py:2714-2774 | try_verify_and_update_config | Per-architecture hooks, once. |
vllm/config/vllm.py:2788-2823 | __str__ | The text of the "Initializing a V1 LLM engine" log line. |
vllm/config/vllm.py:2999-3033 | _get_v2_model_runner_unsupported_features | The fallback reasons to Model Runner V1. |
vllm/config/vllm.py:3381-3511 | Model validators | Pydantic checks after the constructor. |
vllm/config/vllm.py:3516-3590 | set_current_vllm_config, get_current_vllm_config | The active config as a module global. |
Walk through the main methods#
The fields#
support different configs. Make sure the configs are valid for the platform
you are using. Contents must be hashable."""
instance_id: str = ""
"""The ID of the vLLM instance."""
optimization_level: OptimizationLevel = OptimizationLevel.O2
"""The optimization level. These levels trade startup time cost for
performance, with -O0 having the best startup time and -O3 having the best
performance. -O2 is used by default. See OptimizationLevel for full
description."""
performance_mode: PerformanceMode = "balanced"
"""Performance mode for runtime behavior, 'balanced' is the default.
'interactivity' favors low end-to-end per-request latency at small batch
sizes (fine-grained CUDA graphs, latency-oriented kernels).
'throughput' favors aggregate tokens/sec at high concurrency (larger CUDA
graphs, more aggressive batching, throughput-oriented kernels)."""
Most fields are sub-configs with a default factory (vllm/config/vllm.py:357-422). The top-level settings are additional_config, instance_id, optimization_level (default O2), performance_mode (default "balanced") and shutdown_timeout (vllm/config/vllm.py:428-457). The generated table of all fields is on Config classes.
The optimization level tables#
OPTIMIZATION_LEVEL_02 = {
"compilation_config": {
"pass_config": {
"fuse_norm_quant": enable_norm_fusion,
"fuse_act_quant": enable_act_fusion,
"fuse_allreduce_rms": enable_allreduce_rms_fusion,
"fuse_attn_quant": IS_QUANTIZED,
"fuse_act_padding": enable_norm_pad_fusion,
"fuse_mla_dual_rms_norm": enable_mla_dual_rms_norm_fusion,
"fuse_rope_kvcache": enable_rope_kvcache_fusion,
"fuse_qk_norm_rope_kvcache": enable_qk_norm_rope_kvcache,
"enable_qk_norm_rope_fusion": False,
"fuse_rope_kvcache_cat_mla": enable_rope_kvcache_mla_fusion,
},
"cudagraph_mode": CUDAGraphMode.FULL_AND_PIECEWISE,
"use_inductor_graph_partition": False,
},
"kernel_config": {
"enable_flashinfer_autotune": True,
},
}O0 turns all fusions off and sets CUDA graphs to NONE; O1 uses PIECEWISE; O2 and O3 use FULL_AND_PIECEWISE and are equal at this commit (vllm/config/vllm.py:254-337). The docstring of O3 says "Currently the same as -O2s." (vllm/config/vllm.py:142-143). A value is either a constant or a function of the config, for example enable_allreduce_rms_fusion (vllm/config/vllm.py:178-202). apply_recursive walks the nested table. For each leaf it calls _set_config_default, which calls the function and writes the result only when the field is None (vllm/config/vllm.py:1014-1019):
def apply_recursive(config_obj: Any, config_defaults: dict[str, Any]) -> None:
"""Recursively apply defaults to config_obj, using self as root."""
for key, value in config_defaults.items():
if not hasattr(config_obj, key):
continue
current = getattr(config_obj, key)
if isinstance(value, dict) and is_dataclass(current):
apply_recursive(current, value)
else:
self._set_config_default(config_obj, key, value)
apply_recursive(self, defaults)So the tables never overwrite a value that the user or an earlier phase set. The page torch.compile and CUDA graphs in vLLM explains what each pass and mode does.
__post_init__, phase by phase#
The method is about 830 lines long. Its docstring is "Verify configs are valid & consistent with each other." (vllm/config/vllm.py:1493). We read it in nine phases.
Phase 1: early exits and model hooks.
self.instance_id = f"{time.time_ns()}"
if self.model_config is not None and self.model_config.is_submodel_config:
# with_hf_config() view: the parent config was already validated,
# and this view's empty architecture list makes the model-dependent checks
# below unsafe (e.g. use_mla resolves the architecture registry).
return
self._resolve_mm_encoder_only()
if self.is_mm_encoder_only and self.cache_config.enable_prefix_caching:
# Such an instance publishes encoder embeddings and runs no language
# model, so it holds no KV cache for prefix caching to reuse and its
# coordinator would have no group to manage. Disable before
# `try_verify_and_update_config` so model config hooks (e.g. the
# hybrid mamba hook setting `mamba_block_size`) already see prefix
# caching as disabled.
logger.info(
"Disabling prefix caching: this instance runs the "
"multi-modal encoder only."
)
self.cache_config.enable_prefix_caching = False
if self.performance_mode != "balanced":
logger.info_once("Performance mode set to '%s'.", self.performance_mode)
self.try_verify_and_update_config()
self._reject_mismatched_float_kv_cache()
self._resolve_and_verify_engram_config()
self._check_supports_watermarking()
# Models may have supplied their own DCP defaults above; anything still
# unset falls back to the stock ones.
self.parallel_config.set_dcp_defaults()
if self.model_config is not None:
self.model_config.verify_with_parallel_config(self.parallel_config)
self.model_config.verify_dual_chunk_attention_config(self.load_config)
self.parallel_config.is_moe_model = self.model_config.is_moe- Line 1495.
instance_idfrom the clock, for profile names. - Lines 1497-1501. A sub-model view made by
with_hf_configskips all checks. - Lines 1505-1516. An instance that runs only the multimodal encoder turns prefix caching off.
- Line 1521. The per-architecture hooks run once; a flag on the model config stops a second run (
vllm/config/vllm.py:2718-2721). - Lines 1531-1534. The model checks itself against the parallel config, and the parallel config learns if the model is MoE.
Phase 2: quantization and weights. quant_config comes from the model and load configs (vllm/config/vllm.py:1586-1589). For most load formats, the model config may untie the word embeddings (lines 1594-1599).
Phase 3: async scheduling.
elif self.scheduler_config.async_scheduling is None:
# Enable async scheduling unless there is an incompatible option.
if (
self.model_config is not None
and self.model_config.runner_type == "pooling"
):
# The current implementation of asynchronous scheduling negatively
# impacts performance of pooling models, so we disable by default.
logger.debug(
"Disabling asynchronous scheduling by default for pooling model."
)
self.scheduler_config.async_scheduling = False
elif not executor_supports_async_sched:
logger.warning_once(
"Async scheduling will be disabled because it is not supported "
"with the `%s` distributed executor backend. ",
executor_backend,
)
self.scheduler_config.async_scheduling = False
elif uses_rocm_deepep_ht_dbo:
logger.warning_once(
"Async scheduling is disabled for ROCm DeepEP "
"high-throughput DBO because that combination can corrupt "
"DP+EP generation accuracy."
)
self.scheduler_config.async_scheduling = False
elif v1_async_sched_incompat is not None:
logger.warning_once(
"Async scheduling is disabled because the V1 model runner "
"does not support it with %s.",
v1_async_sched_incompat,
)
self.scheduler_config.async_scheduling = False
else:
self.scheduler_config.async_scheduling = TrueAn explicit --async-scheduling raises on a conflict; an unset value only switches it off, with a log (lines 1647-1663). The page Async scheduling: overlap the CPU and the GPU explains the feature.
Phase 4: eager mode and compile switches. --enforce-eager sets the compile mode and the CUDA graph mode to NONE (vllm/config/vllm.py:1745-1763). TORCH_COMPILE_DISABLE=1 sets the compile mode to NONE (lines 1765-1770). For some architectures, _maybe_enable_breakable_cudagraph writes VLLM_USE_BREAKABLE_CUDAGRAPH=1 into os.environ and turns compilation off (vllm/config/vllm.py:824-851).
Phase 5: platform defaults, compile mode, level defaults.
current_platform.apply_config_platform_defaults(self)
if self.compilation_config.mode is None:
if self.optimization_level > OptimizationLevel.O0:
self.compilation_config.mode = CompilationMode.VLLM_COMPILE
else:
self.compilation_config.mode = CompilationMode.NONE
# By default, enable torch wrapping only when using custom Inductor lowering
if self.compilation_config.ir_enable_torch_wrap is None:
self.compilation_config.ir_enable_torch_wrap = (
self.compilation_config.mode == CompilationMode.VLLM_COMPILE
and self.compilation_config.backend == "inductor"
)
if all(s not in self.compilation_config.custom_ops for s in ("all", "none")):
if (
self.compilation_config.backend == "inductor"
and self.compilation_config.mode != CompilationMode.NONE
):
self.compilation_config.custom_ops.append("none")
else:
self.compilation_config.custom_ops.append("all")
# This populates IR op priorities,
# must happen after compilation mode and backend are decided,
# but before fusion defaults are applied as those may depend on op priority.
self.kernel_config.set_platform_defaults(self)
default_config = OPTIMIZATION_LEVEL_TO_CONFIG[self.optimization_level]
self._apply_optimization_level_defaults(default_config)- Line 1804. The first platform hook. It runs before the level defaults, so a platform can set a field and keep it.
- Lines 1806-1810. No mode set:
VLLM_COMPILEabove O0, elseNONE. - Lines 1819-1826. The base entry of the custom op list:
"none"with Inductor and compilation, else"all"(CustomOp, out-of-tree ops and IR op providers). - Line 1831. The IR op priorities, before the fusion defaults that depend on them.
- Lines 1833-1834. The level table.
Phase 6: CUDA graphs. Piecewise modes need VLLM_COMPILE, else the mode falls back (vllm/config/vllm.py:1111-1131, vllm/config/vllm.py:1909-1911). On a platform with static graphs, pooling models get PIECEWISE, encoder-decoder models FULL_DECODE_ONLY, and some KV connectors PIECEWISE (vllm/config/vllm.py:1929-1980). With --enforce-eager, the eager branch clears the sizes. Then _set_cudagraph_sizes runs in both cases (vllm/config/vllm.py:1982-1992).
Phase 7: the platform's last word. current_platform.check_and_update_config(self) (vllm/config/vllm.py:2046). A comment right after it says that the platform "has the last word on async scheduling" (line 2048).
Phase 8: model runner and compile ranges.
if self.use_v2_model_runner:
self._disable_cudagraphs_for_v2_stock_torch_compile()
self._validate_v2_model_runner()
else:
self._validate_v1_model_runner()
self._validate_profiler_config()
self._validate_batch_sharded_sampling()
self._validate_adaptive_verification()
# Re-compute compile ranges after platform-specific config updates
# (e.g., XPU may lower max_num_batched_tokens when MLA is enabled)
self._set_compile_ranges()An unsupported feature normally selects Model Runner V1 (the use_v2_model_runner property below). It raises ValueError only when Model Runner V2 is forced, for example with VLLM_USE_V2_MODEL_RUNNER=1 (vllm/config/vllm.py:3283-3292, vllm/config/vllm.py:730-732). The compile ranges are computed again, because the platform hook can change max_num_batched_tokens (comment at lines 2081-2082).
Phase 9: KV transfer, hybrid KV cache manager, final checks. _post_init_kv_transfer_config resolves KV offloading (vllm/config/vllm.py:2199). The hybrid KV cache manager is turned off when the platform or the KV connector does not support it; an explicit --enable-hybrid-kv-cache-manager then raises (vllm/config/vllm.py:2236-2273). The last line logs the enabled compile passes (vllm/config/vllm.py:2318).
compute_hash#
factors: list[Any] = []
# summarize vllm config
vllm_factors: list[Any] = []
if include_version:
from vllm import __version__
vllm_factors.append(__version__)
if self.model_config:
vllm_factors.append(self.model_config.compute_hash())
if (
self.compilation_config
and getattr(self.compilation_config, "compile_mm_encoder", False)
and self.model_config.multimodal_config
):
vllm_factors.append(self.model_config.multimodal_config.compute_hash())
else:
vllm_factors.append("None")
if self.cache_config:
vllm_factors.append(self.cache_config.compute_hash())
else:
vllm_factors.append("None")
if self.parallel_config:
vllm_factors.append(self.parallel_config.compute_hash())
else:
vllm_factors.append("None")
if self.scheduler_config:
vllm_factors.append(self.scheduler_config.compute_hash())The method collects one hash per sub-config in a fixed order; a missing optional config adds "None". additional_config is hashed as sorted JSON (vllm/config/vllm.py:559-567). The result is the first 10 hex digits of safe_hash(str(factors)) (vllm/config/vllm.py:572-575), which is MD5, or SHA-256 when MD5 is not available (vllm/utils/hashing.py:106-121). The docstring warns that a new field must join the list if it changes the computation graph (vllm/config/vllm.py:460-468).
use_v2_model_runner and max_concurrent_batches#
@property
def use_v2_model_runner(self) -> bool:
if self.attention_config.hisparse_config is not None:
if envs.VLLM_USE_V2_MODEL_RUNNER is False:
raise ValueError(
"HiSparse requires Model Runner V2; remove "
"VLLM_USE_V2_MODEL_RUNNER=0."
)
return True
if getattr(self, "watermark_config", None) is not None:
if envs.VLLM_USE_V2_MODEL_RUNNER is False:
logger.info_once(
"Watermarking requires Model Runner V2 and overrides "
"VLLM_USE_V2_MODEL_RUNNER=0."
)
return True
use_v2_model_runner = envs.VLLM_USE_V2_MODEL_RUNNER
if use_v2_model_runner is not None:
return use_v2_model_runner
if not HAS_TRITON:
logger.warning_once(
"Model Runner V2 requires Triton; using the V1 model runner instead."
)
return False
unsupported = self._get_v2_model_runner_unsupported_features()
if unsupported:
logger.warning_once(
"Model Runner V2 does not yet support %s; using the V1 model "
"runner instead.",
", ".join(unsupported),
)
return False
return True- Lines 714-728. HiSparse and watermarking need Model Runner V2. HiSparse with
VLLM_USE_V2_MODEL_RUNNER=0raises; watermarking ignores the 0. - Lines 730-732. Otherwise the variable decides, if it is set.
- Lines 734-747. No Triton, or a feature from
_get_v2_model_runner_unsupported_features: Model Runner V1, with a warning (The model runner (Model Runner V2)).
max_concurrent_batches is pp_size + 1 with async scheduling and Model Runner V2, so 2 on one GPU (vllm/config/vllm.py:586-597). The engine core makes a batch queue only when the value is above 1 (vllm/v1/engine/core.py:205-211).
set_current_vllm_config#
@contextmanager
def set_current_vllm_config(
vllm_config: VllmConfig, check_compile=False, prefix: str | None = None
):
"""Temporarily set the current vLLM config.
Used during model initialization.
We save the current vLLM config in a global variable,
so that all modules can access it, e.g. custom ops
can access the vLLM config to determine how to dispatch.
"""
global _current_vllm_config, _current_prefix
old_vllm_config = _current_vllm_config
old_prefix = _current_prefix
from vllm.compilation.counter import compilation_counter
num_models_seen = compilation_counter.num_models_seen
try:
# Clear the compilation config cache when context changes.
# This is needed since the old config may have been accessed
# and cached before the new config is set.
get_cached_compilation_config.cache_clear()
_current_vllm_config = vllm_config
_current_prefix = prefix
yieldThe context manager puts the config into a module global for the time of the block, for example while the worker builds the model (vllm/v1/worker/gpu_worker.py:592-598). get_current_vllm_config() raises an AssertionError outside such a block (lines 3576-3586). A CustomOp made at import time hits this error, as the message says.
Deep dive#
The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.
Deep diveWorked example: the compilation fields of one start
vllm serve meta-llama/Llama-3.1-8B-Instruct on one H100, with no flags. The table follows the compilation fields through __post_init__. (Derived from the lines named.)
| Field | After the constructor | Phase and line | Value after |
|---|---|---|---|
compilation_config.mode | None (vllm/config/compilation.py:432) | 5: vllm/config/vllm.py:1806-1808 | VLLM_COMPILE |
custom_ops | [] | 5: vllm/config/vllm.py:1819-1824 | ["none"] |
cudagraph_mode | None (vllm/config/compilation.py:610) | 5: O2 table, vllm/config/vllm.py:310 | FULL_AND_PIECEWISE |
max_cudagraph_capture_size | None | 6: vllm/config/vllm.py:2439-2449 | min(1024 × 1 × 2, 512) = 512 |
scheduler_config.async_scheduling | None | 3: vllm/config/vllm.py:1697-1698 | True |
parallel_config.worker_cls | "auto" | 7: vllm/platforms/cuda.py:330-331 | vllm.v1.worker.gpu_worker.Worker |
With --enforce-eager, phase 4 sets the mode and the graph mode to NONE first. The level table then leaves both alone, because they are not None any more (vllm/config/vllm.py:1014-1019).
Deep dive__post_init__ also writes the environment
Two places in __post_init__ write os.environ:
VLLM_USE_BREAKABLE_CUDAGRAPH=1for the default breakable-graph architectures, or for some batch-invariant BF16 models (vllm/config/vllm.py:824-843).NCCL_GRAPH_REGISTER=0when the CUDA graph pool uses cumem, with a warning if the user set another value (vllm/config/vllm.py:2309-2316).
In vllm serve, this runs in the API server before the engine core starts. A spawned child gets a copy of the parent environment, so the engine core and the workers see the values (derived; see How configuration works, Step 9). So in vLLM some configuration travels in the environment, not in the config object.
Deep diveError paths: what raises, what only logs
| Situation | Result | Where |
|---|---|---|
Explicit --async-scheduling with an unsupported executor | ValueError | vllm/config/vllm.py:1660-1663 |
| Async scheduling unset, executor unsupported | warning; async off | vllm/config/vllm.py:1676-1682 |
--language-model-only with cudagraph_mm_encoder | ValueError | vllm/config/vllm.py:1536-1547 |
mm_tensor_ipc="torch_shm" without spawn | ValueError | vllm/config/vllm.py:1722-1731 |
| Inductor turned off while fusion options are set | warning: those options "will be ignored" | vllm/config/vllm.py:1774-1785 |
| Pooling model with full CUDA graphs | warning; mode becomes PIECEWISE | vllm/config/vllm.py:1933-1940 |
| Model Runner V2 and an unsupported feature (forced V2) | ValueError | vllm/config/vllm.py:3283-3292 |
| Explicit hybrid KV cache manager that the setup cannot use | ValueError | vllm/config/vllm.py:2260-2269 |
get_current_vllm_config() outside a config block | AssertionError | vllm/config/vllm.py:3576-3586 |
Deep diveEnvironment variables read in this file
| Name | Read at | Effect |
|---|---|---|
VLLM_USE_V2_MODEL_RUNNER | vllm/config/vllm.py:715, vllm/config/vllm.py:730 | Forces Model Runner V2 or V1 (with exceptions). |
TORCH_COMPILE_DISABLE (read with os.environ) | vllm/config/vllm.py:1765 | Compilation mode NONE. |
VLLM_USE_BREAKABLE_CUDAGRAPH (read and written) | vllm/config/vllm.py:826 | Breakable CUDA graphs; turns compilation off. |
VLLM_WORKER_MULTIPROC_METHOD (read with os.environ) | vllm/config/vllm.py:1726, vllm/config/vllm.py:2017 | Checks for torch_shm and Whisper. |
VLLM_DEBUG_DUMP_PATH | vllm/config/vllm.py:2289-2297 | Overrides the compile debug dump path. |
Related concepts#
- How configuration works: the whole path from flags to processes.
- engine/arg_utils.py: EngineArgs, the flags and create_engine_config: the code that makes this object.
- torch.compile and CUDA graphs in vLLM: the compile modes, graph modes and the cache that uses
compute_hash. - The Platform interface: the two platform hooks inside
__post_init__. - Config classes: every field of every config class.
Check yourself#
- 1What doeseasy
-O3change compared to the default at this commit?Show answer
Nothing. The O3 table has the same values as the O2 table (
vllm/config/vllm.py:296-337), and the docstring says "Currently the same as -O2s." (vllm/config/vllm.py:142-143). - 2A platform plugin wants a different default CUDA graph mode, but it must not override a user's value. Which hook should it use?medium
Show answer
apply_config_platform_defaults. It runs before the level defaults (vllm/config/vllm.py:1804,vllm/config/vllm.py:1833-1834). If the hook sets the mode only when it isNone, the level table then keeps the value, because it fills onlyNonefields (vllm/config/vllm.py:1014-1019). - 3Why does a change ofmedium
--max-num-seqsmake the next start compile again?Show answer
SchedulerConfig.compute_hashincludesmax_num_seqs(vllm/config/scheduler.py:282-284).VllmConfig.compute_hashincludes that hash (vllm/config/vllm.py:500-501), and the compile cache folder name depends on it (vllm/compilation/backends.py:1091-1097). - 4You starthard
vllm servewith--enforce-eager -O2. Which CUDA graph capture sizes does the config end with, and which lines decide it?Show answer
An empty list, with
max_cudagraph_capture_size = 0. Phase 4 sets the graph mode toNONE(vllm/config/vllm.py:1745-1747); the O2 table does not override it, because the field is notNone(vllm/config/vllm.py:1014-1019). On a static-graph platform, the eager branch then clears the sizes (vllm/config/vllm.py:1983-1988).