main
py 152 lines 7.07 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import os
5 from typing import TYPE_CHECKING, Any, Literal
6
7 import torch
8 from pydantic import ConfigDict, Field, model_validator
9 from typing_extensions import Self
10
11 from vllm import envs
12 from vllm.config.utils import config
13 from vllm.logger import init_logger
14 from vllm.platforms import current_platform
15 from vllm.utils.hashing import safe_hash
16
17 if TYPE_CHECKING:
18 from vllm.config import ModelConfig
19 from vllm.config.cache import CacheConfig
20 else:
21 ModelConfig = Any
22 CacheConfig = Any
23
24 logger = init_logger(__name__)
25
26 LoRADType = Literal["auto", "float16", "bfloat16"]
27 MaxLoRARanks = Literal[1, 8, 16, 32, 64, 128, 256, 320, 512]
28 LoRAExtraVocabSize = Literal[256, 512]
29
30
31 @config(config=ConfigDict(arbitrary_types_allowed=True))
32 class LoRAConfig:
33 """Configuration for LoRA."""
34
35 max_lora_rank: MaxLoRARanks = 16
36 """Max LoRA rank."""
37 max_loras: int = Field(default=1, ge=1)
38 """Max number of LoRAs in a single batch."""
39 fully_sharded_loras: bool = False
40 """By default, only half of the LoRA computation is sharded with tensor
41 parallelism. Enabling this will use the fully sharded layers. At high
42 sequence length, max rank or tensor parallel size, this is likely faster.
43 """
44 max_cpu_loras: int | None = None
45 """Maximum number of LoRAs to store in CPU memory. Must be >= than
46 `max_loras`."""
47 lora_dtype: torch.dtype | LoRADType = "auto"
48 """Data type for LoRA. If auto, will default to base model dtype."""
49 target_modules: list[str] | None = None
50 """Restrict LoRA to specific module suffixes (e.g., ["o_proj", "qkv_proj"]).
51 If None, all supported LoRA modules are used. This allows deployment-time
52 control over which modules have LoRA applied, useful for performance tuning."""
53 default_mm_loras: dict[str, str] | None = None
54 """Dictionary mapping specific modalities to LoRA model paths; this field
55 is only applicable to multimodal models and should be leveraged when a
56 model always expects a LoRA to be active when a given modality is present.
57 Note that currently, if a request provides multiple additional
58 modalities, each of which have their own LoRA, we do NOT apply
59 default_mm_loras because we currently only support one lora adapter
60 per prompt. When run in offline mode, the lora IDs for n modalities
61 will be automatically assigned to 1-n with the names of the modalities
62 in alphabetic order."""
63 enable_tower_connector_lora: bool = False
64 """If `True`, LoRA support for the tower (vision encoder) and connector
65 of multimodal models will be enabled. This is an experimental feature and
66 currently only supports some MM models such as the Qwen VL series. The default
67 is False."""
68 specialize_active_lora: bool = False
69 """Whether to construct lora kernel grid by the number of active LoRA adapters.
70 When set to True, separate cuda graphs will be captured for different counts
71 of active LoRAs (powers of 2 up to max_loras), which can improve performance
72 for variable LoRA usage patterns at the cost of increased startup time and
73 memory usage. Only takes effect when cudagraph_specialize_lora is True.
74 """
75 enable_mixed_moe_lora_format: bool = False
76 """If True, force the engine to use the universal 2D MoE LoRA wrapper
77 (`FusedMoEWithLoRA`) regardless of the model's `is_3d_moe_weight` flag, so
78 that 2D-format and 3D-format MoE LoRA adapters can be served in the same
79 deployment. Only meaningful for MoE models; ignored otherwise. Default False
80 keeps the existing model-driven behavior."""
81 enable_moe_shared_loras: bool = False
82 """If True, load MoE expert adapters in the "shared-outer" layout, where the
83 gate/up (`w1`/`w3`) lora_A and the down (`w2`) lora_B are shared across all
84 experts (stored once with expert-dim 1) instead of per-expert. The shared
85 factors are broadcast to the expert count at kernel time. Only meaningful for
86 MoE models whose adapters use this layout; ignored otherwise."""
87 max_lora_cls_labels: int | None = Field(default=None, ge=1)
88 """Maximum output size for LoRA classification heads. Defaults to the
89 base classification head size."""
90
91 def compute_hash(self) -> str:
92 """WARNING: Whenever a new field is added to this config,
93 ensure that it is included in the factors list if
94 it affects the computation graph.
95
96 Provide a hash that uniquely identifies all the configs
97 that affect the structure of the computation
98 graph from input ids/embeddings to the final hidden states,
99 excluding anything before input ids/embeddings and after
100 the final hidden states.
101 """
102 factors: list[Any] = []
103 factors.append(self.max_lora_rank)
104 factors.append(self.max_loras)
105 factors.append(self.fully_sharded_loras)
106 factors.append(self.lora_dtype)
107 factors.append(self.enable_tower_connector_lora)
108 factors.append(self.enable_mixed_moe_lora_format)
109 factors.append(self.enable_moe_shared_loras)
110 factors.append(self.max_lora_cls_labels)
111 # target_modules affects which modules get LoRA applied
112 factors.append(
113 tuple(sorted(self.target_modules)) if self.target_modules else None
114 )
115
116 hash_str = safe_hash(str(factors).encode(), usedforsecurity=False).hexdigest()
117 return hash_str
118
119 @model_validator(mode="after")
120 def _validate_lora_config(self) -> Self:
121 if self.max_cpu_loras is None:
122 self.max_cpu_loras = self.max_loras
123 elif self.max_cpu_loras < self.max_loras:
124 raise ValueError(
125 f"max_cpu_loras ({self.max_cpu_loras}) must be >= "
126 f"max_loras ({self.max_loras})."
127 )
128 if envs.VLLM_LORA_ENABLE_DUAL_STREAM and not current_platform.is_cuda_alike():
129 raise ValueError("Dual CUDA streams are only supported on CUDA platforms.")
130 if envs.VLLM_LORA_ENABLE_DUAL_STREAM and self.fully_sharded_loras:
131 logger.warning_once(
132 "fully_sharded_loras isn't compatible with "
133 "VLLM_LORA_ENABLE_DUAL_STREAM, set VLLM_LORA_ENABLE_DUAL_STREAM=False"
134 )
135 envs.VLLM_LORA_ENABLE_DUAL_STREAM = False
136 return self
137
138 def verify_with_model_config(self, model_config: ModelConfig):
139 if self.lora_dtype in (None, "auto"):
140 self.lora_dtype = model_config.dtype
141 elif isinstance(self.lora_dtype, str):
142 self.lora_dtype = getattr(torch, self.lora_dtype)
143
144 architectures = getattr(model_config, "architectures", None) or []
145 is_inkling = any("Inkling" in arch for arch in architectures)
146 if is_inkling and os.environ.get("INKLING_MULTIMEM_AR", "1") != "0":
147 raise ValueError(
148 "Inkling LoRA requires INKLING_MULTIMEM_AR=0: the Lamport "
149 "fused-collective path bypasses the LoRA-wrapped wo_ud and dense "
150 "down_proj layers on decode-sized batches, silently dropping "
151 "their LoRA. Set INKLING_MULTIMEM_AR=0."
152 )