main
py 238 lines 9.88 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from typing import Any
4
5 import torch
6 import torch.nn as nn
7
8 from vllm.config import VllmConfig
9 from vllm.config.compilation import CUDAGraphMode
10 from vllm.v1.core.sched.output import NewRequestData
11 from vllm.v1.kv_cache_interface import KVCacheConfig
12 from vllm.v1.worker.gpu.attn_utils import (
13 build_attn_metadata,
14 compute_mm_prefix_ranges,
15 )
16 from vllm.v1.worker.gpu.input_batch import InputBatch
17 from vllm.v1.worker.gpu.mm.encoder_cache import EncoderCache
18 from vllm.v1.worker.gpu.mm.rope import get_rope_state
19 from vllm.v1.worker.gpu.model_states.interface import (
20 ModelSpecificAttnMetadata,
21 ModelState,
22 )
23 from vllm.v1.worker.gpu.model_states.mm_pruning import maybe_create_mm_pruner
24 from vllm.v1.worker.gpu.model_states.prompt_embeds import PromptEmbedsState
25 from vllm.v1.worker.gpu.states import RequestState
26 from vllm.v1.worker.utils import AttentionGroup
27
28
29 class DefaultModelState(ModelState):
30 supports_prompt_embeds = True
31
32 def __init__(
33 self,
34 vllm_config: VllmConfig,
35 model: nn.Module,
36 encoder_cache: EncoderCache | None,
37 device: torch.device,
38 ):
39 super().__init__(vllm_config, model, encoder_cache, device)
40
41 self.prompt_embeds_state: PromptEmbedsState | None = None
42 if self.model_config.enable_prompt_embeds:
43 self.prompt_embeds_state = PromptEmbedsState(
44 self.max_num_reqs, self.inputs_embeds_size, self.dtype, self.device
45 )
46 if not self.supports_mm_inputs:
47 # Persistent buffer analogous to encoder_runner.inputs_embeds.
48 embeds_buffer_size = (self.max_num_tokens, self.inputs_embeds_size)
49 self.inputs_embeds = torch.zeros(
50 embeds_buffer_size, dtype=self.dtype, device=self.device
51 )
52
53 self.rope_state = get_rope_state(
54 self.model_config,
55 model,
56 max_num_reqs=self.max_num_reqs,
57 max_num_tokens=self.max_num_tokens,
58 max_model_len=self.max_model_len,
59 device=self.device,
60 )
61
62 # Pruner is used for multimodal embedding pruning (EVS).
63 self.mm_pruner = maybe_create_mm_pruner(
64 self.model_config, model, self.rope_state, encoder_cache
65 )
66
67 def add_request(self, req_index: int, new_req_data: NewRequestData) -> None:
68 if self.rope_state is not None:
69 assert new_req_data.prefill_token_ids is not None
70 # `prompt_embeds` is a passthrough modality with no grid info, but
71 # M-RoPE assumes per-feature grids. Filter it out.
72 mm_features = [
73 f for f in new_req_data.mm_features if f.modality != "prompt_embeds"
74 ]
75 self.rope_state.init_prefill_positions(
76 req_index,
77 self.model,
78 new_req_data.prefill_token_ids,
79 mm_features=mm_features,
80 )
81 if self.prompt_embeds_state is not None:
82 self.prompt_embeds_state.add_request(req_index, new_req_data)
83
84 def remove_request(self, req_id: str) -> None:
85 if self.prompt_embeds_state is not None:
86 self.prompt_embeds_state.remove_request(req_id)
87
88 def apply_staged_writes(self) -> None:
89 if self.rope_state is not None:
90 self.rope_state.apply_staged_writes()
91 if self.prompt_embeds_state is not None:
92 self.prompt_embeds_state.apply_staged_writes()
93
94 def dummy_inputs_embeds(self, num_tokens: int) -> torch.Tensor:
95 """Pre-allocated inputs_embeds buffer for dummy runs (contents unused)."""
96 if self.supports_mm_inputs:
97 return self.encoder_runner.inputs_embeds[:num_tokens]
98 return self.inputs_embeds[:num_tokens]
99
100 def prepare_inputs_embeds(
101 self,
102 scheduled_encoder_inputs: dict[str, list[int]],
103 input_batch: InputBatch,
104 req_states: RequestState,
105 ) -> torch.Tensor:
106 # Use unpadded input_ids to match is_mm_embed size (num_tokens).
107 # input_batch.input_ids may be padded for CUDA graphs.
108 input_ids_unpadded = input_batch.input_ids[: input_batch.num_tokens]
109
110 if self.supports_mm_inputs:
111 self.execute_mm_encoder(scheduled_encoder_inputs)
112
113 mm_embeds, is_mm_embed = super().gather_mm_embeddings(input_batch)
114 if self.mm_pruner is not None and mm_embeds:
115 # EVS: recompute mrope positions for pruned media.
116 mm_embeds = self.mm_pruner.recompute(mm_embeds, input_batch, req_states)
117 # We must flush the staged rope updates for prepare_inputs() to pick up.
118 self.apply_staged_writes()
119
120 inputs_embeds = self.encoder_runner.get_inputs_embeds(
121 input_ids_unpadded, mm_embeds, is_mm_embed
122 )
123 else:
124 input_embeddings = self.model.embed_input_ids(input_ids_unpadded)
125 self.inputs_embeds[: input_embeddings.shape[0]] = input_embeddings
126 inputs_embeds = self.inputs_embeds
127
128 if self.prompt_embeds_state is not None:
129 self.prompt_embeds_state.apply(
130 input_batch, req_states.num_computed_tokens.gpu, inputs_embeds
131 )
132
133 return inputs_embeds[: input_batch.num_tokens_after_padding]
134
135 def gather_mm_embeddings(
136 self, input_batch: InputBatch, draft_lookahead: int = 0
137 ) -> tuple[list[torch.Tensor], torch.Tensor]:
138 mm_embeds, is_mm_embed = super().gather_mm_embeddings(
139 input_batch, draft_lookahead
140 )
141 if self.mm_pruner is not None:
142 # EVS: strip the appended mrope-position channels.
143 mm_embeds = self.mm_pruner.strip(mm_embeds)
144 return mm_embeds, is_mm_embed
145
146 def prepare_inputs(
147 self, input_batch: InputBatch, req_states: RequestState
148 ) -> dict[str, torch.Tensor | None]:
149 if self.rope_state is None:
150 return {} # Common case (1D positions).
151
152 self.rope_state.prepare_positions(
153 input_batch.idx_mapping,
154 input_batch.query_start_loc,
155 req_states.prefill_len.gpu,
156 req_states.num_computed_tokens.gpu,
157 )
158 positions = self.rope_state.get_positions(input_batch.num_tokens_after_padding)
159 return {"positions": positions}
160
161 def prepare_dummy_inputs(self, num_reqs: int, num_tokens: int) -> dict[str, Any]:
162 model_inputs = {}
163 if self.supports_mm_inputs or self.prompt_embeds_state is not None:
164 model_inputs["inputs_embeds"] = self.dummy_inputs_embeds(num_tokens)
165 if self.rope_state is not None:
166 model_inputs["positions"] = self.rope_state.get_positions(num_tokens)
167 return model_inputs
168
169 def prepare_attn(
170 self,
171 input_batch: InputBatch,
172 cudagraph_mode: CUDAGraphMode,
173 block_tables: tuple[torch.Tensor, ...],
174 slot_mappings: torch.Tensor,
175 attn_groups: list[list[AttentionGroup]],
176 kv_cache_config: KVCacheConfig,
177 for_capture: bool = False,
178 ubatch_idx: int = 0,
179 model_specific_attn_metadata: ModelSpecificAttnMetadata | None = None,
180 ) -> dict[str, Any]:
181 if cudagraph_mode == CUDAGraphMode.FULL:
182 # Use padded sizes - padding is handled by model_runner.prepare_attn.
183 num_reqs = input_batch.num_reqs_after_padding
184 num_tokens = input_batch.num_tokens_after_padding
185 else:
186 # For piecewise cudagraphs and eager, use unpadded sizes.
187 num_reqs = input_batch.num_reqs
188 num_tokens = input_batch.num_tokens
189 query_start_loc_cpu = torch.from_numpy(
190 input_batch.query_start_loc_np[: num_reqs + 1]
191 )
192 query_start_loc_gpu = input_batch.query_start_loc[: num_reqs + 1]
193 max_query_len = input_batch.max_query_len
194 if max_query_len is None:
195 max_query_len = input_batch.num_scheduled_tokens.max().item()
196 seq_lens_cpu_upper_bound = input_batch.seq_lens_cpu_upper_bound
197 if for_capture:
198 # Capture with worst-case max_seq_len so the graph is valid at any replay.
199 max_seq_len = self.max_model_len
200 else:
201 max_seq_len = seq_lens_cpu_upper_bound[:num_reqs].max().item()
202 req_doc_ranges: dict[int, list[tuple[int, int]]] | None = None
203 if (
204 self.supports_mm_inputs
205 and self.encoder_cache is not None
206 and self.model_config.is_mm_prefix_lm
207 ):
208 req_doc_ranges = compute_mm_prefix_ranges(
209 req_ids=input_batch.req_ids,
210 mm_features=self.encoder_cache.mm_features,
211 sliding_window=self.model_config.get_sliding_window(),
212 )
213 attn_metadata = build_attn_metadata(
214 attn_groups=attn_groups,
215 num_reqs=num_reqs,
216 num_tokens=num_tokens,
217 query_start_loc_gpu=query_start_loc_gpu,
218 query_start_loc_cpu=query_start_loc_cpu,
219 max_query_len=max_query_len,
220 seq_lens=input_batch.seq_lens,
221 max_seq_len=max_seq_len,
222 block_tables=block_tables,
223 slot_mappings=slot_mappings,
224 kv_cache_config=kv_cache_config,
225 seq_lens_cpu_upper_bound=seq_lens_cpu_upper_bound,
226 dcp_local_seq_lens=input_batch.dcp_local_seq_lens,
227 dcp_local_seq_lens_cpu_upper_bound=input_batch.dcp_local_seq_lens_cpu_upper_bound,
228 positions=input_batch.positions,
229 is_prefilling=torch.from_numpy(input_batch.is_prefilling_np),
230 mm_req_doc_ranges=req_doc_ranges,
231 for_cudagraph_capture=for_capture,
232 rswa_prefix_lens=input_batch.prompt_lens,
233 req_idx=input_batch.idx_mapping_np,
234 ubatch_idx=ubatch_idx,
235 fast_prefill=input_batch.fast_prefill,
236 model_specific_attn_metadata=model_specific_attn_metadata,
237 )
238 return attn_metadata