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