Skip to content

Commit eeecbb5

Browse files
wojciech-waisfede-kamelaoshen02
committed
[Bugfix][V1/V2] Fix prompt_logprobs to respect logprobs_mode
prompt_logprobs ignored the logprobs_mode config — all modes returned log_softmax results. This fix threads the mode through both V1 and V2 GPU model runners so that *_logits modes return raw logits and *_logprobs modes return log_softmax values. V1 runner (gpu_model_runner.py): inline mode branch before gather. V2 runner: add logits_mode flag to compute_topk_scores, thread logprobs_mode through Sampler, PromptLogprobsWorker, and RejectionSampler. Config (vllm.py): remove V2 runner unsupported-feature gate for raw_logits/processed_logits now that V2 handles them. Fixes: #35832 Co-authored-by: Federico Kamelhar <209537060+fede-kamel@users.noreply.github.com> Co-authored-by: Allen Shen <aoshen@inferact.ai> Signed-off-by: Wojciech Wais <wojciech.wais@gmail.com> Signed-off-by: Federico Kamelhar <209537060+fede-kamel@users.noreply.github.com> Signed-off-by: Allen Shen <aoshen@inferact.ai>
1 parent e55cc59 commit eeecbb5

10 files changed

Lines changed: 98 additions & 40 deletions

File tree

tests/v1/sample/test_logprobs.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,38 @@ def test_logprobs_mode(logprobs_mode: LogprobsMode):
563563
cleanup_dist_env_and_memory()
564564

565565

566+
@pytest.mark.parametrize("logprobs_mode", get_args(LogprobsMode))
567+
def test_prompt_logprobs_mode(logprobs_mode: LogprobsMode):
568+
"""prompt_logprobs must respect logprobs_mode. Prompt tokens skip
569+
sampling processors, so processed_* == raw_* on the prompt side."""
570+
from vllm import LLM
571+
572+
llm = LLM(
573+
"facebook/opt-125m",
574+
enable_prefix_caching=False,
575+
gpu_memory_utilization=0.05,
576+
max_model_len=16,
577+
logprobs_mode=logprobs_mode,
578+
)
579+
try:
580+
results = llm.generate(
581+
["Hello world"],
582+
sampling_params=SamplingParams(
583+
max_tokens=1, prompt_logprobs=0, temperature=0
584+
),
585+
)
586+
assert results[0].prompt_logprobs is not None
587+
assert results[0].prompt_logprobs[1] is not None
588+
tok_id = results[0].prompt_token_ids[1]
589+
value = results[0].prompt_logprobs[1][tok_id].logprob
590+
if logprobs_mode in ("raw_logprobs", "processed_logprobs"):
591+
assert value <= 0
592+
finally:
593+
del llm
594+
torch.accelerator.empty_cache()
595+
cleanup_dist_env_and_memory()
596+
597+
566598
class TestCorrectDecodedToken:
567599
"""Unit tests for _correct_decoded_token method in LogprobsProcessor.
568600

vllm/config/vllm.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,13 +2117,6 @@ def _get_v2_model_runner_unsupported_features(self) -> list[str]:
21172117
if model_config is not None and model_config.enable_prompt_embeds:
21182118
unsupported.append("prompt embeds")
21192119

2120-
if (
2121-
model_config is not None
2122-
and model_config.runner_type == "generate"
2123-
and model_config.logprobs_mode in ("raw_logits", "processed_logits")
2124-
):
2125-
unsupported.append(f"logprobs mode '{model_config.logprobs_mode}'")
2126-
21272120
if self.cache_config.kv_sharing_fast_prefill:
21282121
# Will be added by https://github.com/vllm-project/vllm/pull/35045
21292122
unsupported.append("KV sharing fast prefill")

vllm/model_executor/models/diffusion_gemma.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
from vllm.v1.worker.gpu.buffer_utils import UvaBackedTensor, async_copy_to_gpu
5454
from vllm.v1.worker.gpu.input_batch import InputBatch
5555
from vllm.v1.worker.gpu.model_states.interface import ModelState
56-
from vllm.v1.worker.gpu.sample.logprob import compute_topk_logprobs
56+
from vllm.v1.worker.gpu.sample.logprob import compute_topk_scores
5757
from vllm.v1.worker.gpu.sample.output import SamplerOutput
5858
from vllm.v1.worker.gpu.sample.penalties import use_penalty
5959
from vllm.v1.worker.gpu.states import RequestState
@@ -1359,10 +1359,12 @@ def __call__(
13591359
# positions are never emitted.
13601360
k_i = int(valid_canvas_len_np[start_req + li])
13611361
pos = li * CL
1362-
self._pending_logprobs[slot.item()] = compute_topk_logprobs(
1362+
self._pending_logprobs[slot.item()] = compute_topk_scores(
13631363
flat_logits[pos : pos + k_i],
13641364
max_num_logprobs,
13651365
argmax_tokens[local_idx][:k_i],
1366+
logits_mode=self.model_config.logprobs_mode
1367+
in ("raw_logits", "processed_logits"),
13661368
)
13671369

13681370
# Commit steps: is_committing was True at entry. Reassemble previously

vllm/v1/sample/rejection_sampler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,14 @@ def __init__(
6767
self.sampler = sampler
6868
self.use_fp64_gumbel = getattr(sampler, "use_fp64_gumbel", False)
6969
logprobs_mode = self.sampler.logprobs_mode
70-
self.is_processed_logprobs_mode = logprobs_mode.startswith("processed")
71-
self.is_logits_logprobs_mode = logprobs_mode.endswith("logits")
70+
self.is_processed_logprobs_mode = logprobs_mode in (
71+
"processed_logprobs",
72+
"processed_logits",
73+
)
74+
self.is_logits_logprobs_mode = logprobs_mode in (
75+
"raw_logits",
76+
"processed_logits",
77+
)
7278

7379
self.synthetic_conditional_rates: torch.Tensor | None = None
7480
if (

vllm/v1/worker/gpu/model_runner.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ def load_model(self, load_dummy_weights: bool = False, *args, **kwargs) -> None:
341341
self.speculative_config,
342342
self.device,
343343
)
344-
self.prompt_logprobs_worker = PromptLogprobsWorker(self.max_num_reqs)
344+
self.prompt_logprobs_worker = PromptLogprobsWorker(
345+
self.max_num_reqs,
346+
logprobs_mode=self.model_config.logprobs_mode,
347+
)
345348
self.structured_outputs_worker = StructuredOutputsWorker(
346349
max_num_logits=self.max_num_reqs * self.decode_query_len,
347350
vocab_size=self.vocab_size,

vllm/v1/worker/gpu/sample/logprob.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,15 @@ def compute_token_logprobs(
106106
return logprobs
107107

108108

109-
def compute_topk_logprobs(
109+
def compute_topk_scores(
110110
logits: torch.Tensor,
111111
num_logprobs: int,
112112
sampled_token_ids: torch.Tensor,
113113
cu_num_logits: list[int] | None = None,
114114
logprob_token_ids_state: "LogprobTokenIdsState | None" = None,
115115
expanded_idx_mapping: torch.Tensor | None = None,
116116
max_per_req_token_ids: int = 0,
117+
logits_mode: bool = False,
117118
) -> LogprobsTensors:
118119
assert num_logprobs >= 0
119120
batch_size, vocab_size = logits.shape
@@ -124,7 +125,10 @@ def compute_topk_logprobs(
124125
if num_logprobs > 0:
125126
topk_indices = torch.topk(logits, num_logprobs, dim=-1).indices
126127
logprob_token_ids = torch.cat((logprob_token_ids, topk_indices), dim=1)
127-
logprobs = compute_token_logprobs(logits, logprob_token_ids)
128+
if logits_mode:
129+
scores = logits.gather(-1, logprob_token_ids).to(torch.float32)
130+
else:
131+
scores = compute_token_logprobs(logits, logprob_token_ids)
128132
else:
129133
# Some requests specified logprob_token_ids. Build the [batch_size,
130134
# 1 + max_cols] token_ids matrix and validity mask on the GPU via a
@@ -158,8 +162,11 @@ def compute_topk_logprobs(
158162
NUM_TOPK=num_logprobs,
159163
PADDED_COLS=triton.next_power_of_2(num_cols),
160164
)
161-
logprobs = compute_token_logprobs(logits, logprob_token_ids)
162-
logprobs = logprobs.masked_fill(~valid_mask, float("-inf"))
165+
if logits_mode:
166+
scores = logits.gather(-1, logprob_token_ids).to(torch.float32)
167+
else:
168+
scores = compute_token_logprobs(logits, logprob_token_ids)
169+
scores = scores.masked_fill(~valid_mask, float("-inf"))
163170

164171
token_ranks = torch.empty(batch_size, dtype=torch.int64, device=logits.device)
165172
_ranks_kernel[(batch_size,)](
@@ -172,7 +179,7 @@ def compute_topk_logprobs(
172179
)
173180
return LogprobsTensors(
174181
logprob_token_ids=logprob_token_ids,
175-
logprobs=logprobs,
182+
logprobs=scores,
176183
selected_token_ranks=token_ranks,
177184
cu_num_generated_tokens=cu_num_logits,
178185
)

vllm/v1/worker/gpu/sample/prompt_logprob.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
import numpy as np
66
import torch
77

8+
from vllm.config.model import LogprobsMode
89
from vllm.sampling_params import SamplingParams
910
from vllm.triton_utils import tl, triton
1011
from vllm.v1.outputs import LogprobsTensors
1112
from vllm.v1.worker.gpu.input_batch import InputBatch
12-
from vllm.v1.worker.gpu.sample.logprob import compute_topk_logprobs
13+
from vllm.v1.worker.gpu.sample.logprob import compute_topk_scores
1314

1415

1516
class PromptLogprobsWorker:
16-
def __init__(self, max_num_reqs: int):
17+
def __init__(self, max_num_reqs: int, logprobs_mode: LogprobsMode = "raw_logprobs"):
1718
self.max_num_reqs = max_num_reqs
19+
self.logprobs_mode = logprobs_mode
1820

1921
self.uses_prompt_logprobs = np.zeros(self.max_num_reqs, dtype=bool)
2022
self.num_prompt_logprobs = np.zeros(self.max_num_reqs, dtype=np.int32)
@@ -82,6 +84,7 @@ def compute_prompt_logprobs(
8284
hidden_states[: input_batch.num_tokens],
8385
logits_fn,
8486
max_num_prompt_logprobs,
87+
self.logprobs_mode,
8588
)
8689
)
8790

@@ -206,33 +209,36 @@ def compute_prompt_logprobs_with_chunking(
206209
prompt_hidden_states: torch.Tensor,
207210
logits_fn: Callable[[torch.Tensor], torch.Tensor],
208211
num_prompt_logprobs: int,
212+
logprobs_mode: LogprobsMode = "raw_logprobs",
209213
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
210214
# Since materializing the full prompt logits can take too much memory,
211215
# we compute it in chunks.
212216
CHUNK_SIZE = 1024
213217
token_ids = []
214-
logprobs = []
218+
scores = []
215219
ranks = []
220+
logits_mode = logprobs_mode in ("raw_logits", "processed_logits")
216221
prompt_token_ids = prompt_token_ids.to(torch.int64)
217222
for start_idx in range(0, prompt_token_ids.shape[0], CHUNK_SIZE):
218223
end_idx = start_idx + CHUNK_SIZE
219224
# NOTE(woosuk): logits_fn can be slow because it involves all-gather.
220225
prompt_logits = logits_fn(prompt_hidden_states[start_idx:end_idx])
221-
requested_num_prompt_logprobs = (
226+
requested_num = (
222227
prompt_logits.shape[-1]
223228
if num_prompt_logprobs == -1
224229
else num_prompt_logprobs
225230
)
226-
prompt_logprobs = compute_topk_logprobs(
231+
result = compute_topk_scores(
227232
prompt_logits,
228-
requested_num_prompt_logprobs,
233+
requested_num,
229234
prompt_token_ids[start_idx:end_idx],
235+
logits_mode=logits_mode,
230236
)
231-
token_ids.append(prompt_logprobs.logprob_token_ids)
232-
logprobs.append(prompt_logprobs.logprobs)
233-
ranks.append(prompt_logprobs.selected_token_ranks)
237+
token_ids.append(result.logprob_token_ids)
238+
scores.append(result.logprobs)
239+
ranks.append(result.selected_token_ranks)
234240

235241
token_ids = torch.cat(token_ids, dim=0) if len(token_ids) > 1 else token_ids[0]
236-
logprobs = torch.cat(logprobs, dim=0) if len(logprobs) > 1 else logprobs[0]
242+
scores = torch.cat(scores, dim=0) if len(scores) > 1 else scores[0]
237243
ranks = torch.cat(ranks, dim=0) if len(ranks) > 1 else ranks[0]
238-
return token_ids, logprobs, ranks
244+
return token_ids, scores, ranks

vllm/v1/worker/gpu/sample/sampler.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from vllm.v1.worker.gpu.sample.logit_bias import LogitBiasState
2020
from vllm.v1.worker.gpu.sample.logprob import (
2121
LogprobTokenIdsState,
22-
compute_topk_logprobs,
22+
compute_topk_scores,
2323
)
2424
from vllm.v1.worker.gpu.sample.output import SamplerOutput
2525
from vllm.v1.worker.gpu.sample.penalties import PenaltiesState
@@ -38,8 +38,6 @@ def __init__(
3838
num_speculative_tokens: int = 1,
3939
use_fp64_gumbel: bool = False,
4040
):
41-
if logprobs_mode not in ("processed_logprobs", "raw_logprobs"):
42-
raise NotImplementedError(f"Unsupported logprobs_mode: {logprobs_mode}")
4341
self.logprobs_mode = logprobs_mode
4442
self.compute_nans = envs.VLLM_COMPUTE_NANS_IN_LOGITS # False by default.
4543
self.use_fp64_gumbel = use_fp64_gumbel
@@ -102,19 +100,20 @@ def __call__(
102100
)
103101

104102
if return_logprobs:
105-
if self.logprobs_mode == "processed_logprobs":
103+
if self.logprobs_mode in ("processed_logprobs", "processed_logits"):
106104
logits = processed_logits
107105
expanded_logits = logits.shape[0] != idx_mapping_np.shape[0]
108106
cu_num_logits = cu_num_logits_np.tolist() if expanded_logits else None
109107
num_logprobs = max_num_logprobs if max_num_logprobs != NO_LOGPROBS else 0
110-
logprobs_tensors = compute_topk_logprobs(
108+
logprobs_tensors = compute_topk_scores(
111109
logits,
112110
num_logprobs,
113111
sampled,
114112
cu_num_logits,
115113
logprob_token_ids_state=self.logprob_token_ids_state,
116114
expanded_idx_mapping=input_batch.expanded_idx_mapping,
117115
max_per_req_token_ids=max_per_req_token_ids,
116+
logits_mode=self.logprobs_mode in ("raw_logits", "processed_logits"),
118117
)
119118
else:
120119
logprobs_tensors = None
@@ -222,7 +221,10 @@ def sample(
222221
# any greedy requests or per-request seeds, or if post-processed
223222
# logprobs need to be returned for any requests.
224223
(top_k is None and top_p is None)
225-
or (return_logprobs and self.logprobs_mode == "processed_logprobs")
224+
or (
225+
return_logprobs
226+
and self.logprobs_mode in ("processed_logprobs", "processed_logits")
227+
)
226228
or self.sampling_states.any_greedy(idx_mapping_np)
227229
or self.sampling_states.any_explicit_seed(idx_mapping_np)
228230
)

vllm/v1/worker/gpu/spec_decode/rejection_sampler.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
get_num_sampled_and_rejected,
1212
)
1313
from vllm.v1.worker.gpu.metrics.logits import get_num_nans
14-
from vllm.v1.worker.gpu.sample.logprob import compute_topk_logprobs
14+
from vllm.v1.worker.gpu.sample.logprob import compute_topk_scores
1515
from vllm.v1.worker.gpu.sample.output import SamplerOutput
1616
from vllm.v1.worker.gpu.sample.sampler import Sampler
1717
from vllm.v1.worker.gpu.sample.states import NO_LOGPROBS
@@ -91,11 +91,13 @@ def _get_logprobs_tensors(
9191
num_warps=1,
9292
)
9393
expanded_logits = num_logits != input_batch.idx_mapping.shape[0]
94-
return compute_topk_logprobs(
94+
return compute_topk_scores(
9595
logits,
9696
max_num_logprobs,
9797
flat_sampled,
9898
input_batch.cu_num_logits_np.tolist() if expanded_logits else None,
99+
logits_mode=self.sampler.logprobs_mode
100+
in ("raw_logits", "processed_logits"),
99101
)
100102

101103
def __call__(
@@ -139,7 +141,7 @@ def __call__(
139141
sampled,
140142
num_sampled,
141143
processed_logits
142-
if self.sampler.logprobs_mode == "processed_logprobs"
144+
if self.sampler.logprobs_mode in ("processed_logprobs", "processed_logits")
143145
else logits,
144146
)
145147

vllm/v1/worker/gpu_model_runner.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,10 +5593,15 @@ def _get_prompt_logprobs_dict(
55935593
# to gather the logprob for.
55945594
tgt_token_ids = prompt_token_ids[start_tok : start_tok + num_logits]
55955595

5596-
# Compute prompt logprobs.
5597-
logprobs = self.sampler.compute_logprobs(logits)
5596+
# Compute prompt scores respecting logprobs_mode.
5597+
# NOTE: prompt tokens skip sampling processors, so
5598+
# processed_* and raw_* yield the same scores here.
5599+
if self.model_config.logprobs_mode in ("raw_logits", "processed_logits"):
5600+
scores = logits.to(torch.float32)
5601+
else:
5602+
scores = self.sampler.compute_logprobs(logits)
55985603
token_ids, logprobs, ranks, _ = self.sampler.gather_logprobs(
5599-
logprobs, num_prompt_logprobs, tgt_token_ids
5604+
scores, num_prompt_logprobs, tgt_token_ids
56005605
)
56015606

56025607
# Transfer GPU->CPU async.

0 commit comments

Comments
 (0)