@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / ea5ff3a1f6
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 8, 08:45 UTC

[Refactor] Simplify BOS/EOS token handling (#34435)

Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>

Cyrus Leung committed Feb 13, 2026 at 10:18 UTC ea5ff3a1f60e1b9f01af17260608009c184e7ff0
29 files changed +123 -123
tests/detokenizer/test_min_tokens.py
-1
@@ -39,7 +39,6 @@ def test_min_tokens_with_stop(min_tokens: int, stop: str, truth: str):
39 mm_features=None,
40 sampling_params=params,
41 pooling_params=None,
42 - eos_token_id=None,
42 arrival_time=0.0,
43 lora_request=None,
44 cache_salt=None,
tests/detokenizer/test_stop_string_while_stop_model_terminates.py
-1
@@ -35,7 +35,6 @@ def _make_request(stop, include_stop_str_in_output: bool, min_tokens: int = 0):
35 mm_features=None,
36 sampling_params=params,
37 pooling_params=None,
38 - eos_token_id=None,
38 arrival_time=0.0,
39 lora_request=None,
40 cache_salt=None,
tests/tokenizers_/test_detokenize.py
-1
@@ -67,7 +67,6 @@ def _run_incremental_decode(
67 mm_features=None,
68 sampling_params=params,
69 pooling_params=None,
70 - eos_token_id=None,
70 arrival_time=0.0,
71 lora_request=None,
72 cache_salt=None,
tests/tool_parsers/test_step3p5_tool_parser.py
+1 -1
@@ -1123,7 +1123,7 @@ rectangle
1123
1124 # Encode all content tokens at once
1125 all_token_ids = step3p5_tokenizer.encode(model_output, add_special_tokens=False)
1126 - eos_token_id = getattr(step3p5_tokenizer, "eos_token_id", None)
1126 + eos_token_id = step3p5_tokenizer.eos_token_id
1127
1128 # Include EOS token in delta_token_ids if available
1129 if eos_token_id is not None:
tests/v1/core/test_kv_cache_utils.py
+4 -2
@@ -84,13 +84,15 @@ def make_request(
84 )
85 mm_features.append(mm_feature)
86
87 + sampling_params = SamplingParams(max_tokens=17)
88 + sampling_params.update_from_generation_config({}, eos_token_id=100)
89 +
90 return Request(
91 request_id=request_id,
92 prompt_token_ids=prompt_token_ids,
93 mm_features=mm_features if mm_features else None,
91 - sampling_params=SamplingParams(max_tokens=17),
94 + sampling_params=sampling_params,
95 pooling_params=None,
93 - eos_token_id=100,
96 lora_request=None,
97 cache_salt=cache_salt,
98 block_hasher=get_request_block_hasher(block_size, hash_fn),
tests/v1/core/test_prefix_caching.py
+4 -2
@@ -75,13 +75,15 @@ def make_request(
75 )
76 mm_features.append(mm_feature)
77
78 + sampling_params = SamplingParams(max_tokens=17, prompt_logprobs=prompt_logprobs)
79 + sampling_params.update_from_generation_config({}, eos_token_id=100)
80 +
81 return Request(
82 request_id=request_id,
83 prompt_token_ids=prompt_token_ids,
84 mm_features=mm_features if mm_features else None,
82 - sampling_params=SamplingParams(max_tokens=17, prompt_logprobs=prompt_logprobs),
85 + sampling_params=sampling_params,
86 pooling_params=None,
84 - eos_token_id=100,
87 lora_request=lora_request,
88 cache_salt=cache_salt,
89 block_hasher=get_request_block_hasher(block_size, hash_fn),
tests/v1/core/test_priority_scheduler_random.py
+3 -5
@@ -48,10 +48,9 @@ def _create_random_request(
48
49 request_id = uuid.uuid4().hex
50
51 - sampling_params = SamplingParams(
52 - ignore_eos=False,
53 - max_tokens=max_tokens,
54 - )
51 + sampling_params = SamplingParams(ignore_eos=False, max_tokens=max_tokens)
52 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
53 +
54 mm_features = []
55 for j, position in enumerate(mm_positions):
56 identifier = f"{request_id}_hash_{j}"
@@ -79,7 +78,6 @@ def _create_random_request(
78 sampling_params=sampling_params,
79 pooling_params=None,
80 mm_features=mm_features if mm_features else None,
82 - eos_token_id=EOS_TOKEN_ID,
81 arrival_time=arrival_time,
82 priority=priority,
83 block_hasher=block_hasher,
tests/v1/core/test_scheduler.py
+6 -7
@@ -469,8 +469,7 @@ def test_stop_via_update_from_output():
469
470 # Test case 4: Ignore EOS flag
471 scheduler = create_scheduler(num_speculative_tokens=2)
472 - requests = create_requests(num_requests=1, max_tokens=10)
473 - requests[0].sampling_params.ignore_eos = True
472 + requests = create_requests(num_requests=1, max_tokens=10, ignore_eos=True)
473 requests[0].num_computed_tokens = requests[0].num_tokens
474 scheduler.requests[requests[0].request_id] = requests[0]
475 scheduler.running.append(requests[0])
@@ -515,12 +514,12 @@ def test_check_stop_min_tokens():
514 max_tokens=20,
515 min_tokens=5,
516 )
517 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
518 request = Request(
519 request_id="0",
520 prompt_token_ids=[0, 1, 2],
521 sampling_params=sampling_params,
522 pooling_params=None,
523 - eos_token_id=EOS_TOKEN_ID,
523 )
524 # Simulate having generated 3 output tokens (less than min_tokens=5)
525 request.append_output_token_ids([10, 11, EOS_TOKEN_ID]) # EOS token present
@@ -551,12 +550,12 @@ def test_check_stop_min_tokens():
550 max_tokens=20,
551 min_tokens=0,
552 )
553 + sampling_params_no_min.update_from_generation_config({}, EOS_TOKEN_ID)
554 request_no_min = Request(
555 request_id="1",
556 prompt_token_ids=[0, 1, 2],
557 sampling_params=sampling_params_no_min,
558 pooling_params=None,
559 - eos_token_id=EOS_TOKEN_ID,
559 )
560 request_no_min.append_output_token_ids([10, EOS_TOKEN_ID])
561
@@ -571,12 +570,12 @@ def test_check_stop_min_tokens():
570 min_tokens=5,
571 stop_token_ids=[42],
572 )
573 + sampling_params_stop.update_from_generation_config({}, EOS_TOKEN_ID)
574 request_stop = Request(
575 request_id="2",
576 prompt_token_ids=[0, 1, 2],
577 sampling_params=sampling_params_stop,
578 pooling_params=None,
579 - eos_token_id=EOS_TOKEN_ID,
579 )
580 # Only 3 output tokens, less than min_tokens=5, but has stop token
581 request_stop.append_output_token_ids([10, 11, 42])
@@ -1877,6 +1876,7 @@ def create_requests_with_priority(
1876 stop_token_ids=stop_token_ids,
1877 prompt_logprobs=prompt_logprobs,
1878 )
1879 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
1880 requests = []
1881
1882 if mm_hashes_list is not None:
@@ -1938,7 +1938,6 @@ def create_requests_with_priority(
1938 sampling_params=sampling_params,
1939 pooling_params=None,
1940 mm_features=mm_features if mm_features else None,
1941 - eos_token_id=EOS_TOKEN_ID,
1941 arrival_time=arrival_times[i],
1942 priority=priorities[i],
1943 block_hasher=block_hasher,
@@ -2429,13 +2428,13 @@ def test_schedule_skip_tokenizer_init_structured_output_request():
2428 max_tokens=16,
2429 structured_outputs=structured_outputs_params,
2430 )
2431 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
2432 request = Request(
2433 request_id="0",
2434 prompt_token_ids=[0, 1],
2435 mm_features=None,
2436 sampling_params=sampling_params,
2437 pooling_params=None,
2438 - eos_token_id=EOS_TOKEN_ID,
2438 )
2439 scheduler.add_request(request)
2440 output = scheduler.schedule()
tests/v1/core/utils.py
+3 -2
@@ -174,6 +174,7 @@ def create_requests(
174 num_tokens: int = 10,
175 mm_hashes_list: list[list[str]] | None = None,
176 mm_positions: list[list[PlaceholderRange]] | None = None,
177 + ignore_eos: bool = False,
178 max_tokens: int = 16,
179 stop_token_ids: list[int] | None = None,
180 prompt_logprobs: int | None = None,
@@ -188,11 +189,12 @@ def create_requests(
189
190 block_hasher = get_request_block_hasher(block_size, sha256)
191 sampling_params = SamplingParams(
191 - ignore_eos=False,
192 + ignore_eos=ignore_eos,
193 max_tokens=max_tokens,
194 stop_token_ids=stop_token_ids,
195 prompt_logprobs=prompt_logprobs,
196 )
197 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
198 requests = []
199
200 if mm_hashes_list is not None:
@@ -250,7 +252,6 @@ def create_requests(
252 sampling_params=sampling_params,
253 pooling_params=None,
254 mm_features=mm_features if mm_features else None,
253 - eos_token_id=EOS_TOKEN_ID,
255 block_hasher=block_hasher,
256 )
257 requests.append(request)
tests/v1/engine/test_engine_core.py
-1
@@ -54,7 +54,6 @@ def make_request() -> EngineCoreRequest:
54 mm_features=None,
55 sampling_params=SamplingParams(),
56 pooling_params=None,
57 - eos_token_id=None,
57 arrival_time=time.time(),
58 lora_request=None,
59 cache_salt=None,
tests/v1/engine/test_engine_core_client.py
-1
@@ -69,7 +69,6 @@ def make_request(
69 mm_features=None,
70 sampling_params=params,
71 pooling_params=None,
72 - eos_token_id=None,
72 arrival_time=time.time(),
73 lora_request=None,
74 cache_salt=None,
tests/v1/engine/test_fast_incdec_prefix_err.py
-1
@@ -32,7 +32,6 @@ def test_fast_inc_detok_invalid_utf8_err_case():
32 mm_features=None,
33 sampling_params=params,
34 pooling_params=None,
35 - eos_token_id=None,
35 arrival_time=0.0,
36 lora_request=None,
37 cache_salt=None,
tests/v1/engine/test_output_processor.py
+16 -21
@@ -66,7 +66,6 @@ def test_incremental_detokenization(
66 external_req_id=f"request-{idx}",
67 prompt_token_ids=prompt_tokens,
68 mm_features=None,
69 - eos_token_id=None,
69 arrival_time=0,
70 lora_request=None,
71 cache_salt=None,
@@ -487,7 +486,6 @@ def test_logprobs_processor(
486 external_req_id=request_id_list[idx],
487 prompt_token_ids=prompt_tokens,
488 mm_features=None,
490 - eos_token_id=None,
489 arrival_time=0,
490 lora_request=None,
491 cache_salt=None,
@@ -663,6 +661,19 @@ def test_stop_token(
661 prompt_string = dummy_test_vectors.prompt_strings[0]
662 prompt_tokens = dummy_test_vectors.prompt_tokens[0]
663
664 + sampling_params = SamplingParams(
665 + skip_special_tokens=False,
666 + spaces_between_special_tokens=False,
667 + output_kind=RequestOutputKind.DELTA,
668 + stop=[],
669 + stop_token_ids=stop_token_ids,
670 + include_stop_str_in_output=include_stop_str_in_output,
671 + logprobs=num_sample_logprobs,
672 + prompt_logprobs=None,
673 + ignore_eos=ignore_eos,
674 + )
675 + sampling_params.update_from_generation_config({}, eos_token_id)
676 +
677 # Make request.
678 request_id = "request-0"
679 request = EngineCoreRequest(
@@ -670,22 +681,11 @@ def test_stop_token(
681 external_req_id=request_id + "-ext",
682 prompt_token_ids=prompt_tokens,
683 mm_features=None,
673 - eos_token_id=eos_token_id,
684 arrival_time=0,
685 lora_request=None,
686 cache_salt=None,
687 data_parallel_rank=None,
678 - sampling_params=SamplingParams(
679 - skip_special_tokens=False,
680 - spaces_between_special_tokens=False,
681 - output_kind=RequestOutputKind.DELTA,
682 - stop=[],
683 - stop_token_ids=stop_token_ids,
684 - include_stop_str_in_output=include_stop_str_in_output,
685 - logprobs=num_sample_logprobs,
686 - prompt_logprobs=None,
687 - ignore_eos=ignore_eos,
688 - ),
688 + sampling_params=sampling_params,
689 pooling_params=None,
690 )
691
@@ -693,9 +693,8 @@ def test_stop_token(
693 tokens_list=[generation_tokens],
694 generated_logprobs_raw=[generation_logprobs] if do_logprobs else None,
695 prompt_logprobs_raw=None,
696 - eos_token_id=eos_token_id,
697 - stop_token_ids=stop_token_ids,
698 - ignore_eos=ignore_eos,
696 + eos_token_id=sampling_params.eos_token_id,
697 + stop_token_ids=sampling_params.stop_token_ids,
698 request_ids=[request.request_id],
699 )
700
@@ -775,7 +774,6 @@ def test_stop_string(
774 external_req_id=request_id_list[idx],
775 prompt_token_ids=prompt_tokens,
776 mm_features=None,
778 - eos_token_id=None,
777 arrival_time=0,
778 lora_request=None,
779 cache_salt=None,
@@ -907,7 +905,6 @@ def test_iteration_stats(dummy_test_vectors):
905 external_req_id=f"request-{idx}-ext",
906 prompt_token_ids=prompt_tokens,
907 mm_features=None,
910 - eos_token_id=None,
908 arrival_time=0,
909 lora_request=None,
910 cache_salt=None,
@@ -994,7 +991,6 @@ def test_lora_request_tracking(log_stats: bool, dummy_test_vectors):
991 external_req_id=f"request-{idx}",
992 prompt_token_ids=prompt_tokens,
993 mm_features=None,
997 - eos_token_id=None,
994 arrival_time=0,
995 lora_request=lora_assignments[idx],
996 cache_salt=None,
@@ -1315,7 +1311,6 @@ def test_abort_requests(runner: str, abort_by: str, dummy_test_vectors):
1311 external_req_id=f"external-{idx}",
1312 prompt_token_ids=prompt_tokens,
1313 mm_features=None,
1318 - eos_token_id=None,
1314 arrival_time=0,
1315 lora_request=None,
1316 cache_salt=None,
tests/v1/engine/test_parallel_sampling.py
-1
@@ -76,7 +76,6 @@ def make_request(sampling_params: SamplingParams) -> EngineCoreRequest:
76 mm_features=None,
77 sampling_params=sampling_params,
78 pooling_params=None,
79 - eos_token_id=None,
79 arrival_time=0.0,
80 lora_request=None,
81 cache_salt=None,
tests/v1/engine/utils.py
+1 -3
@@ -342,7 +342,6 @@ class MockEngineCore:
342 prompt_logprobs_raw: list[LogprobsTensors] | None = None,
343 eos_token_id: int | None = None,
344 stop_token_ids: list[int] | None = None,
345 - ignore_eos: bool = False,
345 request_ids: list[str] | None = None,
346 ) -> None:
347 self.num_requests = len(tokens_list)
@@ -355,7 +354,6 @@ class MockEngineCore:
354 self.request_finished = [False for _ in range(self.num_requests)]
355 self.eos_token_id = eos_token_id
356 self.stop_token_ids = stop_token_ids
358 - self.ignore_eos = ignore_eos
357 self.request_ids = (
358 request_ids
359 if request_ids is not None
@@ -400,7 +398,7 @@ class MockEngineCore:
398 if token_idx == len(token_ids) - 1:
399 output.finish_reason = FinishReason.LENGTH
400 self.request_finished[req_idx] = True
403 - if not self.ignore_eos and new_token_id == self.eos_token_id:
401 + if new_token_id == self.eos_token_id:
402 output.finish_reason = FinishReason.STOP
403 self.request_finished[req_idx] = True
404 if new_token_id in (self.stop_token_ids or ()):
tests/v1/kv_connector/unit/test_decode_bench_connector.py
+4 -2
@@ -93,12 +93,14 @@ class DecodeBenchTestRunner:
93 """Create a new request with given token IDs."""
94 self.req_id += 1
95
96 + sampling_params = SamplingParams(max_tokens=100)
97 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
98 +
99 req = Request(
100 request_id=str(self.req_id),
101 prompt_token_ids=token_ids,
99 - sampling_params=SamplingParams(max_tokens=100),
102 + sampling_params=sampling_params,
103 pooling_params=None,
101 - eos_token_id=EOS_TOKEN_ID,
104 block_hasher=self._block_hasher,
105 )
106
tests/v1/kv_connector/unit/test_lmcache_integration.py
+4 -2
@@ -142,12 +142,14 @@ def test_request_interface():
142 from vllm.sampling_params import SamplingParams
143 from vllm.v1.request import Request
144
145 + sampling_params = SamplingParams(max_tokens=10)
146 + sampling_params.update_from_generation_config({}, eos_token_id=100)
147 +
148 req = Request(
149 request_id="test_request",
150 prompt_token_ids=[1, 2, 3],
148 - sampling_params=SamplingParams(max_tokens=10),
151 + sampling_params=sampling_params,
152 pooling_params=None,
150 - eos_token_id=100,
153 lora_request=None,
154 )
155 assumes(req, "mm_features", is_instance_of=(list, NoneType))
tests/v1/kv_connector/unit/test_offloading_connector.py
+4 -2
@@ -226,12 +226,14 @@ class RequestRunner:
226 def new_request(self, token_ids: list[int]):
227 self.req_id += 1
228
229 + sampling_params = SamplingParams(max_tokens=1000)
230 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
231 +
232 req = Request(
233 request_id=str(self.req_id),
234 prompt_token_ids=token_ids,
232 - sampling_params=SamplingParams(max_tokens=1000),
235 + sampling_params=sampling_params,
236 pooling_params=None,
234 - eos_token_id=EOS_TOKEN_ID,
237 block_hasher=self._block_hasher,
238 )
239
tests/v1/kv_connector/unit/utils.py
+1 -1
@@ -212,6 +212,7 @@ def create_request(
212
213 max_tokens = 1 if do_remote_decode else max_tokens
214 sampling_params = SamplingParams(max_tokens=max_tokens)
215 + sampling_params.update_from_generation_config({}, EOS_TOKEN_ID)
216
217 common_prefix = [1] * common_prefix_len if common_prefix_len > 0 else []
218 suffix = [i * request_id for i in range(num_tokens - common_prefix_len)]
@@ -223,7 +224,6 @@ def create_request(
224 sampling_params=sampling_params,
225 pooling_params=None,
226 mm_features=None,
226 - eos_token_id=EOS_TOKEN_ID,
227 block_hasher=get_request_block_hasher(block_size, hash_fn),
228 )
229 req.kv_transfer_params = kv_transfer_params
tests/v1/streaming_input/test_scheduler_streaming.py
-1
@@ -43,7 +43,6 @@ class DummyRequest(Request):
43 stop_token_ids=[STOP_TOKEN], max_tokens=max_tokens
44 ),
45 pooling_params=None,
46 - eos_token_id=None,
46 mm_features=mm_features,
47 resumable=resumable,
48 )
tests/v1/structured_output/test_backend_guidance.py
+2 -2
@@ -83,6 +83,7 @@ def test_grammar_bitmask_with_specdec():
83 ),
84 )
85 sampling_params.structured_outputs._backend = "guidance"
86 + sampling_params.update_from_generation_config({}, tokenizer.eos_token_id)
87
88 my_req_id = f"my_req_id_{i}"
89 request = Request(
@@ -90,7 +91,6 @@ def test_grammar_bitmask_with_specdec():
91 prompt_token_ids=prompt[:i],
92 sampling_params=sampling_params,
93 pooling_params=None,
93 - eos_token_id=tokenizer.eos_token_id,
94 )
95
96 structured_output_manager.grammar_init(request)
@@ -147,13 +147,13 @@ def test_grammar_init_async_and_sync(async_grammar):
147 ),
148 )
149 sampling_params.structured_outputs._backend = "guidance"
150 + sampling_params.update_from_generation_config({}, tokenizer.eos_token_id)
151
152 request = Request(
153 "test_request",
154 prompt_token_ids=prompt,
155 sampling_params=sampling_params,
156 pooling_params=None,
156 - eos_token_id=tokenizer.eos_token_id,
157 )
158
159 structured_output_manager.grammar_init(request)
vllm/inputs/preprocess.py
+3 -22
@@ -77,24 +77,6 @@ class InputPreprocessor:
77 def get_tokenizer(self) -> TokenizerLike:
78 return self.renderer.get_tokenizer()
79
80 - def get_bos_token_id(self) -> int | None:
81 - if self.tokenizer is None:
82 - logger.warning_once(
83 - "Using None for BOS token id because tokenizer is not initialized"
84 - )
85 - return None
86 -
87 - return self.tokenizer.bos_token_id
88 -
89 - def get_eos_token_id(self) -> int | None:
90 - if self.tokenizer is None:
91 - logger.warning_once(
92 - "Using None for EOS token id because tokenizer is not initialized"
93 - )
94 - return None
95 -
96 - return self.tokenizer.eos_token_id
97 -
80 def get_decoder_start_token_id(self) -> int:
81 """
82 Obtain the decoder start token id employed by an encoder/decoder
@@ -106,11 +88,10 @@ class InputPreprocessor:
88
89 if dec_start_token_id is None:
90 logger.warning_once(
109 - "Falling back on <BOS> for decoder start token "
110 - "id because decoder start token id is not "
111 - "available."
91 + "Falling back on <BOS> for decoder start token id "
92 + "because decoder start token id is not available."
93 )
113 - dec_start_token_id = self.get_bos_token_id()
94 + dec_start_token_id = self.renderer.get_bos_token_id()
95
96 if dec_start_token_id is None:
97 raise RuntimeError("Cannot find decoder start token id or <BOS>")
vllm/renderers/base.py
+21
@@ -6,6 +6,7 @@ from collections.abc import Sequence
6 from typing import TYPE_CHECKING, Any, overload
7
8 from vllm.inputs import EmbedsPrompt, TextPrompt, TokensPrompt
9 +from vllm.logger import init_logger
10 from vllm.tokenizers import TokenizerLike
11 from vllm.utils.async_utils import AsyncMicrobatchTokenizer
12
@@ -26,6 +27,8 @@ if TYPE_CHECKING:
27 ConversationMessage,
28 )
29
30 +logger = init_logger(__name__)
31 +
32
33 class BaseRenderer(ABC):
34 @classmethod
@@ -63,6 +66,24 @@ class BaseRenderer(ABC):
66
67 return self._async_tokenizer
68
69 + def get_bos_token_id(self) -> int | None:
70 + if self.tokenizer is None:
71 + logger.warning_once(
72 + "Using None for BOS token id because tokenizer is not initialized"
73 + )
74 + return None
75 +
76 + return self.tokenizer.bos_token_id
77 +
78 + def get_eos_token_id(self) -> int | None:
79 + if self.tokenizer is None:
80 + logger.warning_once(
81 + "Using None for EOS token id because tokenizer is not initialized"
82 + )
83 + return None
84 +
85 + return self.tokenizer.eos_token_id
86 +
87 # Step 1: Convert raw inputs to prompts
88 def render_prompt(
89 self,
vllm/sampling_params.py
+12 -5
@@ -223,6 +223,7 @@ class SamplingParams(
223 # The below fields are not supposed to be used as an input.
224 # They are set in post_init.
225 output_text_buffer_length: int = 0
226 + _eos_token_id: int | None = None
227 _all_stop_token_ids: set[int] = msgspec.field(default_factory=set)
228
229 # Fields used to construct logits processors
@@ -477,24 +478,26 @@ class SamplingParams(
478 def update_from_generation_config(
479 self,
480 generation_config: dict[str, Any],
480 - model_eos_token_id: int | None = None,
481 + eos_token_id: int | None = None,
482 ) -> None:
483 """Update if there are non-default values from generation_config"""
484 + if not self.ignore_eos:
485 + self._eos_token_id = eos_token_id
486
484 - if model_eos_token_id is not None:
487 + if eos_token_id is not None:
488 # Add the eos token id into the sampling_params to support
489 # min_tokens processing.
487 - self._all_stop_token_ids.add(model_eos_token_id)
490 + self._all_stop_token_ids.add(eos_token_id)
491
492 # Update eos_token_id for generation
493 if (eos_ids := generation_config.get("eos_token_id")) is not None:
494 # it can be either int or list of int
495 eos_ids = {eos_ids} if isinstance(eos_ids, int) else set(eos_ids)
493 - if model_eos_token_id is not None:
496 + if eos_token_id is not None:
497 # We don't need to include the primary eos_token_id in
498 # stop_token_ids since it's handled separately for stopping
499 # purposes.
497 - eos_ids.discard(model_eos_token_id)
500 + eos_ids.discard(eos_token_id)
501 if eos_ids:
502 self._all_stop_token_ids.update(eos_ids)
503 if not self.ignore_eos:
@@ -550,6 +553,10 @@ class SamplingParams(
553 return SamplingType.RANDOM_SEED
554 return SamplingType.RANDOM
555
556 + @property
557 + def eos_token_id(self) -> int | None:
558 + return self._eos_token_id
559 +
560 @property
561 def all_stop_token_ids(self) -> set[int]:
562 return self._all_stop_token_ids
vllm/v1/core/sched/utils.py
+1 -1
@@ -47,7 +47,7 @@ def check_stop(request: Request, max_model_len: int) -> bool:
47 return False
48
49 last_token_id = request.output_token_ids[-1]
50 - if not sampling_params.ignore_eos and last_token_id == request.eos_token_id:
50 + if last_token_id == sampling_params.eos_token_id:
51 request.status = RequestStatus.FINISHED_STOPPED
52 return True
53
vllm/v1/engine/__init__.py
+12 -1
@@ -9,6 +9,7 @@ from typing import Any, Literal
9 import msgspec
10 import numpy as np
11 import torch
12 +from typing_extensions import deprecated
13
14 from vllm.lora.request import LoRARequest
15 from vllm.multimodal.inputs import MultiModalFeatureSpec
@@ -63,7 +64,6 @@ class EngineCoreRequest(
64 mm_features: list[MultiModalFeatureSpec] | None
65 sampling_params: SamplingParams | None
66 pooling_params: PoolingParams | None
66 - eos_token_id: int | None
67 arrival_time: float
68 lora_request: LoRARequest | None
69 cache_salt: str | None
@@ -99,6 +99,17 @@ class EngineCoreRequest(
99 assert self.pooling_params is not None
100 return self.pooling_params
101
102 + @property
103 + @deprecated(
104 + "EngineCoreRequest.eos_token_id will be removed in v0.18. "
105 + "Please use EngineCoreRequest.sampling_params.eos_token_id instead."
106 + )
107 + def eos_token_id(self) -> int | None:
108 + if self.sampling_params is None:
109 + return None
110 +
111 + return self.sampling_params.eos_token_id
112 +
113
114 class EngineCoreEventType(enum.IntEnum):
115 """The type of engine core request event."""
vllm/v1/engine/input_processor.py
+1 -4
@@ -376,8 +376,6 @@ class InputProcessor:
376 processed_inputs=processed_inputs,
377 )
378
379 - eos_token_id = self.input_preprocessor.get_eos_token_id()
380 -
379 encoder_inputs, decoder_inputs = split_enc_dec_inputs(processed_inputs)
380 self._validate_model_inputs(encoder_inputs, decoder_inputs)
381
@@ -403,7 +401,7 @@ class InputProcessor:
401
402 sampling_params.update_from_generation_config(
403 self.generation_config_fields,
406 - None if self.tokenizer is None else self.tokenizer.eos_token_id,
404 + self.renderer.get_eos_token_id(),
405 )
406 if self.tokenizer is not None:
407 sampling_params.update_from_tokenizer(self.tokenizer)
@@ -446,7 +444,6 @@ class InputProcessor:
444 mm_features=mm_features,
445 sampling_params=sampling_params,
446 pooling_params=pooling_params,
449 - eos_token_id=eos_token_id,
447 arrival_time=arrival_time,
448 lora_request=lora_request,
449 cache_salt=decoder_inputs.get("cache_salt"),
vllm/v1/request.py
+12 -4
@@ -9,6 +9,7 @@ from dataclasses import dataclass
9 from typing import TYPE_CHECKING, Any
10
11 import torch
12 +from typing_extensions import deprecated
13
14 from vllm.multimodal.inputs import MultiModalFeatureSpec
15 from vllm.pooling_params import PoolingParams
@@ -62,7 +63,6 @@ class Request:
63 prompt_token_ids: list[int] | None,
64 sampling_params: SamplingParams | None,
65 pooling_params: PoolingParams | None,
65 - eos_token_id: int | None,
66 client_index: int = 0,
67 arrival_time: float | None = None,
68 prompt_embeds: torch.Tensor | None = None,
@@ -80,8 +80,6 @@ class Request:
80 self.priority = priority
81 self.sampling_params = sampling_params
82 self.pooling_params = pooling_params
83 - # Because of LoRA, the eos token id can be different for each request.
84 - self.eos_token_id = eos_token_id
83 self.lora_request = lora_request
84 self.structured_output_request = StructuredOutputRequest.from_sampling_params(
85 sampling_params
@@ -176,6 +174,17 @@ class Request:
174 # None entry in the queue means finished.
175 self.streaming_queue: deque[StreamingUpdate | None] | None = None
176
177 + @property
178 + @deprecated(
179 + "Request.eos_token_id will be removed in v0.18. "
180 + "Please use Request.sampling_params.eos_token_id instead."
181 + )
182 + def eos_token_id(self) -> int | None:
183 + if self.sampling_params is None:
184 + return None
185 +
186 + return self.sampling_params.eos_token_id
187 +
188 @classmethod
189 def from_engine_core_request(
190 cls,
@@ -190,7 +199,6 @@ class Request:
199 mm_features=request.mm_features,
200 sampling_params=request.sampling_params,
201 pooling_params=request.pooling_params,
193 - eos_token_id=request.eos_token_id,
202 arrival_time=request.arrival_time,
203 lora_request=request.lora_request,
204 cache_salt=request.cache_salt,
vllm/v1/structured_output/utils.py
+8 -26
@@ -185,14 +185,13 @@ re_llama_byte_token = re.compile(r"^<0x[0-9A-F]{2}>$")
185 re_replacement_seq = re.compile(r"^.{0,6}�+.{0,6}$")
186
187
188 -def _reduced_vocabulary(
189 - tokenizer: TokenizerLike, eos_token_id: int
190 -) -> dict[bytes, list[int]]:
188 +def _reduced_vocabulary(tokenizer: TokenizerLike) -> dict[bytes, list[int]]:
189 """Create a map from vocabulary tokens to lists of equivalent token ids.
190
191 Returns:
192 A Dict of token string -> equivalent token ids
193 """
194 + eos_token_id = tokenizer.eos_token_id
195
196 unicode_to_bytes = {
197 v: k for k, v in convert_slow_tokenizer.bytes_to_unicode().items()
@@ -260,30 +259,13 @@ def get_outlines_vocabulary(tokenizer: TokenizerLike) -> oc.Vocabulary:
259 if hasattr(tokenizer, "_outlines_vocabulary"):
260 return tokenizer._outlines_vocabulary # type: ignore
261
263 - try:
264 - if hasattr(tokenizer, "eos_token_id") and tokenizer.eos_token_id is not None:
265 - eos_token_id = tokenizer.eos_token_id
266 - else:
267 - raise ValueError(
268 - "Error during structured outputs setup for outlines: Tokenizer "
269 - f"({type(tokenizer)}) has no `eos_token_id` property, but "
270 - "`eos_token_id` is required for structured outputs to work properly."
271 - )
272 -
273 - reduced_vocab = _reduced_vocabulary(
274 - tokenizer,
275 - eos_token_id, # type: ignore
276 - )
277 - vocabulary = OutlinesVocabulary(oc.Vocabulary(eos_token_id, reduced_vocab))
278 - tokenizer._outlines_vocabulary = vocabulary # type: ignore
262 + reduced_vocab = _reduced_vocabulary(tokenizer)
263 + vocabulary = OutlinesVocabulary(
264 + oc.Vocabulary(tokenizer.eos_token_id, reduced_vocab)
265 + )
266 + tokenizer._outlines_vocabulary = vocabulary # type: ignore
267
280 - return vocabulary
281 - except AttributeError as e:
282 - raise ValueError(
283 - "Cannot get the vocabulary of the tokenizer "
284 - f"({type(tokenizer)}). The tokenizer should have a "
285 - "get_vocab method."
286 - ) from e
268 + return vocabulary
269
270
271 def grammar_is_likely_lark(grammar_str: str) -> bool: