main
py 147 lines 5.19 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Model-runner state for Qwen4Exp PLE inputs."""
4
5 from typing import Any
6
7 import torch
8 import torch.nn as nn
9
10 from vllm.config import VllmConfig
11 from vllm.v1.worker.gpu.input_batch import InputBatch
12 from vllm.v1.worker.gpu.mm.encoder_cache import EncoderCache
13 from vllm.v1.worker.gpu.model_states.mamba_hybrid import MambaHybridModelState
14 from vllm.v1.worker.gpu.states import RequestState
15
16
17 class Qwen4ExpModelState(MambaHybridModelState):
18 """Add rollback-safe PLE n-gram context to the model inputs."""
19
20 def __init__(
21 self,
22 vllm_config: VllmConfig,
23 model: nn.Module,
24 encoder_cache: EncoderCache | None,
25 device: torch.device,
26 ) -> None:
27 super().__init__(vllm_config, model, encoder_cache, device)
28 config = self.model_config.hf_text_config
29 self.uses_ngram_embedding = bool(config.ple_layer_ids)
30 if not self.uses_ngram_embedding:
31 self.ngram_context_len = 0
32 self.ngram_eos_token_id = 0
33 return
34
35 if vllm_config.parallel_config.pipeline_parallel_size > 1:
36 raise RuntimeError(
37 "N-gram PLE embedding currently requires "
38 "pipeline_parallel_size=1 because non-first pipeline ranks do "
39 "not receive the raw input_ids required by PLE. Please run "
40 "with PP=1."
41 )
42
43 self.ngram_context_len = int(config.ngram_size) - 1
44 if self.ngram_context_len <= 0:
45 raise ValueError("N-gram embedding requires context length >= 1.")
46 self.ngram_eos_token_id = int(config.eos_token_id)
47 # PLE runs inside captured regions, so these buffers keep a fixed shape
48 # and address as the active request count changes between replays.
49 self.ngram_context = torch.full(
50 (self.max_num_reqs, self.ngram_context_len),
51 self.ngram_eos_token_id,
52 dtype=torch.int32,
53 device=self.device,
54 )
55 self.ngram_context_offsets = torch.arange(
56 -self.ngram_context_len,
57 0,
58 dtype=torch.int64,
59 device=self.device,
60 )
61 self.ple_query_start_loc = torch.zeros(
62 self.max_num_reqs + 1,
63 dtype=torch.int32,
64 device=self.device,
65 )
66
67 def _prepare_ngram_context(
68 self,
69 input_batch: InputBatch,
70 req_states: RequestState,
71 ) -> torch.Tensor:
72 num_reqs = input_batch.num_reqs
73 context = self.ngram_context
74 context.fill_(self.ngram_eos_token_id)
75 if num_reqs == 0:
76 return context
77
78 request_indices = input_batch.idx_mapping[:num_reqs].long()
79 context_end = req_states.num_computed_tokens.gpu[request_indices].long()
80 token_indices = context_end.unsqueeze(1) + self.ngram_context_offsets
81 valid_tokens = token_indices >= 0
82 token_indices.clamp_min_(0)
83 context_tokens = req_states.all_token_ids.gpu[
84 request_indices.unsqueeze(1), token_indices
85 ]
86 context[:num_reqs].copy_(
87 torch.where(
88 valid_tokens,
89 context_tokens,
90 context_tokens.new_full((), self.ngram_eos_token_id),
91 )
92 )
93 return context
94
95 def prepare_inputs(
96 self,
97 input_batch: InputBatch,
98 req_states: RequestState,
99 ) -> dict[str, Any]:
100 model_inputs = super().prepare_inputs(input_batch, req_states)
101 if not self.uses_ngram_embedding:
102 return model_inputs
103
104 num_reqs_padded = input_batch.num_reqs_after_padding
105 query_start_loc = self.ple_query_start_loc
106 query_start_loc[: num_reqs_padded + 1].copy_(input_batch.query_start_loc)
107 # Represent unused capacity as trailing zero-length requests.
108 query_start_loc[num_reqs_padded + 1 :].copy_(input_batch.query_start_loc[-1])
109 model_inputs.update(
110 query_start_loc=query_start_loc,
111 ngram_context=self._prepare_ngram_context(input_batch, req_states),
112 )
113 return model_inputs
114
115 def prepare_dummy_inputs(
116 self,
117 num_reqs: int,
118 num_tokens: int,
119 ) -> dict[str, Any]:
120 model_inputs = super().prepare_dummy_inputs(num_reqs, num_tokens)
121 if not self.uses_ngram_embedding:
122 return model_inputs
123
124 query_start_loc = self.ple_query_start_loc
125 query_start_loc[0] = 0
126 tokens_per_req, num_extra_tokens = divmod(num_tokens, num_reqs)
127 query_lens = torch.full(
128 (num_reqs,),
129 tokens_per_req,
130 dtype=query_start_loc.dtype,
131 device=query_start_loc.device,
132 )
133 if num_extra_tokens > 0:
134 query_lens[-num_extra_tokens:] += 1
135 torch.cumsum(query_lens, dim=0, out=query_start_loc[1 : num_reqs + 1])
136 query_start_loc[num_reqs + 1 :].fill_(num_tokens)
137
138 ngram_context = self.ngram_context
139 ngram_context.fill_(self.ngram_eos_token_id)
140 model_inputs.update(
141 query_start_loc=query_start_loc,
142 ngram_context=ngram_context,
143 )
144 return model_inputs
145
146
147 __all__ = ["Qwen4ExpModelState"]