main
py 114 lines 3.58 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from types import SimpleNamespace
5 from typing import Any
6
7 import numpy as np
8 import pytest
9 import torch
10
11 pytest.importorskip("triton")
12 if not torch.cuda.is_available():
13 pytest.skip(
14 "CUDA required for Model Runner V2 logits processor tests",
15 allow_module_level=True,
16 )
17
18 from vllm.sampling_params import SamplingParams
19 from vllm.v1.worker.gpu.sample.logits_processor import (
20 LogitsContext,
21 LogitsProcessor,
22 )
23 from vllm.v1.worker.gpu.sample.sampler import Sampler
24 from vllm.v1.worker.gpu.states import RequestState
25
26 DEVICE = torch.device("cuda")
27 VOCAB_SIZE = 128
28 TARGET_TOKEN = 7
29 BIAS = 5.0
30 BOOST = 100.0
31 TEMPERATURE = 2.0
32
33
34 class MockReasoningConfig:
35 reasoning_start_token_ids = [90]
36 reasoning_end_token_ids = [91]
37 natural_reasoning_end_token_ids = [91]
38
39
40 class RecordingProcessor(LogitsProcessor):
41 """Adds BOOST to TARGET_TOKEN and records every lifecycle callback."""
42
43 def __init__(self, vllm_config: Any, req_state: Any):
44 self.events: list[str] = []
45 self.seen_logits: torch.Tensor | None = None
46
47 def add_request(self, req_idx: int, sampling_params: SamplingParams) -> bool:
48 self.events.append(f"add_request:{req_idx}")
49 return True
50
51 def apply_staged_writes(self) -> None:
52 self.events.append("apply_staged_writes")
53
54 def apply(self, logits: torch.Tensor, ctx: LogitsContext) -> torch.Tensor:
55 self.events.append("apply")
56 self.seen_logits = logits.clone()
57 logits[:, TARGET_TOKEN] += BOOST
58 return logits
59
60
61 def _make_sampler(processor: RecordingProcessor) -> Sampler:
62 req_states = RequestState(
63 max_num_reqs=4,
64 max_model_len=64,
65 max_num_batched_tokens=16,
66 num_speculative_steps=1,
67 vocab_size=VOCAB_SIZE,
68 device=DEVICE,
69 )
70 return Sampler(
71 vllm_config=SimpleNamespace(reasoning_config=MockReasoningConfig()),
72 max_num_reqs=4,
73 vocab_size=VOCAB_SIZE,
74 device=DEVICE,
75 req_states=req_states,
76 custom_logits_processors=[processor],
77 )
78
79
80 def test_custom_processor_lifecycle_and_pipeline_position():
81 """A custom processor's hooks fire in order, it runs after the built-in
82 bias stage, and before temperature scaling."""
83 proc = RecordingProcessor(None, None)
84 sampler = _make_sampler(proc)
85 sampler.add_request(
86 0,
87 SamplingParams(logit_bias={TARGET_TOKEN: BIAS}, temperature=TEMPERATURE),
88 )
89 sampler.apply_staged_writes()
90
91 num_reqs = 1
92 idx_mapping = torch.arange(num_reqs, dtype=torch.int32, device=DEVICE)
93 raw = torch.zeros(num_reqs, VOCAB_SIZE, device=DEVICE)
94 out = sampler.apply_sampling_params(
95 raw,
96 expanded_idx_mapping=idx_mapping,
97 idx_mapping=idx_mapping,
98 idx_mapping_np=np.arange(num_reqs, dtype=np.intp),
99 pos=torch.zeros(num_reqs, dtype=torch.int32, device=DEVICE),
100 input_ids=torch.zeros(num_reqs, dtype=torch.int32, device=DEVICE),
101 expanded_local_pos=torch.zeros(num_reqs, dtype=torch.int32, device=DEVICE),
102 seq_lens_upper_bound_np=np.ones(num_reqs, dtype=np.int64),
103 )
104
105 assert proc.events == [
106 "add_request:0",
107 "apply_staged_writes",
108 "apply",
109 ]
110 # The processor sees the built-in bias applied but no temperature scaling.
111 assert proc.seen_logits is not None
112 assert proc.seen_logits[0, TARGET_TOKEN].item() == BIAS
113 # Its own modification is then temperature-scaled by the sampler.
114 assert out[0, TARGET_TOKEN].item() == (BIAS + BOOST) / TEMPERATURE