@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 7f1f36bf91
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 6, 20:30 UTC

[CI] Fix mypy for vllm/reasoning (#35742)

Signed-off-by: Martin Hickey <martin.hickey@ie.ibm.com> Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>

Martin Hickey committed Mar 12, 2026 at 12:21 UTC 7f1f36bf91860aed64aea58e61b23c01cf85d551
21 files changed +143 -158
tests/entrypoints/openai/test_gptoss_structural_tags_integration.py
+1 -1
@@ -23,7 +23,7 @@ class TestGptOssStructuralTagsIntegration:
23 """Create a mock tokenizer."""
24 tokenizer = Mock()
25 tokenizer.encode = Mock(return_value=[1, 2, 3, 4, 5])
26 - tokenizer.vocab = {"<|end|>": 6}
26 + tokenizer.get_vocab = Mock(return_value={"<|end|>": 6})
27 return tokenizer
28
29 @pytest.fixture
tests/v1/structured_output/test_gptoss_structural_tags.py
+1 -1
@@ -25,7 +25,7 @@ class TestGptOssReasoningParser:
25 """Create a mock tokenizer for testing."""
26 tokenizer = Mock()
27 tokenizer.encode = Mock(return_value=[1, 2, 3, 4, 5])
28 - tokenizer.vocab = {"<|end|>": 6}
28 + tokenizer.get_vocab = Mock(return_value={"<|end|>": 6})
29 return tokenizer
30
31 @pytest.fixture
tools/pre_commit/mypy.py
-1
@@ -41,7 +41,6 @@ EXCLUDE = [
41 # TODO: Remove these entries after fixing mypy errors.
42 "vllm/benchmarks",
43 "vllm/config",
44 - "vllm/reasoning",
44 ]
45
46
vllm/reasoning/abs_reasoning_parsers.py
+9 -24
@@ -6,7 +6,7 @@ import os
6 from abc import abstractmethod
7 from collections.abc import Callable, Iterable, Sequence
8 from functools import cached_property
9 -from typing import TYPE_CHECKING, Any
9 +from typing import TYPE_CHECKING
10
11 from vllm.entrypoints.mcp.tool_server import ToolServer
12 from vllm.logger import init_logger
@@ -14,21 +14,10 @@ from vllm.utils.collection_utils import is_list_of
14 from vllm.utils.import_utils import import_from_path
15
16 if TYPE_CHECKING:
17 - from vllm.entrypoints.openai.chat_completion.protocol import (
18 - ChatCompletionRequest,
19 - )
20 - from vllm.entrypoints.openai.engine.protocol import (
21 - DeltaMessage,
22 - )
23 - from vllm.entrypoints.openai.responses.protocol import (
24 - ResponsesRequest,
25 - )
17 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
18 + from vllm.entrypoints.openai.engine.protocol import DeltaMessage
19 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
20 from vllm.tokenizers import TokenizerLike
27 -else:
28 - ChatCompletionRequest = Any
29 - DeltaMessage = Any
30 - ResponsesRequest = Any
31 - TokenizerLike = Any
21
22 logger = init_logger(__name__)
23
@@ -41,7 +30,7 @@ class ReasoningParser:
30 It is used to extract reasoning content from the model output.
31 """
32
44 - def __init__(self, tokenizer: TokenizerLike, *args, **kwargs):
33 + def __init__(self, tokenizer: "TokenizerLike", *args, **kwargs):
34 self.model_tokenizer = tokenizer
35
36 @cached_property
@@ -127,7 +116,7 @@ class ReasoningParser:
116 def extract_reasoning(
117 self,
118 model_output: str,
130 - request: ChatCompletionRequest | ResponsesRequest,
119 + request: "ChatCompletionRequest | ResponsesRequest",
120 ) -> tuple[str | None, str | None]:
121 """
122 Extract reasoning content from a complete model-generated string.
@@ -136,14 +125,10 @@ class ReasoningParser:
125 available before sending to the client.
126
127 Parameters:
139 - model_output: str
140 - The model-generated string to extract reasoning content from.
141 -
142 - request: ChatCompletionRequest
143 - The request object that was used to generate the model_output.
128 + model_output: The model-generated string to extract reasoning content from.
129 + request: The request object that was used to generate the model_output.
130
131 Returns:
146 - tuple[Optional[str], Optional[str]]
132 A tuple containing the reasoning content and the content.
133 """
134
@@ -156,7 +141,7 @@ class ReasoningParser:
141 previous_token_ids: Sequence[int],
142 current_token_ids: Sequence[int],
143 delta_token_ids: Sequence[int],
159 - ) -> DeltaMessage | None:
144 + ) -> "DeltaMessage | None":
145 """
146 Instance method that should be implemented for extracting reasoning
147 from an incomplete response; for use when handling reasoning calls and
vllm/reasoning/basic_parsers.py
+9 -14
@@ -4,22 +4,15 @@
4 from abc import abstractmethod
5 from collections.abc import Iterable, Sequence
6 from itertools import islice
7 -from typing import TYPE_CHECKING, Any
7 +from typing import TYPE_CHECKING
8
9 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 from vllm.reasoning.abs_reasoning_parsers import ReasoningParser
11 from vllm.tokenizers import TokenizerLike
12
13 if TYPE_CHECKING:
14 - from vllm.entrypoints.openai.chat_completion.protocol import (
15 - ChatCompletionRequest,
16 - )
17 - from vllm.entrypoints.openai.responses.protocol import (
18 - ResponsesRequest,
19 - )
20 -else:
21 - ChatCompletionRequest = Any
22 - ResponsesRequest = Any
14 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
15 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
16
17
18 class BaseThinkingReasoningParser(ReasoningParser):
@@ -58,13 +51,15 @@ class BaseThinkingReasoningParser(ReasoningParser):
51 if not self.start_token or not self.end_token:
52 raise ValueError("start_token and end_token must be defined in subclasses")
53
61 - self.start_token_id = self.vocab.get(self.start_token)
62 - self.end_token_id = self.vocab.get(self.end_token)
63 - if self.start_token_id is None or self.end_token_id is None:
54 + start_token_id = self.vocab.get(self.start_token)
55 + end_token_id = self.vocab.get(self.end_token)
56 + if start_token_id is None or end_token_id is None:
57 raise RuntimeError(
58 f"{self.__class__.__name__} reasoning parser could not locate "
59 "think start/end tokens in the tokenizer!"
60 )
61 + self.start_token_id: int = start_token_id
62 + self.end_token_id: int = end_token_id
63
64 def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
65 start_token_id = self.start_token_id
@@ -152,7 +147,7 @@ class BaseThinkingReasoningParser(ReasoningParser):
147 return DeltaMessage(content=delta_text)
148
149 def extract_reasoning(
155 - self, model_output: str, request: ChatCompletionRequest | ResponsesRequest
150 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
151 ) -> tuple[str | None, str | None]:
152 """
153 Extract reasoning content from the model output.
vllm/reasoning/deepseek_v3_reasoning_parser.py
+9 -6
@@ -2,19 +2,21 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable, Sequence
5 +from typing import TYPE_CHECKING
6
7 from transformers import PreTrainedTokenizerBase
8
8 -from vllm.entrypoints.openai.chat_completion.protocol import (
9 - ChatCompletionRequest,
10 -)
11 -from vllm.entrypoints.openai.engine.protocol import DeltaMessage
9 from vllm.logger import init_logger
10 from vllm.reasoning import ReasoningParser
11 from vllm.reasoning.deepseek_r1_reasoning_parser import DeepSeekR1ReasoningParser
12
13 from .identity_reasoning_parser import IdentityReasoningParser
14
15 +if TYPE_CHECKING:
16 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
17 + from vllm.entrypoints.openai.engine.protocol import DeltaMessage
18 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
19 +
20 logger = init_logger(__name__)
21
22
@@ -32,6 +34,7 @@ class DeepSeekV3ReasoningParser(ReasoningParser):
34 enable_thinking = bool(chat_kwargs.get("enable_thinking", False))
35 thinking = thinking or enable_thinking
36
37 + self._parser: ReasoningParser
38 if thinking:
39 self._parser = DeepSeekR1ReasoningParser(tokenizer, *args, **kwargs)
40 else:
@@ -49,7 +52,7 @@ class DeepSeekV3ReasoningParser(ReasoningParser):
52 return self._parser.extract_content_ids(input_ids)
53
54 def extract_reasoning(
52 - self, model_output: str, request: ChatCompletionRequest
55 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
56 ) -> tuple[str | None, str | None]:
57 return self._parser.extract_reasoning(model_output, request)
58
@@ -61,7 +64,7 @@ class DeepSeekV3ReasoningParser(ReasoningParser):
64 previous_token_ids: Sequence[int],
65 current_token_ids: Sequence[int],
66 delta_token_ids: Sequence[int],
64 - ) -> DeltaMessage | None:
67 + ) -> "DeltaMessage | None":
68 return self._parser.extract_reasoning_streaming(
69 previous_text,
70 current_text,
vllm/reasoning/ernie45_reasoning_parser.py
+6 -12
@@ -2,16 +2,18 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
7 from transformers import PreTrainedTokenizerBase
8
8 -from vllm.entrypoints.openai.chat_completion.protocol import (
9 - ChatCompletionRequest,
10 -)
9 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 from vllm.logger import init_logger
11 from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
12
13 +if TYPE_CHECKING:
14 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
15 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
16 +
17 logger = init_logger(__name__)
18
19
@@ -46,20 +48,12 @@ class Ernie45ReasoningParser(BaseThinkingReasoningParser):
48 "constructor during construction."
49 )
50
49 - self.start_token_id = self.vocab.get(self.start_token)
50 - self.end_token_id = self.vocab.get(self.end_token)
51 self.response_start_token_id = self.vocab.get(self.response_start_token)
52 self.response_end_token_id = self.vocab.get(self.response_end_token)
53 self.newline_token_id = self.vocab.get(self.newline_token)
54
55 self.parser_token_ids = [self.end_token_id, self.response_end_token_id]
56
57 - if self.start_token_id is None or self.end_token_id is None:
58 - raise RuntimeError(
59 - "Ernie45 reasoning parser could not locate think start/end "
60 - "tokens in the tokenizer!"
61 - )
62 -
57 def extract_reasoning_streaming(
58 self,
59 previous_text: str,
@@ -144,7 +138,7 @@ class Ernie45ReasoningParser(BaseThinkingReasoningParser):
138 return DeltaMessage(reasoning=delta_text)
139
140 def extract_reasoning(
147 - self, model_output: str, request: ChatCompletionRequest
141 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
142 ) -> tuple[str | None, str | None]:
143 """
144 Extract reasoning content from the model output.
vllm/reasoning/gptoss_reasoning_parser.py
+7 -5
@@ -2,18 +2,20 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import json
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
7 from transformers import PreTrainedTokenizerBase
8
9 from vllm.entrypoints.mcp.tool_server import ToolServer
9 -from vllm.entrypoints.openai.chat_completion.protocol import (
10 - ChatCompletionRequest,
11 -)
10 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
11 from vllm.entrypoints.openai.parser.harmony_utils import parse_chat_output
12 from vllm.logger import init_logger
13 from vllm.reasoning import ReasoningParser
14
15 +if TYPE_CHECKING:
16 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
17 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
18 +
19 logger = init_logger(__name__)
20
21 no_func_reaonsing_tag = {
@@ -78,7 +80,7 @@ class GptOssReasoningParser(ReasoningParser):
80 self.reasoning_end_token_ids_suffix = self.model_tokenizer.encode("<|message|>")
81 # We also need to check for the <|end|> token to avoid false positives from
82 # previous messages in multi-turn conversations.
81 - self.eom_token_id = self.model_tokenizer.vocab["<|end|>"]
83 + self.eom_token_id = self.vocab["<|end|>"]
84 self.reasoning_max_num_between_tokens = 20
85
86 def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
@@ -148,7 +150,7 @@ class GptOssReasoningParser(ReasoningParser):
150 def extract_reasoning(
151 self,
152 model_output: str,
151 - request: ChatCompletionRequest,
153 + request: "ChatCompletionRequest | ResponsesRequest",
154 ) -> tuple[str | None, str | None]:
155 raise NotImplementedError(
156 "gpt-oss has a special branch for parsing reasoning in non-streaming mode. This method shouldn't be used." # noqa: E501
vllm/reasoning/granite_reasoning_parser.py
+6 -4
@@ -2,17 +2,19 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
7 import regex as re
8 from transformers import PreTrainedTokenizerBase
9
9 -from vllm.entrypoints.openai.chat_completion.protocol import (
10 - ChatCompletionRequest,
11 -)
10 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
11 from vllm.logger import init_logger
12 from vllm.reasoning import ReasoningParser
13
14 +if TYPE_CHECKING:
15 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
16 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
17 +
18 logger = init_logger(__name__)
19
20
@@ -53,7 +55,7 @@ class GraniteReasoningParser(ReasoningParser):
55 )
56
57 def extract_reasoning(
56 - self, model_output: str, request: ChatCompletionRequest
58 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
59 ) -> tuple[str | None, str | None]:
60 """Extract the reasoning content & content sections, respectively.
61 If the sequence doesn't match what we expect, i.e., the model generates
vllm/reasoning/hunyuan_a13b_reasoning_parser.py
+9 -7
@@ -2,17 +2,19 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
7 import regex as re
8 from transformers import PreTrainedTokenizerBase
9
9 -from vllm.entrypoints.openai.chat_completion.protocol import (
10 - ChatCompletionRequest,
11 -)
10 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
11 from vllm.logger import init_logger
12 from vllm.reasoning import ReasoningParser
13
14 +if TYPE_CHECKING:
15 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
16 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
17 +
18 logger = init_logger(__name__)
19
20
@@ -65,8 +67,8 @@ class HunyuanA13BReasoningParser(ReasoningParser):
67 self.fast_think_ids = [14023, 771, 1363, 524, 27963, 397, 27, 9399, 397]
68
69 # when state change, send out all the buffered text in last state
68 - self.buffered_text = []
69 - self.buffered_ids = []
70 + self.buffered_text: list[str] = []
71 + self.buffered_ids: list[int] = []
72
73 self.current_state = "reasoning"
74 self.all_states = ["reasoning", "response"]
@@ -76,7 +78,7 @@ class HunyuanA13BReasoningParser(ReasoningParser):
78 # this sequence only for the think start, it has two way to start.
79 self.expected_sequence_side = self.think_start_ids_fast
80 self.sequence_index = 0
79 - self.token_buffer = []
81 + self.token_buffer: list[int] = []
82 self.text_buffer = ""
83
84 def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
@@ -90,7 +92,7 @@ class HunyuanA13BReasoningParser(ReasoningParser):
92 return []
93
94 def extract_reasoning(
93 - self, model_output: str, request: ChatCompletionRequest
95 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
96 ) -> tuple[str | None, str | None]:
97 """Extract the reasoning content & content sections, respectively.
98 If the sequence doesn't match what we expect, i.e., the model generates
vllm/reasoning/identity_reasoning_parser.py
+6 -4
@@ -2,16 +2,18 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable, Sequence
5 +from typing import TYPE_CHECKING
6
7 from transformers import PreTrainedTokenizerBase
8
8 -from vllm.entrypoints.openai.chat_completion.protocol import (
9 - ChatCompletionRequest,
10 -)
9 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 from vllm.logger import init_logger
11 from vllm.reasoning import ReasoningParser
12
13 +if TYPE_CHECKING:
14 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
15 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
16 +
17 logger = init_logger(__name__)
18
19
@@ -59,7 +61,7 @@ class IdentityReasoningParser(ReasoningParser):
61 return None
62
63 def extract_reasoning(
62 - self, model_output: str, request: ChatCompletionRequest
64 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
65 ) -> tuple[str | None, str | None]:
66 # No reasoning separation: return None for reasoning,
67 # and full model_output as content
vllm/reasoning/kimi_k2_reasoning_parser.py
+19 -17
@@ -1,17 +1,19 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 -from collections.abc import Sequence
4 +from collections.abc import Iterable, Sequence
5 +from typing import TYPE_CHECKING
6
7 from transformers import PreTrainedTokenizerBase
8
8 -from vllm.entrypoints.openai.chat_completion.protocol import (
9 - ChatCompletionRequest,
10 -)
9 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 from vllm.reasoning.abs_reasoning_parsers import ReasoningParser
11 from vllm.reasoning.identity_reasoning_parser import IdentityReasoningParser
12
13 +if TYPE_CHECKING:
14 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
15 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
16 +
17
18 class KimiK2ReasoningParser(ReasoningParser):
19 """
@@ -39,6 +41,7 @@ class KimiK2ReasoningParser(ReasoningParser):
41 thinking = bool(chat_kwargs.get("thinking", True))
42
43 # If thinking is not enabled, use identity parser to fall through
44 + self._identity_parser: IdentityReasoningParser | None
45 if not thinking:
46 self._identity_parser = IdentityReasoningParser(tokenizer, *args, **kwargs)
47 else:
@@ -62,10 +65,6 @@ class KimiK2ReasoningParser(ReasoningParser):
65 "tokens in the tokenizer!"
66 )
67
65 - def _is_identity_mode(self) -> bool:
66 - """Check if parser is in identity mode (no reasoning extraction)."""
67 - return self._identity_parser is not None
68 -
68 def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
69 """
70 Check if the reasoning content ends in the input_ids.
@@ -74,7 +73,7 @@ class KimiK2ReasoningParser(ReasoningParser):
73 1. The end token (</think>)
74 2. The tool section start token (<|tool_calls_section_begin|>)
75 """
77 - if self._is_identity_mode():
76 + if self._identity_parser is not None:
77 return self._identity_parser.is_reasoning_end(input_ids)
78
79 start_token_id = self._start_token_id
@@ -95,29 +94,32 @@ class KimiK2ReasoningParser(ReasoningParser):
94 return False
95
96 def is_reasoning_end_streaming(
98 - self, input_ids: Sequence[int], delta_ids: Sequence[int]
97 + self, input_ids: Sequence[int], delta_ids: Iterable[int]
98 ) -> bool:
99 """
100 Check if the reasoning content ends in the input_ids on a decode step.
101 """
103 - if self._is_identity_mode():
102 + if self._identity_parser is not None:
103 return self._identity_parser.is_reasoning_end_streaming(
104 input_ids, delta_ids
105 )
106
107 + # Materialize iterable for membership checks
108 + delta_ids_set = set(delta_ids)
109 +
110 # Check for explicit end token or implicit tool section start in delta
109 - if self._end_token_id in delta_ids:
111 + if self._end_token_id in delta_ids_set:
112 return True
113 return (
114 self._tool_section_start_token_id is not None
113 - and self._tool_section_start_token_id in delta_ids
115 + and self._tool_section_start_token_id in delta_ids_set
116 )
117
118 def extract_content_ids(self, input_ids: list[int]) -> list[int]:
119 """
120 Extract content token ids from the input_ids.
121 """
120 - if self._is_identity_mode():
122 + if self._identity_parser is not None:
123 return self._identity_parser.extract_content_ids(input_ids)
124
125 if self._end_token_id in input_ids:
@@ -145,12 +147,12 @@ class KimiK2ReasoningParser(ReasoningParser):
147 return []
148
149 def extract_reasoning(
148 - self, model_output: str, request: ChatCompletionRequest
150 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
151 ) -> tuple[str | None, str | None]:
152 """
153 Extract reasoning content from the model output.
154 """
153 - if self._is_identity_mode():
155 + if self._identity_parser is not None:
156 return self._identity_parser.extract_reasoning(model_output, request)
157
158 # thinking does not require a think start token but consume it if present
@@ -189,7 +191,7 @@ class KimiK2ReasoningParser(ReasoningParser):
191 """
192 Extract reasoning content from a delta message during streaming.
193 """
192 - if self._is_identity_mode():
194 + if self._identity_parser is not None:
195 return self._identity_parser.extract_reasoning_streaming(
196 previous_text,
197 current_text,
vllm/reasoning/minimax_m2_reasoning_parser.py
+6 -7
@@ -2,21 +2,20 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
6 -from vllm.entrypoints.openai.chat_completion.protocol import (
7 - ChatCompletionRequest,
8 -)
7 from vllm.entrypoints.openai.engine.protocol import (
8 DeltaMessage,
9 )
12 -from vllm.entrypoints.openai.responses.protocol import (
13 - ResponsesRequest,
14 -)
10 from vllm.logger import init_logger
11 from vllm.reasoning.abs_reasoning_parsers import ReasoningParser
12 from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
13 from vllm.tokenizers import TokenizerLike
14
15 +if TYPE_CHECKING:
16 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
17 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
18 +
19 logger = init_logger(__name__)
20
21
@@ -114,6 +113,6 @@ class MiniMaxM2AppendThinkReasoningParser(ReasoningParser):
113 return DeltaMessage(content=delta_text)
114
115 def extract_reasoning(
117 - self, model_output: str, request: ChatCompletionRequest | ResponsesRequest
116 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
117 ) -> tuple[str | None, str | None]:
118 return None, "<think>" + model_output
vllm/reasoning/mistral_reasoning_parser.py
+6 -7
@@ -3,18 +3,17 @@
3
4 from collections.abc import Sequence
5 from functools import cached_property
6 +from typing import TYPE_CHECKING
7
7 -from vllm.entrypoints.openai.chat_completion.protocol import (
8 - ChatCompletionRequest,
9 -)
10 -from vllm.entrypoints.openai.responses.protocol import (
11 - ResponsesRequest,
12 -)
8 from vllm.logger import init_logger
9 from vllm.reasoning import ReasoningParser
10 from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
11 from vllm.tokenizers.mistral import MistralTokenizer
12
13 +if TYPE_CHECKING:
14 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
15 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
16 +
17 logger = init_logger(__name__)
18
19
@@ -113,7 +112,7 @@ class MistralReasoningParser(BaseThinkingReasoningParser):
112 return input_ids[:eot_token_index] + input_ids[eot_token_index + 1 :]
113
114 def extract_reasoning(
116 - self, model_output: str, request: ChatCompletionRequest | ResponsesRequest
115 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
116 ) -> tuple[str | None, str | None]:
117 """
118 Extract reasoning content from the model output.
vllm/reasoning/olmo3_reasoning_parser.py
+9 -14
@@ -8,20 +8,15 @@ from typing import TYPE_CHECKING
8
9 import regex as re
10
11 -if TYPE_CHECKING:
12 - from vllm.tokenizers import TokenizerLike
13 -from vllm.entrypoints.openai.chat_completion.protocol import (
14 - ChatCompletionRequest,
15 -)
16 -from vllm.entrypoints.openai.engine.protocol import (
17 - DeltaMessage,
18 -)
19 -from vllm.entrypoints.openai.responses.protocol import (
20 - ResponsesRequest,
21 -)
11 +from vllm.entrypoints.openai.engine.protocol import DeltaMessage
12 from vllm.logger import init_logger
13 from vllm.reasoning import ReasoningParser
14
15 +if TYPE_CHECKING:
16 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
17 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
18 + from vllm.tokenizers import TokenizerLike
19 +
20 logger = init_logger(__name__)
21
22
@@ -256,15 +251,15 @@ class Olmo3ReasoningParser(ReasoningParser):
251 def extract_reasoning(
252 self,
253 model_output: str,
259 - request: ChatCompletionRequest | ResponsesRequest,
254 + request: "ChatCompletionRequest | ResponsesRequest",
255 ) -> tuple[str | None, str | None]:
256 """Extract the reasoning content & content sections, respectively.
257 If the sequence doesn't match what we expect, i.e., the model generates
258 something else, all content is considered non-reasoning content.
259
260 Args:
266 - model_output (str): Output of the model to be parsed.
267 - request (ChatCompletionRequest | ResponsesRequest): Request being
261 + model_output: Output of the model to be parsed.
262 + request: Request being
263 processed.
264
265 Returns:
vllm/reasoning/qwen3_reasoning_parser.py
+8 -9
@@ -2,16 +2,15 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Sequence
5 +from typing import TYPE_CHECKING
6
6 -from vllm.entrypoints.openai.chat_completion.protocol import (
7 - ChatCompletionRequest,
8 -)
7 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 -from vllm.entrypoints.openai.responses.protocol import (
11 - ResponsesRequest,
12 -)
8 from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
14 -from vllm.tokenizers import TokenizerLike
9 +
10 +if TYPE_CHECKING:
11 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
12 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
13 + from vllm.tokenizers import TokenizerLike
14
15
16 class Qwen3ReasoningParser(BaseThinkingReasoningParser):
@@ -34,7 +33,7 @@ class Qwen3ReasoningParser(BaseThinkingReasoningParser):
33 it is stripped before extraction (non-streaming) or skipped (streaming).
34 """
35
37 - def __init__(self, tokenizer: TokenizerLike, *args, **kwargs):
36 + def __init__(self, tokenizer: "TokenizerLike", *args, **kwargs):
37 super().__init__(tokenizer, *args, **kwargs)
38
39 chat_kwargs = kwargs.get("chat_template_kwargs", {}) or {}
@@ -53,7 +52,7 @@ class Qwen3ReasoningParser(BaseThinkingReasoningParser):
52 return "</think>"
53
54 def extract_reasoning(
56 - self, model_output: str, request: ChatCompletionRequest | ResponsesRequest
55 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
56 ) -> tuple[str | None, str | None]:
57 """
58 Extract reasoning content from the model output.
vllm/reasoning/step3_reasoning_parser.py
+10 -10
@@ -3,17 +3,19 @@
3
4 from collections.abc import Iterable, Sequence
5 from itertools import islice
6 +from typing import TYPE_CHECKING
7
8 import regex as re
9 from transformers import PreTrainedTokenizerBase
10
10 -from vllm.entrypoints.openai.chat_completion.protocol import (
11 - ChatCompletionRequest,
12 -)
11 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
12 from vllm.logger import init_logger
13 from vllm.reasoning import ReasoningParser
14
15 +if TYPE_CHECKING:
16 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
17 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
18 +
19 logger = init_logger(__name__)
20
21
@@ -37,12 +39,13 @@ class Step3ReasoningParser(ReasoningParser):
39 "constructor during construction."
40 )
41
40 - self.think_end_token_id = self.vocab.get(self.think_end_token)
41 - if self.think_end_token_id is None:
42 + think_end_token_id = self.vocab.get(self.think_end_token)
43 + if think_end_token_id is None:
44 raise RuntimeError(
45 "Step3 reasoning parser could not locate think end "
46 "token in the tokenizer!"
47 )
48 + self.think_end_token_id: int = think_end_token_id
49
50 def extract_reasoning_streaming(
51 self,
@@ -82,7 +85,7 @@ class Step3ReasoningParser(ReasoningParser):
85 return DeltaMessage(reasoning=delta_text)
86
87 def extract_reasoning(
85 - self, model_output: str, request: ChatCompletionRequest
88 + self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
89 ) -> tuple[str | None, str | None]:
90 # Check if the model output contains the </think> token
91 if self.think_end_token not in model_output:
@@ -94,10 +97,7 @@ class Step3ReasoningParser(ReasoningParser):
97 reasoning = model_output[:end_index]
98
99 # Content after </think> token
97 - content = model_output[end_index + len(self.think_end_token) :]
98 -
99 - if len(content) == 0:
100 - content = None
100 + content = model_output[end_index + len(self.think_end_token) :] or None
101
102 return reasoning, content
103
vllm/reasoning/step3p5_reasoning_parser.py
+7 -8
@@ -2,17 +2,16 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable, Sequence
5 +from typing import TYPE_CHECKING
6
6 -from vllm.entrypoints.openai.chat_completion.protocol import (
7 - ChatCompletionRequest,
8 -)
7 from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10 -from vllm.entrypoints.openai.responses.protocol import (
11 - ResponsesRequest,
12 -)
8 from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
9 from vllm.tokenizers import TokenizerLike
10
11 +if TYPE_CHECKING:
12 + from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
13 + from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
14 +
15
16 class Step3p5ReasoningParser(BaseThinkingReasoningParser):
17 """
@@ -50,7 +49,7 @@ class Step3p5ReasoningParser(BaseThinkingReasoningParser):
49 self, input_ids: Sequence[int], delta_ids: Iterable[int]
50 ) -> bool:
51 # Only examine newly generated tokens; they may contain multiple ids.
53 - return self._is_reasoning_end_from_ids(delta_ids)
52 + return self._is_reasoning_end_from_ids(tuple(delta_ids))
53
54 def _is_reasoning_end_from_ids(self, input_ids: Sequence[int]) -> bool:
55 # Scan backwards to find the last special token, <think> or </think>.
@@ -96,7 +95,7 @@ class Step3p5ReasoningParser(BaseThinkingReasoningParser):
95 def extract_reasoning(
96 self,
97 model_output: str,
99 - request: ChatCompletionRequest | ResponsesRequest,
98 + request: "ChatCompletionRequest | ResponsesRequest",
99 ) -> tuple[str | None, str | None]:
100 reasoning, content = super().extract_reasoning(model_output, request)
101 if reasoning is not None:
vllm/tokenizers/grok2.py
+5 -3
@@ -4,7 +4,7 @@
4
5 import functools
6 import json
7 -from collections.abc import Collection, Set
7 +from collections.abc import Collection, Sequence, Set
8 from pathlib import Path
9 from typing import Any, Literal, overload
10
@@ -348,7 +348,9 @@ class Grok2Tokenizer(TokenizerLike):
348 tokens = self._maybe_truncate(tokens, max_length)
349 return tokens
350
351 - def decode(self, ids: list[int] | int, skip_special_tokens: bool = False) -> str:
351 + def decode(
352 + self, ids: Sequence[int] | int, skip_special_tokens: bool = False
353 + ) -> str:
354 if isinstance(ids, int):
355 ids = [ids]
356 if skip_special_tokens:
@@ -371,7 +373,7 @@ class Grok2Tokenizer(TokenizerLike):
373 return [self._token_to_id.get(token, self._unk_token_id) for token in tokens]
374
375 def convert_ids_to_tokens(
374 - self, ids: list[int], skip_special_tokens: bool = False
376 + self, ids: Sequence[int], skip_special_tokens: bool = False
377 ) -> list[str]:
378 tokens = []
379 for token_id in ids:
vllm/tokenizers/mistral.py
+5 -2
@@ -1,5 +1,6 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +from collections.abc import Sequence
4 from pathlib import Path
5 from typing import TYPE_CHECKING, Any, cast, overload
6
@@ -434,7 +435,9 @@ class MistralTokenizer(TokenizerLike):
435 return_dict=False,
436 )
437
437 - def decode(self, ids: list[int] | int, skip_special_tokens: bool = False) -> str:
438 + def decode(
439 + self, ids: Sequence[int] | int, skip_special_tokens: bool = False
440 + ) -> str:
441 # TODO(juliendenize): once https://github.com/huggingface/transformers/pull/41962
442 # is in, directly call self.transformers_tokenizer.decode(...).
443 if isinstance(ids, int):
@@ -512,7 +515,7 @@ class MistralTokenizer(TokenizerLike):
515
516 def convert_ids_to_tokens(
517 self,
515 - ids: list[int],
518 + ids: Sequence[int],
519 skip_special_tokens: bool = False,
520 ) -> list[str]:
521 if not skip_special_tokens:
vllm/tokenizers/protocol.py
+5 -2
@@ -1,5 +1,6 @@
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +from collections.abc import Sequence
4 from pathlib import Path
5 from typing import TYPE_CHECKING, Any, Protocol, overload
6
@@ -116,12 +117,14 @@ class TokenizerLike(Protocol):
117 def convert_tokens_to_string(self, tokens: list[str]) -> str:
118 raise NotImplementedError
119
119 - def decode(self, ids: list[int] | int, skip_special_tokens: bool = False) -> str:
120 + def decode(
121 + self, ids: Sequence[int] | int, skip_special_tokens: bool = False
122 + ) -> str:
123 raise NotImplementedError
124
125 def convert_ids_to_tokens(
126 self,
124 - ids: list[int],
127 + ids: Sequence[int],
128 skip_special_tokens: bool = False,
129 ) -> list[str]:
130 raise NotImplementedError