@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 26e673fe93
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 14, 05:30 UTC

[V0 Deprecation] Remove V0 Sequence class & Sampler (#25332)

Signed-off-by: Woosuk Kwon <woosuk.kwon@berkeley.edu> Signed-off-by: Woosuk Kwon <woosuk@thinkingmachines.ai>

Woosuk Kwon committed Sep 21, 2025 at 08:52 UTC 26e673fe9303ad759a47e19a087764393c69109f
27 files changed +70 -3697
tests/conftest.py
+1 -1
@@ -48,10 +48,10 @@ from vllm.distributed import (cleanup_dist_env_and_memory,
48 initialize_model_parallel)
49 from vllm.inputs import TextPrompt
50 from vllm.logger import init_logger
51 +from vllm.logprobs import Logprob
52 from vllm.multimodal.utils import fetch_image
53 from vllm.outputs import RequestOutput
54 from vllm.sampling_params import BeamSearchParams
54 -from vllm.sequence import Logprob
55 from vllm.transformers_utils.utils import maybe_model_redirect
56 from vllm.utils import set_default_torch_num_threads
57
tests/models/multimodal/generation/test_granite_speech.py
+1 -1
@@ -7,8 +7,8 @@ from typing import Optional
7 import pytest
8 from transformers import AutoModelForSpeechSeq2Seq
9
10 +from vllm.logprobs import SampleLogprobs
11 from vllm.lora.request import LoRARequest
11 -from vllm.sequence import SampleLogprobs
12
13 from ....conftest import (AudioTestAssets, HfRunner, PromptAudioInput,
14 VllmRunner)
tests/models/multimodal/generation/test_phi4mm.py
+1 -1
@@ -12,10 +12,10 @@ from huggingface_hub import snapshot_download
12 from transformers import AutoTokenizer
13
14 from vllm.assets.image import ImageAsset
15 +from vllm.logprobs import SampleLogprobs
16 from vllm.lora.request import LoRARequest
17 from vllm.multimodal.image import convert_image_mode, rescale_image_size
18 from vllm.platforms import current_platform
18 -from vllm.sequence import SampleLogprobs
19
20 from ....conftest import (IMAGE_ASSETS, HfRunner, PromptAudioInput,
21 PromptImageInput, VllmRunner)
tests/models/multimodal/generation/test_pixtral.py
+1 -1
@@ -13,8 +13,8 @@ from mistral_common.tokens.tokenizers.multimodal import image_from_chunk
13 from transformers import AutoProcessor
14
15 from vllm import SamplingParams, TextPrompt, TokensPrompt
16 +from vllm.logprobs import Logprob, SampleLogprobs
17 from vllm.multimodal import MultiModalDataBuiltins
17 -from vllm.sequence import Logprob, SampleLogprobs
18
19 from ....utils import VLLM_PATH, large_gpu_test
20 from ...utils import check_logprobs_close
tests/models/multimodal/generation/vlm_utils/model_utils.py
+1 -1
@@ -19,7 +19,7 @@ from transformers import (AutoConfig, AutoTokenizer, BatchFeature,
19 GenerationConfig, GenerationMixin)
20 from transformers.video_utils import VideoMetadata
21
22 -from vllm.sequence import SampleLogprobs
22 +from vllm.logprobs import SampleLogprobs
23 from vllm.utils import is_list_of
24
25 from .....conftest import HfRunner, ImageAsset, ImageTestAssets
tests/models/multimodal/generation/vlm_utils/types.py
+1 -1
@@ -12,7 +12,7 @@ from transformers import AutoModelForCausalLM
12 from transformers.models.auto.auto_factory import _BaseAutoModelClass
13
14 from vllm.config import RunnerOption
15 -from vllm.sequence import SampleLogprobs
15 +from vllm.logprobs import SampleLogprobs
16 from vllm.transformers_utils.tokenizer import AnyTokenizer
17
18 from .....conftest import (AUDIO_ASSETS, IMAGE_ASSETS, HfRunner, ImageAsset,
tests/models/utils.py
+1 -1
@@ -12,7 +12,7 @@ from transformers import PretrainedConfig
12
13 from vllm.config import ModelConfig, ModelDType, RunnerOption
14 from vllm.inputs import InputContext
15 -from vllm.sequence import Logprob, PromptLogprobs, SampleLogprobs
15 +from vllm.logprobs import Logprob, PromptLogprobs, SampleLogprobs
16
17 from .registry import HF_EXAMPLE_MODELS
18
tests/tokenization/test_detokenize.py
+1 -139
@@ -8,10 +8,7 @@ import pytest
8 from transformers import (AutoTokenizer, PreTrainedTokenizer,
9 PreTrainedTokenizerFast)
10
11 -from vllm.inputs import token_inputs
12 -from vllm.sequence import Logprob, SamplingParams, Sequence, SequenceGroup
13 -from vllm.transformers_utils.detokenizer import Detokenizer
14 -from vllm.transformers_utils.tokenizer import get_tokenizer
11 +from vllm.sampling_params import SamplingParams
12 from vllm.transformers_utils.tokenizers.mistral import MistralTokenizer
13 from vllm.v1.engine import EngineCoreRequest
14 from vllm.v1.engine.detokenizer import (FastIncrementalDetokenizer,
@@ -217,138 +214,3 @@ def test_oov_decode(tokenizer, fast):
214
215 assert decoded_text == ''
216 assert out_ids == [len(tokenizer)]
220 -
221 -
222 -@pytest.fixture
223 -def detokenizer(tokenizer_name: str) -> Detokenizer:
224 - tokenizer = get_tokenizer(
225 - tokenizer_name,
226 - tokenizer_mode="mistral" if "mistral" in tokenizer_name else "auto",
227 - trust_remote_code=False,
228 - revision=None,
229 - )
230 -
231 - return Detokenizer(tokenizer)
232 -
233 -
234 -@pytest.fixture(name="complete_sequence_token_ids")
235 -def create_complete_sequence_token_ids(complete_sequence: str,
236 - tokenizer) -> list[int]:
237 - return tokenizer(complete_sequence, add_special_tokens=False).input_ids
238 -
239 -
240 -def create_sequence(prompt_token_ids=None):
241 - prompt_token_ids = prompt_token_ids or []
242 - return Sequence(
243 - seq_id=0,
244 - inputs=token_inputs(prompt_token_ids),
245 - block_size=16,
246 - )
247 -
248 -
249 -def create_dummy_logprobs(
250 - complete_sequence_token_ids: list[int]) -> list[dict[int, Logprob]]:
251 - return [{
252 - token_id: Logprob(logprob=0.0),
253 - token_id + 1: Logprob(logprob=0.1)
254 - } for token_id in complete_sequence_token_ids]
255 -
256 -
257 -def create_dummy_prompt_logprobs(
258 - complete_sequence_token_ids: list[int]
259 -) -> list[Optional[dict[int, Any]]]:
260 - # logprob for the first prompt token is None.
261 - logprobs: list[Optional[dict[int, Any]]] = [None]
262 - logprobs.extend(create_dummy_logprobs(complete_sequence_token_ids)[1:])
263 - return logprobs
264 -
265 -
266 -@pytest.mark.parametrize("complete_sequence", TRUTH)
267 -@pytest.mark.parametrize("tokenizer_name", TOKENIZERS)
268 -@pytest.mark.parametrize("skip_special_tokens", [True, False], indirect=True)
269 -def test_decode_sequence_logprobs(complete_sequence: str,
270 - complete_sequence_token_ids: list[int],
271 - detokenizer: Detokenizer,
272 - skip_special_tokens: bool):
273 - """Verify Detokenizer decodes logprobs correctly."""
274 - sampling_params = SamplingParams(skip_special_tokens=skip_special_tokens,
275 - logprobs=2)
276 -
277 - # Run sequentially.
278 - seq = create_sequence()
279 - dummy_logprobs = create_dummy_logprobs(complete_sequence_token_ids)
280 - sequential_logprobs_text_chosen_token: list[str] = []
281 - sequential_logprobs_text_other_token: list[str] = []
282 - for new_token, logprobs in zip(complete_sequence_token_ids,
283 - dummy_logprobs):
284 - seq.append_token_id(new_token, logprobs)
285 - detokenizer.decode_sequence_inplace(seq, sampling_params)
286 - sequential_logprobs_text_chosen_token.append(
287 - seq.output_logprobs[-1][new_token].decoded_token)
288 - sequential_logprobs_text_other_token.append(
289 - seq.output_logprobs[-1][new_token + 1].decoded_token)
290 - sequential_result = seq.output_text
291 -
292 - assert sequential_result == "".join(sequential_logprobs_text_chosen_token)
293 - assert sequential_result != "".join(sequential_logprobs_text_other_token)
294 -
295 - if not skip_special_tokens:
296 - # Text for logprobs for the chosen token should be the same as the
297 - # generated text. Note that this will only be true if we skip
298 - # special tokens.
299 - assert sequential_result == complete_sequence
300 -
301 -
302 -@pytest.mark.parametrize("complete_sequence", TRUTH)
303 -@pytest.mark.parametrize("tokenizer_name", TOKENIZERS)
304 -def test_decode_prompt_logprobs(complete_sequence: str,
305 - complete_sequence_token_ids: list[int],
306 - detokenizer: Detokenizer):
307 -
308 - # We want to use skip_special_tokens=False here but Mistral tokenizers
309 - # don't support that.
310 - if complete_sequence not in SPECIAL_TOKS_TRUTH:
311 - skip_special_tokens = True
312 - elif not isinstance(detokenizer.tokenizer, MistralTokenizer):
313 - skip_special_tokens = False
314 - else:
315 - pytest.skip("MistralTokenizers don't support "
316 - "skip_special_tokens=False")
317 - return
318 - """Verify Detokenizer decodes prompt logprobs correctly."""
319 - sampling_params = SamplingParams(skip_special_tokens=skip_special_tokens,
320 - prompt_logprobs=1)
321 -
322 - # Run sequentially.
323 - seq = create_sequence(complete_sequence_token_ids)
324 - seq_group = SequenceGroup(request_id="1",
325 - seqs=[seq],
326 - sampling_params=sampling_params,
327 - arrival_time=0.0)
328 - dummy_logprobs = create_dummy_prompt_logprobs(complete_sequence_token_ids)
329 - detokenizer.decode_prompt_logprobs_inplace(seq_group,
330 - dummy_logprobs,
331 - position_offset=0)
332 - # First logprob is None.
333 - decoded_prompt_logprobs: list[dict[int, Any]] = dummy_logprobs[
334 - 1:] # type: ignore
335 -
336 - # decoded_prompt_logprobs doesn't contain the first token.
337 - token_ids = complete_sequence_token_ids
338 - tokenizer = detokenizer.tokenizer
339 - text_full = tokenizer.decode(token_ids,
340 - skip_special_tokens=skip_special_tokens)
341 - text_first = tokenizer.decode(token_ids[0],
342 - skip_special_tokens=skip_special_tokens)
343 - text = text_full[len(text_first):]
344 -
345 - # Text for logprobs for the chosen token should be the same as the
346 - # prompt text. Note that the first logprob is None.
347 - assert text == "".join([
348 - logprobs[token_id].decoded_token
349 - for token_id, logprobs in zip(token_ids[1:], decoded_prompt_logprobs)
350 - ])
351 - assert text != "".join([
352 - logprobs[token_id + 1].decoded_token
353 - for token_id, logprobs in zip(token_ids[1:], decoded_prompt_logprobs)
354 - ])
tests/tool_use/test_jamba_tool_parser.py
+1 -1
@@ -12,7 +12,7 @@ from partial_json_parser.core.options import Allow
12 from vllm.entrypoints.openai.protocol import (DeltaMessage, FunctionCall,
13 ToolCall)
14 from vllm.entrypoints.openai.tool_parsers import JambaToolParser
15 -from vllm.transformers_utils.detokenizer import detokenize_incrementally
15 +from vllm.transformers_utils.detokenizer_utils import detokenize_incrementally
16 from vllm.transformers_utils.tokenizer import AnyTokenizer, get_tokenizer
17
18 MODEL = "ai21labs/Jamba-tiny-dev"
tests/tool_use/test_qwen3coder_tool_parser.py
+1 -1
@@ -13,7 +13,7 @@ from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13 ToolCall)
14 from vllm.entrypoints.openai.tool_parsers.qwen3coder_tool_parser import (
15 Qwen3CoderToolParser)
16 -from vllm.transformers_utils.detokenizer import detokenize_incrementally
16 +from vllm.transformers_utils.detokenizer_utils import detokenize_incrementally
17 from vllm.transformers_utils.tokenizer import AnyTokenizer, get_tokenizer
18
19 MODEL = "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
tests/tool_use/test_seed_oss_tool_parser.py
+1 -1
@@ -13,7 +13,7 @@ from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13 DeltaMessage, FunctionCall,
14 ToolCall)
15 from vllm.entrypoints.openai.tool_parsers import SeedOssToolParser
16 -from vllm.transformers_utils.detokenizer import detokenize_incrementally
16 +from vllm.transformers_utils.detokenizer_utils import detokenize_incrementally
17 from vllm.transformers_utils.tokenizer import AnyTokenizer, get_tokenizer
18
19 # Use a common model that is likely to be available
tests/tool_use/test_xlam_tool_parser.py
+1 -1
@@ -11,7 +11,7 @@ from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
11 DeltaMessage, FunctionCall,
12 ToolCall)
13 from vllm.entrypoints.openai.tool_parsers import xLAMToolParser
14 -from vllm.transformers_utils.detokenizer import detokenize_incrementally
14 +from vllm.transformers_utils.detokenizer_utils import detokenize_incrementally
15 from vllm.transformers_utils.tokenizer import AnyTokenizer, get_tokenizer
16
17 # Use a common model that is likely to be available
tests/v1/engine/test_output_processor.py
+1 -1
@@ -12,9 +12,9 @@ from tests.v1.engine.utils import (NUM_PROMPT_LOGPROBS_UNDER_TEST,
12 STOP_STRINGS,
13 DummyOutputProcessorTestVectors,
14 MockEngineCore)
15 +from vllm.logprobs import PromptLogprobs, SampleLogprobs
16 from vllm.outputs import CompletionOutput, RequestOutput
17 from vllm.sampling_params import RequestOutputKind, SamplingParams
17 -from vllm.sequence import PromptLogprobs, SampleLogprobs
18 from vllm.transformers_utils.tokenizer import AnyTokenizer
19 from vllm.v1.engine import EngineCoreRequest
20 from vllm.v1.engine.output_processor import (OutputProcessor,
vllm/executor/executor_base.py
+1 -1
@@ -15,10 +15,10 @@ from vllm.config import VllmConfig
15 from vllm.distributed.kv_transfer.kv_connector.utils import KVOutputAggregator
16 from vllm.logger import init_logger
17 from vllm.lora.request import LoRARequest
18 -from vllm.model_executor.layers.sampler import SamplerOutput
18 from vllm.sequence import ExecuteModelRequest, PoolerOutput
19 from vllm.tasks import SupportedTask
20 from vllm.utils import make_async
21 +from vllm.v1.outputs import SamplerOutput
22 from vllm.worker.worker_base import WorkerBase
23
24 logger = init_logger(__name__)
vllm/executor/ray_distributed_executor.py
+1 -1
@@ -17,12 +17,12 @@ from vllm.executor.msgspec_utils import encode_hook
17 from vllm.executor.ray_utils import (RayWorkerWrapper, initialize_ray_cluster,
18 ray)
19 from vllm.logger import init_logger
20 -from vllm.model_executor.layers.sampler import SamplerOutput
20 from vllm.platforms import current_platform
21 from vllm.ray.ray_env import get_env_vars_to_copy
22 from vllm.sequence import ExecuteModelRequest
23 from vllm.utils import (_run_task_with_lock, get_distributed_init_method,
24 get_ip, get_open_port, make_async)
25 +from vllm.v1.outputs import SamplerOutput
26
27 if ray is not None:
28 from ray.actor import ActorHandle
vllm/inputs/__init__.py
+1 -12
@@ -7,15 +7,7 @@ from .data import (DataPrompt, DecoderOnlyInputs, EmbedsInputs, EmbedsPrompt,
7 SingletonPrompt, TextPrompt, TokenInputs, TokensPrompt,
8 build_explicit_enc_dec_prompt, embeds_inputs,
9 to_enc_dec_tuple_list, token_inputs, zip_enc_dec_prompts)
10 -from .registry import (DummyData, InputContext, InputProcessingContext,
11 - InputRegistry)
12 -
13 -INPUT_REGISTRY = InputRegistry()
14 -"""
15 -The global [`InputRegistry`][vllm.inputs.registry.InputRegistry] which is used
16 -by [`LLMEngine`][vllm.LLMEngine] to dispatch data processing according to the
17 -target model.
18 -"""
10 +from .registry import InputContext, InputProcessingContext
11
12 __all__ = [
13 "DataPrompt",
@@ -36,9 +28,6 @@ __all__ = [
28 "build_explicit_enc_dec_prompt",
29 "to_enc_dec_tuple_list",
30 "zip_enc_dec_prompts",
39 - "INPUT_REGISTRY",
40 - "DummyData",
31 "InputContext",
32 "InputProcessingContext",
43 - "InputRegistry",
33 ]
vllm/inputs/registry.py
+1 -66
@@ -2,7 +2,7 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from collections.abc import Mapping
4 from dataclasses import dataclass
5 -from typing import TYPE_CHECKING, Any, NamedTuple, Optional, Union
5 +from typing import TYPE_CHECKING, Any, Union
6
7 import torch
8 from transformers import BatchFeature, PretrainedConfig, ProcessorMixin
@@ -15,16 +15,9 @@ from vllm.utils.jsontree import JSONTree, json_map_leaves
15
16 if TYPE_CHECKING:
17 from vllm.config import ModelConfig
18 - from vllm.multimodal import (MultiModalDataDict, MultiModalPlaceholderDict,
19 - MultiModalRegistry)
20 - from vllm.sequence import SequenceData
18 from vllm.transformers_utils.tokenizer import AnyTokenizer
19 else:
20 ModelConfig = Any
24 - MultiModalDataDict = Any
25 - MultiModalPlaceholderDict = Any
26 - MultiModalRegistry = Any
27 - SequenceData = Any
21 AnyTokenizer = Any
22
23 _T = TypeVar("_T")
@@ -191,61 +184,3 @@ class InputProcessingContext(InputContext):
184 f"on data={data} with kwargs={allowed_kwargs}")
185
186 raise ValueError(msg) from exc
194 -
195 -
196 -class DummyData(NamedTuple):
197 - """
198 - Dummy data used for profiling.
199 -
200 - Note: This is only used in V0.
201 - """
202 -
203 - seq_data: SequenceData
204 - multi_modal_data: Optional[MultiModalDataDict] = None
205 - multi_modal_placeholders: Optional[MultiModalPlaceholderDict] = None
206 -
207 -
208 -class InputRegistry:
209 - """
210 - Note: This is only used in V0.
211 - """
212 -
213 - def dummy_data_for_profiling(
214 - self,
215 - model_config: ModelConfig,
216 - seq_len: int,
217 - mm_registry: MultiModalRegistry,
218 - is_encoder_data: bool = False,
219 - ) -> DummyData:
220 - """
221 - Create dummy data for profiling the memory usage of a model.
222 -
223 - The model is identified by ``model_config``.
224 - """
225 - # Avoid circular import
226 - from vllm.multimodal.cache import processor_only_cache_from_config
227 - from vllm.sequence import SequenceData
228 -
229 - if not model_config.is_multimodal_model:
230 - seq_data = SequenceData.from_prompt_token_counts((0, seq_len))
231 - return DummyData(seq_data=seq_data)
232 -
233 - cache = processor_only_cache_from_config(model_config, mm_registry)
234 -
235 - # Encoder dummy data does not contain multi-modal data
236 - if is_encoder_data:
237 - enc_data = mm_registry.get_encoder_dummy_data(model_config,
238 - seq_len,
239 - cache=cache)
240 - seq_data = SequenceData.from_seqs(enc_data.prompt_token_ids)
241 - return DummyData(seq_data=seq_data)
242 -
243 - dec_data = mm_registry.get_decoder_dummy_data(model_config,
244 - seq_len,
245 - cache=cache)
246 -
247 - return DummyData(
248 - seq_data=SequenceData.from_seqs(dec_data.prompt_token_ids),
249 - multi_modal_data=dec_data.multi_modal_data.get_data(),
250 - multi_modal_placeholders=dec_data.multi_modal_placeholders,
251 - )
vllm/model_executor/__init__.py
+1 -3
@@ -3,13 +3,11 @@
3
4 from vllm.model_executor.parameter import (BasevLLMParameter,
5 PackedvLLMParameter)
6 -from vllm.model_executor.sampling_metadata import (SamplingMetadata,
7 - SamplingMetadataCache)
6 +from vllm.model_executor.sampling_metadata import SamplingMetadata
7 from vllm.model_executor.utils import set_random_seed
8
9 __all__ = [
10 "SamplingMetadata",
12 - "SamplingMetadataCache",
11 "set_random_seed",
12 "BasevLLMParameter",
13 "PackedvLLMParameter",
vllm/model_executor/layers/logits_processor.py
-91
@@ -1,13 +1,10 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """A layer that compute logits from hidden_stats."""
4 -import inspect
5 -from concurrent.futures import ThreadPoolExecutor
4 from typing import Optional
5
6 import torch
7
10 -import vllm.envs as envs
8 from vllm.distributed import (tensor_model_parallel_all_gather,
9 tensor_model_parallel_gather)
10 from vllm.model_executor.custom_op import CustomOp
@@ -16,11 +13,6 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
13 from vllm.model_executor.sampling_metadata import SamplingMetadata
14 from vllm.platforms import current_platform
15
19 -_logits_processor_threadpool: Optional[ThreadPoolExecutor] = None
20 -if envs.VLLM_LOGITS_PROCESSOR_THREADS is not None:
21 - _logits_processor_threadpool = ThreadPoolExecutor(
22 - envs.VLLM_LOGITS_PROCESSOR_THREADS)
23 -
16
17 @CustomOp.register("logits_processor")
18 class LogitsProcessor(CustomOp):
@@ -60,15 +52,10 @@ class LogitsProcessor(CustomOp):
52 hidden_states: torch.Tensor,
53 sampling_metadata: Optional[SamplingMetadata] = None,
54 embedding_bias: Optional[torch.Tensor] = None,
63 - prune_hidden_states: bool = True,
55 ) -> Optional[torch.Tensor]:
56 if self.logits_as_input:
57 logits = hidden_states
58 else:
68 - if sampling_metadata is not None and prune_hidden_states:
69 - hidden_states = _prune_hidden_states(hidden_states,
70 - sampling_metadata)
71 -
59 # Get the logits for the next tokens.
60 logits = self._get_logits(hidden_states, lm_head, embedding_bias)
61 if logits is not None:
@@ -79,12 +66,6 @@ class LogitsProcessor(CustomOp):
66
67 if self.scale != 1.0:
68 logits *= self.scale
82 -
83 - # Apply logits processors (if any).
84 - if sampling_metadata is not None and \
85 - sampling_metadata.seq_groups is not None:
86 - logits = _apply_logits_processors(logits, sampling_metadata)
87 -
69 return logits
70
71 def _gather_logits(self, logits: torch.Tensor) -> torch.Tensor:
@@ -125,75 +106,3 @@ class LogitsProcessor(CustomOp):
106 s += f", org_vocab_size={self.org_vocab_size}"
107 s += f", scale={self.scale}, logits_as_input={self.logits_as_input}"
108 return s
128 -
129 -
130 -def _prune_hidden_states(
131 - hidden_states: torch.Tensor,
132 - sampling_metadata: SamplingMetadata,
133 -) -> torch.Tensor:
134 - # NOTE(kzawora): The if guard is needed for Gaudi - in some scenarios
135 - # (warmup, profile_run) we might not have selected_token_indices,
136 - # so we skip pruning.
137 - if sampling_metadata.selected_token_indices is not None:
138 - return hidden_states.index_select(
139 - 0, sampling_metadata.selected_token_indices)
140 - else:
141 - return hidden_states
142 -
143 -
144 -def _apply_logits_processors(
145 - logits: torch.Tensor,
146 - sampling_metadata: SamplingMetadata,
147 -) -> torch.Tensor:
148 - found_logits_processors = False
149 - logits_processed = 0
150 - logits_row_ids_and_logits_row_futures = []
151 - for seq_group in sampling_metadata.seq_groups:
152 - seq_ids = seq_group.seq_ids
153 - sampling_params = seq_group.sampling_params
154 - logits_processors = sampling_params.logits_processors
155 - if logits_processors:
156 - found_logits_processors = True
157 -
158 - for seq_id, logits_row_idx in zip(seq_ids,
159 - seq_group.sample_indices):
160 - logits_row = logits[logits_row_idx]
161 - past_tokens_ids = seq_group.seq_data[seq_id].output_token_ids
162 - prompt_tokens_ids = seq_group.seq_data[seq_id].prompt_token_ids
163 -
164 - if _logits_processor_threadpool is not None:
165 - logits_row_ids_and_logits_row_futures.append(
166 - (logits_row_idx,
167 - _logits_processor_threadpool.submit(
168 - _apply_logits_processors_single_seq, logits_row,
169 - logits_processors, past_tokens_ids,
170 - prompt_tokens_ids)))
171 - else:
172 - logits[logits_row_idx] = \
173 - _apply_logits_processors_single_seq(
174 - logits_row, logits_processors, past_tokens_ids,
175 - prompt_tokens_ids)
176 -
177 - logits_processed += len(seq_group.sample_indices) + len(
178 - seq_group.prompt_logprob_indices)
179 -
180 - for logits_row_idx, future in logits_row_ids_and_logits_row_futures:
181 - logits[logits_row_idx] = future.result()
182 -
183 - if found_logits_processors:
184 - # verifies that no rows in logits were missed unexpectedly
185 - assert logits_processed == logits.shape[0]
186 - return logits
187 -
188 -
189 -def _apply_logits_processors_single_seq(logits_row, logits_processors,
190 - past_tokens_ids,
191 - prompt_tokens_ids) -> torch.Tensor:
192 - for logits_processor in logits_processors:
193 - parameters = inspect.signature(logits_processor).parameters
194 - if len(parameters) == 3:
195 - logits_row = logits_processor(prompt_tokens_ids, past_tokens_ids,
196 - logits_row)
197 - else:
198 - logits_row = logits_processor(past_tokens_ids, logits_row)
199 - return logits_row
vllm/model_executor/layers/sampler.py deleted
-1198
@@ -1,1198 +0,0 @@
1 -# SPDX-License-Identifier: Apache-2.0
2 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 -"""A layer that samples the next tokens from the model's outputs."""
4 -import itertools
5 -from collections.abc import Iterator
6 -from dataclasses import dataclass
7 -from importlib.util import find_spec
8 -from math import inf
9 -from typing import Optional, Union
10 -
11 -import msgspec
12 -import torch
13 -import torch.nn as nn
14 -
15 -import vllm.envs as envs
16 -from vllm.logprobs import Logprob, PromptLogprobs, SampleLogprobs
17 -from vllm.model_executor.layers.utils import apply_penalties
18 -from vllm.model_executor.sampling_metadata import (SamplingMetadata,
19 - SamplingTensors,
20 - SequenceGroupToSample)
21 -from vllm.sampling_params import SamplingType
22 -from vllm.sequence import (VLLM_INVALID_TOKEN_ID,
23 - CompletionSequenceGroupOutput, SequenceOutput)
24 -
25 -if envs.VLLM_USE_FLASHINFER_SAMPLER and find_spec("flashinfer"):
26 - # yapf: disable
27 - from flashinfer.sampling import (
28 - top_k_top_p_sampling_from_probs as flashinfer_top_k_top_p_sampling)
29 -
30 - # yapf: enable
31 -else:
32 - flashinfer_top_k_top_p_sampling = None
33 -
34 -from vllm.logger import init_logger
35 -
36 -logger = init_logger(__name__)
37 -
38 -
39 -def get_sampler() -> torch.nn.Module:
40 - if envs.VLLM_USE_V1:
41 - # Lazy import: the v1 package isn't distributed
42 - from vllm.v1.sample.sampler import Sampler as V1Sampler
43 - return V1Sampler()
44 - return Sampler()
45 -
46 -
47 -# (num_token_ids, num_parent_ids) per sequence group.
48 -SampleResultType = list[tuple[list[int], list[int]]]
49 -
50 -# Types of temporary data structures used for
51 -# computing sample_result
52 -SampleMetadataType = dict[SamplingType, tuple[list[int],
53 - list[SequenceGroupToSample]]]
54 -MultinomialSamplesType = dict[SamplingType, torch.Tensor]
55 -SampleResultsDictType = dict[int, tuple[list[int], list[int]]]
56 -
57 -
58 -# Encapsulates temporary data structures for computing
59 -# sample_result.
60 -#
61 -# * For multi-step scheduling: must be returned
62 -# by `Sampler.forward()` and used later to compute the pythonized
63 -# sample_result
64 -#
65 -# * For single-step scheduling: consumed immediately
66 -# inside `Sampler.forward()` to compute pythonized sample_result.
67 -@dataclass
68 -class SampleResultArgsType:
69 - sample_metadata: SampleMetadataType
70 - multinomial_samples: MultinomialSamplesType
71 - sample_results_dict: SampleResultsDictType
72 - sampling_metadata: SamplingMetadata
73 - greedy_samples: Optional[torch.Tensor]
74 -
75 -
76 -# Union of non-deferred (single-step scheduling)
77 -# vs deferred (multi-step scheduling)
78 -# sample result types
79 -MaybeDeferredSampleResultType = Union[SampleResultType, SampleResultArgsType]
80 -
81 -# Abbreviation of the _sample() return type
82 -SampleReturnType = tuple[MaybeDeferredSampleResultType, Optional[torch.Tensor]]
83 -
84 -
85 -class SamplerOutput(
86 - msgspec.Struct,
87 - omit_defaults=True, # type: ignore[call-arg]
88 - array_like=True): # type: ignore[call-arg]
89 - """For each sequence group, we generate a list of SequenceOutput object,
90 - each of which contains one possible candidate for the next token.
91 -
92 - This data structure implements methods, so it can be used like a list, but
93 - also has optional fields for device tensors.
94 - """
95 -
96 - outputs: list[CompletionSequenceGroupOutput]
97 -
98 - # On-device tensor containing probabilities of each token.
99 - sampled_token_probs: Optional[torch.Tensor] = None
100 -
101 - # On-device tensor containing the logprobs of each token.
102 - logprobs: Optional["torch.Tensor"] = None
103 -
104 - # Holds either (1) the pythonized sampler result (single-step scheduling)
105 - # or (2) what will be arguments for later deferred pythonization of the
106 - # sampler result (muliti-step scheduling)
107 - deferred_sample_results_args: Optional[SampleResultArgsType] = None
108 -
109 - # On-device tensor containing the sampled token ids.
110 - sampled_token_ids: Optional[torch.Tensor] = None
111 - # CPU tensor containing the sampled token ids. Used during multi-step to
112 - # return the sampled token ids from last rank to AsyncLLMEngine to be
113 - # 'broadcasted' to all other PP ranks for next step.
114 - sampled_token_ids_cpu: Optional[torch.Tensor] = None
115 -
116 - # On-device tensor containing the sampled token embeddings (embeddings
117 - # corresponding to the sampled token ids). Used when prompt embeddings are
118 - # specified in lieu of prompt token ids or text.
119 - sampled_token_embeds: Optional[torch.Tensor] = None
120 -
121 - # Optional last hidden states from the model.
122 - hidden_states: Optional[torch.Tensor] = None
123 -
124 - # Optional prefill hidden states from the model
125 - # (used for models like EAGLE).
126 - prefill_hidden_states: Optional[torch.Tensor] = None
127 -
128 - # Time taken in the forward pass for this across all workers
129 - model_forward_time: Optional[float] = None
130 -
131 - # Time taken in the model execute function. This will include model forward,
132 - # block/sync across workers, cpu-gpu sync time and sampling time.
133 - model_execute_time: Optional[float] = None
134 -
135 - def __getitem__(self, idx: int) -> CompletionSequenceGroupOutput:
136 - return self.outputs[idx]
137 -
138 - def __setitem__(self, idx: int, value):
139 - self.outputs[idx] = value
140 -
141 - def __iter__(self) -> Iterator[CompletionSequenceGroupOutput]:
142 - return iter(self.outputs)
143 -
144 - def __len__(self):
145 - return len(self.outputs)
146 -
147 - def __eq__(self, other: object):
148 - return isinstance(other,
149 - self.__class__) and self.outputs == other.outputs
150 -
151 - def __repr__(self) -> str:
152 - """Show the shape of a tensor instead of its values to reduce noise.
153 - """
154 - sampled_token_probs_repr = ("None" if self.sampled_token_probs is None
155 - else self.sampled_token_probs.shape)
156 - sampled_token_ids_repr = ("None" if self.sampled_token_ids is None else
157 - self.sampled_token_ids.shape)
158 - return (f"SamplerOutput(outputs={self.outputs}, "
159 - f"sampled_token_probs={sampled_token_probs_repr}, "
160 - f"sampled_token_ids={sampled_token_ids_repr})")
161 -
162 -
163 -class Sampler(nn.Module):
164 - """Samples the next tokens from the model's outputs.
165 -
166 - This layer does the following:
167 - 1. Discard the hidden states that are not used for sampling (i.e., all
168 - tokens except the final one in each prompt).
169 - 2. Compute the logits for the next tokens.
170 - 3. Apply presence, frequency and repetition penalties.
171 - 4. Apply temperature scaling.
172 - 5. Apply top-p and top-k truncation.
173 - 6. Sample the next tokens.
174 - Here, each sequence group within the batch can have different sampling
175 - parameters (e.g., sampling method, temperature, top-p, top-k, etc.).
176 -
177 - The structure of the logits tensor is coupled with the seq_groups in
178 - sampling_metadata. Typically, each sequence in each seq_group has one row in
179 - logits for the next token to be sampled; however, for a seq_group with a
180 - prompt request with the prompt_logprobs sampling parameter, there are rows
181 - in logits for each token in the input prompt.
182 - """
183 -
184 - def __init__(self):
185 - super().__init__()
186 -
187 - # Whether or not the SamplerOutput should have on-device tensors
188 - # containing the sampled token ids and probabilities. This is used by
189 - # speculative decoding and when prompt embeddings are specified.
190 - self.include_gpu_probs_tensor = False
191 - self.should_modify_greedy_probs_inplace = False
192 -
193 - def _init_sampling_tensors(
194 - self,
195 - logits: torch.Tensor,
196 - sampling_metadata: SamplingMetadata,
197 - ):
198 - """The goal here is to reuse sampling tensors between similar decode
199 - runs. This is possible because sampling logic does not change between
200 - decodes of the same sequences.
201 - """
202 - _, vocab_size = logits.shape
203 -
204 - # First free any existing stored sampling tensors.
205 - # This is necessary because some sampling tensors may
206 - # have pinned memory.
207 - self._sampling_tensors = None
208 -
209 - # Initialize new sampling tensors
210 - (sampling_tensors, do_penalties, do_top_p_top_k,
211 - do_min_p) = SamplingTensors.from_sampling_metadata(
212 - sampling_metadata, vocab_size, logits.device, logits.dtype)
213 -
214 - self._sampling_tensors = sampling_tensors
215 - self._do_penalties = do_penalties
216 - self._do_top_p_top_k = do_top_p_top_k
217 - self._do_min_p = do_min_p
218 -
219 - def forward(
220 - self,
221 - logits: torch.Tensor,
222 - sampling_metadata: SamplingMetadata,
223 - ) -> Optional[SamplerOutput]:
224 - """
225 - Single-step scheduling:
226 - * Perform GPU-side sampling computation & compute
227 - GPU-side logprobs tensor
228 - * Pythonize sampling result & logprobs tensor
229 -
230 - Multi-step scheduling:
231 - * Perform GPU-side sampling computation & compute
232 - GPU-side logprobs tensor
233 - * Defer Pythonization of sampling result & logprobs
234 - tensor
235 - * Encapsulate arguments required for deferred Pythonization
236 - in the
237 - [`SamplerOutput`][vllm.model_executor.layers.sampler.SamplerOutput]
238 - structure
239 -
240 - Args:
241 - logits: (num_tokens, vocab_size).
242 - sampling_metadata: Metadata for sampling.
243 - """
244 - assert logits is not None
245 - _, vocab_size = logits.shape
246 -
247 - # Prepare sampling tensors with pinned memory to avoid blocking.
248 - if not sampling_metadata.reuse_sampling_tensors:
249 - self._init_sampling_tensors(logits, sampling_metadata)
250 - elif self._do_penalties:
251 - # In this case, the sampling tensors logic depends on
252 - # "output_tokens" of a sequence. As a result, we cannot
253 - # reuse sampling tensors, since "output_tokens" changes
254 - # between decode runs.
255 - self._init_sampling_tensors(logits, sampling_metadata)
256 -
257 - assert self._sampling_tensors is not None
258 - sampling_tensors = self._sampling_tensors
259 - do_penalties = self._do_penalties
260 - do_top_p_top_k = self._do_top_p_top_k
261 - do_min_p = self._do_min_p
262 -
263 - logits = _apply_min_tokens_penalty(logits, sampling_metadata)
264 -
265 - # Apply presence and frequency penalties.
266 - if do_penalties:
267 - logits = apply_penalties(logits, sampling_tensors.prompt_tokens,
268 - sampling_tensors.output_tokens,
269 - sampling_tensors.presence_penalties,
270 - sampling_tensors.frequency_penalties,
271 - sampling_tensors.repetition_penalties)
272 -
273 - # Use float32 to apply temperature scaling.
274 - # Use in-place division to avoid creating a new tensor.
275 - logits = logits.to(torch.float)
276 - logits.div_(sampling_tensors.temperatures.unsqueeze(dim=1))
277 -
278 - if do_top_p_top_k and flashinfer_top_k_top_p_sampling is None:
279 - logits = _apply_top_k_top_p(logits, sampling_tensors.top_ps,
280 - sampling_tensors.top_ks)
281 -
282 - if do_min_p:
283 - logits = _apply_min_p(logits, sampling_tensors.min_ps)
284 -
285 - # We use float32 for probabilities and log probabilities.
286 - # Compute the probabilities.
287 - probs = torch.softmax(logits, dim=-1, dtype=torch.float)
288 - # Compute the log probabilities.
289 - logprobs = torch.log_softmax(logits, dim=-1, dtype=torch.float)
290 -
291 - # Sample the next tokens.
292 - maybe_deferred_sample_results, maybe_sampled_tokens_tensor = _sample(
293 - probs,
294 - logprobs,
295 - sampling_metadata,
296 - sampling_tensors,
297 - include_gpu_probs_tensor=self.include_gpu_probs_tensor,
298 - modify_greedy_probs=self._should_modify_greedy_probs_inplace,
299 - )
300 -
301 - if self.include_gpu_probs_tensor:
302 - # Since we will defer sampler result Pythonization,
303 - # preserve GPU-side tensors in support of later
304 - # deferred pythonization of logprobs
305 - assert maybe_sampled_tokens_tensor is not None
306 - on_device_tensors = (probs, logprobs, maybe_sampled_tokens_tensor)
307 - else:
308 - # Since Pythonization has already happened, don't preserve
309 - # GPU-side tensors.
310 - on_device_tensors = None
311 -
312 - # Get the logprobs query results.
313 - prompt_logprobs = None
314 - sample_logprobs = None
315 - if not sampling_metadata.skip_sampler_cpu_output:
316 - # Pythonize logprobs now (GPU -> CPU); do not defer.
317 - assert not isinstance(maybe_deferred_sample_results,
318 - SampleResultArgsType)
319 - prompt_logprobs, sample_logprobs = get_logprobs(
320 - logprobs, sampling_metadata, maybe_deferred_sample_results)
321 -
322 - return _build_sampler_output(
323 - maybe_deferred_sample_results,
324 - sampling_metadata,
325 - prompt_logprobs,
326 - sample_logprobs,
327 - on_device_tensors=on_device_tensors,
328 - skip_sampler_cpu_output=sampling_metadata.skip_sampler_cpu_output)
329 -
330 - @property
331 - def _should_modify_greedy_probs_inplace(self) -> bool:
332 - """Whether or not the sampler should modify the probability distribution
333 - of greedily-sampled tokens such that multinomial sampling would sample
334 - the greedily-sampled token.
335 -
336 - In other words, if True then we set the probability of the greedily-
337 - sampled token to 1.
338 -
339 - This is used by speculative decoding, which requires that the sampling
340 - method be encoded into the probability distribution.
341 - """
342 - return self.should_modify_greedy_probs_inplace
343 -
344 -
345 -def _apply_min_tokens_penalty(
346 - logits: torch.Tensor,
347 - sampling_metadata: SamplingMetadata,
348 -) -> torch.Tensor:
349 - """Apply min_tokens penalty which sets stop tokens to -inf if min_tokens
350 - have not been generated yet
351 - """
352 - # list of indices in logits that will be set to -inf
353 - logits_to_penalize: list[tuple[int, int]] = []
354 - logits_applied = 0
355 - for seq_group in sampling_metadata.seq_groups:
356 - seq_ids = seq_group.seq_ids
357 - sampling_params = seq_group.sampling_params
358 -
359 - sample_indices = seq_group.sample_indices
360 - logits_applied += len(sample_indices) + len(
361 - seq_group.prompt_logprob_indices)
362 - if not seq_group.do_sample:
363 - continue
364 -
365 - start_idx = sample_indices[0]
366 - min_tokens = sampling_params.min_tokens
367 - token_ids_to_penalize = sampling_params.all_stop_token_ids
368 - if min_tokens > 0 and token_ids_to_penalize:
369 - seqs_to_penalize: list[int] = []
370 - for j, seq_id in enumerate(seq_ids):
371 - seq_data = seq_group.seq_data[seq_id]
372 - if len(seq_data.output_token_ids_array) < min_tokens:
373 - seqs_to_penalize.append(j)
374 -
375 - if seqs_to_penalize:
376 - # convert to the index into logits
377 - seqs_to_penalize = [start_idx + j for j in seqs_to_penalize]
378 - # itertools.product pairs each seq index with every token id
379 - logits_to_penalize.extend(
380 - itertools.product(seqs_to_penalize, token_ids_to_penalize))
381 -
382 - if logits_to_penalize:
383 - # use zip and * to group indices along each dimension
384 - # eg. [ (1,2), (1,3), (5,6) ] -> ( (1,1,5), (2,3,6) )
385 - logits[tuple(zip(*logits_to_penalize))] = -float("inf")
386 -
387 - # verifies that no rows in logits were missed unexpectedly
388 - assert logits_applied == logits.shape[0]
389 - return logits
390 -
391 -
392 -def _apply_top_k_top_p(
393 - logits: torch.Tensor,
394 - p: torch.Tensor,
395 - k: torch.Tensor,
396 -) -> torch.Tensor:
397 - logits_sort, logits_idx = logits.sort(dim=-1, descending=False)
398 -
399 - # Apply top-k.
400 - top_k_mask = logits_sort.size(1) - k.to(torch.long)
401 - # Get all the top_k values.
402 - top_k_mask = logits_sort.gather(1, top_k_mask.unsqueeze(dim=1))
403 - top_k_mask = logits_sort < top_k_mask
404 - logits_sort.masked_fill_(top_k_mask, -float("inf"))
405 -
406 - # Apply top-p.
407 - probs_sort = logits_sort.softmax(dim=-1)
408 - probs_sum = probs_sort.cumsum(dim=-1)
409 - top_p_mask = probs_sum <= 1 - p.unsqueeze(dim=1)
410 - # at least one
411 - top_p_mask[:, -1] = False
412 - logits_sort.masked_fill_(top_p_mask, -float("inf"))
413 -
414 - # Re-sort the probabilities.
415 - logits = torch.empty_like(logits_sort).scatter_(dim=-1,
416 - index=logits_idx,
417 - src=logits_sort)
418 - return logits
419 -
420 -
421 -def _apply_min_p(
422 - logits: torch.Tensor,
423 - min_p: torch.Tensor,
424 -) -> torch.Tensor:
425 - """
426 - Adapted from
427 - https://github.com/oobabooga/text-generation-webui/blob/3146124ec01f02c8fb1650a6517cf1b60b537aaf/modules/sampler_hijack.py#L16C17-L16C17
428 - """
429 - probs = torch.softmax(logits, dim=-1)
430 - top_probs, _ = probs.max(dim=-1, keepdim=True)
431 - scaled_min_p = min_p.unsqueeze_(dim=1) * top_probs
432 - tokens_to_remove = probs < scaled_min_p
433 - logits = logits.masked_fill_(tokens_to_remove, -float("inf"))
434 -
435 - return logits
436 -
437 -
438 -def _greedy_sample(
439 - selected_seq_groups: list[SequenceGroupToSample],
440 - samples: torch.Tensor,
441 -) -> SampleResultType:
442 - """Run greedy sampling on a given samples.
443 -
444 - Args:
445 - selected_seq_groups: A list of sequence groups batched.
446 - samples: (num_selected_samples,) A tensor of samples. The length of
447 - samples could be smaller than selected_seq_groups if
448 - seq_group.do_sample is False.
449 - Returns:
450 - Tuple of (next_token_ids, parent_ids). The length of returned list is
451 - same as the length of selected_seq_groups. If the corresponding
452 - seq_group has do_sample=False, tuple contains ([], [])
453 - """
454 - samples_lst = samples.tolist()
455 - sample_idx = 0
456 - results: SampleResultType = []
457 - for seq_group in selected_seq_groups:
458 - if not seq_group.do_sample:
459 - results.append(([], []))
460 - continue
461 -
462 - seq_ids = seq_group.seq_ids
463 - num_parent_seqs = len(seq_ids)
464 - assert num_parent_seqs == 1, (
465 - "Greedy sampling should have only one seq.")
466 - parent_ids = list(range(num_parent_seqs))
467 - next_token_ids = [samples_lst[sample_idx]]
468 - results.append((next_token_ids, parent_ids))
469 - sample_idx += num_parent_seqs
470 - return results
471 -
472 -
473 -def _random_sample(
474 - selected_seq_groups: list[SequenceGroupToSample],
475 - random_samples: torch.Tensor,
476 -) -> SampleResultType:
477 - """Run random sampling on a given samples.
478 -
479 - Args:
480 - selected_seq_groups: A list of sequence groups batched.
481 - random_samples: (num_selected_samples,) A tensor of samples. The
482 - length of samples could be smaller than selected_seq_groups if
483 - seq_group.do_sample is False.
484 - Returns:
485 - Tuple of (next_token_ids, parent_ids). The length of returned list is
486 - same as the length of selected_seq_groups. If the corresponding
487 - seq_group has do_sample=False, tuple contains ([], [])
488 - """
489 - # Find the maximum n value of the prompt phase requests.
490 - random_samples = random_samples.cpu()
491 - sample_idx = 0
492 - results: SampleResultType = []
493 - for seq_group in selected_seq_groups:
494 - if not seq_group.do_sample:
495 - results.append(([], []))
496 - continue
497 -
498 - seq_ids = seq_group.seq_ids
499 - sampling_params = seq_group.sampling_params
500 - is_prompt = seq_group.is_prompt
501 - num_parent_seqs = len(seq_ids)
502 - if is_prompt:
503 - # Prompt phase.
504 - parent_ids = [0] * sampling_params.n
505 - next_token_ids = random_samples[
506 - sample_idx, :sampling_params.n].tolist()
507 - else:
508 - # Generation phase.
509 - parent_ids = list(range(num_parent_seqs))
510 - next_token_ids = random_samples[sample_idx:sample_idx +
511 - num_parent_seqs, 0].tolist()
512 - results.append((next_token_ids, parent_ids))
513 - sample_idx += num_parent_seqs
514 - return results
515 -
516 -
517 -# torch.multinomial forces a GPU<->CPU sync.
518 -# Therefore, we use an optimized implementation instead.
519 -# Note that we always sample with replacement.
520 -# probs will be modified in place, but this is fine, as we pass
521 -# in a copy already.
522 -def _multinomial(
523 - probs: torch.Tensor,
524 - num_samples: int,
525 - seq_groups: Optional[list[SequenceGroupToSample]] = None,
526 -) -> torch.Tensor:
527 - if num_samples > 1:
528 - probs = probs.repeat_interleave(num_samples, dim=0)
529 - q = torch.empty_like(probs)
530 - if seq_groups is None:
531 - q.exponential_()
532 - else:
533 - sample_idx = 0
534 - for seq_group in seq_groups:
535 - seq_ids = seq_group.seq_ids
536 - stride = len(seq_ids) * num_samples
537 - assert seq_group.generator is not None
538 - q[sample_idx:sample_idx +
539 - stride].exponential_(generator=seq_group.generator)
540 - sample_idx += stride
541 - return probs.div_(q).argmax(dim=1).view(-1, num_samples)
542 -
543 -
544 -def _top_k_top_p_multinomial_with_flashinfer(
545 - probs: torch.Tensor, top_ks: torch.Tensor, top_ps: torch.Tensor,
546 - num_samples: int, seq_groups: Optional[list[SequenceGroupToSample]]):
547 - if num_samples > 1:
548 - probs = probs.repeat_interleave(num_samples, dim=0)
549 - top_ks = top_ks.repeat_interleave(num_samples)
550 - top_ps = top_ps.repeat_interleave(num_samples)
551 - batch_next_token_ids = flashinfer_top_k_top_p_sampling(
552 - probs,
553 - top_ks,
554 - top_ps,
555 - )
556 - return batch_next_token_ids.view(-1, num_samples)
557 -
558 -
559 -def get_pythonized_sample_results(
560 - sample_result_args: SampleResultArgsType) -> SampleResultType:
561 - '''This function consumes GPU-side sampler results and computes
562 - Pythonized CPU-side sampler results (GPU -> CPU sync.)
563 -
564 - Single-step scheduling: this function is invoked at sampling-time
565 - for immediate Pythonization.
566 -
567 - Multi-step scheduling: Pythonization is deferred until after multiple
568 - GPU-side steps have been completed.
569 -
570 - Args:
571 - sample_result_args: GPU-side inputs to the Pythonization process
572 -
573 - Returns:
574 - Pythonized sampler results
575 - '''
576 -
577 - (
578 - sample_metadata,
579 - sampling_metadata,
580 - greedy_samples,
581 - multinomial_samples,
582 - sample_results_dict,
583 - ) = (
584 - sample_result_args.sample_metadata,
585 - sample_result_args.sampling_metadata,
586 - sample_result_args.greedy_samples,
587 - sample_result_args.multinomial_samples,
588 - sample_result_args.sample_results_dict,
589 - )
590 -
591 - for sampling_type in SamplingType:
592 - if sampling_type not in sample_metadata:
593 - continue
594 - (seq_group_id, seq_groups) = sample_metadata[sampling_type]
595 - if sampling_type == SamplingType.GREEDY:
596 - sample_results = _greedy_sample(seq_groups, greedy_samples)
597 - elif sampling_type in (SamplingType.RANDOM, SamplingType.RANDOM_SEED):
598 - sample_results = _random_sample(seq_groups,
599 - multinomial_samples[sampling_type])
600 - sample_results_dict.update(zip(seq_group_id, sample_results))
601 -
602 - return [
603 - sample_results_dict.get(i, ([], []))
604 - for i in range(len(sampling_metadata.seq_groups))
605 - ]
606 -
607 -
608 -def _sample_with_torch(
609 - probs: torch.Tensor,
610 - logprobs: torch.Tensor,
611 - sampling_metadata: SamplingMetadata,
612 - sampling_tensors: SamplingTensors,
613 - include_gpu_probs_tensor: bool,
614 - modify_greedy_probs: bool,
615 -) -> SampleReturnType:
616 - '''Torch-oriented _sample() implementation.
617 -
618 - Single-step scheduling:
619 - * Perform GPU-side sampling computation
620 - * Immediately Pythonize sampling result
621 -
622 - Multi-step scheduling:
623 - * Perform GPU-side sampling computation
624 - * Defer Pythonization & preserve GPU-side
625 - tensors required for Pythonization
626 - '''
627 -
628 - categorized_seq_group_ids: dict[SamplingType, list[int]] = {
629 - t: []
630 - for t in SamplingType
631 - }
632 - categorized_sample_indices = sampling_metadata.categorized_sample_indices
633 - for i, seq_group in enumerate(sampling_metadata.seq_groups):
634 - sampling_params = seq_group.sampling_params
635 - sampling_type = sampling_params.sampling_type
636 - categorized_seq_group_ids[sampling_type].append(i)
637 -
638 - sample_results_dict: SampleResultsDictType = {}
639 - sample_metadata: SampleMetadataType = {}
640 - multinomial_samples: MultinomialSamplesType = {}
641 - greedy_samples: Optional[torch.Tensor] = None
642 -
643 - # Create output tensor for sampled token ids.
644 - if include_gpu_probs_tensor:
645 - sampled_token_ids_tensor = torch.full((logprobs.shape[0], 1),
646 - VLLM_INVALID_TOKEN_ID,
647 - dtype=torch.long,
648 - device=logprobs.device)
649 - else:
650 - sampled_token_ids_tensor = None
651 -
652 - # Counterintuitively, having two loops here is actually faster.
653 - # The first loop can run without waiting on GPU<->CPU sync.
654 - for sampling_type in SamplingType:
655 - sample_indices = categorized_sample_indices[sampling_type]
656 - num_tokens = len(sample_indices)
657 - if num_tokens == 0:
658 - continue
659 -
660 - seq_group_id = categorized_seq_group_ids[sampling_type]
661 - seq_groups = [sampling_metadata.seq_groups[i] for i in seq_group_id]
662 - sample_metadata[sampling_type] = (seq_group_id, seq_groups)
663 - long_sample_indices = sample_indices.long()
664 - if sampling_type == SamplingType.GREEDY:
665 - greedy_samples = torch.argmax(logprobs[long_sample_indices],
666 - dim=-1)
667 -
668 - if sampled_token_ids_tensor is not None:
669 - # Store sampled tokens in output tensor.
670 - sampled_token_ids_tensor[
671 - long_sample_indices] = greedy_samples.unsqueeze(-1)
672 -
673 - if modify_greedy_probs:
674 - # If required, modify the probabilities such that sampling from
675 - # the modified distribution would always sample the argmax
676 - # token id.
677 - _modify_greedy_probs_inplace(logprobs, probs,
678 - long_sample_indices,
679 - greedy_samples)
680 -
681 - elif sampling_type in (SamplingType.RANDOM, SamplingType.RANDOM_SEED):
682 - max_n_in_batch = 1
683 - for seq_group in seq_groups:
684 - if seq_group.is_prompt:
685 - sampling_params = seq_group.sampling_params
686 - max_n_in_batch = max(max_n_in_batch, sampling_params.n)
687 - seq_groups_arg = (None if sampling_type == SamplingType.RANDOM else
688 - seq_groups)
689 -
690 - if flashinfer_top_k_top_p_sampling is not None:
691 - logger.warning("FlashInfer 0.2.3+ does not support "
692 - "per-request generators. Falling back to "
693 - "PyTorch-native implementation.")
694 -
695 - multinomial_samples[sampling_type] = _multinomial(
696 - probs[long_sample_indices],
697 - max_n_in_batch,
698 - seq_groups=seq_groups_arg)
699 -
700 - if sampled_token_ids_tensor is not None:
701 - # Store sampled tokens in output tensor.
702 - sampled_token_ids_tensor[long_sample_indices] = \
703 - multinomial_samples[sampling_type].to(torch.long)
704 -
705 - else:
706 - raise ValueError(f"Unsupported sampling type: {sampling_type}")
707 -
708 - # Encapsulate arguments for computing Pythonized sampler
709 - # results, whether deferred or otherwise.
710 - maybe_deferred_args = SampleResultArgsType(
711 - sampling_metadata=sampling_metadata,
712 - sample_metadata=sample_metadata,
713 - multinomial_samples=multinomial_samples,
714 - greedy_samples=greedy_samples,
715 - sample_results_dict=sample_results_dict)
716 -
717 - if not sampling_metadata.skip_sampler_cpu_output:
718 - # GPU<->CPU sync happens here.
719 - # This also converts the sampler output to a Python object.
720 - # Return Pythonized sampler result & sampled token ids
721 - return get_pythonized_sample_results(
722 - maybe_deferred_args), sampled_token_ids_tensor
723 - else:
724 - # Defer sampler result Pythonization; return deferred
725 - # Pythonization args & sampled token ids
726 - return (
727 - maybe_deferred_args,
728 - sampled_token_ids_tensor,
729 - )
730 -
731 -
732 -def _sample(
733 - probs: torch.Tensor,
734 - logprobs: torch.Tensor,
735 - sampling_metadata: SamplingMetadata,
736 - sampling_tensors: SamplingTensors,
737 - include_gpu_probs_tensor: bool,
738 - modify_greedy_probs: bool,
739 -) -> SampleReturnType:
740 - """
741 - Args:
742 - probs: (num_query_tokens_in_batch, num_vocab)
743 - logprobs: (num_query_tokens_in_batch, num_vocab)
744 - sampling_metadata: The metadata for a batch for sampling.
745 - sampling_tensors: Tensors that include sampling related metadata.
746 -
747 - Returns:
748 - (next_token_ids, parent_seq_ids) for each seq group in a batch.
749 - If sampling is skipped, it returns ([], [])
750 - sampled_token_ids_tensor: A tensor of sampled token ids.
751 - """
752 - return _sample_with_torch(
753 - probs,
754 - logprobs,
755 - sampling_metadata,
756 - sampling_tensors,
757 - include_gpu_probs_tensor=include_gpu_probs_tensor,
758 - modify_greedy_probs=modify_greedy_probs,
759 - )
760 -
761 -
762 -def _get_ranks(x: torch.Tensor, indices: torch.Tensor) -> torch.Tensor:
763 - """
764 - This function calculates the ranks of the chosen tokens in a logprob tensor.
765 -
766 - Args:
767 - x (torch.Tensor): 2D logprob tensor of shape (N, M)
768 - where N is the no. of tokens and M is the vocab dim.
769 - indices (torch.Tensor): List of chosen token indices.
770 -
771 - Returns:
772 - torch.Tensor: 1D tensor of shape (N,) where N is the no. of tokens.
773 - Each element in the returned tensor represents the rank
774 - of the chosen token in the input logprob tensor.
775 - """
776 - vals = x[torch.arange(0, len(x), device=x.device, dtype=indices.dtype),
777 - indices]
778 - result = (x > vals[:, None])
779 - del vals
780 - return result.sum(1).add_(1)
781 -
782 -
783 -def get_logprobs(
784 - logprobs: torch.Tensor,
785 - sampling_metadata: SamplingMetadata,
786 - sample_results: SampleResultType,
787 -) -> tuple[list[Optional[PromptLogprobs]], list[SampleLogprobs]]:
788 - """Return sample logprobs and prompt logprobs.
789 -
790 - The logic consists of 3 parts.
791 - - Select indices to compute logprob from, ranks of token ids, and
792 - the top k token ids from logprobs.
793 - - Compute prompt logprobs if required.
794 - - Compute sample logprobs if required.
795 -
796 - Args:
797 - logprobs: (num_query_tokens_across_batch, num_vocab). Each query token's
798 - logprob per vocab. Sequence groups' query tokens are batched in a
799 - single flattened tensor. For example, assuming there are N
800 - seq groups, it is sorted by prefill tokens for seq_group_1 (if
801 - prompt logprob is enabled), decode tokens for seq_group_1 (if
802 - sampling is required), prefill tokens for seq_group_2, ...
803 - sampling_metadata: The sampling metadata.
804 - sample_results: (num_seq_groups) The tuple of (next_token_ids,
805 - parent_ids) for each sequence group. When beam search is enabled,
806 - sample_results can contain different number of seq_ids from
807 - sampling_metadata.seq_groups. It is because beam search creates
808 - 2 * BEAM_WIDTH number of samples (whereas there are only up to
809 - BEAM_WIDTH number of seq_ids).
810 -
811 - Returns:
812 - A tuple of prompt and sample logprobs per sequence group in a batch.
813 - """
814 - # The index of query token to calculate logprobs. It includes both
815 - # prompt and sample logprob indices.
816 - query_indices: list[int] = []
817 - # The next token ids to get the logprob value from.
818 - next_token_ids: list[int] = []
819 - # The largest requested number of logprobs. We find logprobs as many as the
820 - # largest num logprobs in this API. If every logprobs is None, it will be
821 - # set to -1.
822 - largest_num_logprobs = -1
823 -
824 - # Select indices to compute logprob from, ranks of token ids, and the top
825 - # k token ids from logprobs.
826 - for (seq_group, sample_result) in zip(sampling_metadata.seq_groups,
827 - sample_results):
828 - sampling_params = seq_group.sampling_params
829 -
830 - # Update indices and tokens for prompt logprobs.
831 - if (seq_group.is_prompt
832 - and sampling_params.prompt_logprobs is not None):
833 - largest_num_logprobs = max(largest_num_logprobs,
834 - sampling_params.prompt_logprobs)
835 - next_prompt_tokens = _get_next_prompt_tokens(seq_group)
836 - query_indices.extend(seq_group.prompt_logprob_indices)
837 - next_token_ids.extend(next_prompt_tokens)
838 -
839 - # Update indices and next tokenes for sample logprob.
840 - if seq_group.do_sample:
841 - token_ids, parent_seq_ids = sample_result
842 - # NOTE: We cannot directly use sample_indices because
843 - # sample_indices only contain parent seq_ids of a previous step.
844 - # The current step may have different number of seq_ids, and
845 - # we can obtain it from `sample_result[1]`.
846 - query_idx = seq_group.sample_indices[0]
847 - query_indices.extend(
848 - [query_idx + parent_id for parent_id in parent_seq_ids])
849 - next_token_ids.extend(token_ids)
850 -
851 - if sampling_params.logprobs is not None:
852 - largest_num_logprobs = max(largest_num_logprobs,
853 - sampling_params.logprobs)
854 -
855 - assert len(next_token_ids) == len(query_indices)
856 -
857 - if len(query_indices) == 0:
858 - empty_sampled_logprob: SampleLogprobs = []
859 - empty_prompt_logprob: Optional[PromptLogprobs] = None
860 - num_seq_groups = len(sampling_metadata.seq_groups)
861 - return [empty_prompt_logprob
862 - ] * num_seq_groups, [empty_sampled_logprob] * num_seq_groups
863 -
864 - selected_logprobs, ranks = None, None
865 - top_logprobs, top_token_ids = None, None
866 -
867 - # If largest_num_logprobs == -1, i.e. no logprobs are requested, we can
868 - # skip the whole logprob calculation.
869 - if largest_num_logprobs >= 0:
870 - query_indices_gpu = torch.tensor(query_indices, device=logprobs.device)
871 - next_token_ids_gpu = torch.tensor(next_token_ids,
872 - device=logprobs.device)
873 -
874 - # (num_selected_query_tokens, num_logprobs). Note that query_indices can
875 - # contain duplicates if beam search is enabled.
876 - selected_logprobs = logprobs[[
877 - query_indices_gpu,
878 - next_token_ids_gpu,
879 - ]]
880 - ranks = _get_ranks(
881 - logprobs[query_indices_gpu],
882 - next_token_ids_gpu,
883 - )
884 - assert selected_logprobs.shape[0] == ranks.shape[0]
885 -
886 - # We need to compute top k only if there exists logprobs > 0.
887 - if largest_num_logprobs > 0:
888 - # Logprobs of topk tokens for a batch of sequence groups.
889 - # (num_query_tokens_across_batch).
890 - top_logprobs, top_token_ids = torch.topk(logprobs,
891 - largest_num_logprobs,
892 - dim=-1)
893 - top_logprobs = top_logprobs.to('cpu')
894 - top_token_ids = top_token_ids.to('cpu')
895 -
896 - selected_logprobs = selected_logprobs.to('cpu')
897 - ranks = ranks.to('cpu')
898 -
899 - # Find prompt/sample logprobs.
900 - prompt_logprobs_per_seq_group: list[Optional[PromptLogprobs]] = []
901 - sample_logprobs_per_seq_group: list[SampleLogprobs] = []
902 - top_logprob_idx = 0
903 - selected_logprobs_idx = 0
904 -
905 - for seq_group, sample_result in zip(sampling_metadata.seq_groups,
906 - sample_results):
907 - (prompt_logprobs, top_logprob_idx,
908 - selected_logprobs_idx) = _get_prompt_logprob_if_needed(
909 - seq_group, selected_logprobs, ranks, top_token_ids, top_logprobs,
910 - selected_logprobs_idx, top_logprob_idx)
911 - prompt_logprobs_per_seq_group.append(prompt_logprobs)
912 -
913 - (sampled_logprobs, top_logprob_idx,
914 - selected_logprobs_idx) = _get_sampled_logprob_if_needed(
915 - seq_group, sample_result, selected_logprobs, ranks, top_token_ids,
916 - top_logprobs, selected_logprobs_idx, top_logprob_idx)
917 - sample_logprobs_per_seq_group.append(sampled_logprobs)
918 -
919 - return prompt_logprobs_per_seq_group, sample_logprobs_per_seq_group
920 -
921 -
922 -def _get_prompt_logprob_if_needed(
923 - seq_group: SequenceGroupToSample,
924 - selected_logprobs: torch.Tensor,
925 - ranks: torch.Tensor,
926 - top_token_ids: torch.Tensor,
927 - top_logprobs: torch.Tensor,
928 - selected_logprobs_idx: int,
929 - top_logprob_idx: int,
930 -):
931 - """Compute the prompt logprob from a sequence group if needed."""
932 - sampling_params = seq_group.sampling_params
933 - is_prompt = seq_group.is_prompt
934 -
935 - # Find prompt logprobs
936 - prompt_logprobs: Optional[PromptLogprobs] = None
937 - if is_prompt and sampling_params.prompt_logprobs is not None:
938 - prompt_logprobs = []
939 - num_logprobs = sampling_params.prompt_logprobs
940 - next_prompt_tokens = _get_next_prompt_tokens(seq_group)
941 - # Pre-select indexes and create a list. It is faster than calling .item
942 - # repetitively.
943 - selected_logprob_items = selected_logprobs[
944 - selected_logprobs_idx:selected_logprobs_idx +
945 - len(next_prompt_tokens)].tolist()
946 - rank_items = ranks[selected_logprobs_idx:selected_logprobs_idx +
947 - len(next_prompt_tokens)].tolist()
948 -
949 - for idx, token_id in enumerate(next_prompt_tokens):
950 - # Calculate the prompt logprob of the real prompt tokens.
951 - # {token_id: (logprob, rank_from_vocab)}
952 - prompt_logprobs_dict: dict[int, tuple[float, int]] = {
953 - token_id: (selected_logprob_items[idx], rank_items[idx])
954 - }
955 -
956 - # Add top K prompt logprobs along with its rank.
957 - if num_logprobs > 0:
958 - top_ids = top_token_ids[
959 - top_logprob_idx, :num_logprobs].tolist()
960 - top_probs = top_logprobs[
961 - top_logprob_idx, :num_logprobs].tolist()
962 - # Top K is already sorted by rank, so we can use 1 ~
963 - # num_logprobs + 1 for rank.
964 - top_ranks = range(1, num_logprobs + 1)
965 - prompt_logprobs_dict.update({
966 - top_id: (top_prob, rank)
967 - for top_id, top_prob, rank in zip(top_ids, top_probs,
968 - top_ranks)
969 - })
970 - prompt_logprobs.append({
971 - token_id: Logprob(*logprob_and_rank)
972 - for token_id, logprob_and_rank in prompt_logprobs_dict.items()
973 - })
974 - # + 1 to go to the next prompt token.
975 - top_logprob_idx += 1
976 -
977 - # + len(next_prompt_tokens) to go to the next prompt.
978 - selected_logprobs_idx += len(next_prompt_tokens)
979 - return prompt_logprobs, top_logprob_idx, selected_logprobs_idx
980 -
981 -
982 -def _get_sampled_logprob_if_needed(
983 - seq_group: SequenceGroupToSample,
984 - sample_result: tuple[list[int], list[int]],
985 - selected_logprobs: torch.Tensor,
986 - ranks: torch.Tensor,
987 - top_token_ids: torch.Tensor,
988 - top_logprobs: torch.Tensor,
989 - selected_logprobs_idx: int,
990 - top_logprob_idx: int,
991 -):
992 - """Compute the sample logprob if needed."""
993 - seq_ids = seq_group.seq_ids
994 - num_logprobs = seq_group.sampling_params.logprobs
995 - sampled_logprobs: SampleLogprobs = []
996 - next_token_ids, parent_seq_ids = sample_result
997 -
998 - if seq_group.do_sample:
999 - assert len(next_token_ids) > 0
1000 - if num_logprobs is None:
1001 - for next_token_id in next_token_ids:
1002 - # Use a dummy logprob
1003 - sampled_logprobs.append({next_token_id: Logprob(inf)})
1004 - else:
1005 - # Pre-select items from tensor. tolist() is faster than repetitive
1006 - # `.item()` calls.
1007 - selected_logprob_items = selected_logprobs[
1008 - selected_logprobs_idx:selected_logprobs_idx +
1009 - len(next_token_ids)].tolist()
1010 - rank_items = ranks[selected_logprobs_idx:selected_logprobs_idx +
1011 - len(next_token_ids)].tolist()
1012 - for idx, (next_token_id, parent_id) in enumerate(
1013 - zip(next_token_ids, parent_seq_ids)):
1014 - # Get the logprob of a sampled token.
1015 - sampled_logprobs_dict = {
1016 - next_token_id:
1017 - (selected_logprob_items[idx], rank_items[idx])
1018 - }
1019 - if num_logprobs is not None and num_logprobs > 0:
1020 - # Get top K logprobs.
1021 - top_ids = top_token_ids[top_logprob_idx +
1022 - parent_id, :num_logprobs].tolist()
1023 - top_probs = top_logprobs[
1024 - top_logprob_idx + parent_id, :num_logprobs].tolist()
1025 - # Top K is already sorted by rank, so we can use 1 ~
1026 - # num_logprobs + 1 for rank.
1027 - top_ranks = range(1, num_logprobs + 1)
1028 - sampled_logprobs_dict.update({
1029 - top_id: (top_prob, rank)
1030 - for top_id, top_prob, rank in zip(
1031 - top_ids, top_probs, top_ranks)
1032 - })
1033 -
1034 - sampled_logprobs.append({
1035 - token_id: Logprob(*logprob_and_rank)
1036 - for token_id, logprob_and_rank in
1037 - sampled_logprobs_dict.items()
1038 - })
1039 -
1040 - # NOTE: This part of code is not intuitive. `selected_logprobs` include
1041 - # logprobs for the current step, which has len(next_token_ids) tokens
1042 - # per sequence group. `logprobs` includes logprobs from the previous
1043 - # steps, which has len(seq_ids) tokens per sequence group.
1044 -
1045 - # Iterate to the next sequence group in a batch.
1046 - selected_logprobs_idx += len(next_token_ids)
1047 - # Iterate to the next sequence group in a batch.
1048 - top_logprob_idx += len(seq_ids)
1049 - return sampled_logprobs, top_logprob_idx, selected_logprobs_idx
1050 -
1051 -
1052 -def _modify_greedy_probs_inplace(logprobs: torch.Tensor, probs: torch.Tensor,
1053 - sample_indices: torch.Tensor,
1054 - greedy_samples: torch.Tensor) -> None:
1055 - """Modify the probability distributions of the greedily-sampled tokens such
1056 - that each sampled token has a "probability" of 1.0. This is required by
1057 - speculative decoding, which depends on the sampling method being encoded
1058 - within the probability distribution for correctness.
1059 -
1060 - # Why do we only need to do this for greedy sampling?
1061 -
1062 - vLLM's sampler performs the following steps for greedy or multinomial
1063 - (random) sampling:
1064 - 1. Get logits from model.
1065 - 2. Modify logits according to per-sequence sampling parameters.
1066 - - Multiply by temperature, top-k and top-p masking, penalize tokens
1067 - according to their frequency, etc.
1068 - 3. Sample a token.
1069 - - Random sampling simply samples from the modified probability
1070 - distribution.
1071 - - Greedy sampling performs `argmax` to obtain the token with the
1072 - highest likelihood.
1073 -
1074 - Ignoring greedy sampling for a moment, we find that the computed probability
1075 - distribution has the following property: we can sample from it independently
1076 - and find that the token sampled by the Sampler has a frequency corresponding
1077 - to how often we see it in our sampling. In other words, for tokens sampled
1078 - with vLLM's random SamplingType, the computed probability distribution
1079 - encodes the sampling methodology completely.
1080 -
1081 - Greedy sampling does not normally have this property. vLLM modifies logits
1082 - according to sampling params, then performs `argmax`, then returns the
1083 - sampled token and the computed probability distribution. If we sample from
1084 - the distribution, we'll find the likelihood of the greedily-sampled token
1085 - is not always 1.0.
1086 -
1087 - Since lossless speculative decoding requires that the sampling methodology
1088 - be encoded within the probability distribution, we are motivated to modify
1089 - the probability distribution such that the sampled token has probability 1
1090 - when speculative decoding is used.
1091 -
1092 - NOTE: Alternatively, we could use an extremely low temperature to achieve
1093 - greedy sampling using multinomial computation and unite the codepaths. This
1094 - has implications on the overall design of the sampler, e.g. how to record
1095 - accurate logprobs for the user, so this improvement is deferred to later.
1096 - """
1097 - # NOTE: logprobs are not modified so they can be returned to the user.
1098 - probs[sample_indices, :] = 0
1099 - probs[sample_indices, greedy_samples] = 1.0
1100 -
1101 -
1102 -def _build_sampler_output(
1103 - maybe_deferred_sample_results: MaybeDeferredSampleResultType,
1104 - sampling_metadata: SamplingMetadata,
1105 - prompt_logprobs: Optional[list[Optional[PromptLogprobs]]],
1106 - sample_logprobs: Optional[list[SampleLogprobs]],
1107 - on_device_tensors: Optional[tuple[torch.Tensor, torch.Tensor,
1108 - torch.Tensor]],
1109 - skip_sampler_cpu_output: bool = False,
1110 -) -> SamplerOutput:
1111 - """Construct Python objects with the output of sampling.
1112 -
1113 - Args:
1114 - on_device_tensors: Tuple containing on-device tensors with the
1115 - probabilities used in sampling and the sampled token ids. This
1116 - allows post-processing without copies to CPU/serialization, e.g. in
1117 - speculative decoding rejection sampling.
1118 - """
1119 - sampler_output: list[CompletionSequenceGroupOutput] = []
1120 -
1121 - if skip_sampler_cpu_output:
1122 - assert isinstance(maybe_deferred_sample_results, SampleResultArgsType)
1123 - deferred_sample_results_args = maybe_deferred_sample_results
1124 - else:
1125 - assert prompt_logprobs is not None
1126 - assert sample_logprobs is not None
1127 - assert not isinstance(maybe_deferred_sample_results,
1128 - SampleResultArgsType)
1129 - assert len(sampling_metadata.seq_groups) \
1130 - == len(maybe_deferred_sample_results) \
1131 - == len(prompt_logprobs) \
1132 - == len(sample_logprobs)
1133 - deferred_sample_results_args = None
1134 -
1135 - for (seq_group, sample_result, group_prompt_logprobs,
1136 - group_sample_logprobs) in zip(sampling_metadata.seq_groups,
1137 - maybe_deferred_sample_results,
1138 - prompt_logprobs, sample_logprobs):
1139 - seq_ids = seq_group.seq_ids
1140 - next_token_ids, parent_ids = sample_result
1141 - seq_outputs: list[SequenceOutput] = []
1142 - for parent_id, next_token_id, logprobs in zip(
1143 - parent_ids, next_token_ids, group_sample_logprobs):
1144 - seq_outputs.append(
1145 - SequenceOutput(seq_ids[parent_id], next_token_id,
1146 - logprobs))
1147 - sampler_output.append(
1148 - CompletionSequenceGroupOutput(seq_outputs,
1149 - group_prompt_logprobs))
1150 -
1151 - # If not specified, store None values in SamplerOutput.
1152 - if on_device_tensors is not None:
1153 - (sampled_token_probs, logprobs_tensor,
1154 - sampled_token_ids) = on_device_tensors
1155 - else:
1156 - sampled_token_probs, logprobs_tensor, sampled_token_ids = (None, None,
1157 - None)
1158 -
1159 - return SamplerOutput(
1160 - outputs=sampler_output,
1161 - sampled_token_probs=sampled_token_probs,
1162 - sampled_token_ids=sampled_token_ids,
1163 - logprobs=logprobs_tensor,
1164 - deferred_sample_results_args=deferred_sample_results_args)
1165 -
1166 -
1167 -def _get_next_prompt_tokens(
1168 - seq_group: SequenceGroupToSample) -> tuple[int, ...]:
1169 - """Get a list of next prompt tokens to compute logprob from a
1170 - given sequence group.
1171 -
1172 - It is used to compute prompt logprob. Imagine you have logprob for each
1173 - query token. Query token needs to know the next prompt token id to compute
1174 - prompt logprob. This is a helper to obtain next prompt token ids.
1175 -
1176 - This API has to be used only when the caller knows seq_group is in prefill
1177 - stage.
1178 -
1179 - Returns:
1180 - A list of next prompt tokens to compute logprob.
1181 - """
1182 - assert seq_group.is_prompt, (
1183 - "Caller should ensure the sequence group is in a prefill stage.")
1184 - seq_ids = seq_group.seq_ids
1185 - query_len = seq_group.query_len
1186 - assert query_len is not None
1187 - # prompt has only 1 seq id.
1188 - assert len(seq_ids) == 1
1189 - seq_data = seq_group.seq_data[seq_ids[0]]
1190 - computed_len = seq_data.get_num_computed_tokens()
1191 - prompt_tokens = seq_data.prompt_token_ids
1192 - # +1 because we are looking for a next prompt token.
1193 - next_token_index_start = computed_len + 1
1194 - next_token_index_end = min(computed_len + query_len + 1,
1195 - len(prompt_tokens))
1196 - next_prompt_tokens = prompt_tokens[
1197 - next_token_index_start:next_token_index_end]
1198 - return next_prompt_tokens
vllm/model_executor/models/medusa.py
+4 -56
@@ -2,18 +2,15 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable
5 -from typing import Optional
5
6 import torch
7 import torch.nn as nn
8
9 from vllm.config import VllmConfig
10 from vllm.model_executor.layers.logits_processor import LogitsProcessor
12 -from vllm.model_executor.layers.sampler import SamplerOutput
11 from vllm.model_executor.layers.vocab_parallel_embedding import (
12 DEFAULT_VOCAB_PADDING_SIZE, ParallelLMHead)
13 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
16 -from vllm.model_executor.sampling_metadata import SamplingMetadata
14
15 from .utils import maybe_prefix
16
@@ -105,8 +102,10 @@ class Medusa(nn.Module):
102 return [block(hidden_states) for block in self.blocks]
103
104 def compute_logits(
108 - self, hidden_states: list[torch.Tensor],
109 - sampling_metadata: SamplingMetadata) -> list[torch.Tensor]:
105 + self,
106 + hidden_states: list[torch.Tensor],
107 + sampling_metadata,
108 + ) -> list[torch.Tensor]:
109 logits_lst: list[torch.Tensor] = []
110
111 for hs, lm_head in zip(hidden_states, self.lm_heads):
@@ -130,57 +129,6 @@ class Medusa(nn.Module):
129
130 return logits_lst
131
133 - def sample(
134 - self,
135 - logits: list[torch.Tensor],
136 - sampling_metadata: SamplingMetadata,
137 - ) -> list[SamplerOutput]:
138 - logits = torch.stack(logits, dim=0).float()
139 - logprobs = torch.log_softmax(logits, dim=-1)
140 - token_ids = logits.argmax(-1) # support only top-1 for now
141 - probs = torch.softmax(logits, dim=-1)
142 -
143 - token_id_list = []
144 - token_prob_list = []
145 - token_logprob_list = []
146 -
147 - for idx, seq_group in enumerate(sampling_metadata.seq_groups):
148 - token_id_list.append(token_ids[:, seq_group.sample_indices])
149 - token_prob_list.append(probs[:, seq_group.sample_indices])
150 - token_logprob_list.append(logprobs[:, seq_group.sample_indices])
151 -
152 - outputs: list[Optional[SamplerOutput]] = []
153 - for idx in range(len(sampling_metadata.seq_groups)):
154 - outputs.append(
155 - SamplerOutput(
156 - outputs=None,
157 - sampled_token_probs=token_prob_list[idx].squeeze(1),
158 - logprobs=token_logprob_list[idx].squeeze(1),
159 - sampled_token_ids=token_id_list[idx].squeeze(1),
160 - ))
161 -
162 - return outputs
163 -
164 - def generate_proposals(
165 - self,
166 - previous_hidden_states: torch.Tensor,
167 - sampling_metadata: SamplingMetadata,
168 - ) -> Optional[list[SamplerOutput]]:
169 - # During preemption, we may receive an empty tensor (batch_size=0)
170 - if previous_hidden_states.size(0) == 0:
171 - # Return None to signal the Top1Proposer that no proposals
172 - # were generated for this batch, allowing it to handle this
173 - # special case appropriately
174 - return None
175 -
176 - return self.sample(
177 - logits=self.compute_logits(
178 - hidden_states=self.forward(previous_hidden_states),
179 - sampling_metadata=sampling_metadata,
180 - ),
181 - sampling_metadata=sampling_metadata,
182 - )
183 -
132 def load_weights(self, weights: Iterable[tuple[str,
133 torch.Tensor]]) -> set[str]:
134 params_dict = dict(self.named_parameters())
vllm/model_executor/models/mlp_speculator.py
+40 -40
@@ -8,9 +8,7 @@ import torch
8 import torch.nn as nn
9
10 from vllm.config import VllmConfig
11 -from vllm.model_executor import SamplingMetadata
11 from vllm.model_executor.layers.logits_processor import LogitsProcessor
13 -from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler
12 from vllm.model_executor.layers.vocab_parallel_embedding import (
13 ParallelLMHead, VocabParallelEmbedding)
14 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
@@ -141,55 +139,57 @@ class MLPSpeculator(nn.Module):
139 self.config = config
140 self.logits_processor = LogitsProcessor(config.vocab_size,
141 config.vocab_size, 1.0)
144 - self.sampler = get_sampler()
142
146 - def generate_proposals(
147 - self,
148 - input_ids: torch.Tensor,
149 - previous_hidden_states: torch.Tensor,
150 - num_predict_tokens: int,
151 - sampling_metadata: SamplingMetadata,
152 - ) -> list[SamplerOutput]:
153 - if num_predict_tokens > self.max_speculative_tokens:
154 - raise ValueError(f"Max speculative tokens for model is "
155 - f"{self.max_speculative_tokens}, but "
156 - f"{num_predict_tokens} were requested")
157 -
158 - # b x 1 x d
159 - previous_hidden_states = previous_hidden_states.unsqueeze(1)
143 + # NOTE(woosuk): This method is commented out because it is old code
144 + # using V0. We should either port it to V1 or remove it.
145
161 - if self.scale_input:
162 - previous_hidden_states = self.ln0(previous_hidden_states) / SQRT2
146 + # def generate_proposals(
147 + # self,
148 + # input_ids: torch.Tensor,
149 + # previous_hidden_states: torch.Tensor,
150 + # num_predict_tokens: int,
151 + # sampling_metadata: SamplingMetadata,
152 + # ) -> list[SamplerOutput]:
153 + # if num_predict_tokens > self.max_speculative_tokens:
154 + # raise ValueError(f"Max speculative tokens for model is "
155 + # f"{self.max_speculative_tokens}, but "
156 + # f"{num_predict_tokens} were requested")
157 +
158 + # # b x 1 x d
159 + # previous_hidden_states = previous_hidden_states.unsqueeze(1)
160 +
161 + # if self.scale_input:
162 + # previous_hidden_states = self.ln0(previous_hidden_states) / SQRT2
163
164 - # b x 1
165 - last_tokens = input_ids.unsqueeze(1)
164 + # # b x 1
165 + # last_tokens = input_ids.unsqueeze(1)
166
167 - next_tokens = []
167 + # next_tokens = []
168
169 - for head_index in range(num_predict_tokens):
169 + # for head_index in range(num_predict_tokens):
170
171 - # Project and predict
172 - z = self.emb[head_index](last_tokens) # b k d
173 - states = self.proj[head_index](previous_hidden_states)
171 + # # Project and predict
172 + # z = self.emb[head_index](last_tokens) # b k d
173 + # states = self.proj[head_index](previous_hidden_states)
174
175 - # Weighted add of state_weight*state and emb_weight*z
176 - # Let subsequent LN take care of denominator
177 - # state_weight is close to 1, so shouldn't be any precision issues
178 - states.add_(z, alpha=self.emb_weight / self.state_weight)
175 + # # Weighted add of state_weight*state and emb_weight*z
176 + # # Let subsequent LN take care of denominator
177 + # # state_weight is close to 1, so shouldn't be any precision issues
178 + # states.add_(z, alpha=self.emb_weight / self.state_weight)
179
180 - states = self.activation(self.ln[head_index](states)) # b k d
181 - previous_hidden_states = states
182 - # TODO: not yet supporting top_k_tokens_per_head
183 - states = states.flatten(0, 1)
180 + # states = self.activation(self.ln[head_index](states)) # b k d
181 + # previous_hidden_states = states
182 + # # TODO: not yet supporting top_k_tokens_per_head
183 + # states = states.flatten(0, 1)
184
185 - logits = self.logits_processor(self.head[head_index], states,
186 - sampling_metadata)
185 + # logits = self.logits_processor(self.head[head_index], states,
186 + # sampling_metadata)
187
188 - output = self.sampler(logits, sampling_metadata)
189 - last_tokens = output.sampled_token_ids
190 - next_tokens.append(output)
188 + # output = self.sampler(logits, sampling_metadata)
189 + # last_tokens = output.sampled_token_ids
190 + # next_tokens.append(output)
191
192 - return next_tokens
192 + # return next_tokens
193
194 def load_weights(self, weights: Iterable[tuple[str,
195 torch.Tensor]]) -> set[str]:
vllm/model_executor/models/phi4flash.py
+1 -5
@@ -697,16 +697,12 @@ class Phi4FlashForCausalLM(nn.Module, HasInnerState, IsHybrid, SupportsV0Only):
697 hidden_states: torch.Tensor,
698 sampling_metadata: SamplingMetadata,
699 ) -> Optional[torch.Tensor]:
700 - # If the shape is the same, it means that we have already
701 - # prune hidden states manually.
702 - prune_hidden_states = hidden_states.size(
703 - 0) != sampling_metadata.selected_token_indices.size(0)
700 processed_logits = self.logits_processor(
701 self.lm_head,
702 hidden_states,
703 sampling_metadata,
704 self.embedding_bias,
709 - prune_hidden_states=prune_hidden_states)
705 + )
706 return processed_logits
707
708 def load_weights(
vllm/model_executor/sampling_metadata.py
+2 -592
@@ -1,597 +1,7 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 -from array import array
5 -from dataclasses import dataclass
6 -from typing import Optional
7 -
8 -import torch
9 -
10 -from vllm.sampling_params import SamplingParams, SamplingType
11 -from vllm.sequence import (VLLM_TOKEN_ID_ARRAY_TYPE, SequenceData,
12 - SequenceGroupMetadata)
13 -from vllm.utils import (PyObjectCache, async_tensor_h2d,
14 - is_pin_memory_available, make_tensor_with_pad)
15 -
16 -_SAMPLING_EPS = 1e-5
17 -
18 -
19 -@dataclass
20 -class SequenceGroupToSample:
21 - # |---------- N-1 iteration --------|
22 - # |---------------- N iteration ---------------------|
23 - # |- tokenA -|......................|-- newTokens ---|
24 - # |---------- context_len ----------|
25 - # |-------------------- seq_len ----------------------|
26 - # |-- query_len ---|
27 -
28 - # Sequence ids for the sequence group in a previous step.
29 - seq_ids: list[int]
30 - sampling_params: SamplingParams
31 - # seq_id -> sequence data.
32 - seq_data: dict[int, SequenceData]
33 - # The length of the sequence (all tokens seen in the past + new token to
34 - # compute attention) of the sequence group. None if it is in a decode
35 - # stage.
36 - seq_len: Optional[int]
37 - # The length of new query tokens to compute in the current step. None if it
38 - # is in a decode stage. The length of query_len <= seq_len if chunked
39 - # prefill is enabled.
40 - query_len: Optional[int]
41 - # A random number generator for sampling.
42 - generator: Optional[torch.Generator]
43 - # True if the sequence group is in prefill stage. False if it is in a
44 - # decode stage.
45 - is_prompt: bool
46 - # Query token indices from logits. to compute prompt logprob. Empty if
47 - # prompt logprob is not required.
48 - prompt_logprob_indices: list[int]
49 - # Sample token indices from logits. Empty if sampling is not required.
50 - sample_indices: list[int]
51 -
52 - @property
53 - def do_sample(self):
54 - return len(self.sample_indices) > 0
55 -
56 - def __post_init__(self):
57 - if len(self.prompt_logprob_indices) > 0:
58 - assert self.sampling_params.prompt_logprobs is not None
59 - if self.is_prompt:
60 - assert self.seq_len is not None
61 - assert self.query_len is not None
62 -
63 -
64 -def gen_seq_group_to_sample_builder(num_seqs: int):
65 - return lambda: SequenceGroupToSample(
66 - seq_ids=[0] * num_seqs,
67 - sampling_params=None,
68 - seq_data=None, # type: ignore
69 - seq_len=0,
70 - query_len=0,
71 - generator=None,
72 - is_prompt=True,
73 - prompt_logprob_indices=[],
74 - sample_indices=[],
75 - )
76 -
77 -
78 -class SamplingMetadataCache:
79 - """Used to cache SamplingMetadata objects between scheduler iterations"""
80 -
81 - def __init__(self):
82 - self._seq_group_to_sample_cache: dict[int, PyObjectCache] = {}
83 -
84 - def get_cached_seq_group_to_sample(self, num_seqs):
85 - if num_seqs not in self._seq_group_to_sample_cache:
86 - self._seq_group_to_sample_cache[num_seqs] = PyObjectCache(
87 - gen_seq_group_to_sample_builder(num_seqs))
88 -
89 - obj = self._seq_group_to_sample_cache[num_seqs].get_object()
90 - return obj
91 -
92 - def reset(self):
93 - for cache in self._seq_group_to_sample_cache.values():
94 - cache.reset()
95 -
4
5 class SamplingMetadata:
98 - """Metadata for input sequences. Used in sampler.
99 -
100 - The usage is as follows;
101 - ```
102 - hidden_states = execute_model(...)
103 - logits = hidden_states[sampling_metadata.selected_token_indices]
104 - sample(logits)
105 -
106 - def sample(logits):
107 - # Use categorized_sample_indices for sampling....
108 - ```
109 -
110 - Args:
111 - seq_groups: List of batched sequence groups.
112 - selected_token_indices: (num_query_tokens_to_logprob). Indices to find
113 - logits from the initial model output hidden states.
114 - categorized_sample_indices: SamplingType -> token indices to sample.
115 - Each token indices is 2D tensor of (num_indices, num_indices) where
116 - the first item means the sample index within the returned logit
117 - (before pruning padding), and the second item means the sample
118 - index after pruning using selected_token_indices.
119 - For example, if the returned logit is [1, 2, 3], and we select
120 - [1, 2] for sampling, the pruned logit will be [2, 3]. In this case,
121 - The first tuple is [1, 2] (sampled index within original logit),
122 - and the second tuple is [0, 1] (sampled index within pruned logit).
123 - num_prompts: Number of prompt sequence groups in seq_groups.
124 - skip_sampler_cpu_output: Indicates if we want to skip the GPU=>CPU
125 - serialization of token outputs.
126 - reuse_sampling_tensors: Indicates if we want to reuse sampling
127 - tensors that are part of the sampler forward pass. Currently,
128 - it is mainly used for multi-step decode.
129 -
130 - """
131 -
132 - def __init__(
133 - self,
134 - seq_groups: list[SequenceGroupToSample],
135 - selected_token_indices: torch.Tensor,
136 - categorized_sample_indices: dict[SamplingType, torch.Tensor],
137 - num_prompts: int,
138 - skip_sampler_cpu_output: bool = False,
139 - reuse_sampling_tensors: bool = False,
140 - ) -> None:
141 - self.seq_groups = seq_groups
142 - self.selected_token_indices = selected_token_indices
143 - self.categorized_sample_indices = categorized_sample_indices
144 - self.num_prompts = num_prompts
145 - self.skip_sampler_cpu_output = skip_sampler_cpu_output
146 - self.reuse_sampling_tensors = reuse_sampling_tensors
147 -
148 - @staticmethod
149 - def prepare(
150 - seq_group_metadata_list: list[SequenceGroupMetadata],
151 - seq_lens: list[int],
152 - query_lens: list[int],
153 - device: str,
154 - pin_memory: bool,
155 - generators: Optional[dict[str, torch.Generator]] = None,
156 - cache: Optional[SamplingMetadataCache] = None,
157 - ) -> "SamplingMetadata":
158 - (
159 - seq_groups,
160 - selected_token_indices,
161 - categorized_sample_indices,
162 - num_prompts,
163 - ) = _prepare_seq_groups(seq_group_metadata_list, seq_lens, query_lens,
164 - device, generators, cache)
165 - selected_token_indices = async_tensor_h2d(
166 - selected_token_indices,
167 - dtype=torch.long,
168 - target_device=device,
169 - pin_memory=pin_memory,
170 - )
171 - categorized_sample_indices = {
172 - t:
173 - async_tensor_h2d(
174 - seq_ids,
175 - dtype=torch.int,
176 - target_device=device,
177 - pin_memory=pin_memory,
178 - )
179 - for t, seq_ids in categorized_sample_indices.items()
180 - }
181 -
182 - sampling_metadata = SamplingMetadata(
183 - seq_groups=seq_groups,
184 - selected_token_indices=selected_token_indices,
185 - categorized_sample_indices=categorized_sample_indices,
186 - num_prompts=num_prompts,
187 - )
188 - return sampling_metadata
189 -
190 - def __repr__(self) -> str:
191 - return (
192 - "SamplingMetadata("
193 - f"seq_groups={self.seq_groups}, "
194 - f"selected_token_indices={self.selected_token_indices}, "
195 - f"categorized_sample_indices={self.categorized_sample_indices})")
196 -
197 -
198 -def _prepare_seq_groups(
199 - seq_group_metadata_list: list[SequenceGroupMetadata],
200 - seq_lens: list[int],
201 - query_lens: list[int],
202 - device: str,
203 - generators: Optional[dict[str, torch.Generator]] = None,
204 - cache: Optional[SamplingMetadataCache] = None,
205 -) -> tuple[
206 - list[SequenceGroupToSample],
207 - list[int],
208 - dict[SamplingType, list[int]],
209 - int,
210 -]:
211 - """Prepare sequence groups and indices for sampling.
212 -
213 - Args:
214 - seq_group_metadata_list: A list of sequence group to batch.
215 - seq_lens: A list of sequence lens per sequence group.
216 - Index of prompt len should match with seq_group_metadata_list.
217 - query_lens: A list of query lengths. Prompt lens include the length
218 - of entire prompt tokens, and it could be shorter.
219 - device: A device to use for random number generators,
220 - `SequenceGroupToSample.generator`.
221 - generators: A store of per-request random number generators used
222 - for seeded requests.
223 -
224 - Returns:
225 - seq_groups: A list of sequence group to sample.
226 - selected_token_indices: See the definition from `SamplingMetadata`.
227 - categorized_sample_indices: See the definition from `SamplingMetadata`.
228 - num_prompts: Total number of prompts from `seq_group_metadata_list`.
229 - """
230 - # Batched sequence groups for the current model forward stsep.
231 - seq_groups: list[SequenceGroupToSample] = []
232 - # A list of token indices to sample/compute logprob. It is used to
233 - # prune the outcome logits from the model for the performance.
234 - selected_token_indices: list[int] = []
235 - # Used for selected_token_indices.
236 - model_output_idx = 0
237 -
238 - # Sampling type -> (
239 - # indices to sample/prompt logprob within pruned output logits,
240 - # indices to sample within pruned logits)
241 - categorized_sample_indices: dict[SamplingType, list[int]] = {
242 - t: []
243 - for t in SamplingType
244 - }
245 - # Index of logits to compute logprob. Logits include both prompt logprob
246 - # and sample logprob indices.
247 - logit_idx = 0
248 - # Total number of prompts from given sequence groups.
249 - num_prompts = 0
250 -
251 - for i, seq_group_metadata in enumerate(seq_group_metadata_list):
252 - seq_ids = seq_group_metadata.seq_data.keys()
253 -
254 - if cache is not None:
255 - sample_obj = cache.get_cached_seq_group_to_sample(len(seq_ids))
256 -
257 - for j, seq_id in enumerate(seq_ids):
258 - sample_obj.seq_ids[j] = seq_id
259 -
260 - sample_obj.prompt_logprob_indices.clear()
261 - sample_obj.sample_indices.clear()
262 -
263 - sampling_params = seq_group_metadata.sampling_params
264 - is_prompt = seq_group_metadata.is_prompt
265 - generator: Optional[torch.Generator] = None
266 - # If the current seq group is in decode stage, it is None.
267 - seq_len: Optional[int] = None
268 - query_len: Optional[int] = None
269 - prompt_logprob_indices: list[int] = (sample_obj.prompt_logprob_indices
270 - if cache is not None else [])
271 - sample_indices: list[int] = (sample_obj.sample_indices
272 - if cache is not None else [])
273 - do_sample = seq_group_metadata.do_sample
274 -
275 - if seq_group_metadata.is_prompt:
276 - if sampling_params.seed is not None:
277 - generator = torch.Generator(device=device).manual_seed(
278 - sampling_params.seed)
279 - if generators is not None:
280 - generators[seq_group_metadata.request_id] = generator
281 -
282 - num_prompts += 1
283 - num_prefill_sample = len(seq_ids)
284 - assert num_prefill_sample == 1
285 - assert query_lens is not None and seq_lens is not None
286 - query_len, seq_len = query_lens[i], seq_lens[i]
287 - # If we need sampling, exclude num_prefill_sample tokens from
288 - # prompt logprob.
289 - prompt_logprob_len = (query_len - num_prefill_sample
290 - if do_sample else query_len)
291 - sample_len = num_prefill_sample if do_sample else 0
292 - else:
293 - # Decode
294 - prompt_logprob_len = 0
295 - query_len = query_lens[i] if query_lens is not None and len(
296 - query_lens) > 0 else 1
297 - sample_len = len(seq_ids) * query_len if do_sample else 0
298 -
299 - if sampling_params.seed is not None and generators is not None:
300 - generator = generators.get(seq_group_metadata.request_id)
301 -
302 - # Update indices to select from the model output.
303 - """
304 - This blocks computes selected_token_indices which is used in the
305 - following way.
306 -
307 - hidden_states = model(...)
308 - logits = hidden_states[selected_token_indices]
309 - """
310 -
311 - if sampling_params.prompt_logprobs is not None:
312 - selected_token_indices.extend(
313 - range(model_output_idx, model_output_idx + prompt_logprob_len))
314 - model_output_idx += prompt_logprob_len
315 - if do_sample:
316 - selected_token_indices.extend(
317 - range(model_output_idx, model_output_idx + sample_len))
318 - model_output_idx += sample_len
319 -
320 - # We now find indices for logprob computation and sampling.
321 - """
322 - This block computes categorized_sample_indices which is used in the
323 - following way.
324 -
325 - hidden_states = model(...)
326 - logits = hidden_states[selected_token_indices]
327 - def sample(logits):
328 - # Use categorized_sample_indices for sampling.
329 - # prompt_logprob_indices to find prompt logprob indices.
330 - # sample_indices to find sample indices.
331 - """
332 -
333 - if sampling_params.prompt_logprobs is not None:
334 - prompt_logprob_indices.extend(
335 - range(logit_idx, logit_idx + prompt_logprob_len))
336 - logit_idx += prompt_logprob_len
337 - if do_sample:
338 - sample_indices.extend(range(logit_idx, logit_idx + sample_len))
339 - categorized_sample_indices[sampling_params.sampling_type].extend(
340 - list(range(logit_idx, logit_idx + sample_len)))
341 - logit_idx += sample_len
342 -
343 - if cache is not None:
344 - sample_obj.sampling_params = sampling_params
345 - sample_obj.seq_data = seq_group_metadata.seq_data
346 - sample_obj.seq_len = seq_len
347 - sample_obj.query_len = query_len
348 - sample_obj.generator = generator
349 - sample_obj.is_prompt = is_prompt
350 - else:
351 - sample_obj = SequenceGroupToSample(
352 - seq_ids=list(seq_ids),
353 - sampling_params=sampling_params,
354 - seq_data=seq_group_metadata.seq_data,
355 - seq_len=seq_len,
356 - query_len=query_len,
357 - generator=generator,
358 - is_prompt=is_prompt,
359 - prompt_logprob_indices=list(prompt_logprob_indices),
360 - sample_indices=list(sample_indices),
361 - )
362 -
363 - seq_groups.append(sample_obj)
364 -
365 - if cache is not None:
366 - cache.reset()
367 -
368 - return (seq_groups, selected_token_indices, categorized_sample_indices,
369 - num_prompts)
370 -
371 -
372 -@dataclass
373 -class SamplingTensors:
374 - """Tensors for sampling."""
375 -
376 - temperatures: torch.Tensor
377 - top_ps: torch.Tensor
378 - top_ks: torch.Tensor
379 - min_ps: torch.Tensor
380 - presence_penalties: torch.Tensor
381 - frequency_penalties: torch.Tensor
382 - repetition_penalties: torch.Tensor
383 - prompt_tokens: torch.Tensor
384 - output_tokens: torch.Tensor
385 -
386 - @classmethod
387 - def from_sampling_metadata(
388 - cls,
389 - sampling_metadata: "SamplingMetadata",
390 - vocab_size: int,
391 - device: torch.device,
392 - dtype: torch.dtype,
393 - ) -> tuple["SamplingTensors", bool, bool, bool]:
394 - prompt_tokens: list[array] = []
395 - output_tokens: list[array] = []
396 - top_ks: list[int] = []
397 - temperatures: list[float] = []
398 - top_ps: list[float] = []
399 - min_ps: list[float] = []
400 - presence_penalties: list[float] = []
401 - frequency_penalties: list[float] = []
402 - repetition_penalties: list[float] = []
403 - do_penalties = False
404 - do_top_p_top_k = False
405 - do_min_p = False
406 -
407 - assert sampling_metadata.seq_groups is not None
408 - for seq_group in sampling_metadata.seq_groups:
409 - seq_ids = seq_group.seq_ids
410 - sampling_params = seq_group.sampling_params
411 - temperature = sampling_params.temperature
412 - p = sampling_params.presence_penalty
413 - f = sampling_params.frequency_penalty
414 - r = sampling_params.repetition_penalty
415 - top_p = sampling_params.top_p
416 - min_p = sampling_params.min_p
417 -
418 - # k should not be greater than the vocab size.
419 - top_k = min(sampling_params.top_k, vocab_size)
420 - top_k = vocab_size if top_k < 1 else top_k
421 - if temperature < _SAMPLING_EPS:
422 - # NOTE: Zero temperature means deterministic sampling
423 - # (i.e., greedy sampling or beam search).
424 - # Set the temperature to 1 to avoid division by zero.
425 - temperature = 1.0
426 - if not do_top_p_top_k and (top_p < 1.0 - _SAMPLING_EPS
427 - or top_k != vocab_size):
428 - do_top_p_top_k = True
429 - if not do_min_p and min_p > _SAMPLING_EPS:
430 - do_min_p = True
431 - if not do_penalties and (abs(p) >= _SAMPLING_EPS
432 - or abs(f) >= _SAMPLING_EPS
433 - or abs(r - 1.0) >= _SAMPLING_EPS):
434 - do_penalties = True
435 -
436 - is_prompt = seq_group.is_prompt
437 - if is_prompt and sampling_params.prompt_logprobs is not None:
438 - # For tokens in the prompt that we only need to get
439 - # their logprobs
440 - query_len = seq_group.query_len
441 - assert query_len is not None
442 - prefill_len = len(seq_group.prompt_logprob_indices)
443 - temperatures += [temperature] * prefill_len
444 - top_ps += [top_p] * prefill_len
445 - top_ks += [top_k] * prefill_len
446 - min_ps += [min_p] * prefill_len
447 - presence_penalties += [0] * prefill_len
448 - frequency_penalties += [0] * prefill_len
449 - repetition_penalties += [1] * prefill_len
450 -
451 - if seq_group.do_sample:
452 - sample_lens = len(seq_group.sample_indices)
453 - assert sample_lens >= len(seq_ids)
454 - temperatures += [temperature] * sample_lens
455 - top_ps += [top_p] * sample_lens
456 - top_ks += [top_k] * sample_lens
457 - min_ps += [min_p] * sample_lens
458 - presence_penalties += [p] * sample_lens
459 - frequency_penalties += [f] * sample_lens
460 - repetition_penalties += [r] * sample_lens
461 -
462 - if do_penalties:
463 - for seq_group in sampling_metadata.seq_groups:
464 - seq_ids = seq_group.seq_ids
465 - sampling_params = seq_group.sampling_params
466 - if (seq_group.is_prompt
467 - and sampling_params.prompt_logprobs is not None):
468 - prefill_len = len(seq_group.prompt_logprob_indices)
469 - prompt_tokens.extend(
470 - array(VLLM_TOKEN_ID_ARRAY_TYPE)
471 - for _ in range(prefill_len))
472 - output_tokens.extend(
473 - array(VLLM_TOKEN_ID_ARRAY_TYPE)
474 - for _ in range(prefill_len))
475 - if seq_group.do_sample:
476 - for seq_id in seq_ids:
477 - seq_data = seq_group.seq_data[seq_id]
478 - prompt_tokens.append(seq_data.prompt_token_ids_array)
479 - output_tokens.append(seq_data.output_token_ids_array)
480 -
481 - sampling_tensors = SamplingTensors.from_lists(
482 - temperatures,
483 - top_ps,
484 - top_ks,
485 - min_ps,
486 - presence_penalties,
487 - frequency_penalties,
488 - repetition_penalties,
489 - prompt_tokens,
490 - output_tokens,
491 - vocab_size,
492 - device,
493 - dtype,
494 - )
495 - return (sampling_tensors, do_penalties, do_top_p_top_k, do_min_p)
496 -
497 - @classmethod
498 - def from_lists(
499 - cls,
500 - temperatures: list[float],
501 - top_ps: list[float],
502 - top_ks: list[int],
503 - min_ps: list[float],
504 - presence_penalties: list[float],
505 - frequency_penalties: list[float],
506 - repetition_penalties: list[float],
507 - prompt_tokens: list[array],
508 - output_tokens: list[array],
509 - vocab_size: int,
510 - device: torch.device,
511 - dtype: torch.dtype,
512 - ) -> "SamplingTensors":
513 - # Note that the performance will be very bad without
514 - # pinned memory.
515 - pin_memory = is_pin_memory_available()
516 -
517 - do_penalties = prompt_tokens or output_tokens
518 -
519 - if do_penalties:
520 - prompt_t = make_tensor_with_pad(
521 - prompt_tokens,
522 - vocab_size,
523 - device="cpu",
524 - dtype=torch.int64,
525 - pin_memory=pin_memory,
526 - )
527 - output_t = make_tensor_with_pad(
528 - output_tokens,
529 - vocab_size,
530 - device="cpu",
531 - dtype=torch.int64,
532 - pin_memory=pin_memory,
533 - )
534 - else:
535 - empty_tensor = torch.empty(0, device=device, dtype=torch.long)
536 - prompt_t = empty_tensor
537 - output_t = empty_tensor
538 -
539 - temperatures_t = torch.tensor(
540 - temperatures,
541 - device="cpu",
542 - dtype=dtype,
543 - pin_memory=pin_memory,
544 - )
545 - top_ps_t = torch.tensor(
546 - top_ps,
547 - device="cpu",
548 - dtype=dtype,
549 - pin_memory=pin_memory,
550 - )
551 - min_ps_t = torch.tensor(
552 - min_ps,
553 - device="cpu",
554 - dtype=dtype,
555 - pin_memory=pin_memory,
556 - )
557 - presence_penalties_t = torch.tensor(
558 - presence_penalties,
559 - device="cpu",
560 - dtype=dtype,
561 - pin_memory=pin_memory,
562 - )
563 - frequency_penalties_t = torch.tensor(
564 - frequency_penalties,
565 - device="cpu",
566 - dtype=dtype,
567 - pin_memory=pin_memory,
568 - )
569 - repetition_penalties_t = torch.tensor(
570 - repetition_penalties,
571 - device="cpu",
572 - dtype=dtype,
573 - pin_memory=pin_memory,
574 - )
575 - top_ks_t = torch.tensor(
576 - top_ks,
577 - device="cpu",
578 - dtype=torch.int,
579 - pin_memory=pin_memory,
580 - )
581 - # Because the memory is pinned, we can do non-blocking
582 - # transfer to device.
583 -
584 - return cls(
585 - temperatures=temperatures_t.to(device=device, non_blocking=True),
586 - top_ps=top_ps_t.to(device=device, non_blocking=True),
587 - top_ks=top_ks_t.to(device=device, non_blocking=True),
588 - min_ps=min_ps_t.to(device=device, non_blocking=True),
589 - presence_penalties=presence_penalties_t.to(device=device,
590 - non_blocking=True),
591 - frequency_penalties=frequency_penalties_t.to(device=device,
592 - non_blocking=True),
593 - repetition_penalties=repetition_penalties_t.to(device=device,
594 - non_blocking=True),
595 - prompt_tokens=prompt_t.to(device=device, non_blocking=True),
596 - output_tokens=output_t.to(device=device, non_blocking=True),
597 - )
6 + # Placeholder until it can be safely removed.
7 + pass
vllm/sequence.py
+4 -1318
@@ -1,28 +1,13 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Sequence and its related classes."""
4 -import copy
5 -import enum
6 -from abc import ABC, abstractmethod
7 -from array import array
8 -from collections import defaultdict
9 -from collections.abc import Mapping
10 -from collections.abc import Sequence as GenericSequence
11 -from dataclasses import dataclass, field
12 -from functools import reduce
13 -from typing import TYPE_CHECKING, Any, Callable, Optional, Union
4 +from dataclasses import dataclass
5 +from typing import TYPE_CHECKING, Any, Optional, Union
6
7 import msgspec
8 import torch
9
18 -from vllm.inputs import SingletonInputs
19 -from vllm.logprobs import Logprob, PromptLogprobs, SampleLogprobs
20 -from vllm.multimodal import MultiModalKwargs, MultiModalPlaceholderDict
21 -from vllm.pooling_params import PoolingParams
22 -from vllm.sampling_params import RequestOutputKind, SamplingParams
23 -
10 if TYPE_CHECKING:
25 - from vllm.lora.request import LoRARequest
11 from vllm.v1.worker.kv_connector_model_runner_mixin import (
12 KVConnectorOutput)
13 else:
@@ -34,50 +19,6 @@ VLLM_TOKEN_ID_ARRAY_TYPE = "l"
19 VLLM_INVALID_TOKEN_ID = -1
20
21
37 -def array_full(token_id: int, count: int):
38 - """[`array`][] equivalent of [numpy.full][]."""
39 - return array(VLLM_TOKEN_ID_ARRAY_TYPE, [token_id]) * count
40 -
41 -
42 -class SequenceStatus(enum.IntEnum):
43 - """Status of a sequence."""
44 - WAITING = 0
45 - RUNNING = 1
46 - SWAPPED = 2
47 - # Note: anything after SWAPPED (2) will be considered
48 - # as a finished status.
49 - FINISHED_STOPPED = 3
50 - FINISHED_LENGTH_CAPPED = 4
51 - FINISHED_ABORTED = 5
52 - FINISHED_IGNORED = 6
53 -
54 - @staticmethod
55 - def is_finished(status: "SequenceStatus") -> bool:
56 - return status > SequenceStatus.SWAPPED
57 -
58 - @staticmethod
59 - def get_finished_reason(status: "SequenceStatus") -> Union[str, None]:
60 - if status == SequenceStatus.FINISHED_STOPPED:
61 - finish_reason = "stop"
62 - elif status == SequenceStatus.FINISHED_LENGTH_CAPPED:
63 - finish_reason = "length"
64 - elif status == SequenceStatus.FINISHED_ABORTED:
65 - finish_reason = "abort"
66 - elif status == SequenceStatus.FINISHED_IGNORED:
67 - # The ignored sequences are the sequences whose prompt lengths
68 - # are longer than the model's length cap. Therefore, the stop
69 - # reason should also be "length" as in OpenAI API.
70 - finish_reason = "length"
71 - else:
72 - finish_reason = None
73 - return finish_reason
74 -
75 -
76 -class SequenceStage(enum.Enum):
77 - PREFILL = enum.auto()
78 - DECODE = enum.auto()
79 -
80 -
22 @dataclass
23 class RequestMetrics:
24 """Metrics associated with a request.
@@ -107,971 +48,12 @@ class RequestMetrics:
48 model_execute_time: Optional[float] = None
49
50
110 -class SequenceDataDelta(
111 - msgspec.Struct,
112 - array_like=True, # type: ignore[call-arg]
113 - omit_defaults=True): # type: ignore[call-arg]
114 - """Delta SequenceData to send to workers per step."""
115 - # A new token to be appended to existing SequenceData.
116 - new_output_token_ids: list[int]
117 - # Overwriting existing `cumulative_logprob`
118 - new_cumulative_logprob: float
119 - # Overwriting existing `num_computed_tokens`.
120 - new_num_computed_tokens: int
121 - # Overwriting existing `stage`.
122 - new_stage: SequenceStage
123 -
124 -
125 -class SequenceData(msgspec.Struct,
126 - omit_defaults=True): # type: ignore[call-arg]
127 - """Data associated with a sequence."""
128 - # NOTE: we cannot use Union[list, array] because msgspec cannot support
129 - # union of 2 list types.
130 - _prompt_token_ids: array
131 - _output_token_ids: array = msgspec.field(
132 - default_factory=lambda: array(VLLM_TOKEN_ID_ARRAY_TYPE, []))
133 -
134 - _prompt_embeds: Optional[torch.Tensor] = None
135 - _output_embeds: Optional[torch.Tensor] = None
136 -
137 - ### The below fields should not be passed as an argument ###
138 - _cumulative_logprob: float = 0.0
139 - _prompt_token_ids_tuple: tuple[int,
140 - ...] = msgspec.field(default_factory=tuple)
141 - # The number of tokens that are computed (that run against the model).
142 - _num_computed_tokens: int = 0
143 - # The number of tokens with prefix cache hit.
144 - _num_cached_tokens: int = 0
145 - _stage: SequenceStage = SequenceStage.PREFILL
146 - _cached_all_token_ids: list[int] = msgspec.field(default_factory=list)
147 - _cached_all_token_embeds: Optional[torch.Tensor] = None
148 -
149 - # It is used to get delta input. It is reset when `get_delta_and_reset`
150 - # is called.
151 - _new_appended_tokens: list[int] = msgspec.field(default_factory=list)
152 -
153 - # It is used to compute mrope_position_ids.
154 - _mrope_position_delta: Optional[int] = None
155 -
156 - @staticmethod
157 - def from_prompt_token_counts(
158 - *token_counts: tuple[int, int]) -> "SequenceData":
159 - """
160 - Construct a [`SequenceData`][vllm.sequence.SequenceData] instance
161 - by concatenating prompt token sequences.
162 -
163 - Each tuple represents one token sequence, expressed in the form
164 - `(token_id, count)`.
165 - """
166 - if len(token_counts) == 0:
167 - return SequenceData.from_seqs([])
168 -
169 - prompt_token_ids_arr = reduce(
170 - array.__iadd__,
171 - (array_full(token_id, count) for token_id, count in token_counts),
172 - )
173 -
174 - return SequenceData(prompt_token_ids_arr)
175 -
176 - @staticmethod
177 - def from_seqs(
178 - prompt_token_ids: GenericSequence[int],
179 - output_token_ids: Optional[GenericSequence[int]] = None,
180 - *,
181 - prompt_embeds: Optional[torch.Tensor] = None,
182 - ) -> "SequenceData":
183 - """
184 - Construct a [`SequenceData`][vllm.sequence.SequenceData] instance
185 - from prompt and output token sequences.
186 - """
187 - prompt_token_ids_arr = array(VLLM_TOKEN_ID_ARRAY_TYPE,
188 - prompt_token_ids)
189 -
190 - if output_token_ids is None:
191 - return SequenceData(prompt_token_ids_arr,
192 - _prompt_embeds=prompt_embeds)
193 -
194 - output_token_ids_arr = array(VLLM_TOKEN_ID_ARRAY_TYPE,
195 - output_token_ids)
196 -
197 - return SequenceData(prompt_token_ids_arr,
198 - _output_token_ids=output_token_ids_arr,
199 - _prompt_embeds=prompt_embeds)
200 -
201 - def __post_init__(self) -> None:
202 - assert self._prompt_token_ids.typecode == "l"
203 - assert self._output_token_ids.typecode == "l"
204 - self._prompt_token_ids_tuple: tuple[int, ...] = tuple(
205 - self._prompt_token_ids)
206 - self._update_cached_all_tokens()
207 - if self._prompt_embeds is not None:
208 - self._update_cached_all_token_embeds()
209 -
210 - def _update_cached_all_tokens(self):
211 - assert isinstance(self._prompt_token_ids, array)
212 - assert isinstance(self._output_token_ids, array)
213 - self._cached_all_token_ids: list[int] = list(self._prompt_token_ids +
214 - self._output_token_ids)
215 -
216 - def _update_cached_all_token_embeds(self):
217 - assert isinstance(self._prompt_embeds, torch.Tensor)
218 - self._cached_all_token_embeds: torch.Tensor = self._prompt_embeds
219 - if self._output_embeds is not None:
220 - self._cached_all_token_embeds = torch.cat(
221 - (self._cached_all_token_embeds, self._output_embeds), dim=0)
222 -
223 - @property
224 - def cumulative_logprob(self) -> float:
225 - """The cumulative log probability of the output."""
226 - return self._cumulative_logprob
227 -
228 - @property
229 - def prompt_token_ids(self) -> tuple[int, ...]:
230 - """The token IDs of the prompt."""
231 - return self._prompt_token_ids_tuple
232 -
233 - @prompt_token_ids.setter
234 - def prompt_token_ids(self, new_prompt_token_ids) -> None:
235 - raise NotImplementedError
236 -
237 - @property
238 - def prompt_token_ids_array(self) -> array:
239 - """Return the prompt token ids in array type.
240 -
241 - Note that the array is in "I" type, and it is not compatible
242 - with torch.long (2 bytes vs 4 bytes). So beware of the usage.
243 - """
244 - return self._prompt_token_ids
245 -
246 - @property
247 - def output_token_ids(self) -> tuple[int, ...]:
248 - """The token IDs of the output."""
249 - return tuple(self._output_token_ids)
250 -
251 - @output_token_ids.setter
252 - def output_token_ids(self,
253 - new_output_token_ids: GenericSequence[int]) -> None:
254 - self._output_token_ids = array(VLLM_TOKEN_ID_ARRAY_TYPE,
255 - new_output_token_ids)
256 - self._update_cached_all_tokens()
257 -
258 - @property
259 - def output_embeds(self) -> Optional[torch.Tensor]:
260 - return self._output_embeds
261 -
262 - @output_embeds.setter
263 - def output_embeds(self, new_output_token_embeds: torch.Tensor) -> None:
264 - self._output_token_embeds = new_output_token_embeds
265 - self._update_cached_all_token_embeds()
266 -
267 - @property
268 - def output_token_ids_array(self) -> array:
269 - """Return the prompt token ids in array type.
270 -
271 - Note that the array is in "I" type, and it is not compatible
272 - with torch.long (2 bytes vs 4 bytes). So beware of the usage.
273 - """
274 - assert isinstance(self._output_token_ids, array)
275 - return self._output_token_ids
276 -
277 - @property
278 - def prompt_embeds(self) -> Optional[torch.Tensor]:
279 - return self._prompt_embeds
280 -
281 - @prompt_embeds.setter
282 - def prompt_embeds(self, prompt_embeds: torch.Tensor) -> None:
283 - self._prompt_embeds = prompt_embeds
284 - self._update_cached_all_token_embeds()
285 -
286 - @property
287 - def mrope_position_delta(self) -> Optional[int]:
288 - return self._mrope_position_delta
289 -
290 - @mrope_position_delta.setter
291 - def mrope_position_delta(self, new_mrope_position_delta):
292 - self._mrope_position_delta = new_mrope_position_delta
293 -
294 - def append_token_id(self,
295 - token_id: int,
296 - logprob: float,
297 - token_embed: Optional[torch.Tensor] = None) -> None:
298 - self._output_token_ids.append(token_id)
299 - self._new_appended_tokens.append(token_id)
300 - self._cached_all_token_ids.append(token_id)
301 - self._cumulative_logprob += logprob
302 - if token_embed is not None:
303 - # Do not pass in with batch or sequence dimensions
304 - assert token_embed.ndim == 1
305 - token_embed = token_embed.detach().cpu().unsqueeze(0)
306 - if self._output_embeds is None:
307 - self._output_embeds = token_embed
308 - else:
309 - self._output_embeds = torch.cat(
310 - (self._output_embeds, token_embed), dim=0)
311 - assert self._cached_all_token_embeds is not None
312 - self._cached_all_token_embeds = torch.cat(
313 - (self._cached_all_token_embeds,
314 - token_embed.to(device=self._cached_all_token_embeds.device)),
315 - dim=0)
316 -
317 - def get_len(self) -> int:
318 - return len(self._output_token_ids) + len(self._prompt_token_ids)
319 -
320 - def get_prompt_len(self) -> int:
321 - return len(self._prompt_token_ids)
322 -
323 - def get_output_len(self) -> int:
324 - return len(self._output_token_ids)
325 -
326 - def get_token_ids(self) -> list[int]:
327 - return self._cached_all_token_ids
328 -
329 - def get_token_embeddings(self) -> Optional[torch.Tensor]:
330 - return self._cached_all_token_embeds
331 -
332 - def get_prefix_token_ids(
333 - self, num_tokens: int
334 - ) -> tuple[tuple[int, ...], Optional[tuple[int, ...]]]:
335 - """Get prefix tokens, and make the return value hashable"""
336 - prompt_length = self.get_prompt_len()
337 - if num_tokens > prompt_length:
338 - return (self._prompt_token_ids_tuple,
339 - tuple(self._output_token_ids[:num_tokens - prompt_length]))
340 - else:
341 - return (self._prompt_token_ids_tuple[:num_tokens], None)
342 -
343 - def get_num_computed_tokens(self) -> int:
344 - """Return the number of prefill tokens that are already computed."""
345 - return self._num_computed_tokens
346 -
347 - def update_num_computed_tokens(self, num_new_computed_tokens: int):
348 - """Update number of tokens computed so far."""
349 - self._num_computed_tokens += num_new_computed_tokens
350 - assert self._num_computed_tokens <= self.get_len(), (
351 - self._num_computed_tokens, self.get_len())
352 - # If all tokens are computed, it means it is in decoding phase.
353 - if self.get_num_uncomputed_tokens() == 0:
354 - self._stage = SequenceStage.DECODE
355 -
356 - def get_num_cached_tokens(self) -> int:
357 - """Return the number of tokens with prefix cache hit."""
358 - return self._num_cached_tokens
359 -
360 - def update_num_cached_tokens(self, num_cached_tokens: int):
361 - """Update the number of tokens with prefix cache hit."""
362 - self._num_cached_tokens = num_cached_tokens
363 -
364 - def reset_state_for_recompute(self) -> None:
365 - """Reset the number of computed tokens from this sequence. It is
366 - supposed to be called when a sequence needs to be started from
367 - the beginning again (e.g., sequence is preempted).
368 - """
369 - self._num_computed_tokens = 0
370 - self._stage = SequenceStage.PREFILL
371 - self._new_appended_tokens = []
372 -
373 - def get_num_uncomputed_tokens(self) -> int:
374 - """Return the number of prefill tokens that are not computed."""
375 - # we use `get_len()` which includes prompt_len + output_len instead
376 - # of prompt_len here. This is because during recompute we need to
377 - # prefill for both prompt and output.
378 - return self.get_len() - self.get_num_computed_tokens()
379 -
380 - def get_last_token_id(self) -> int:
381 - if not self._output_token_ids:
382 - return self._prompt_token_ids[-1]
383 - return self._output_token_ids[-1]
384 -
385 - def get_prompt_token_ids(self) -> tuple[int, ...]:
386 - return self.prompt_token_ids
387 -
388 - def get_output_token_ids(self) -> tuple[int, ...]:
389 - return self.output_token_ids
390 -
391 - def get_delta_and_reset(self) -> SequenceDataDelta:
392 - delta = SequenceDataDelta(self._new_appended_tokens,
393 - self._cumulative_logprob,
394 - self.get_num_computed_tokens(), self.stage)
395 - # Reset delta state.
396 - self._new_appended_tokens = []
397 - return delta
398 -
399 - def apply_delta(self, delta: SequenceDataDelta):
400 - self._num_computed_tokens = delta.new_num_computed_tokens
401 - self._cumulative_logprob = delta.new_cumulative_logprob
402 - self._stage = delta.new_stage
403 - self._output_token_ids.extend(delta.new_output_token_ids)
404 - self._cached_all_token_ids.extend(delta.new_output_token_ids)
405 -
406 - @property
407 - def stage(self) -> SequenceStage:
408 - return self._stage
409 -
410 - def __repr__(self) -> str:
411 - return (f"SequenceData("
412 - f"prompt_token_ids={self._prompt_token_ids}, "
413 - f"prompt_embeds.shape="
414 - f"{getattr(self._prompt_embeds, 'shape', None)}, "
415 - f"output_token_ids={self.output_token_ids}, "
416 - f"cumulative_logprob={self.cumulative_logprob}, "
417 - f"get_num_computed_tokens={self.get_num_computed_tokens()})")
418 -
419 -
420 -class Sequence:
421 - """Stores the data, status, and block information of a sequence.
422 -
423 - The sequence is constructed from the
424 - [`DecoderOnlyInputs`][vllm.inputs.data.DecoderOnlyInputs] (for decoder-only)
425 - or [`EncoderDecoderInputs`][vllm.inputs.data.EncoderDecoderInputs]
426 - (for encoder-decoder) instance passed in through the `inputs`
427 - constructor argument.
428 -
429 - Args:
430 - seq_id: The ID of the sequence.
431 - inputs: The inputs of the sequence.
432 - block_size: The block size of the sequence. Should be the same as the
433 - block size used by the block manager and cache engine.
434 - eos_token_id: The end-of-sequence (EOS) token id recognized by this LLM.
435 - lora_request: LoRA request.
436 - """
437 -
438 - def __init__(
439 - self,
440 - seq_id: int,
441 - inputs: SingletonInputs,
442 - block_size: int,
443 - eos_token_id: Optional[int] = None,
444 - lora_request: Optional[LoRARequest] = None,
445 - ) -> None:
446 - self.seq_id = seq_id
447 - self.inputs = inputs
448 - self.block_size = block_size
449 - self.eos_token_id = eos_token_id
450 - self.lora_request = lora_request
451 -
452 - self.data = SequenceData.from_seqs(
453 - self.prompt_token_ids,
454 - prompt_embeds=self.inputs["prompt_embeds"]
455 - if self.inputs["type"] == "embeds" else None)
456 - self.output_logprobs: SampleLogprobs = []
457 - self.output_text = ""
458 -
459 - self.status = SequenceStatus.WAITING
460 - self.stop_reason: Union[int, str, None] = None
461 -
462 - # These are used to keep track of delta outputs
463 - self._last_output_token_ids_offset: int = 0
464 - self._last_output_text_offset: int = 0
465 -
466 - # Used for incremental detokenization
467 - self.prefix_offset = 0
468 - self.read_offset = 0
469 - # Input + output tokens
470 - self.tokens: Optional[list[str]] = None
471 -
472 - @property
473 - def n_blocks(self) -> int:
474 - return (self.get_len() + self.block_size - 1) // self.block_size
475 -
476 - @property
477 - def prompt(self) -> Optional[str]:
478 - if self.inputs["type"] == "embeds":
479 - return None
480 - return self.inputs.get("prompt")
481 -
482 - @property
483 - def prompt_token_ids(self) -> list[int]:
484 - if self.inputs["type"] == "embeds":
485 - return [0] * len(self.inputs["prompt_embeds"])
486 - return self.inputs["prompt_token_ids"]
487 -
488 - @property
489 - def multi_modal_data(self) -> MultiModalKwargs:
490 - if self.inputs["type"] == "multimodal":
491 - return self.inputs["mm_kwargs"].get_data()
492 -
493 - return MultiModalKwargs()
494 -
495 - @property
496 - def multi_modal_placeholders(self) -> MultiModalPlaceholderDict:
497 - if self.inputs["type"] == "multimodal":
498 - return self.inputs["mm_placeholders"]
499 -
500 - return {}
501 -
502 - @property
503 - def lora_int_id(self) -> int:
504 - return self.lora_request.lora_int_id if self.lora_request else 0
505 -
506 - def get_output_text_to_return(self, buffer_length: int,
507 - delta: bool) -> str:
508 - """If delta is True, only new text since the last call to
509 - this method is returned"""
510 -
511 - # We return the full output text if the sequence is finished.
512 - truncate = buffer_length and not self.is_finished()
513 - if not delta:
514 - return self.output_text[:-buffer_length] if truncate else (
515 - self.output_text)
516 - length = len(self.output_text)
517 - if truncate:
518 - length -= buffer_length
519 - last_offset = self._last_output_text_offset
520 - if last_offset < length:
521 - self._last_output_text_offset = length
522 - return self.output_text[last_offset:length]
523 - return ""
524 -
525 - def get_output_token_ids_to_return(
526 - self, delta: bool) -> Union[GenericSequence[int], int]:
527 - """If delta is True, only new tokens since the last call to
528 - this method are returned"""
529 - if not delta:
530 - return self.get_output_token_ids()
531 -
532 - output_len = self.get_output_len()
533 -
534 - # Get the number of new tokens
535 - num_new_tokens = output_len - self._last_output_token_ids_offset
536 - self._last_output_token_ids_offset = output_len
537 -
538 - # Return new tokens
539 - if num_new_tokens == 1:
540 - # Optimization for single decode token case
541 - # (which is what we have most of the time)
542 - return self.data._cached_all_token_ids[-1]
543 -
544 - if num_new_tokens == 0:
545 - return []
546 -
547 - return self.data._cached_all_token_ids[-num_new_tokens:]
548 -
549 - def hash_of_block(self, logical_idx: int) -> int:
550 - # TODO This can produce incorrect hash when block size > prompt size
551 -
552 - # Compute the number of tokens in the sequence
553 - # TODO: The current hashing function is O(L^2). We should optimize
554 - # this in the future.
555 - num_tokens = self.num_hashed_tokens_of_block(logical_idx)
556 - hashed_tokens = self.data.get_prefix_token_ids(num_tokens)
557 - return hash((hashed_tokens, self.lora_int_id))
558 -
559 - def extra_hash(self) -> Optional[int]:
560 - """
561 - This function computes an extra hash for a sequence, specifically
562 - designed for prefix caching mode. The final sequence hash is determined
563 - by applying token_ids from the sequence's blocks.
564 - """
565 - if self.lora_int_id == 0:
566 - return None
567 -
568 - # NOTE: If there are additional factors influencing the block aside from
569 - # token_ids, include them as input parameters to the hash.
570 - return hash(self.lora_int_id)
571 -
572 - def num_hashed_tokens_of_block(self, logical_idx: int):
573 - return logical_idx * self.block_size + self.block_size
574 -
575 - def reset_state_for_recompute(self):
576 - """Reset the sequence states for recomputation."""
577 - self.data.reset_state_for_recompute()
578 -
579 - def append_token_id(self,
580 - token_id: int,
581 - logprobs: dict[int, Logprob],
582 - token_embed: Optional[torch.Tensor] = None) -> None:
583 - assert token_id in logprobs
584 - self.output_logprobs.append(logprobs)
585 - self.data.append_token_id(token_id, logprobs[token_id].logprob,
586 - token_embed)
587 -
588 - def get_len(self) -> int:
589 - return self.data.get_len()
590 -
591 - def get_prompt_len(self) -> int:
592 - return self.data.get_prompt_len()
593 -
594 - def get_output_len(self) -> int:
595 - return self.data.get_output_len()
596 -
597 - def get_token_ids(self) -> list[int]:
598 - return self.data.get_token_ids()
599 -
600 - def get_prompt_token_ids(self) -> tuple[int, ...]:
601 - return self.data.get_prompt_token_ids()
602 -
603 - def get_last_token_id(self) -> int:
604 - return self.data.get_last_token_id()
605 -
606 - def get_output_token_ids(self) -> tuple[int, ...]:
607 - return self.data.get_output_token_ids()
608 -
609 - def get_cumulative_logprob(self) -> float:
610 - return self.data.cumulative_logprob
611 -
612 - def is_finished(self) -> bool:
613 - return SequenceStatus.is_finished(self.status)
614 -
615 - def fork(self, new_seq_id: int) -> "Sequence":
616 - new_seq = copy.deepcopy(self)
617 - new_seq.seq_id = new_seq_id
618 - return new_seq
619 -
620 - def get_num_new_tokens(self) -> int:
621 - """Get the number of new tokens to be computed.
622 -
623 - Returns:
624 - The new number of tokens to be computed. I.e., 1 for decode, or
625 - the remaining prompt size for prefill.
626 - """
627 - if self.data.stage == SequenceStage.DECODE:
628 - return 1
629 - return self.data.get_num_uncomputed_tokens()
630 -
631 - def get_num_computed_tokens(self) -> int:
632 - return self.data.get_num_computed_tokens()
633 -
634 - def is_prefill(self) -> bool:
635 - return self.data.stage == SequenceStage.PREFILL
636 -
637 - def __repr__(self) -> str:
638 - return (f"Sequence(seq_id={self.seq_id}, "
639 - f"status={self.status.name}, "
640 - f"num_blocks={self.n_blocks})")
641 -
642 -
643 -class SequenceGroupState(msgspec.Struct,
644 - omit_defaults=True): # type: ignore[call-arg]
645 - """Mutable state tied to a specific sequence group"""
646 -
647 - # for multi-step decoding
648 - num_steps: int = 1
649 - current_step: int = 0
650 -
651 - @property
652 - def remaining_steps(self) -> int:
653 - return self.num_steps - self.current_step
654 -
655 -
656 -class SequenceGroup:
657 - """A group of sequences that are generated from the same prompt.
658 -
659 - Args:
660 - request_id: The ID of the request.
661 - seqs: The list of sequences.
662 - sampling_params: The sampling parameters used to generate the outputs.
663 - arrival_time: The arrival time of the request.
664 - lora_request: LoRA request.
665 - pooling_params: The parameters used to generate the pooler
666 - for a pooling model.
667 - pooled_data: The extracted hidden states from a pooling model.
668 - encoder_seq: Optional, the single encoder sequence. Should be None
669 - unless you are working with an encoder/decoder model.
670 - trace_headers: OpenTelemetry trace headers.
671 - priority: User-defined priority of the request.
672 - draft_size: The number of speculative tokens plus one from the target
673 - model; equal to max number of tokens a step can generate
674 - for single-draft speculative decoding but larger than
675 - that for multi-draft SD (currently not supported).
676 - """
677 -
678 - def __init__(self,
679 - request_id: str,
680 - seqs: list[Sequence],
681 - arrival_time: float,
682 - sampling_params: Optional[SamplingParams] = None,
683 - lora_request: Optional[LoRARequest] = None,
684 - pooling_params: Optional[PoolingParams] = None,
685 - pooled_data: Optional[torch.Tensor] = None,
686 - encoder_seq: Optional[Sequence] = None,
687 - trace_headers: Optional[Mapping[str, str]] = None,
688 - priority: int = 0,
689 - draft_size: int = 1) -> None:
690 - self.request_id = request_id
691 - self.seqs = seqs
692 - self.first_seq = seqs[0]
693 - self.arrival_time = arrival_time
694 - self.is_single_seq = len(seqs) == 1
695 - self.seqs_dict = {seq.seq_id: seq for seq in seqs}
696 -
697 - self.sampling_params = sampling_params
698 - self.metrics = RequestMetrics(arrival_time=arrival_time,
699 - last_token_time=arrival_time,
700 - first_scheduled_time=None,
701 - first_token_time=None,
702 - time_in_queue=None)
703 - self.last_token_latency = 0.0
704 - self.lora_request = lora_request
705 - self.prompt_logprobs: Optional[PromptLogprobs] = None
706 - self.state = SequenceGroupState()
707 - self.pooling_params = pooling_params
708 - self.pooled_data = pooled_data
709 - self.encoder_seq = encoder_seq
710 - self.trace_headers = trace_headers
711 - self.priority = priority
712 -
713 - self.cached_request_output = None
714 -
715 - @property
716 - def prompt(self) -> Optional[str]:
717 - return self.first_seq.prompt
718 -
719 - @property
720 - def prompt_token_ids(self) -> list[int]:
721 - return self.first_seq.prompt_token_ids
722 -
723 - @property
724 - def encoder_prompt(self) -> Optional[str]:
725 - # There are either 0 or 1 encoder sequences
726 - # If one is present, its prompt is distinct
727 - # from the decoder's.
728 - return (self.encoder_seq.prompt
729 - if self.encoder_seq is not None else None)
730 -
731 - @property
732 - def encoder_prompt_token_ids(self) -> Optional[list[int]]:
733 - # There are either 0 or 1 encoder sequences
734 - # If one is present, its prompt token ids are
735 - # distinct from the decoder's.
736 - return (self.encoder_seq.prompt_token_ids
737 - if self.encoder_seq is not None else None)
738 -
739 - @property
740 - def multi_modal_data(self) -> MultiModalKwargs:
741 - if self.first_seq.multi_modal_data:
742 - return self.first_seq.multi_modal_data
743 - elif self.encoder_seq is not None:
744 - return self.encoder_seq.multi_modal_data
745 - return MultiModalKwargs()
746 -
747 - @property
748 - def multi_modal_placeholders(self) -> MultiModalPlaceholderDict:
749 - if self.first_seq.multi_modal_data:
750 - return self.first_seq.multi_modal_placeholders
751 - elif self.encoder_seq is not None:
752 - return self.encoder_seq.multi_modal_placeholders
753 - return {}
754 -
755 - @property
756 - def lora_int_id(self) -> int:
757 - return self.lora_request.lora_int_id if self.lora_request else 0
758 -
759 - def set_last_token_time(self, now: float) -> None:
760 - """Sets the last token time for Request level timings."""
761 - # If still in prefill phase, assertion fails.
762 - assert not self.is_prefill(), (
763 - "seq_group.set_last_token_time() should not be called "
764 - "if the seq_group is in prefill phase.")
765 - self.last_token_latency = now - self.metrics.last_token_time
766 - self.metrics.last_token_time = now
767 -
768 - def get_last_token_latency(self) -> float:
769 - """Returns the latency of the last token."""
770 - assert not self.is_prefill(), (
771 - "seq_group.get_last_token_latency() should not be called "
772 - "if the seq_group is in prefill phase.")
773 - return self.last_token_latency
774 -
775 - def maybe_set_first_token_time(self, time: float) -> None:
776 - """Sets the first token time for Request level timings."""
777 - # Note: in a case where a sequence_group is swapped and
778 - # recomputed, the time between iterations is counted
779 - # in TPOT, rather than recalculating TTFT (since from the )
780 - # POV of the user, there is simply a long generation delay.
781 - if (self.metrics.first_token_time is None
782 - and self.first_seq.get_output_len() == 1):
783 - self.metrics.first_token_time = time
784 -
785 - def maybe_set_first_scheduled_time(self, time: float) -> None:
786 - """Sets the first scheduled time and time in queue for Request
787 - level timings."""
788 - if self.metrics.first_scheduled_time is None:
789 - self.metrics.first_scheduled_time = time
790 - self.metrics.time_in_queue = time - self.metrics.arrival_time
791 -
792 - def set_finished_time(self, time: Optional[float]) -> None:
793 - """Sets the finished time for Request level timings."""
794 - self.metrics.finished_time = time
795 -
796 - def get_max_num_running_seqs(self) -> int:
797 - """The maximum number of sequences running in parallel in the remaining
798 - lifetime of the request."""
799 - if self.is_single_seq:
800 - return 0 if self.first_seq.is_finished() else 1
801 - return self.num_seqs() - self.num_finished_seqs()
802 -
803 - def get_seqs(
804 - self,
805 - status: Optional[SequenceStatus] = None,
806 - ) -> list[Sequence]:
807 - if status is None:
808 - return self.seqs
809 -
810 - if self.is_single_seq:
811 - return self.seqs if self.first_seq.status == status else []
812 -
813 - return [seq for seq in self.seqs if seq.status == status]
814 -
815 - def is_encoder_decoder(self) -> bool:
816 - return self.encoder_seq is not None
817 -
818 - def get_encoder_seq(self) -> Optional[Sequence]:
819 - return self.encoder_seq
820 -
821 - def get_finished_seqs(self) -> list[Sequence]:
822 - if self.is_single_seq:
823 - return self.seqs if self.first_seq.is_finished() else []
824 -
825 - return [seq for seq in self.seqs if seq.is_finished()]
826 -
827 - def update_num_computed_tokens(self, num_new_computed_tokens: int):
828 - """Update number of tokens computed so far."""
829 - for seq in self.seqs:
830 - if not seq.is_finished():
831 - seq.data.update_num_computed_tokens(num_new_computed_tokens)
832 -
833 - def get_num_uncomputed_tokens(self) -> int:
834 - num_uncomputed_tokens = 0
835 - for seq in self.seqs:
836 - if not seq.is_finished():
837 - num_uncomputed_tokens += seq.data.get_num_uncomputed_tokens()
838 - return num_uncomputed_tokens
839 -
840 - def num_seqs(self, status: Optional[SequenceStatus] = None) -> int:
841 - # Optimization. We don't need to call get_seqs if we don't need to
842 - # filter by states.
843 - if status is None:
844 - return len(self.seqs)
845 -
846 - if self.is_single_seq:
847 - return 1 if self.seqs[0].status == status else 0
848 -
849 - return len(self.get_seqs(status))
850 -
851 - def num_finished_seqs(self) -> int:
852 - if self.is_single_seq:
853 - return 1 if self.seqs[0].is_finished() else 0
854 - return len(self.get_finished_seqs())
855 -
856 - def is_finished(self) -> bool:
857 - if self.is_single_seq:
858 - return self.first_seq.is_finished()
859 - return all(seq.is_finished() for seq in self.seqs)
860 -
861 - def is_prefill(self) -> bool:
862 - return self.first_seq.is_prefill()
863 -
864 - def __repr__(self) -> str:
865 - return (f"SequenceGroup(request_id={self.request_id}, "
866 - f"sampling_params={self.sampling_params}, "
867 - f"num_seqs={len(self.seqs)})")
868 -
869 - def uses_prompt_embeds(self) -> bool:
870 - """Returns True if the sequence group uses input embeds."""
871 - return any(seq.data.prompt_embeds is not None for seq in self.seqs)
872 -
873 -
874 -class SequenceGroupMetadataDelta(
875 - msgspec.Struct,
876 - tag=True, # type: ignore[call-arg]
877 - array_like=True, # type: ignore[call-arg]
878 - omit_defaults=True): # type: ignore[call-arg]
879 - """Delta of SequenceGroupMetadata.
880 -
881 - After sending the first SequenceGroupMetadata, vLLM scheduler
882 - only sends delta to reduce the data payload size.
883 - """
884 - seq_data_delta: dict[int, SequenceDataDelta]
885 - request_id: str
886 - block_tables: dict[int, list[int]]
887 - is_prompt: bool
888 - do_sample: bool = True
889 - token_chunk_size: Optional[int] = None
890 - computed_block_nums: Optional[list[int]] = None
891 - state: Optional[SequenceGroupState] = msgspec.field(
892 - default_factory=lambda: SequenceGroupState())
893 -
894 -
895 -class SequenceGroupMetadata(
896 - msgspec.Struct,
897 - tag=True, # type: ignore[call-arg]
898 - array_like=True, # type: ignore[call-arg]
899 - omit_defaults=True): # type: ignore[call-arg]
900 - """Metadata for a sequence group. Used to create `AttentionMetadata`.
901 -
902 - Attributes:
903 - request_id: The ID of the request.
904 - is_prompt: Whether the request is at prompt stage.
905 - seq_data: The sequence data. (Seq id -> sequence data)
906 - sampling_params: The sampling parameters used to generate the outputs.
907 - block_tables: The block tables. (Seq id -> list of physical block
908 - numbers)
909 - do_sample: True if sampling is required. Sampling is not required when
910 - e.g., prefill is chunked, and the current iteration only computes
911 - query tokens for prefill, we don't need sampling.
912 - pooling_params: Pooling parameters.
913 - lora_request: LoRA request.
914 - computed_block_nums: The block numbers that are already computed,
915 - used in prefix caching.
916 - state: Internal state tied to this sequence group.
917 - token_type_ids: Token type IDs.
918 - multi_modal_data: Multi modal data.
919 - multi_modal_placeholders: Multi modal placeholders.
920 - encoder_seq_data: Optional sequence data for encoder prompt
921 - (SequenceGroup.encoder_seq). Should be None
922 - unless you are working with an encoder/decoder
923 - model.
924 - cross_block_table: Optional cross-attention block table associated
925 - with the encoder prompt
926 - (SequenceGroup.encoder_seq). Should be None
927 - unless you are working with an encoder/decoder
928 - model.
929 - """
930 -
931 - request_id: str
932 - is_prompt: bool
933 - seq_data: dict[int, SequenceData]
934 - sampling_params: Optional[SamplingParams]
935 - block_tables: dict[int, list[int]]
936 - do_sample: bool = True
937 - pooling_params: Optional[PoolingParams] = None
938 - lora_request: Optional[LoRARequest] = None
939 - computed_block_nums: Optional[list[int]] = None
940 - state: Optional[SequenceGroupState] = msgspec.field(
941 - default_factory=lambda: SequenceGroupState())
942 - multi_modal_data: Optional[MultiModalKwargs] = None
943 - multi_modal_placeholders: Optional[MultiModalPlaceholderDict] = None
944 - encoder_seq_data: Optional[SequenceData] = None
945 - cross_block_table: Optional[list[int]] = None
946 - token_chunk_size: Optional[int] = None
947 -
948 - ### Stateful fields that are lazily defined. ###
949 - # The number of speculative tokens adopted in this request.
950 - # None means specuative decoding is not used.
951 - # Zero means speculative decoding is disabled for some reasons.
952 - # TODO: We should maintain this states out of the sequence group.
953 - num_speculative_tokens: Optional[int] = None
954 -
955 - def __post_init__(self):
956 - if self.seq_data is not None and self.token_chunk_size is None:
957 - if self.is_prompt:
958 - self.token_chunk_size = next(iter(
959 - self.seq_data.values())).get_len()
960 - else:
961 - self.token_chunk_size = 1
962 -
963 - @property
964 - def lora_int_id(self) -> int:
965 - return self.lora_request.lora_int_id if self.lora_request else 0
966 -
967 - # Multi-Step Chunked-Prefill property
968 - @property
969 - def is_single_step_prompt(self) -> bool:
970 - # do_sample is true, only when the token_chunk_size matches the
971 - # num_uncomputed_tokens of the sequence. This indicates that
972 - # the prompt will finish processing in a single `execute_model`
973 - # step.
974 - return self.is_prompt and self.do_sample
975 -
976 - def get_first_seq_id(self) -> int:
977 - # This is an efficient way of fetching the seq_id when
978 - # we know this SequenceGroup has only one sequence.
979 - return next(iter(self.seq_data))
980 -
981 - def apply_delta(self,
982 - sequence_group_metadata_delta: SequenceGroupMetadataDelta):
983 - for id, delta in sequence_group_metadata_delta.seq_data_delta.items():
984 - self.seq_data[id].apply_delta(delta)
985 - assert self.request_id == sequence_group_metadata_delta.request_id
986 - self.block_tables = sequence_group_metadata_delta.block_tables
987 - self.token_chunk_size = sequence_group_metadata_delta.token_chunk_size
988 - self.do_sample = sequence_group_metadata_delta.do_sample
989 - self.is_prompt = sequence_group_metadata_delta.is_prompt
990 -
991 - def finish_step(self) -> None:
992 - assert self.state is not None
993 - assert self.state.current_step < self.state.num_steps, \
994 - f"current step {self.state.current_step}, num_steps {self.state.num_steps}" # noqa
995 - self.state.current_step += 1
996 -
997 -
998 -class SequenceOutput(
999 - msgspec.Struct,
1000 - omit_defaults=True, # type: ignore[call-arg]
1001 - array_like=True): # type: ignore[call-arg]
1002 - """The model output associated with a sequence.
1003 -
1004 - Attributes:
1005 - parent_seq_id: The ID of the parent sequence (for forking in beam
1006 - search).
1007 - output_token: The output token ID.
1008 - logprobs: The logprobs of the output token.
1009 - (Token id -> logP(x_i+1 | x_0, ..., x_i))
1010 - output_embed: Optional output embedding tensor.
1011 - """
1012 - parent_seq_id: int
1013 - output_token: int
1014 - logprobs: dict[int, Logprob]
1015 - output_embed: Optional[torch.Tensor] = None
1016 -
1017 - def __repr__(self) -> str:
1018 - output_embed_shape = \
1019 - self.output_embed.shape if self.output_embed is not None else None
1020 - return (f"SequenceOutput(parent_seq_id={self.parent_seq_id}, "
1021 - f"output_token={self.output_token}, "
1022 - f"output_embed.shape={output_embed_shape}, "
1023 - f"logprobs={self.logprobs})")
1024 -
1025 - def __eq__(self, other: object) -> bool:
1026 - if not isinstance(other, SequenceOutput):
1027 - raise NotImplementedError()
1028 - equal = (self.parent_seq_id == other.parent_seq_id
1029 - and self.output_token == other.output_token)
1030 - log_probs_equal = other.logprobs == self.logprobs
1031 - return equal and log_probs_equal
1032 -
1033 -
1034 -class SequenceGroupOutput(ABC):
1035 - """The base class for model outputs associated with a sequence group."""
1036 -
1037 - @abstractmethod
1038 - def __repr__(self) -> str:
1039 - pass
1040 -
1041 - @abstractmethod
1042 - def __eq__(self, other: object) -> bool:
1043 - pass
1044 -
1045 -
1046 -class CompletionSequenceGroupOutput(
1047 - msgspec.Struct,
1048 - omit_defaults=True, # type: ignore[call-arg]
1049 - array_like=True): # type: ignore[call-arg]
1050 - """The model output associated with a completion sequence group."""
1051 - __metaclass__ = SequenceGroupOutput
1052 - samples: list[SequenceOutput]
1053 - # Prompt logprob for each prompt query token.
1054 - prompt_logprobs: Optional[PromptLogprobs]
1055 - step_index: Optional[int] = 0
1056 -
1057 - def __repr__(self) -> str:
1058 - return (f"CompletionSequenceGroupOutput(samples={self.samples}, "
1059 - f"prompt_logprobs={self.prompt_logprobs})")
1060 -
1061 - def __eq__(self, other: object) -> bool:
1062 - if not isinstance(other, CompletionSequenceGroupOutput):
1063 - raise NotImplementedError()
1064 - return (self.samples == other.samples
1065 - and self.prompt_logprobs == other.prompt_logprobs)
1066 -
1067 -
51 class PoolingSequenceGroupOutput(
52 msgspec.Struct,
53 omit_defaults=True, # type: ignore[call-arg]
54 array_like=True, # type: ignore[call-arg]
55 ):
56 """The model output associated with a pooling sequence group."""
1074 - __metaclass__ = SequenceGroupOutput
57 # Annotated as Any to be compatible with msgspec
58 # The actual type is in SequenceGroup.pooled_data
59 data: Any
@@ -1161,305 +143,9 @@ class PoolerOutput(
143 self.__class__) and self.outputs == other.outputs
144
145
1164 -def get_all_seq_ids(
1165 - seq_group_metadata_list: list[SequenceGroupMetadata]) -> list[int]:
1166 - """Given a list of SequenceGroupMetadata, create a list of all
1167 - sequence ids.
1168 - """
1169 - return [seq_id for sg in seq_group_metadata_list for seq_id in sg.seq_data]
1170 -
1171 -
1172 -def get_all_seq_ids_and_request_ids(
1173 - seq_group_metadata_list: list[SequenceGroupMetadata]
1174 -) -> tuple[list[int], dict[str, set[int]]]:
1175 - """Given a list of SequenceGroupMetadata, create a list of all
1176 - sequence ids.
1177 - """
1178 - seq_ids: list[int] = []
1179 - request_id_seq_ids_mapping: defaultdict[str, set[int]] = defaultdict(set)
1180 - for sg in seq_group_metadata_list:
1181 - for seq_id in sg.seq_data:
1182 - seq_ids.append(seq_id)
1183 - request_id_seq_ids_mapping[sg.request_id].add(seq_id)
1184 - return seq_ids, request_id_seq_ids_mapping
1185 -
1186 -
1187 -class HiddenStates(msgspec.Struct, array_like=True,
1188 - omit_defaults=True): # type: ignore[call-arg]
1189 - """Hidden states corresponding to in-progress sequences.
1190 - Used in speculative decoding to pass hidden states from
1191 - the target model to the proposer model.
1192 -
1193 - seq_ids are the sequence ids of each entry of the batch
1194 - dimension of the hidden_states tensor"""
1195 - # Scorer hidden states. For prefill step, it is used for hidden states of
1196 - # all tokens, whereas for decode step, it is used for last accepted tokens.
1197 - hidden_states: torch.Tensor
1198 - # The sequence group metadata list. Only needed for decode step.
1199 - seq_group_metadata_list: Optional[list[SequenceGroupMetadata]] = None
1200 - # Scorer hidden states of the 2nd last token proposed by the proposer (
1201 - # irrespective of whether it was accepted or not). Only used for cases when
1202 - # last proposed token is accepted (i.e., in case of bonus tokens). For the
1203 - # case of no bonus tokens, these are ignored.
1204 - second_last_token_hidden_states: Optional[torch.Tensor] = None
1205 -
1206 - _seq_ids: list[int] = msgspec.field(default_factory=list)
1207 -
1208 - def __post_init__(self):
1209 - if self.seq_group_metadata_list is not None:
1210 - assert len(self.seq_group_metadata_list) == len(self.hidden_states)
1211 - self._seq_ids = get_all_seq_ids(self.seq_group_metadata_list)
1212 -
1213 - @property
1214 - def seq_ids(self) -> list[int]:
1215 - return self._seq_ids
1216 -
1217 - def update(self,
1218 - hidden_states: torch.Tensor,
1219 - seq_group_metadata_list: list[SequenceGroupMetadata],
1220 - second_last_token_hidden_states: Optional[torch.Tensor] = None):
1221 - """Update hidden states from target model invocation. Only used for
1222 - decode steps"""
1223 - assert len(seq_group_metadata_list) == len(hidden_states)
1224 - self._seq_ids.extend(get_all_seq_ids(seq_group_metadata_list))
1225 - self.hidden_states = torch.cat([self.hidden_states, hidden_states])
1226 -
1227 - if self.second_last_token_hidden_states is not None:
1228 - # Adding dummy hidden_states to this to maintain same shape
1229 - self.second_last_token_hidden_states = torch.cat([
1230 - self.second_last_token_hidden_states,
1231 - torch.zeros_like(hidden_states)
1232 - if second_last_token_hidden_states is None else
1233 - second_last_token_hidden_states
1234 - ])
1235 -
1236 - def prune(self,
1237 - seq_group_metadata_list: list[SequenceGroupMetadata]) -> None:
1238 - """Prune to provided list of sequence ids. Only used for decode steps.
1239 - """
1240 - # Currently this prunes all seq_ids not present in
1241 - # seq_group_metadata_list which might cause problems where a sequence
1242 - # may be "paused" then "resumed" later. This should only prune sequences
1243 - # which are confirmed to be aborted.
1244 - seq_ids = get_all_seq_ids(seq_group_metadata_list)
1245 - # Only keep sequence IDs that exist in self._seq_ids
1246 - seq_ids = [seq_id for seq_id in seq_ids if seq_id in self._seq_ids]
1247 - if seq_ids != self._seq_ids:
1248 - # Batch contents changed - prune removed sequences.
1249 - index = [self._seq_ids.index(seq_id) for seq_id in seq_ids]
1250 - self.hidden_states = self.hidden_states[index]
1251 - if self.second_last_token_hidden_states is not None:
1252 - self.second_last_token_hidden_states = self\
1253 - .second_last_token_hidden_states[index]
1254 - self._seq_ids = seq_ids
1255 -
1256 - def expand_with_bonus_tokens(
1257 - self, seq_with_bonus_token_in_last_step: set) -> None:
1258 - """Expand hidden states for sequences with bonus tokens. This is in
1259 - alignment with `MultiStepWorker._expand_execute_model_request`."""
1260 - if self.second_last_token_hidden_states is None \
1261 - or not seq_with_bonus_token_in_last_step:
1262 - return
1263 -
1264 - index = []
1265 - for seq_id in self._seq_ids:
1266 - i = self._seq_ids.index(seq_id)
1267 - if seq_id in seq_with_bonus_token_in_last_step:
1268 - index.append(i + len(self._seq_ids))
1269 - index.append(i)
1270 -
1271 - self.hidden_states = torch.cat(
1272 - [self.hidden_states, self.second_last_token_hidden_states])[index]
1273 -
1274 -
146 class ExecuteModelRequest(
147 msgspec.Struct,
148 array_like=True, # type: ignore[call-arg]
149 omit_defaults=True): # type: ignore[call-arg]
1279 - """The model execution request, containing CPU metadata only. The LLM
1280 - engine should create an instance of this class for each request batch."""
1281 - # The sequence group metadata list.
1282 - seq_group_metadata_list: list[Union[SequenceGroupMetadata,
1283 - SequenceGroupMetadataDelta]]
1284 - # Blocks to swap in. List of CPU -> GPU block number.
1285 - blocks_to_swap_in: list[tuple[int,
1286 - int]] = msgspec.field(default_factory=list)
1287 - # Blocks to swap out. List of GPU -> CPU block number.
1288 - blocks_to_swap_out: list[tuple[int,
1289 - int]] = msgspec.field(default_factory=list)
1290 - # Blocks to copy. Source to dest block.
1291 - blocks_to_copy: list[tuple[int, int]] = msgspec.field(default_factory=list)
1292 - # Virtual engine ID for pipeline parallel.
1293 - virtual_engine: int = 0
1294 - # The number of slots for lookahead decoding.
1295 - num_lookahead_slots: int = 0
1296 - # The number of requests in the running queue.
1297 - running_queue_size: int = 0
1298 - # Optional hidden states from prior step.
1299 - previous_hidden_states: Optional[HiddenStates] = None
1300 - # The number of forward steps to run.
1301 - num_steps: int = 1
1302 - # Finished request ids since last step.
1303 - finished_requests_ids: list[str] = msgspec.field(default_factory=list)
1304 - # The last sampled token ids for multi step decoding.
1305 - last_sampled_token_ids: Optional[torch.Tensor] = None
1306 - # Async callback
1307 - async_callback: Optional[Callable] = None
1308 -
1309 - @property
1310 - def is_last_step(self) -> bool:
1311 - # TODO(will) make this be able to handle batches with variable number of
1312 - # steps
1313 - assert len(self.seq_group_metadata_list) > 0
1314 - first_seq_group = self.seq_group_metadata_list[0]
1315 - assert first_seq_group.state is not None
1316 - return first_seq_group.state.remaining_steps == 1
1317 -
1318 - @property
1319 - def current_step(self) -> int:
1320 - # TODO(will) make this be able to handle batches with variable number of
1321 - # steps
1322 - assert len(self.seq_group_metadata_list) > 0
1323 - state = self.seq_group_metadata_list[0].state
1324 - assert state is not None
1325 - return state.current_step
1326 -
1327 - def clone(
1328 - self, seq_group_metadata_list: list[Union[SequenceGroupMetadata,
1329 - SequenceGroupMetadataDelta]]
1330 - ) -> "ExecuteModelRequest":
1331 - """Clone the request with a new sequence group metadata list."""
1332 - return ExecuteModelRequest(
1333 - seq_group_metadata_list=seq_group_metadata_list,
1334 - blocks_to_swap_in=self.blocks_to_swap_in.copy(),
1335 - blocks_to_swap_out=self.blocks_to_swap_out.copy(),
1336 - blocks_to_copy=self.blocks_to_copy.copy(),
1337 - virtual_engine=self.virtual_engine,
1338 - num_lookahead_slots=self.num_lookahead_slots,
1339 - running_queue_size=self.running_queue_size,
1340 - previous_hidden_states=self.previous_hidden_states,
1341 - num_steps=self.num_steps,
1342 - finished_requests_ids=self.finished_requests_ids,
1343 - last_sampled_token_ids=self.last_sampled_token_ids.clone()
1344 - if self.last_sampled_token_ids is not None else None,
1345 - async_callback=self.async_callback)
1346 -
1347 -
1348 -@dataclass
1349 -class SequenceGroupBase:
1350 - group_id: str # the original request id before splitting
1351 -
1352 - assembled_seq_group: Optional[SequenceGroup] = None
1353 -
1354 - # seq id to a unique index inside this group
1355 - seq_id_to_index: dict[str, int] = field(default_factory=dict)
1356 -
1357 - # seq ids to be finished
1358 - to_be_finished: dict[str, SequenceGroup] = field(default_factory=dict)
1359 -
1360 - # seq id to finished sequences
1361 - finished_reqs: dict[str, SequenceGroup] = field(default_factory=dict)
1362 -
1363 - streaming: bool = False
1364 -
1365 - output_produced: bool = False
1366 -
1367 - @staticmethod
1368 - def add_request(request_id: str, engine, params, *args, **kwargs):
1369 - """When we are ready to add a request with request_id and params
1370 - into the engine, we can split the request into multiple requests.
1371 - """
1372 - raise NotImplementedError
1373 -
1374 - def finish_seq(self, seq: SequenceGroup):
1375 - """The sequence `seq` finishes, we should record the information.
1376 - """
1377 - del self.to_be_finished[seq.request_id]
1378 - self.finished_reqs[seq.request_id] = seq
1379 -
1380 - def maybe_assemble_group(
1381 - self, seq_group: SequenceGroup) -> Optional[SequenceGroup]:
1382 - """Assemble the sequence group, for producing the final
1383 - output, or adding request in the engine again.
1384 - """
1385 - raise NotImplementedError
1386 -
1387 -
1388 -class ParallelSampleSequenceGroup(SequenceGroupBase):
1389 -
1390 - @staticmethod
1391 - def add_request(request_id: str, engine, params, **kwargs):
1392 - original_params = params
1393 - group = ParallelSampleSequenceGroup(request_id)
1394 - seqs = []
1395 - for i in range(original_params.n):
1396 - request_id_i = f"{request_id}_parallel_sample_{i}"
1397 - group.seq_id_to_index[request_id_i] = i
1398 - params = original_params.clone()
1399 - params.n = 1
1400 - if params.seed is not None:
1401 - params.seed += i
1402 - seq_group = engine._add_processed_request(
1403 - request_id_i,
1404 - params=params,
1405 - **kwargs,
1406 - ) # type: ignore
1407 - assert seq_group is not None
1408 - engine.seq_id_to_seq_group[request_id_i] = group
1409 - group.to_be_finished[request_id_i] = seq_group
1410 - seqs.append(seq_group.seqs[0])
1411 -
1412 - # for parallel sampling, the `assembled_seq_group` is always
1413 - # available, since we have all the sequences ready, and they
1414 - # will not change.
1415 - group.assembled_seq_group = SequenceGroup(
1416 - request_id=request_id,
1417 - seqs=seqs,
1418 - arrival_time=seq_group.arrival_time,
1419 - sampling_params=original_params,
1420 - lora_request=seq_group.lora_request,
1421 - pooling_params=seq_group.pooling_params,
1422 - pooled_data=seq_group.pooled_data,
1423 - encoder_seq=seq_group.encoder_seq,
1424 - trace_headers=seq_group.trace_headers,
1425 - priority=seq_group.priority,
1426 - )
1427 -
1428 - group.streaming = params.output_kind == RequestOutputKind.DELTA
1429 - group.output_produced = False
1430 -
1431 - def maybe_assemble_group(
1432 - self, seq_group: SequenceGroup) -> Optional[SequenceGroup]:
1433 -
1434 - # in the streaming mode, we will return the assembled sequence
1435 - # for the first remaining sequence, and then return None for the
1436 - # rest of sequences
1437 - if self.streaming:
1438 - first_remaining_id = next(iter(self.to_be_finished))
1439 - if seq_group.request_id == first_remaining_id:
1440 - return self.assembled_seq_group
1441 - return None
1442 -
1443 - # in the non-streaming mode, we will return the assembled sequence
1444 - # when the last sequences finishes, and then return None for the
1445 - # rest of the time
1446 - if (len(self.to_be_finished) == 1
1447 - and seq_group.request_id in self.to_be_finished
1448 - and seq_group.is_finished()):
1449 - assert self.assembled_seq_group is not None
1450 - params = self.assembled_seq_group.sampling_params
1451 - assert isinstance(params, SamplingParams)
1452 - if not self.output_produced:
1453 - self.output_produced = True
1454 - if params._real_n is not None:
1455 - # Get the top-n sequences.
1456 - n = params._real_n or params.n
1457 - seqs = self.assembled_seq_group.seqs
1458 - sorting_key = lambda seq: seq.get_cumulative_logprob()
1459 - sorted_seqs = sorted(seqs, key=sorting_key, reverse=True)
1460 - top_n_seqs = sorted_seqs[:n]
1461 - self.assembled_seq_group.seqs = top_n_seqs
1462 - return self.assembled_seq_group
1463 - if self.output_produced:
1464 - return None
1465 - return None
150 + # Placeholder. Remove.
151 + pass
vllm/transformers_utils/detokenizer.py deleted
-162
@@ -1,162 +0,0 @@
1 -# SPDX-License-Identifier: Apache-2.0
2 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 -
4 -from typing import Optional
5 -
6 -from vllm.logprobs import Logprob
7 -from vllm.sequence import (VLLM_INVALID_TOKEN_ID, SamplingParams, Sequence,
8 - SequenceGroup)
9 -
10 -from .detokenizer_utils import (convert_prompt_ids_to_tokens,
11 - detokenize_incrementally)
12 -from .tokenizer import AnyTokenizer
13 -
14 -
15 -class Detokenizer:
16 - """Provides methods to decode the output of a model into text."""
17 -
18 - def __init__(self, tokenizer: AnyTokenizer):
19 - self.tokenizer = tokenizer
20 -
21 - def decode_prompt_logprobs_inplace(self, seq_group: SequenceGroup,
22 - prompt_logprobs: list[Optional[dict[
23 - int, Logprob]]],
24 - position_offset: int) -> None:
25 - """Decodes the logprobs for the prompt of a sequence group.
26 -
27 - Args:
28 - seq_group: The sequence group to decode.
29 - prompt_logprobs: The logprobs to decode.
30 - position_offset: Offset of the first index of the logprobs
31 - relative to the start of the sequence (for chunked prefill).
32 -
33 - Returns:
34 - The prompt logprobs with the decoded tokens.
35 - """
36 - prms = seq_group.sampling_params
37 - assert prms is not None
38 -
39 - # We can pick any sequence for the prompt.
40 - seq = seq_group.get_seqs()[0]
41 - # Only prompt, without the generated token.
42 - all_token_ids = seq.get_token_ids()
43 - prompt_token_ids = all_token_ids[:-1]
44 - prefix_offset = 0
45 - read_offset = 0
46 - next_iter_prefix_offset = 0
47 - next_iter_read_offset = 0
48 - next_iter_tokens: list[str] = []
49 - prev_tokens = None
50 -
51 - for token_position_in_logprob, prompt_logprobs_for_token in enumerate(
52 - prompt_logprobs):
53 -
54 - # Absolute token position equals the index in the logprobs
55 - # list plus the offset of the entire logprobs list relative
56 - # to the start of the sequence.
57 - token_position = token_position_in_logprob + position_offset
58 - if not prompt_logprobs_for_token:
59 - continue
60 - for token_id, sample_logprob in prompt_logprobs_for_token.items():
61 - if (sample_logprob.decoded_token is None
62 - and token_id != VLLM_INVALID_TOKEN_ID):
63 - prompt_token_ids_with_token = (
64 - prompt_token_ids[:token_position] + [token_id])
65 - (new_tokens, new_text, new_prefix_offset,
66 - new_read_offset) = detokenize_incrementally(
67 - tokenizer=self.tokenizer,
68 - all_input_ids=prompt_token_ids_with_token,
69 - prev_tokens=prev_tokens,
70 - prefix_offset=prefix_offset,
71 - read_offset=read_offset,
72 - skip_special_tokens=prms.skip_special_tokens,
73 - spaces_between_special_tokens=prms.
74 - spaces_between_special_tokens,
75 - )
76 -
77 - sample_logprob.decoded_token = new_text
78 -
79 - # Use the offsets & prev tokens corresponding to
80 - # real tokens to ensure detokenization is consistent
81 - # actual with prompt.
82 - if token_id == all_token_ids[token_position]:
83 - next_iter_prefix_offset = new_prefix_offset
84 - next_iter_read_offset = new_read_offset
85 - next_iter_tokens = new_tokens
86 -
87 - # Advance to the next token position.
88 - prefix_offset = next_iter_prefix_offset
89 - read_offset = next_iter_read_offset
90 - if prev_tokens is None:
91 - prev_tokens = next_iter_tokens.copy()
92 - else:
93 - prev_tokens.extend(next_iter_tokens)
94 -
95 - def decode_sequence_inplace(self, seq: Sequence,
96 - prms: SamplingParams) -> int:
97 - """Decodes the new token for a sequence. In-place operation.
98 -
99 - Args:
100 - seq: The sequence to decode.
101 - prms: The sampling parameters used to generate the sequence.
102 -
103 - Returns:
104 - The number of characters added to the output text.
105 - """
106 - all_input_ids = seq.get_token_ids()
107 - token_id_generated_this_iteration = all_input_ids[-1]
108 -
109 - # Convert prompt token IDs to tokens if necessary.
110 - # Do it here so that we don't have to repeat this
111 - # computation for each logprob.
112 - if seq.tokens is None:
113 - (seq.tokens, seq.prefix_offset,
114 - seq.read_offset) = convert_prompt_ids_to_tokens(
115 - tokenizer=self.tokenizer,
116 - prompt_ids=all_input_ids[:-1],
117 - skip_special_tokens=prms.skip_special_tokens,
118 - )
119 -
120 - (new_tokens, new_decoded_token_text, prefix_offset,
121 - read_offset) = detokenize_incrementally(
122 - tokenizer=self.tokenizer,
123 - all_input_ids=all_input_ids,
124 - prev_tokens=seq.tokens,
125 - prefix_offset=seq.prefix_offset,
126 - read_offset=seq.read_offset,
127 - skip_special_tokens=prms.skip_special_tokens,
128 - spaces_between_special_tokens=prms.spaces_between_special_tokens,
129 - )
130 -
131 - # Decode logprobs
132 - logprobs = seq.output_logprobs[-1]
133 - if logprobs:
134 - previous_tokens = all_input_ids[:-1]
135 - for token_id, sample_logprob in logprobs.items():
136 - # If the token was generated this iteration,
137 - # use the provided text.
138 - if token_id == token_id_generated_this_iteration:
139 - sample_logprob.decoded_token = new_decoded_token_text
140 - continue
141 -
142 - if (sample_logprob.decoded_token is None
143 - and token_id != VLLM_INVALID_TOKEN_ID):
144 - all_input_ids_with_logprob = previous_tokens + [token_id]
145 - (_, new_text, _, _) = detokenize_incrementally(
146 - tokenizer=self.tokenizer,
147 - all_input_ids=all_input_ids_with_logprob,
148 - prev_tokens=seq.tokens,
149 - prefix_offset=seq.prefix_offset,
150 - read_offset=seq.read_offset,
151 - skip_special_tokens=prms.skip_special_tokens,
152 - spaces_between_special_tokens=prms.
153 - spaces_between_special_tokens,
154 - )
155 - sample_logprob.decoded_token = new_text
156 -
157 - seq.tokens.extend(new_tokens)
158 - seq.prefix_offset = prefix_offset
159 - seq.read_offset = read_offset
160 - seq.output_text += new_decoded_token_text
161 -
162 - return len(new_decoded_token_text)
vllm/worker/worker_base.py
+1 -1
@@ -11,12 +11,12 @@ import torch.nn as nn
11 from vllm.config import VllmConfig, set_current_vllm_config
12 from vllm.logger import init_logger
13 from vllm.lora.request import LoRARequest
14 -from vllm.model_executor.layers.sampler import SamplerOutput
14 from vllm.sequence import ExecuteModelRequest
15 from vllm.utils import (enable_trace_function_call_for_thread,
16 resolve_obj_by_qualname, run_method,
17 update_environment_variables,
18 warn_for_unimplemented_methods)
19 +from vllm.v1.outputs import SamplerOutput
20
21 logger = init_logger(__name__)
22