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 9, 11:15 UTC
[Misc] Provide a DeepSeek ReasoningParser with thinking enabled by default (#33221)
Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
Chauncey committed
Jan 28, 2026 at 21:16 UTC
8e5e40daf40beed71dc0b1c0386bfc614ea78888
6 files changed
+25
-192
tests/reasoning/test_holo2_reasoning_parser.py
+3
-1
@@ -6,7 +6,9 @@ from transformers import AutoTokenizer
6
from tests.reasoning.utils import run_reasoning_extraction
7
from vllm.reasoning import ReasoningParser, ReasoningParserManager
8
from vllm.reasoning.deepseek_r1_reasoning_parser import DeepSeekR1ReasoningParser
9
-from vllm.reasoning.holo2_reasoning_parser import Holo2ReasoningParser
9
+from vllm.reasoning.deepseek_v3_reasoning_parser import (
10
+ DeepSeekV3ReasoningWithThinkingParser as Holo2ReasoningParser,
11
+)
12
from vllm.reasoning.identity_reasoning_parser import IdentityReasoningParser
13
14
REASONING_MODEL_NAME = "HCompany/Holo2-4B"
vllm/reasoning/__init__.py
+6
-6
@@ -33,8 +33,8 @@ _REASONING_PARSERS_TO_REGISTER = {
33
"Ernie45ReasoningParser",
34
),
35
"glm45": (
36
- "glm4_moe_reasoning_parser",
37
- "Glm4MoeModelReasoningParser",
36
+ "deepseek_v3_reasoning_parser",
37
+ "DeepSeekV3ReasoningWithThinkingParser",
38
),
39
"openai_gptoss": (
40
"gptoss_reasoning_parser",
@@ -45,16 +45,16 @@ _REASONING_PARSERS_TO_REGISTER = {
45
"GraniteReasoningParser",
46
),
47
"holo2": (
48
- "holo2_reasoning_parser",
49
- "Holo2ReasoningParser",
48
+ "deepseek_v3_reasoning_parser",
49
+ "DeepSeekV3ReasoningWithThinkingParser",
50
),
51
"hunyuan_a13b": (
52
"hunyuan_a13b_reasoning_parser",
53
"HunyuanA13BReasoningParser",
54
),
55
"kimi_k2": (
56
- "kimi_k2_reasoning_parser",
57
- "KimiK2ReasoningParser",
56
+ "deepseek_v3_reasoning_parser",
57
+ "DeepSeekV3ReasoningWithThinkingParser",
58
),
59
"minimax_m2": (
60
"minimax_m2_reasoning_parser",
vllm/reasoning/deepseek_v3_reasoning_parser.py
+16
@@ -70,3 +70,19 @@ class DeepSeekV3ReasoningParser(ReasoningParser):
70
current_token_ids,
71
delta_token_ids,
72
)
73
+
74
+
75
+class DeepSeekV3ReasoningWithThinkingParser(DeepSeekV3ReasoningParser):
76
+ """
77
+ DeepSeekV3ReasoningParser that defaults to thinking mode.
78
+ """
79
+
80
+ def __init__(self, tokenizer: PreTrainedTokenizerBase, *args, **kwargs):
81
+ chat_kwargs = kwargs.get("chat_template_kwargs", {}) or {}
82
+ thinking = chat_kwargs.get("thinking", None)
83
+ enable_thinking = chat_kwargs.get("enable_thinking", None)
84
+ if thinking is None and enable_thinking is None:
85
+ chat_kwargs["thinking"] = True
86
+ chat_kwargs["enable_thinking"] = True
87
+ kwargs["chat_template_kwargs"] = chat_kwargs
88
+ super().__init__(tokenizer, *args, **kwargs)
vllm/reasoning/glm4_moe_reasoning_parser.py
deleted
-13
@@ -1,13 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-from vllm.reasoning.holo2_reasoning_parser import Holo2ReasoningParser
5
-
6
-
7
-class Glm4MoeModelReasoningParser(Holo2ReasoningParser):
8
- """
9
- Reasoning parser for the Glm4MoeModel model,which inherits from
10
- `Holo2ReasoningParser`.
11
- """
12
-
13
- pass
vllm/reasoning/holo2_reasoning_parser.py
deleted
-92
@@ -1,92 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-from collections.abc import Sequence
5
-
6
-from vllm.entrypoints.openai.chat_completion.protocol import (
7
- ChatCompletionRequest,
8
-)
9
-from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10
-from vllm.logger import init_logger
11
-from vllm.reasoning import (
12
- ReasoningParser,
13
-)
14
-from vllm.reasoning.deepseek_r1_reasoning_parser import DeepSeekR1ReasoningParser
15
-from vllm.reasoning.identity_reasoning_parser import IdentityReasoningParser
16
-from vllm.tokenizers import TokenizerLike
17
-
18
-logger = init_logger(__name__)
19
-
20
-
21
-class Holo2ReasoningParser(ReasoningParser):
22
- """
23
- Reasoning parser for the Holo2 models which are based on Qwen3.
24
-
25
- The Holo2 model uses <think>...</think> tokens to denote reasoning text but <think>
26
- is part of the chat template. This parser extracts the reasoning content until
27
- </think> in the model's output.
28
-
29
- The model provides a switch to enable or disable reasoning
30
- output via the 'thinking=False' parameter.
31
-
32
- Chat template args:
33
- - thinking: Whether to enable reasoning output (default: True)
34
-
35
-
36
- Parsing rules on model output:
37
- - thinking == False
38
- -> Model output is treated as purely the content |content|
39
- - thinking == True
40
- -> Model output is |reasoning_content|</think>|content|
41
- """
42
-
43
- def __init__(self, tokenizer: TokenizerLike, *args, **kwargs):
44
- super().__init__(tokenizer, *args, **kwargs)
45
-
46
- chat_kwargs = kwargs.get("chat_template_kwargs", {}) or {}
47
- # Deepseek V3 and Holo2 are similar. However, Holo2 models think by default.
48
- # this parser without user specified chat template args is initiated once for
49
- # all requests in the structured output manager. So it is important that without
50
- # user specified chat template args, the default thinking is True.
51
-
52
- thinking = bool(chat_kwargs.get("thinking", True))
53
- enable_thinking = bool(chat_kwargs.get("enable_thinking", True))
54
- thinking = thinking and enable_thinking
55
- if thinking:
56
- self._parser = DeepSeekR1ReasoningParser(tokenizer, *args, **kwargs)
57
- else:
58
- self._parser = IdentityReasoningParser(tokenizer, *args, **kwargs)
59
-
60
- def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
61
- return self._parser.is_reasoning_end(input_ids)
62
-
63
- def is_reasoning_end_streaming(
64
- self, input_ids: Sequence[int], delta_ids: Sequence[int]
65
- ) -> bool:
66
- return self._parser.is_reasoning_end_streaming(input_ids, delta_ids)
67
-
68
- def extract_content_ids(self, input_ids: list[int]) -> list[int]:
69
- return self._parser.extract_content_ids(input_ids)
70
-
71
- def extract_reasoning(
72
- self, model_output: str, request: ChatCompletionRequest
73
- ) -> tuple[str | None, str | None]:
74
- return self._parser.extract_reasoning(model_output, request)
75
-
76
- def extract_reasoning_streaming(
77
- self,
78
- previous_text: str,
79
- current_text: str,
80
- delta_text: str,
81
- previous_token_ids: Sequence[int],
82
- current_token_ids: Sequence[int],
83
- delta_token_ids: Sequence[int],
84
- ) -> DeltaMessage | None:
85
- return self._parser.extract_reasoning_streaming(
86
- previous_text,
87
- current_text,
88
- delta_text,
89
- previous_token_ids,
90
- current_token_ids,
91
- delta_token_ids,
92
- )
vllm/reasoning/kimi_k2_reasoning_parser.py
deleted
-80
@@ -1,80 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-from collections.abc import Sequence
5
-from typing import TYPE_CHECKING, Any
6
-
7
-from transformers import PreTrainedTokenizerBase
8
-
9
-from vllm.entrypoints.openai.engine.protocol import DeltaMessage
10
-from vllm.logger import init_logger
11
-from vllm.reasoning import ReasoningParser
12
-from vllm.reasoning.deepseek_r1_reasoning_parser import DeepSeekR1ReasoningParser
13
-
14
-from .identity_reasoning_parser import IdentityReasoningParser
15
-
16
-if TYPE_CHECKING:
17
- from vllm.entrypoints.openai.chat_completion.protocol import (
18
- ChatCompletionRequest,
19
- )
20
-else:
21
- ChatCompletionRequest = Any
22
-
23
-
24
-logger = init_logger(__name__)
25
-
26
-
27
-class KimiK2ReasoningParser(ReasoningParser):
28
- """
29
- Kimi K2 parser that delegates to either DeepSeekR1ReasoningParser or
30
- IdentityReasoningParser based on `thinking` and `separate_reasoning`.
31
-
32
- Unlike DeepSeekV3ReasoningParser which defaults to NOT thinking,
33
- KimiK2ReasoningParser defaults to thinking mode (uses DeepSeekR1ReasoningParser).
34
- """
35
-
36
- def __init__(self, tokenizer: PreTrainedTokenizerBase, *args, **kwargs):
37
- super().__init__(tokenizer, *args, **kwargs)
38
-
39
- chat_kwargs = kwargs.pop("chat_template_kwargs", {}) or {}
40
- # Key difference: default to True instead of False
41
- thinking = bool(chat_kwargs.pop("thinking", True))
42
-
43
- if thinking:
44
- self._parser = DeepSeekR1ReasoningParser(tokenizer, *args, **kwargs)
45
- else:
46
- self._parser = IdentityReasoningParser(tokenizer, *args, **kwargs)
47
-
48
- def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
49
- return self._parser.is_reasoning_end(input_ids)
50
-
51
- def is_reasoning_end_streaming(
52
- self, input_ids: list[int], delta_ids: list[int]
53
- ) -> bool:
54
- return self._parser.is_reasoning_end_streaming(input_ids, delta_ids)
55
-
56
- def extract_content_ids(self, input_ids: list[int]) -> list[int]:
57
- return self._parser.extract_content_ids(input_ids)
58
-
59
- def extract_reasoning(
60
- self, model_output: str, request: "ChatCompletionRequest"
61
- ) -> tuple[str | None, str | None]:
62
- return self._parser.extract_reasoning(model_output, request)
63
-
64
- def extract_reasoning_streaming(
65
- self,
66
- previous_text: str,
67
- current_text: str,
68
- delta_text: str,
69
- previous_token_ids: Sequence[int],
70
- current_token_ids: Sequence[int],
71
- delta_token_ids: Sequence[int],
72
- ) -> DeltaMessage | None:
73
- return self._parser.extract_reasoning_streaming(
74
- previous_text,
75
- current_text,
76
- delta_text,
77
- previous_token_ids,
78
- current_token_ids,
79
- delta_token_ids,
80
- )