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