Files
vllm-project--vllm/vllm/v1/worker/gpu/lora_utils.py
T
wehub-resource-sync 7ce4c8e27e
pre-commit / pre-run-check (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:55:37 +08:00

110 lines
3.7 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""LoRA utilities for the Model Runner V2 and cudagraph."""
from collections.abc import Callable
from typing import TYPE_CHECKING, Any
import numpy as np
from vllm.lora.request import LoRARequest
from vllm.lora.utils import get_captured_lora_counts
if TYPE_CHECKING:
from vllm.config.compilation import CompilationConfig
from vllm.config.lora import LoRAConfig
NO_LORA_ID = 0
def get_lora_capture_cases(
lora_config: "LoRAConfig | None",
compilation_config: "CompilationConfig",
) -> list[int]:
"""
Return num_active_loras values for cudagraph capture.
When cudagraph_specialize_lora=True: powers of 2 up to max_loras, plus
max_loras+1. When False: [0, max_loras+1]. When LoRA disabled: [0].
"""
if lora_config is None:
return [0]
if compilation_config.cudagraph_specialize_lora:
specialize = getattr(lora_config, "specialize_active_lora", False)
captured = get_captured_lora_counts(lora_config.max_loras, specialize)
return [0] + [c for c in captured if c > 0]
return [0, lora_config.max_loras + 1]
def get_num_active_loras_for_dispatch(
lora_config: "LoRAConfig | None",
lora_state: "LoraState",
req_ids: list[str],
dummy_run: bool,
) -> int:
"""Compute num_active_loras for cudagraph dispatch."""
if lora_config and not dummy_run:
return len(lora_state.get_activate_loras(req_ids))
if dummy_run and lora_config:
return lora_config.max_loras + 1
return 0
def create_lora_capture_hook(
lora_config: "LoRAConfig | None",
runner: Any,
) -> Callable[[int, int, int], None] | None:
"""Create a hook to set up LoRA state before each cudagraph capture."""
if lora_config is None:
return None
def hook(num_active_loras: int, num_reqs: int, num_tokens: int) -> None:
num_scheduled = np.full(num_reqs, num_tokens // num_reqs, dtype=np.int32)
num_scheduled[-1] += num_tokens % num_reqs
with runner.maybe_select_dummy_loras(
lora_config, num_scheduled, num_active_loras=num_active_loras
):
pass
return hook
class LoraState:
def __init__(self, max_num_reqs: int):
self.lora_ids = np.zeros(max_num_reqs, dtype=np.int32)
self.lora_ids.fill(NO_LORA_ID)
# req_id -> lora_request
self.lora_requests: dict[str, LoRARequest] = {}
def add_request(
self, req_id: str, req_index: int, lora_request: LoRARequest | None
) -> None:
if lora_request is not None:
self.lora_requests[req_id] = lora_request
self.lora_ids[req_index] = lora_request.lora_int_id
else:
self.lora_ids[req_index] = NO_LORA_ID
def remove_request(self, req_id: str) -> None:
self.lora_requests.pop(req_id, None)
def make_lora_inputs(
self,
req_ids: list[str],
idx_mapping: np.ndarray,
num_scheduled_tokens: np.ndarray,
) -> tuple[tuple[int, ...], tuple[int, ...], set[LoRARequest]]:
lora_ids = self.lora_ids[idx_mapping]
prompt_lora_mapping = tuple(lora_ids)
token_lora_mapping = tuple(lora_ids.repeat(num_scheduled_tokens))
active_lora_requests: set[LoRARequest] = self.get_activate_loras(req_ids)
return prompt_lora_mapping, token_lora_mapping, active_lora_requests
def get_activate_loras(self, req_ids: list[str]) -> set[LoRARequest]:
active_lora_requests: set[LoRARequest] = set()
for req_id in req_ids:
lora_request = self.lora_requests.get(req_id)
if lora_request is not None:
active_lora_requests.add(lora_request)
return active_lora_requests