[MyPy] Fix mypy for `vllm/lora` (#41722)
Signed-off-by: Martin Hickey <martin.hickey@ie.ibm.com>
Martin Hickey committed
Jun 22, 2026 at 15:57 UTC
ccd49f6821ee110cc5a2b1aba620a8a1d66c7cbb
15 files changed
+140
-50
tests/lora/test_lora_manager.py
+21
-8
@@ -124,6 +124,7 @@ def test_replace_submodules(default_vllm_config, dist_init, dummy_model):
124
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
125
),
126
torch.device(DEVICES[0]),
127
+ default_vllm_config,
128
)
129
model = manager.model
130
assert isinstance(model.get_submodule("dense1"), ColumnParallelLinearWithLoRA)
@@ -152,6 +153,7 @@ def test_wrap_replicated_linear_subclasses(default_vllm_config, dist_init, dummy
153
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
154
),
155
torch.device(DEVICES[0]),
156
+ default_vllm_config,
157
)
158
159
assert isinstance(
@@ -172,6 +174,7 @@ def test_wrap_gate_linear(default_vllm_config, dist_init, dummy_model):
174
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
175
),
176
torch.device(DEVICES[0]),
177
+ default_vllm_config,
178
)
179
180
assert isinstance(
@@ -219,6 +222,7 @@ def test_dedup_shared_module_across_paths(default_vllm_config, dist_init, dummy_
222
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
223
),
224
torch.device(DEVICES[0]),
225
+ default_vllm_config,
226
)
227
228
canonical = manager.model.get_submodule("moe.gate")
@@ -263,6 +267,7 @@ def test_lm_head_exempt_from_dedup(default_vllm_config, dist_init, dummy_model):
267
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
268
),
269
torch.device(DEVICES[0]),
270
+ default_vllm_config,
271
)
272
273
# lm_head's special handling still ran: logits_processor got wrapped
@@ -293,6 +298,7 @@ def test_skip_unsupported_matched_modules(default_vllm_config, dist_init, dummy_
298
max_lora_rank=8, max_cpu_loras=8, max_loras=8, lora_dtype=DEFAULT_DTYPE
299
),
300
torch.device(DEVICES[0]),
301
+ default_vllm_config,
302
)
303
304
# Should not crash and should keep unsupported matched modules unchanged.
@@ -325,6 +331,7 @@ def test_target_modules_fail_closed_on_unsupported_matched_modules(
331
target_modules=["dense1"],
332
),
333
torch.device(DEVICES[0]),
334
+ default_vllm_config,
335
)
336
337
@@ -374,6 +381,7 @@ def test_lora_model_manager(default_vllm_config, dist_init, dummy_model, device)
381
max_lora_rank=8, max_cpu_loras=3, max_loras=2, lora_dtype=DEFAULT_DTYPE
382
),
383
device=device,
384
+ vllm_config=default_vllm_config,
385
)
386
assert all(x is None for x in manager.lora_index_to_id)
387
assert manager.add_adapter(model_lora1)
@@ -442,6 +450,7 @@ def test_lora_lru_cache_model_manager(
450
max_lora_rank=8, max_cpu_loras=3, max_loras=2, lora_dtype=DEFAULT_DTYPE
451
),
452
device=device,
453
+ vllm_config=default_vllm_config,
454
)
455
assert all(x is None for x in manager.lora_index_to_id)
456
assert manager.add_adapter(model_lora1)
@@ -535,6 +544,7 @@ def test_lru_lora_model_manager(default_vllm_config, dist_init, dummy_model, dev
544
max_lora_rank=8, max_cpu_loras=2, max_loras=2, lora_dtype=DEFAULT_DTYPE
545
),
546
device=device,
547
+ vllm_config=default_vllm_config,
548
)
549
assert all(x is None for x in manager.lora_index_to_id)
550
@@ -642,9 +652,7 @@ def test_lru_lora_model_manager(default_vllm_config, dist_init, dummy_model, dev
652
653
654
@pytest.mark.parametrize("device", DEVICES)
645
-def test_lru_cache_worker_adapter_manager(
646
- default_vllm_config, dist_init, dummy_model, device, tmp_path
647
-):
655
+def test_lru_cache_worker_adapter_manager(dist_init, dummy_model, device, tmp_path):
656
lora_config = LoRAConfig(
657
max_lora_rank=8, max_cpu_loras=4, max_loras=4, lora_dtype=DEFAULT_DTYPE
658
)
@@ -670,7 +678,7 @@ def test_lru_cache_worker_adapter_manager(
678
worker_adapter_manager.max_num_seqs = 4
679
worker_adapter_manager.max_num_batched_tokens = 2
680
673
- worker_adapter_manager.create_lora_manager(dummy_model)
681
+ worker_adapter_manager.create_lora_manager(dummy_model, vllm_config)
682
683
mapping = LoRAMapping([], [])
684
worker_adapter_manager.set_active_adapters(
@@ -758,9 +766,7 @@ def test_lru_cache_worker_adapter_manager(
766
767
768
@pytest.mark.parametrize("device", DEVICES)
761
-def test_worker_adapter_manager(
762
- default_vllm_config, dist_init, dummy_model_gate_up, device, tmp_path
763
-):
769
+def test_worker_adapter_manager(dist_init, dummy_model_gate_up, device, tmp_path):
770
# Should remove every LoRA not specified in the request.
771
lora_config = LoRAConfig(
772
max_lora_rank=8, max_cpu_loras=4, max_loras=4, lora_dtype=DEFAULT_DTYPE
@@ -774,7 +780,7 @@ def test_worker_adapter_manager(
780
781
worker_adapter_manager = WorkerLoRAManager(vllm_config, device, EMBEDDING_MODULES)
782
worker_adapter_manager.vocab_size = dummy_model_gate_up.unpadded_vocab_size
777
- worker_adapter_manager.create_lora_manager(dummy_model_gate_up)
783
+ worker_adapter_manager.create_lora_manager(dummy_model_gate_up, vllm_config)
784
785
dummy_lora_files = f"{tmp_path}/lora_adapter"
786
os.makedirs(dummy_lora_files, exist_ok=True)
@@ -894,6 +900,7 @@ def test_packed_loras(default_vllm_config, dist_init, dummy_model_gate_up, devic
900
max_lora_rank=8, max_cpu_loras=2, max_loras=2, lora_dtype=DEFAULT_DTYPE
901
),
902
device=device,
903
+ vllm_config=default_vllm_config,
904
)
905
model = manager.model
906
@@ -944,6 +951,7 @@ def _test_target_modules(
951
device: str,
952
expected_lora: list[tuple[str, type]],
953
expected_no_lora: list[tuple[str, type]],
954
+ vllm_config,
955
):
956
"""Create a LoRAModelManager and assert which modules have LoRA applied."""
957
LoRAModelManager(
@@ -959,6 +967,7 @@ def _test_target_modules(
967
target_modules=target_modules,
968
),
969
device=device,
970
+ vllm_config=vllm_config,
971
)
972
for module_path, lora_cls in expected_lora:
973
assert isinstance(model.get_submodule(module_path), lora_cls)
@@ -981,6 +990,7 @@ def test_target_modules_config(default_vllm_config, dist_init, dummy_model, devi
990
("dense2", RowParallelLinearWithLoRA),
991
("layer1.dense2", RowParallelLinearWithLoRA),
992
],
993
+ vllm_config=default_vllm_config,
994
)
995
996
@@ -998,6 +1008,7 @@ def test_target_modules_multiple(default_vllm_config, dist_init, dummy_model, de
1008
("layer1.dense2", RowParallelLinearWithLoRA),
1009
],
1010
expected_no_lora=[],
1011
+ vllm_config=default_vllm_config,
1012
)
1013
1014
@@ -1017,6 +1028,7 @@ def test_target_modules_none_uses_all(
1028
("layer1.dense2", RowParallelLinearWithLoRA),
1029
],
1030
expected_no_lora=[],
1031
+ vllm_config=default_vllm_config,
1032
)
1033
1034
@@ -1036,4 +1048,5 @@ def test_target_modules_match_packed_runtime_modules(
1048
("layer1.dense1", ColumnParallelLinearWithLoRA),
1049
("layer1.dense2", RowParallelLinearWithLoRA),
1050
],
1051
+ vllm_config=default_vllm_config,
1052
)
tools/pre_commit/mypy.py
-2
@@ -25,8 +25,6 @@ import regex as re
25
# from "skip" to "silent", remove its directory from SEPARATE_GROUPS.
26
SEPARATE_GROUPS = [
27
"tests",
28
- # v0 related
29
- "vllm/lora",
28
]
29
30
# TODO(woosuk): Include the code from Megatron and HuggingFace.
vllm/lora/layers/base_linear.py
+11
-2
@@ -17,6 +17,7 @@ from vllm.forward_context import (
17
from vllm.model_executor.layers.linear import (
18
ColumnParallelLinear,
19
LinearBase,
20
+ QuantizeMethodBase,
21
ReplicatedLinear,
22
RowParallelLinear,
23
)
@@ -182,6 +183,14 @@ class BaseLinearLayerWithLoRA(BaseLayerWithLoRA):
183
lora_b, non_blocking=True
184
)
185
186
+ def _get_quant_method(self) -> QuantizeMethodBase:
187
+ quant_method = self.base_layer.quant_method
188
+ if quant_method is None:
189
+ raise RuntimeError(
190
+ f"{type(self.base_layer).__name__} must define quant_method for LoRA."
191
+ )
192
+ return quant_method
193
+
194
def apply(self, x: torch.Tensor, bias: torch.Tensor | None = None) -> torch.Tensor:
195
# is_forward_context_available for tower modules
196
if self._enable_aux_cuda_stream and is_forward_context_available():
@@ -195,7 +204,7 @@ class BaseLinearLayerWithLoRA(BaseLayerWithLoRA):
204
def _apply_sync(
205
self, x: torch.Tensor, bias: torch.Tensor | None = None
206
) -> torch.Tensor:
198
- output = self.base_layer.quant_method.apply(self.base_layer, x, bias)
207
+ output = self._get_quant_method().apply(self.base_layer, x, bias)
208
return self._apply_lora_to_output(x, output)
209
210
def _apply_base_forward(self, x: torch.Tensor) -> torch.Tensor:
@@ -242,7 +251,7 @@ class BaseLinearLayerWithLoRA(BaseLayerWithLoRA):
251
output_size = sum(self.output_slices)
252
253
def base_fn() -> torch.Tensor:
245
- return self.base_layer.quant_method.apply(self.base_layer, x, bias)
254
+ return self._get_quant_method().apply(self.base_layer, x, bias)
255
256
def lora_fn() -> torch.Tensor:
257
# Must be zeros, not empty: _lora_expand_kernel exits early (without
vllm/lora/layers/column_parallel_linear.py
+14
-3
@@ -33,7 +33,7 @@ def _mcp_apply(x, bias, layer: "ColumnParallelLinearWithLoRA"):
33
== len(layer.output_slices)
34
)
35
36
- output = layer.base_layer.quant_method.apply(layer.base_layer, x, bias)
36
+ output = layer._get_quant_method().apply(layer.base_layer, x, bias)
37
38
x = x.view(-1, x.shape[-1])
39
output, out_orig_shape = output.view(-1, output.shape[-1]), output.shape
@@ -73,6 +73,8 @@ def _mcp_apply(x, bias, layer: "ColumnParallelLinearWithLoRA"):
73
)
74
75
if not current_platform.can_update_inplace():
76
+ if lora_output is None:
77
+ raise RuntimeError("LoRA expand must return an output tensor.")
78
output = lora_output
79
80
output = output.view(*out_orig_shape)
@@ -327,12 +329,16 @@ class MergedColumnParallelLinearWithLoRA(ColumnParallelLinearWithLoRA):
329
330
def apply(self, x: torch.Tensor, bias: torch.Tensor | None = None) -> torch.Tensor:
331
merged_cls = maybe_get_oot_by_class(MergedColumnParallelLinear)
332
+ base_forward = getattr(type(self.base_layer), "forward", None)
333
+ merged_forward = getattr(merged_cls, "forward", None)
334
# Effectively unsharded subclasses can safely reuse their custom
335
# forward() implementation before applying the LoRA delta.
336
if (
337
self.tp_size == 1
338
and type(self.base_layer) is not merged_cls
335
- and type(self.base_layer).forward is not merged_cls.forward
339
+ and base_forward is not None
340
+ and merged_forward is not None
341
+ and base_forward is not merged_forward
342
):
343
return self._apply_base_forward(x)
344
return _mcp_apply(x, bias, self)
@@ -482,6 +488,7 @@ class MergedQKVParallelLinearWithLoRA(MergedColumnParallelLinearWithLoRA):
488
lora_config: LoRAConfig,
489
packed_modules_list: list,
490
model_config: PretrainedConfig | None = None,
491
+ decorate: bool = True,
492
) -> bool:
493
return (
494
type(source_layer) is maybe_get_oot_by_class(QKVParallelLinear)
@@ -523,6 +530,7 @@ class ColumnParallelLinearWithShardedLoRA(ColumnParallelLinearWithLoRA):
530
lora_config: LoRAConfig,
531
packed_modules_list: list,
532
model_config: PretrainedConfig | None = None,
533
+ decorate: bool = True,
534
) -> bool:
535
# specifying kwargs so they can be easily accessed in decorator
536
return super().can_replace_layer(
@@ -565,6 +573,7 @@ class MergedColumnParallelLinearWithShardedLoRA(MergedColumnParallelLinearWithLo
573
lora_config: LoRAConfig,
574
packed_modules_list: list,
575
model_config: PretrainedConfig | None = None,
576
+ decorate: bool = True,
577
) -> bool:
578
# specifying kwargs so they can be easily accessed in decorator
579
return super().can_replace_layer(
@@ -650,6 +659,7 @@ class MergedQKVParallelLinearWithShardedLoRA(MergedQKVParallelLinearWithLoRA):
659
lora_config: LoRAConfig,
660
packed_modules_list: list,
661
model_config: PretrainedConfig | None = None,
662
+ decorate: bool = True,
663
) -> bool:
664
# specifying kwargs so they can be easily accessed in decorator
665
return super().can_replace_layer(
@@ -678,6 +688,7 @@ class MergedColumnParallelLinearVariableSliceWithLoRA(
688
lora_config: LoRAConfig,
689
packed_modules_list: list,
690
model_config: PretrainedConfig | None = None,
691
+ decorate: bool = True,
692
) -> bool:
693
# Support MergedColumnParallelLinear with 3 or more slices
694
# (2 slices are handled by MergedColumnParallelLinearWithLoRA)
@@ -727,7 +738,7 @@ class MergedColumnParallelLinearVariableSliceWithLoRA(
738
start_idx = 0
739
for output_size in output_sizes:
740
end_idx = start_idx + output_size
730
- lora_b_list.append(lora_b[start_idx:end_idx, :])
741
+ lora_b_list.append(lora_b[start_idx:end_idx])
742
start_idx = end_idx
743
lora_b = lora_b_list
744
vllm/lora/layers/fused_moe.py
+25
-4
@@ -12,10 +12,16 @@ from vllm.lora.layers.base import BaseLayerWithLoRA
12
from vllm.model_executor.custom_op import maybe_get_oot_by_class
13
from vllm.model_executor.layers.fused_moe import MoERunner
14
from vllm.model_executor.layers.fused_moe.experts.lora_context import MoELoRAContext
15
+from vllm.model_executor.layers.fused_moe.experts.lora_experts_mixin import (
16
+ LoRAExpertsMixin,
17
+)
18
from vllm.model_executor.layers.fused_moe.fused_moe_modular_method import (
19
FusedMoEModularMethod,
20
)
18
-from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEKernel
21
+from vllm.model_executor.layers.fused_moe.modular_kernel import (
22
+ FusedMoEKernel,
23
+ FusedMoEKernelModularImpl,
24
+)
25
from vllm.model_executor.layers.fused_moe.prepare_finalize import (
26
MoEPrepareAndFinalizeNoDPEPModular,
27
)
@@ -58,6 +64,13 @@ class FusedMoEWithLoRA(BaseLayerWithLoRA):
64
routed_experts._ensure_moe_quant_config_init()
65
if getattr(routed_experts.quant_method, "supports_internal_mk", False):
66
moe_kernel = routed_experts.quant_method.moe_kernel
67
+ assert moe_kernel is not None, (
68
+ "Fused MoE quant method must provide a moe_kernel."
69
+ )
70
+ # Don't let the kernel own shared experts so the runner can
71
+ # overlap them with routed experts via a separate CUDA stream.
72
+ assert isinstance(moe_kernel.impl, FusedMoEKernelModularImpl)
73
+ moe_kernel.impl.shared_experts = None
74
else:
75
prepare_finalize = MoEPrepareAndFinalizeNoDPEPModular()
76
moe_kernel = FusedMoEKernel(
@@ -405,7 +418,11 @@ class FusedMoEWithLoRA(BaseLayerWithLoRA):
418
def set_mapping(self, punica_wrapper):
419
super().set_mapping(punica_wrapper)
420
lora_context = self._build_lora_context()
408
- self._moe_kernel.fused_experts.set_lora_context(lora_context)
421
+ fused_experts = self._moe_kernel.fused_experts
422
+ assert isinstance(fused_experts, LoRAExpertsMixin), (
423
+ f"{type(fused_experts).__name__} does not support LoRA context setup."
424
+ )
425
+ fused_experts.set_lora_context(lora_context)
426
prepare_finalize = self._moe_kernel.prepare_finalize
427
if hasattr(prepare_finalize, "set_lora_context"):
428
prepare_finalize.set_lora_context(lora_context)
@@ -482,9 +499,13 @@ class FusedMoE3DWithLoRA(FusedMoEWithLoRA):
499
) -> None:
500
"""Initializes lora matrices."""
501
485
- assert isinstance(model_config, PretrainedConfig)
502
+ if model_config is None:
503
+ raise ValueError("model_config must be provided for MoE LoRA.")
504
+ architectures = model_config.architectures
505
+ if not architectures:
506
+ raise ValueError("model_config.architectures must be defined for MoE LoRA.")
507
self._verify_ep_fs(lora_config)
487
- self._base_model = model_config.architectures[0]
508
+ self._base_model = architectures[0]
509
self.max_loras = lora_config.max_loras
510
self.fully_sharded = lora_config.fully_sharded_loras
511
vllm/lora/layers/row_parallel_linear.py
+1
-1
@@ -116,7 +116,7 @@ class RowParallelLinearWithShardedLoRA(RowParallelLinearWithLoRA):
116
return lora_b
117
118
def apply(self, x: torch.Tensor, bias: torch.Tensor | None = None) -> torch.Tensor:
119
- output = self.base_layer.quant_method.apply(self.base_layer, x, bias)
119
+ output = self._get_quant_method().apply(self.base_layer, x, bias)
120
121
x = x.view(-1, x.shape[-1])
122
output, out_orig_shape = output.view(-1, output.shape[-1]), output.shape
vllm/lora/layers/utils.py
+11
-3
@@ -111,19 +111,27 @@ def try_get_optimal_moe_lora_config(
111
# base MoE weight's block-wise quantization, so block_shape is omitted
112
# from the config lookup — the non-quantized branch in get_default_config
113
# ignores it anyway.
114
- config = try_get_optimal_moe_config(w1_shape, w2_shape, top_k, dtype, M).copy()
114
+ raw_config = try_get_optimal_moe_config(w1_shape, w2_shape, top_k, dtype, M)
115
+ config: dict[str, int | None] = dict(raw_config)
116
if op_type in [
117
"fused_moe_lora_w13_shrink",
118
"fused_moe_lora_w2_shrink",
119
]:
120
+ block_size_n = config.get("BLOCK_SIZE_N")
121
config["BLOCK_SIZE_N"] = min(
120
- config.get("BLOCK_SIZE_N", 64), next_power_of_2(rank)
122
+ block_size_n if block_size_n is not None else 64,
123
+ next_power_of_2(rank),
124
)
125
elif op_type in [
126
"fused_moe_lora_w13_expand",
127
"fused_moe_lora_w2_expand",
128
]:
129
+ block_size_k = config.get("BLOCK_SIZE_K")
130
config["BLOCK_SIZE_K"] = max(
127
- 16, min(config.get("BLOCK_SIZE_K", 32), next_power_of_2(rank))
131
+ 16,
132
+ min(
133
+ block_size_k if block_size_k is not None else 32,
134
+ next_power_of_2(rank),
135
+ ),
136
)
137
return config
vllm/lora/lora_model.py
+4
-3
@@ -245,9 +245,10 @@ class LoRAModel:
245
from tensorizer import TensorDeserializer
246
247
tensorizer_config = TensorizerConfig(**tensorizer_config_dict)
248
- lora_tensor_path = os.path.join(
249
- tensorizer_config.tensorizer_dir, "adapter_model.tensors"
250
- )
248
+ tensorizer_dir = tensorizer_config.tensorizer_dir
249
+ if tensorizer_dir is None:
250
+ raise ValueError("tensorizer_dir must be set in tensorizer config.")
251
+ lora_tensor_path = os.path.join(tensorizer_dir, "adapter_model.tensors")
252
tensorizer_args = tensorizer_config._construct_tensorizer_args()
253
tensors = TensorDeserializer(
254
lora_tensor_path,
vllm/lora/model_manager.py
+17
-10
@@ -34,6 +34,7 @@ from vllm.lora.utils import (
34
from vllm.model_executor.layers.fused_moe import MoERunner
35
from vllm.model_executor.models import (
36
SupportsLoRA,
37
+ SupportsMultiModal,
38
is_pooling_model,
39
supports_multimodal,
40
)
@@ -50,6 +51,12 @@ T = TypeVar("T")
51
DEFAULT_LANGUAGE_WRAPPER_KEY = "language_model"
52
53
54
+class SupportsLoRAModel(nn.Module, SupportsLoRA): ...
55
+
56
+
57
+class SupportsLoRAMultiModalModel(SupportsLoRAModel, SupportsMultiModal): ...
58
+
59
+
60
class AdapterLRUCache(LRUCache[int, T]):
61
def __init__(self, capacity: int, deactivate_fn: Callable[[int], object]):
62
super().__init__(capacity)
@@ -66,13 +73,13 @@ class LoRAModelManager:
73
74
def __init__(
75
self,
69
- model: SupportsLoRA,
76
+ model: SupportsLoRAModel,
77
max_num_seqs: int,
78
max_num_batched_tokens: int,
79
vocab_size: int,
80
lora_config: LoRAConfig,
81
device: torch.device,
75
- vllm_config: VllmConfig | None = None,
82
+ vllm_config: VllmConfig,
83
):
84
"""Create a LoRAModelManager and adapter for a given model.
85
@@ -85,7 +92,7 @@ class LoRAModelManager:
92
vocab_size: the vocab size of the model.
93
lora_config: the LoRA configuration.
94
"""
88
- self.model: SupportsLoRA = model
95
+ self.model: SupportsLoRAModel = model
96
self.supported_lora_modules = get_supported_lora_modules(self.model)
97
assert self.supported_lora_modules, (
98
f"No supported LoRA modules found in {self.model.__class__.__name__}."
@@ -106,7 +113,6 @@ class LoRAModelManager:
113
self.is_pooling_model = is_pooling_model(self.model)
114
self.packed_modules: dict[str, list[str]] = {}
115
self.modules: dict[str, BaseLayerWithLoRA] = {}
109
- # Dict instead of a set for compatibility with LRUCache.
116
self._last_mapping: LoRAMapping | None = None
117
is_moe = is_moe_model(self.model)
118
self._is_moe = is_moe
@@ -272,6 +278,7 @@ class LoRAModelManager:
278
279
@property
280
def capacity(self) -> int:
281
+ assert self.lora_config.max_cpu_loras is not None
282
return self.lora_config.max_cpu_loras
283
284
@property
@@ -1156,7 +1163,7 @@ class LoRAModelManager:
1163
1164
1165
class LoRALRUCache(AdapterLRUCache[LoRAModel]):
1159
- def __init__(self, capacity: int, deactivate_lora_fn: Callable[[int], bool]):
1166
+ def __init__(self, capacity: int, deactivate_lora_fn: Callable[[int], object]):
1167
super().__init__(capacity, deactivate_lora_fn)
1168
1169
@@ -1165,13 +1172,13 @@ class LRUCacheLoRAModelManager(LoRAModelManager):
1172
1173
def __init__(
1174
self,
1168
- model: nn.Module,
1175
+ model: SupportsLoRAModel,
1176
max_num_seqs: int,
1177
max_num_batched_tokens: int,
1178
vocab_size: int,
1179
lora_config: LoRAConfig,
1180
device: torch.device,
1174
- vllm_config: VllmConfig | None = None,
1181
+ vllm_config: VllmConfig,
1182
):
1183
super().__init__(
1184
model,
@@ -1182,10 +1189,10 @@ class LRUCacheLoRAModelManager(LoRAModelManager):
1189
device,
1190
vllm_config,
1191
)
1185
- self._registered_adapters: LoRALRUCache = LoRALRUCache(
1192
+ self._registered_adapters: LoRALRUCache = LoRALRUCache( # type: ignore[assignment]
1193
self.capacity, self.deactivate_adapter
1194
)
1188
- self._active_adapters: LoRALRUCache = LoRALRUCache(
1195
+ self._active_adapters: LoRALRUCache = LoRALRUCache( # type: ignore[assignment]
1196
self.lora_slots, self._deactivate_adapter
1197
)
1198
@@ -1248,7 +1255,7 @@ class LRUCacheLoRAModelManager(LoRAModelManager):
1255
1256
1257
def create_lora_manager(
1251
- model: nn.Module,
1258
+ model: SupportsLoRAModel,
1259
max_num_seqs: int,
1260
max_num_batched_tokens: int,
1261
vocab_size: int,
vllm/lora/peft_helper.py
+5
-3
@@ -91,9 +91,11 @@ class PEFTHelper:
91
tensorizer_args = tensorizer_config._construct_tensorizer_args()
92
from tensorizer.stream_io import open_stream
93
94
- lora_config_path = os.path.join(
95
- tensorizer_config.tensorizer_dir, "adapter_config.json"
96
- )
94
+ tensorizer_dir = tensorizer_config.tensorizer_dir
95
+ if tensorizer_dir is None:
96
+ raise ValueError("tensorizer_dir must be set in tensorizer config.")
97
+
98
+ lora_config_path = os.path.join(tensorizer_dir, "adapter_config.json")
99
with open_stream(
100
lora_config_path, mode="rb", **tensorizer_args.stream_kwargs
101
) as f:
vllm/lora/utils.py
+16
-5
@@ -173,11 +173,18 @@ def parse_fine_tuned_lora_name(
173
# mapping correctly.
174
if name.startswith("base_model.model."):
175
name = name.replace("base_model.model.", "")
176
- name = weights_mapper._map_name(name) if weights_mapper else name
177
- # recover the prefix `base_model.model.`
178
- name = "base_model.model." + name
176
+ if weights_mapper:
177
+ mapped_name = weights_mapper._map_name(name)
178
+ if mapped_name is None:
179
+ raise ValueError("Mapped LoRA weight name cannot be None.")
180
+ # recover the prefix `base_model.model.`
181
+ name = "base_model.model." + mapped_name
182
else:
180
- name = weights_mapper._map_name(name) if weights_mapper else name
183
+ if weights_mapper:
184
+ mapped_name = weights_mapper._map_name(name)
185
+ if mapped_name is None:
186
+ raise ValueError("Mapped LoRA weight name cannot be None.")
187
+ name = mapped_name
188
189
# In some situations, we may not start with `base_model.model.`.
190
# If we don't (e.g., ibm-granite/granite-speech-3.3-8b),
@@ -185,7 +192,11 @@ def parse_fine_tuned_lora_name(
192
start_index = 2 if name.startswith("base_model.model.") else 0
193
194
parts = name.split(".")
188
- if parts[-1] == "weight" and (parts[-2] == "lora_A" or parts[-2] == "lora_B"):
195
+ if (
196
+ parts[-1] == "weight"
197
+ and len(parts) >= 2
198
+ and (parts[-2] == "lora_A" or parts[-2] == "lora_B")
199
+ ):
200
new_name = ".".join(parts[start_index:-2])
201
return new_name, parts[-2] == "lora_A"
202
vllm/lora/worker_manager.py
+11
-3
@@ -7,6 +7,7 @@ from typing import Any, Literal
7
import torch
8
9
from vllm.config import VllmConfig
10
+from vllm.config.lora import LoRAConfig
11
from vllm.exceptions import LoRAAdapterNotFoundError
12
from vllm.logger import init_logger
13
from vllm.lora.lora_model import LoRAModel
@@ -45,7 +46,10 @@ class WorkerLoRAManager:
46
vllm_config.scheduler_config.max_num_batched_tokens
47
)
48
self.vocab_size = vllm_config.model_config.get_vocab_size()
48
- self.lora_config = vllm_config.lora_config
49
+ lora_config = vllm_config.lora_config
50
+ if lora_config is None:
51
+ raise ValueError("LoRA config must be set for WorkerLoRAManager.")
52
+ self.lora_config: LoRAConfig = lora_config
53
54
# Use get_text_config() in case of multimodal models
55
text_config = vllm_config.model_config.hf_config.get_text_config()
@@ -81,8 +85,10 @@ class WorkerLoRAManager:
85
def create_lora_manager(
86
self,
87
model: torch.nn.Module,
84
- vllm_config: VllmConfig | None = None,
88
+ vllm_config: VllmConfig,
89
) -> Any:
90
+ if vllm_config is None:
91
+ raise ValueError("vllm_config must be provided to create a LoRA manager.")
92
lora_manager = create_lora_manager(
93
model,
94
max_num_seqs=self.max_num_seqs,
@@ -240,8 +246,10 @@ class LRUCacheWorkerLoRAManager(WorkerLoRAManager):
246
def create_lora_manager(
247
self,
248
model: torch.nn.Module,
243
- vllm_config: VllmConfig | None = None,
249
+ vllm_config: VllmConfig,
250
) -> Any:
251
+ if vllm_config is None:
252
+ raise ValueError("vllm_config must be provided to create a LoRA manager.")
253
lora_manager = create_lora_manager(
254
model,
255
lora_manager_cls=self._manager_cls,
vllm/model_executor/layers/fused_moe/modular_kernel.py
+1
@@ -1029,6 +1029,7 @@ class FusedMoEKernelModularImpl:
1029
):
1030
self.prepare_finalize = prepare_finalize
1031
self.fused_experts = fused_experts
1032
+ self.shared_experts: SharedExperts | None = None
1033
moe_parallel_config = fused_experts.moe_config.moe_parallel_config
1034
self.moe_parallel_config = moe_parallel_config
1035
self.is_dp_ep = (
vllm/model_executor/models/interfaces.py
+2
@@ -42,6 +42,7 @@ from .interfaces_base import VllmModel
42
43
if TYPE_CHECKING:
44
from vllm.config import VllmConfig
45
+ from vllm.lora.model_manager import LoRAModelManager
46
from vllm.model_executor.models.utils import WeightsMapper
47
from vllm.multimodal.inputs import MultiModalFeatureSpec
48
from vllm.multimodal.registry import _ProcessorFactories
@@ -554,6 +555,7 @@ class SupportsLoRA(Protocol):
555
packed_modules_mapping: dict[str, list[str]] = {}
556
# Module prefixes to skip during LoRA loading (e.g., ["mtp."] for MTP layers)
557
lora_skip_prefixes: ClassVar[list[str]] = []
558
+ lora_manager: "LoRAModelManager | None"
559
560
561
# We can't use runtime_checkable with ClassVar for issubclass checks
vllm/models/deepseek_v4/xpu/mtp.py
+1
-3
@@ -24,9 +24,7 @@ from vllm.distributed import (
24
get_tensor_model_parallel_world_size,
25
)
26
from vllm.logger import init_logger
27
-from vllm.model_executor.layers.fused_moe import (
28
- fused_moe_make_expert_params_mapping,
29
-)
27
+from vllm.model_executor.layers.fused_moe import fused_moe_make_expert_params_mapping
28
from vllm.model_executor.layers.layernorm import RMSNorm
29
from vllm.model_executor.layers.linear import ReplicatedLinear
30
from vllm.model_executor.layers.logits_processor import LogitsProcessor