d2d649e674
Signed-off-by: Juntian Liu <Juntianl777@gmail.com> Signed-off-by: Woosuk Kwon <woosuk@inferact.ai> Co-authored-by: Roger Wang <hey@rogerw.io> Co-authored-by: ywang96 <> Co-authored-by: Woosuk Kwon <woosuk@inferact.ai> Co-authored-by: Codex <noreply@openai.com>
123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from pydantic import Field, model_validator
|
|
from typing_extensions import Self
|
|
|
|
import vllm.envs as envs
|
|
from vllm.config.utils import config, get_hash_factors, hash_factors
|
|
|
|
if TYPE_CHECKING:
|
|
from vllm.config.load import LoadConfig
|
|
from vllm.config.model import ModelConfig
|
|
from vllm.config.parallel import ParallelConfig
|
|
|
|
# Architecture -> the hf_text_config field naming its n-gram layers. A model is
|
|
# only configurable here if it actually has such layers to store.
|
|
_NGRAM_LAYER_FIELDS = {
|
|
"DeepseekV41ForCausalLM": "engram_layer_ids",
|
|
"Qwen4ExpForCausalLM": "ple_layer_ids",
|
|
"Qwen4ExpForConditionalGeneration": "ple_layer_ids",
|
|
}
|
|
|
|
|
|
def _default_cpu_offload() -> bool:
|
|
return envs.VLLM_PLE_CPU_OFFLOAD
|
|
|
|
|
|
def model_has_engram_layers(model_config: "ModelConfig | None") -> bool:
|
|
"""Whether the model carries n-gram embedding layers."""
|
|
if model_config is None:
|
|
return False
|
|
field = _NGRAM_LAYER_FIELDS.get(model_config.architecture)
|
|
if field is None:
|
|
return False
|
|
return bool(getattr(model_config.hf_text_config, field, None))
|
|
|
|
|
|
@config
|
|
class EngramConfig:
|
|
"""Configuration for Engram embedding storage and sharding."""
|
|
|
|
cpu_offload: bool = Field(default_factory=_default_cpu_offload)
|
|
"""Store embedding weights in pinned CPU memory for UVA lookup.
|
|
Defaults to VLLM_PLE_CPU_OFFLOAD, which is enabled by default. An explicit
|
|
value takes precedence over the environment variable."""
|
|
|
|
embedding_across_dp: bool = False
|
|
"""Shard embeddings across TP and all DP ranks when enabled.
|
|
Otherwise, each DP rank has a separate TP-sharded embedding replica."""
|
|
|
|
dp_shared_memory: bool = False
|
|
"""Share CPU-offloaded embedding weights between co-located
|
|
DP replicas. Each node stores one copy of every TP shard, reducing host
|
|
memory without per-step Engram DP collectives. Requires sufficient
|
|
/dev/shm capacity and a shared IPC namespace."""
|
|
|
|
@model_validator(mode="after")
|
|
def _validate_shared_memory(self) -> Self:
|
|
if self.dp_shared_memory and not self.cpu_offload:
|
|
raise ValueError("dp_shared_memory requires cpu_offload=True")
|
|
return self
|
|
|
|
def verify_model_config(self, model_config: "ModelConfig | None") -> None:
|
|
"""Reject Engram configuration for models without n-gram embeddings."""
|
|
from vllm.platforms import current_platform
|
|
|
|
field = (
|
|
_NGRAM_LAYER_FIELDS.get(model_config.architecture)
|
|
if model_config is not None
|
|
else None
|
|
)
|
|
if (
|
|
model_config is None
|
|
or field is None
|
|
or not current_platform.is_cuda()
|
|
or not getattr(model_config.hf_text_config, field, None)
|
|
):
|
|
raise ValueError(
|
|
"EngramConfig requires a model with supported Engram "
|
|
"embeddings, non-empty n-gram layer ids, and CUDA."
|
|
)
|
|
|
|
def verify_parallel_config(self, parallel_config: "ParallelConfig") -> None:
|
|
"""Reject unsupported embedding parallel topologies."""
|
|
if self.dp_shared_memory:
|
|
if parallel_config.data_parallel_size <= 1:
|
|
raise ValueError("dp_shared_memory requires data_parallel_size > 1.")
|
|
if parallel_config.enable_elastic_ep:
|
|
raise ValueError("dp_shared_memory is not supported with elastic EP.")
|
|
if (
|
|
self.embedding_across_dp
|
|
and parallel_config.data_parallel_size > 1
|
|
and parallel_config.enable_elastic_ep
|
|
):
|
|
raise ValueError(
|
|
"Engram embedding_across_dp is not supported with elastic EP yet."
|
|
)
|
|
|
|
def verify_load_config(self, load_config: "LoadConfig") -> None:
|
|
"""Shared tables require a loader that invokes parameter weight callbacks."""
|
|
if self.dp_shared_memory and load_config.load_format not in (
|
|
"auto",
|
|
"safetensors",
|
|
"pt",
|
|
):
|
|
raise ValueError(
|
|
"dp_shared_memory requires load_format 'auto', "
|
|
f"'safetensors' or 'pt'; got {load_config.load_format!r}."
|
|
)
|
|
|
|
def get_parallel_size(self, parallel_config: "ParallelConfig") -> int:
|
|
"""Derive the embedding group size from the parallel configuration."""
|
|
size = parallel_config.tensor_parallel_size
|
|
if self.embedding_across_dp and parallel_config.data_parallel_size > 1:
|
|
size *= parallel_config.data_parallel_size
|
|
return size
|
|
|
|
def compute_hash(self) -> str:
|
|
"""Hash settings that affect embedding execution and graph structure."""
|
|
return hash_factors(get_hash_factors(self, set()))
|