@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 4aed506b65
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

[Core] Streamline some structured output related code (#26737)

Signed-off-by: Nick Hill <nhill@redhat.com>

Nick Hill committed Oct 14, 2025 at 16:27 UTC 4aed506b6538ec4f284c480bf4449e9dc5f72054
13 files changed +119 -136
tests/v1/core/test_scheduler.py
+8 -10
@@ -30,7 +30,6 @@ from vllm.v1.kv_cache_interface import (
30 from vllm.v1.outputs import DraftTokenIds, ModelRunnerOutput
31 from vllm.v1.request import Request, RequestStatus
32 from vllm.v1.structured_output import StructuredOutputManager
33 -from vllm.v1.structured_output.request import StructuredOutputRequest
33
34 from .utils import EOS_TOKEN_ID, create_requests, create_scheduler
35
@@ -335,10 +334,10 @@ def test_stop_via_update_from_output():
334 requests[0].request_id: [],
335 requests[1].request_id: [10],
336 },
338 - num_common_prefix_blocks=0,
337 + num_common_prefix_blocks=[],
338 finished_req_ids=set(),
339 free_encoder_mm_hashes=[],
341 - structured_output_request_ids={},
340 + structured_output_request_ids=[],
341 grammar_bitmask=None,
342 )
343
@@ -383,10 +382,10 @@ def test_stop_via_update_from_output():
382 requests[0].request_id: [10, 42],
383 requests[1].request_id: [13],
384 },
386 - num_common_prefix_blocks=0,
385 + num_common_prefix_blocks=[],
386 finished_req_ids=set(),
387 free_encoder_mm_hashes=[],
389 - structured_output_request_ids={},
388 + structured_output_request_ids=[],
389 grammar_bitmask=None,
390 )
391
@@ -429,10 +428,10 @@ def test_stop_via_update_from_output():
428 requests[0].request_id: [10, 11],
429 requests[1].request_id: [],
430 },
432 - num_common_prefix_blocks=0,
431 + num_common_prefix_blocks=[],
432 finished_req_ids=set(),
433 free_encoder_mm_hashes=[],
435 - structured_output_request_ids={},
434 + structured_output_request_ids=[],
435 grammar_bitmask=None,
436 )
437
@@ -470,10 +469,10 @@ def test_stop_via_update_from_output():
469 total_num_scheduled_tokens=3,
470 scheduled_encoder_inputs={},
471 scheduled_spec_decode_tokens={requests[0].request_id: [EOS_TOKEN_ID, 10]},
473 - num_common_prefix_blocks=0,
472 + num_common_prefix_blocks=[],
473 finished_req_ids=set(),
474 free_encoder_mm_hashes=[],
476 - structured_output_request_ids={},
475 + structured_output_request_ids=[],
476 grammar_bitmask=None,
477 )
478
@@ -1941,7 +1940,6 @@ def test_schedule_skip_tokenizer_init_structured_output_request():
1940 sampling_params=sampling_params,
1941 pooling_params=None,
1942 eos_token_id=EOS_TOKEN_ID,
1944 - structured_output_request=StructuredOutputRequest(sampling_params),
1943 )
1944 scheduler.add_request(request)
1945 output = scheduler.schedule()
tests/v1/kv_connector/unit/test_kv_connector_lifecyle.py
+1 -1
@@ -26,7 +26,7 @@ def _make_empty_scheduler_output():
26 num_common_prefix_blocks=[],
27 finished_req_ids=set(),
28 free_encoder_mm_hashes=[],
29 - structured_output_request_ids={},
29 + structured_output_request_ids=[],
30 grammar_bitmask=None,
31 kv_connector_metadata=SharedStorageConnectorMetadata(),
32 )
tests/v1/tpu/worker/test_tpu_model_runner.py
+12 -12
@@ -89,10 +89,10 @@ def _schedule_new_request(*req_ids: str) -> SchedulerOutput:
89 total_num_scheduled_tokens=total_num_scheduled_tokens,
90 scheduled_spec_decode_tokens={},
91 scheduled_encoder_inputs={},
92 - num_common_prefix_blocks=0,
92 + num_common_prefix_blocks=[],
93 finished_req_ids=set(),
94 free_encoder_mm_hashes=[],
95 - structured_output_request_ids={},
95 + structured_output_request_ids=[],
96 grammar_bitmask=None,
97 )
98
@@ -168,10 +168,10 @@ def test_update_states_request_finished(model_runner):
168 total_num_scheduled_tokens=0,
169 scheduled_spec_decode_tokens={},
170 scheduled_encoder_inputs={},
171 - num_common_prefix_blocks=0,
171 + num_common_prefix_blocks=[],
172 finished_req_ids={req_id},
173 free_encoder_mm_hashes=[],
174 - structured_output_request_ids={},
174 + structured_output_request_ids=[],
175 grammar_bitmask=None,
176 )
177
@@ -198,10 +198,10 @@ def test_update_states_request_resumed(model_runner):
198 total_num_scheduled_tokens=0,
199 scheduled_spec_decode_tokens={},
200 scheduled_encoder_inputs={},
201 - num_common_prefix_blocks=0,
201 + num_common_prefix_blocks=[],
202 finished_req_ids=set(),
203 free_encoder_mm_hashes=[],
204 - structured_output_request_ids={},
204 + structured_output_request_ids=[],
205 grammar_bitmask=None,
206 )
207
@@ -225,10 +225,10 @@ def test_update_states_request_resumed(model_runner):
225 total_num_scheduled_tokens=1,
226 scheduled_spec_decode_tokens={},
227 scheduled_encoder_inputs={},
228 - num_common_prefix_blocks=0,
228 + num_common_prefix_blocks=[],
229 finished_req_ids=set(),
230 free_encoder_mm_hashes=[],
231 - structured_output_request_ids={},
231 + structured_output_request_ids=[],
232 grammar_bitmask=None,
233 )
234
@@ -256,10 +256,10 @@ def test_update_states_no_changes(model_runner):
256 total_num_scheduled_tokens=1,
257 scheduled_spec_decode_tokens={},
258 scheduled_encoder_inputs={},
259 - num_common_prefix_blocks=0,
259 + num_common_prefix_blocks=[],
260 finished_req_ids=set(),
261 free_encoder_mm_hashes=[],
262 - structured_output_request_ids={},
262 + structured_output_request_ids=[],
263 grammar_bitmask=None,
264 )
265
@@ -291,10 +291,10 @@ def test_update_states_request_unscheduled(model_runner):
291 total_num_scheduled_tokens=1,
292 scheduled_spec_decode_tokens={},
293 scheduled_encoder_inputs={},
294 - num_common_prefix_blocks=0,
294 + num_common_prefix_blocks=[],
295 finished_req_ids=set(),
296 free_encoder_mm_hashes=[],
297 - structured_output_request_ids={},
297 + structured_output_request_ids=[],
298 grammar_bitmask=None,
299 )
300
tests/v1/worker/test_gpu_model_runner.py
+12 -12
@@ -146,10 +146,10 @@ def _schedule_new_request(*req_ids: str) -> SchedulerOutput:
146 total_num_scheduled_tokens=total_num_scheduled_tokens,
147 scheduled_spec_decode_tokens={},
148 scheduled_encoder_inputs={},
149 - num_common_prefix_blocks=0,
149 + num_common_prefix_blocks=[],
150 finished_req_ids=set(),
151 free_encoder_mm_hashes=[],
152 - structured_output_request_ids={},
152 + structured_output_request_ids=[],
153 grammar_bitmask=None,
154 )
155
@@ -212,10 +212,10 @@ def test_update_states_request_finished(model_runner, dist_init):
212 total_num_scheduled_tokens=0,
213 scheduled_spec_decode_tokens={},
214 scheduled_encoder_inputs={},
215 - num_common_prefix_blocks=0,
215 + num_common_prefix_blocks=[],
216 finished_req_ids={req_id},
217 free_encoder_mm_hashes=[],
218 - structured_output_request_ids={},
218 + structured_output_request_ids=[],
219 grammar_bitmask=None,
220 )
221
@@ -244,10 +244,10 @@ def test_update_states_request_resumed(model_runner, dist_init):
244 total_num_scheduled_tokens=0,
245 scheduled_spec_decode_tokens={},
246 scheduled_encoder_inputs={},
247 - num_common_prefix_blocks=0,
247 + num_common_prefix_blocks=[],
248 finished_req_ids=set(),
249 free_encoder_mm_hashes=[],
250 - structured_output_request_ids={},
250 + structured_output_request_ids=[],
251 grammar_bitmask=None,
252 )
253
@@ -273,10 +273,10 @@ def test_update_states_request_resumed(model_runner, dist_init):
273 total_num_scheduled_tokens=1,
274 scheduled_spec_decode_tokens={},
275 scheduled_encoder_inputs={},
276 - num_common_prefix_blocks=0,
276 + num_common_prefix_blocks=[],
277 finished_req_ids=set(),
278 free_encoder_mm_hashes=[],
279 - structured_output_request_ids={},
279 + structured_output_request_ids=[],
280 grammar_bitmask=None,
281 )
282
@@ -366,10 +366,10 @@ def test_update_states_no_changes(model_runner, dist_init):
366 total_num_scheduled_tokens=1,
367 scheduled_spec_decode_tokens={},
368 scheduled_encoder_inputs={},
369 - num_common_prefix_blocks=0,
369 + num_common_prefix_blocks=[],
370 finished_req_ids=set(),
371 free_encoder_mm_hashes=[],
372 - structured_output_request_ids={},
372 + structured_output_request_ids=[],
373 grammar_bitmask=None,
374 )
375
@@ -403,10 +403,10 @@ def test_update_states_request_unscheduled(model_runner, dist_init):
403 total_num_scheduled_tokens=1,
404 scheduled_spec_decode_tokens={},
405 scheduled_encoder_inputs={},
406 - num_common_prefix_blocks=0,
406 + num_common_prefix_blocks=[],
407 finished_req_ids=set(),
408 free_encoder_mm_hashes=[],
409 - structured_output_request_ids={},
409 + structured_output_request_ids=[],
410 grammar_bitmask=None,
411 )
412
vllm/v1/core/sched/output.py
+2 -3
@@ -165,9 +165,8 @@ class SchedulerOutput:
165 # freed from the encoder cache.
166 free_encoder_mm_hashes: list[str]
167
168 - # Dict of request ids to their index within the batch
169 - # for filling the next token bitmask
170 - structured_output_request_ids: dict[str, int]
168 + # ids of structured outputs requests included in the bitmask, in order.
169 + structured_output_request_ids: list[str]
170 # the bitmask for the whole batch
171 grammar_bitmask: "npt.NDArray[np.int32] | None"
172
vllm/v1/core/sched/scheduler.py
+30 -35
@@ -5,7 +5,7 @@ import itertools
5 import time
6 from collections import defaultdict
7 from collections.abc import Iterable
8 -from typing import Any
8 +from typing import TYPE_CHECKING, Any
9
10 from vllm.config import VllmConfig
11 from vllm.distributed.kv_events import EventPublisherFactory, KVEventBatch
@@ -34,6 +34,10 @@ from vllm.v1.request import Request, RequestStatus
34 from vllm.v1.spec_decode.metrics import SpecDecodingStats
35 from vllm.v1.structured_output import StructuredOutputManager
36
37 +if TYPE_CHECKING:
38 + import numpy as np
39 + import numpy.typing as npt
40 +
41 logger = init_logger(__name__)
42
43
@@ -608,11 +612,8 @@ class Scheduler(SchedulerInterface):
612 scheduled_spec_decode_tokens,
613 req_to_new_blocks,
614 )
611 - scheduled_requests = (
612 - scheduled_new_reqs + scheduled_running_reqs + scheduled_resumed_reqs
613 - )
615 structured_output_request_ids, grammar_bitmask = self.get_grammar_bitmask(
615 - scheduled_requests, scheduled_spec_decode_tokens
616 + num_scheduled_tokens.keys(), scheduled_spec_decode_tokens
617 )
618 scheduler_output = SchedulerOutput(
619 scheduled_new_reqs=new_reqs_data,
@@ -876,32 +877,28 @@ class Scheduler(SchedulerInterface):
877
878 def get_grammar_bitmask(
879 self,
879 - requests: list[Request],
880 + scheduled_request_ids: Iterable[str],
881 scheduled_spec_decode_tokens: dict[str, list[int]],
881 - ):
882 - # NOTE: structured_output_request_ids maps
883 - # a request's (request that uses structured output)
884 - # request_id to its index in the batch.
885 - # This will help us determine to slice the grammar bitmask
886 - # and only applies valid mask for requests that
887 - # uses structured decoding.
888 - structured_output_request_ids: dict[str, int] = {}
889 - for i, req in enumerate(requests):
890 - if req.use_structured_output:
891 - # PERF: in case of chunked prefill,
892 - # request might not include any new tokens.
893 - # Therefore, we might introduce some additional
894 - # cycle to fill in the bitmask, which could be a big no-op.
895 - structured_output_request_ids[req.request_id] = i
896 -
882 + ) -> tuple[list[str], "npt.NDArray[np.int32] | None"]:
883 + # Collect list of scheduled request ids that use structured output.
884 + # The corresponding rows of the bitmask will be in this order.
885 + # PERF: in case of chunked prefill,
886 + # request might not include any new tokens.
887 + # Therefore, we might introduce some additional
888 + # cycle to fill in the bitmask, which could be a big no-op.
889 + structured_output_request_ids = [
890 + req_id
891 + for req_id in scheduled_request_ids
892 + if (req := self.requests.get(req_id)) and req.use_structured_output
893 + ]
894 if not structured_output_request_ids:
898 - bitmask = None
899 - else:
900 - bitmask = self.structured_output_manager.grammar_bitmask(
901 - self.requests,
902 - structured_output_request_ids,
903 - scheduled_spec_decode_tokens,
904 - )
895 + return structured_output_request_ids, None
896 +
897 + bitmask = self.structured_output_manager.grammar_bitmask(
898 + self.requests,
899 + structured_output_request_ids,
900 + scheduled_spec_decode_tokens,
901 + )
902 return structured_output_request_ids, bitmask
903
904 def update_from_output(
@@ -1013,12 +1010,10 @@ class Scheduler(SchedulerInterface):
1010 new_logprobs = logprobs.slice(req_index, req_index + 1)
1011
1012 if new_token_ids and self.structured_output_manager.should_advance(request):
1016 - # NOTE: structured_output_request
1017 - # should not be None if use_structured_output, we have
1018 - # checked above, so safe to ignore type warning
1019 - request.structured_output_request.grammar.accept_tokens( # type: ignore[union-attr]
1020 - req_id, new_token_ids
1021 - )
1013 + struct_output_request = request.structured_output_request
1014 + assert struct_output_request is not None
1015 + assert struct_output_request.grammar is not None
1016 + struct_output_request.grammar.accept_tokens(req_id, new_token_ids)
1017
1018 if num_nans_in_logits is not None and req_id in num_nans_in_logits:
1019 request.num_nans_in_logits = num_nans_in_logits[req_id]
vllm/v1/request.py
+8 -10
@@ -40,7 +40,6 @@ class Request:
40 prompt_embeds: torch.Tensor | None = None,
41 mm_features: list[MultiModalFeatureSpec] | None = None,
42 lora_request: Optional["LoRARequest"] = None,
43 - structured_output_request: Optional["StructuredOutputRequest"] = None,
43 cache_salt: str | None = None,
44 priority: int = 0,
45 trace_headers: Mapping[str, str] | None = None,
@@ -54,11 +53,12 @@ class Request:
53 # Because of LoRA, the eos token id can be different for each request.
54 self.eos_token_id = eos_token_id
55 self.lora_request = lora_request
57 - self.structured_output_request = structured_output_request
56 + self.structured_output_request = StructuredOutputRequest.from_sampling_params(
57 + sampling_params
58 + )
59 self.arrival_time = arrival_time if arrival_time is not None else time.time()
60
61 self.status = RequestStatus.WAITING
61 - self.use_structured_output = False
62 self.events: list[EngineCoreEvent] = []
63 self.stop_reason: int | str | None = None
64
@@ -72,9 +72,8 @@ class Request:
72 # Generative models.
73 assert sampling_params.max_tokens is not None
74 self.max_tokens = sampling_params.max_tokens
75 - if sampling_params.structured_outputs is not None:
75 + if self.structured_output_request is not None:
76 self.status = RequestStatus.WAITING_FOR_FSM
77 - self.use_structured_output = True
77
78 if sampling_params.extra_args is not None:
79 self.kv_transfer_params = sampling_params.extra_args.get(
@@ -145,11 +144,6 @@ class Request:
144 eos_token_id=request.eos_token_id,
145 arrival_time=request.arrival_time,
146 lora_request=request.lora_request,
148 - structured_output_request=StructuredOutputRequest(
149 - sampling_params=request.sampling_params
150 - )
151 - if request.sampling_params
152 - else None,
147 cache_salt=request.cache_salt,
148 priority=request.priority,
149 trace_headers=request.trace_headers,
@@ -170,6 +164,10 @@ class Request:
164 if self.get_hash_new_full_blocks is not None:
165 self.block_hashes.extend(self.get_hash_new_full_blocks())
166
167 + @property
168 + def use_structured_output(self) -> bool:
169 + return self.structured_output_request is not None
170 +
171 @property
172 def is_output_corrupted(self) -> bool:
173 return self.num_nans_in_logits > 0
vllm/v1/structured_output/__init__.py
+15 -17
@@ -167,7 +167,7 @@ class StructuredOutputManager:
167 def grammar_bitmask(
168 self,
169 requests: dict[str, Request],
170 - structured_output_request_ids: dict[str, int],
170 + structured_output_request_ids: list[str],
171 scheduled_spec_decode_tokens: dict[str, list[int]],
172 ) -> "npt.NDArray[np.int32] | None":
173 # Prepare the structured output bitmask for this batch.
@@ -196,17 +196,16 @@ class StructuredOutputManager:
196 # masks for each request, one for each possible bonus token position.
197 # These are stored inline in the tensor and unpacked by the gpu runner.
198 cumulative_index = 0
199 - ordered_seq = sorted(structured_output_request_ids.items(), key=lambda x: x[1])
199
200 # Optimized parallel filling of bitmasks for
201 # non-spec, large-batch-size cases
202 if (
204 - len(ordered_seq) > self.fill_bitmask_parallel_threshold
203 + len(structured_output_request_ids) > self.fill_bitmask_parallel_threshold
204 and max_num_spec_tokens == 0
205 ):
206 promises = []
207 batch = []
209 - for req_id, _ in ordered_seq:
208 + for req_id in structured_output_request_ids:
209 request = requests[req_id]
210 structured_output_request = request.structured_output_request
211 if TYPE_CHECKING:
@@ -230,7 +229,7 @@ class StructuredOutputManager:
229 promise.result()
230 else:
231 # Fallback to serial filling of bitmasks for small-batch-size cases
233 - for req_id, _ in ordered_seq:
232 + for req_id in structured_output_request_ids:
233 request = requests[req_id]
234 structured_output_request = request.structured_output_request
235
@@ -295,21 +294,20 @@ class StructuredOutputManager:
294 assert request.structured_output_request.grammar is not None
295 # by default, we should always advance
296 # for cases that don't use thinking mode.
298 - if self.reasoner is not None:
299 - structured_req = request.structured_output_request
297 + if self.reasoner is None:
298 + return True
299
301 - if structured_req.reasoning_ended:
302 - return True
300 + structured_req = request.structured_output_request
301 + if structured_req.reasoning_ended:
302 + return True
303
304 - # Check if reasoning ends in *this* step
305 - if self.reasoner.is_reasoning_end(request.all_token_ids):
306 - # Reasoning just ended, so we shouldn't advance til
307 - # next pass
308 - structured_req.reasoning_ended = True
304 + # Check if reasoning ends in *this* step
305 + if self.reasoner.is_reasoning_end(request.all_token_ids):
306 + # Reasoning just ended, so we shouldn't advance til
307 + # next pass
308 + structured_req.reasoning_ended = True
309
310 - return False
311 - else:
312 - return True
310 + return False
311
312 def clear_backend(self) -> None:
313 if self.backend is not None:
vllm/v1/structured_output/backend_guidance.py
+1 -1
@@ -252,7 +252,7 @@ def serialize_guidance_grammar(
252 def validate_guidance_grammar(
253 sampling_params: SamplingParams, tokenizer: llguidance.LLTokenizer | None = None
254 ) -> None:
255 - tp, grm = get_structured_output_key(sampling_params)
255 + tp, grm = get_structured_output_key(sampling_params.structured_outputs)
256 guidance_grm = serialize_guidance_grammar(tp, grm)
257 err = llguidance.LLMatcher.validate_grammar(guidance_grm, tokenizer)
258 if err:
vllm/v1/structured_output/request.py
+25 -19
@@ -7,7 +7,7 @@ from concurrent.futures import Future
7 from concurrent.futures._base import TimeoutError
8 from typing import cast
9
10 -from vllm.sampling_params import SamplingParams
10 +from vllm.sampling_params import SamplingParams, StructuredOutputsParams
11 from vllm.v1.structured_output.backend_types import (
12 StructuredOutputGrammar,
13 StructuredOutputKey,
@@ -17,10 +17,19 @@ from vllm.v1.structured_output.backend_types import (
17
18 @dataclasses.dataclass
19 class StructuredOutputRequest:
20 - sampling_params: SamplingParams
20 + params: StructuredOutputsParams
21 _grammar: Future[StructuredOutputGrammar] | StructuredOutputGrammar | None = None
22 reasoning_ended: bool | None = None
23
24 + @staticmethod
25 + def from_sampling_params(
26 + sampling_params: SamplingParams | None,
27 + ) -> "StructuredOutputRequest | None":
28 + if sampling_params is None:
29 + return None
30 + params = sampling_params.structured_outputs
31 + return StructuredOutputRequest(params=params) if params else None
32 +
33 def _check_grammar_completion(self) -> bool:
34 # NOTE: We have to lazy import to gate circular imports
35 from vllm.v1.request import RequestStatus
@@ -53,31 +62,28 @@ class StructuredOutputRequest:
62
63 @functools.cached_property
64 def structured_output_key(self) -> StructuredOutputKey:
56 - return get_structured_output_key(self.sampling_params)
65 + return get_structured_output_key(self.params)
66
67
59 -def get_structured_output_key(sampling_params: SamplingParams) -> StructuredOutputKey:
60 - params = sampling_params.structured_outputs
61 - assert params is not None, "params can't be None."
68 +def get_structured_output_key(params: StructuredOutputsParams) -> StructuredOutputKey:
69 if params.json is not None:
70 if not isinstance(params.json, str):
71 json_str = json.dumps(params.json)
72 else:
73 json_str = params.json
67 - return (StructuredOutputOptions.JSON, json_str)
68 - elif params.json_object:
69 - return (StructuredOutputOptions.JSON_OBJECT, "")
70 - elif params.regex is not None:
71 - return (StructuredOutputOptions.REGEX, params.regex)
72 - elif params.choice is not None:
74 + return StructuredOutputOptions.JSON, json_str
75 + if params.json_object:
76 + return StructuredOutputOptions.JSON_OBJECT, ""
77 + if params.regex is not None:
78 + return StructuredOutputOptions.REGEX, params.regex
79 + if params.choice is not None:
80 if not isinstance(params.choice, str):
81 json_str = json.dumps(params.choice)
82 else:
83 json_str = params.choice
77 - return (StructuredOutputOptions.CHOICE, json_str)
78 - elif params.grammar is not None:
79 - return (StructuredOutputOptions.GRAMMAR, params.grammar)
80 - elif params.structural_tag is not None:
81 - return (StructuredOutputOptions.STRUCTURAL_TAG, params.structural_tag)
82 - else:
83 - raise ValueError("No valid structured output parameter found")
84 + return StructuredOutputOptions.CHOICE, json_str
85 + if params.grammar is not None:
86 + return StructuredOutputOptions.GRAMMAR, params.grammar
87 + if params.structural_tag is not None:
88 + return StructuredOutputOptions.STRUCTURAL_TAG, params.structural_tag
89 + raise ValueError("No valid structured output parameter found")
vllm/v1/structured_output/utils.py
+2 -7
@@ -47,7 +47,6 @@ def apply_grammar_bitmask(
47 scheduler_output: SchedulerOutput,
48 input_batch: InputBatch,
49 logits: torch.Tensor,
50 - device: torch.device,
50 ) -> None:
51 """
52 Apply grammar bitmask to output logits of the model with xgrammar function.
@@ -56,7 +55,6 @@ def apply_grammar_bitmask(
55 scheduler_output (SchedulerOutput): The result of engine scheduling.
56 input_batch (InputBatch): The input of model runner.
57 logits (torch.Tensor): The output logits of model forward.
59 - device (torch.device): The device that model runner running on.
58 """
59 grammar_bitmask = scheduler_output.grammar_bitmask
60 if grammar_bitmask is None:
@@ -91,10 +89,7 @@ def apply_grammar_bitmask(
89 dtype=grammar_bitmask.dtype,
90 )
91 cumulative_index = 0
94 - seq = sorted(
95 - scheduler_output.structured_output_request_ids.items(), key=lambda x: x[1]
96 - )
97 - for req_id, _ in seq:
92 + for req_id in scheduler_output.structured_output_request_ids:
93 num_spec_tokens = len(
94 scheduler_output.scheduled_spec_decode_tokens.get(req_id, [])
95 )
@@ -117,7 +112,7 @@ def apply_grammar_bitmask(
112
113 xgr.apply_token_bitmask_inplace(
114 logits,
120 - grammar_bitmask.to(device, non_blocking=True),
115 + grammar_bitmask.to(logits.device, non_blocking=True),
116 indices=out_indices if not skip_out_indices else None,
117 )
118
vllm/v1/worker/gpu_model_runner.py
+2 -4
@@ -2568,10 +2568,8 @@ class GPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
2568 logits = model_output_broadcast_data["logits"]
2569
2570 # Apply structured output bitmasks if present
2571 - if scheduler_output.grammar_bitmask is not None:
2572 - apply_grammar_bitmask(
2573 - scheduler_output, self.input_batch, logits, self.device
2574 - )
2571 + if scheduler_output.structured_output_request_ids:
2572 + apply_grammar_bitmask(scheduler_output, self.input_batch, logits)
2573
2574 with record_function_or_nullcontext("Sample"):
2575 sampler_output = self._sample(logits, spec_decode_metadata)
vllm/v1/worker/tpu_model_runner.py
+1 -5
@@ -1963,12 +1963,8 @@ class TPUModelRunner(LoRAModelRunnerMixin, KVConnectorModelRunnerMixin):
1963 self.grammar_bitmask_cpu.zero_()
1964 self.require_structured_out_cpu.zero_()
1965
1966 - sorted_struct_requests = sorted(
1967 - scheduler_output.structured_output_request_ids.items(),
1968 - key=lambda item: item[1],
1969 - )
1966 cumulative_mask_idx = 0
1971 - for req_id, _ in sorted_struct_requests:
1967 + for req_id in scheduler_output.structured_output_request_ids:
1968 if req_id not in self.input_batch.req_id_to_index:
1969 continue
1970 batch_index = self.input_batch.req_id_to_index[req_id]