deepseek_v3_reasoning_parser.py 3.58 KB
Newer Older
1
2
3
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project

4
from collections.abc import Iterable, Sequence
5
from typing import TYPE_CHECKING
6
7
8
9

from transformers import PreTrainedTokenizerBase

from vllm.logger import init_logger
10
11
from vllm.reasoning import ReasoningParser
from vllm.reasoning.deepseek_r1_reasoning_parser import DeepSeekR1ReasoningParser
12
13
14

from .identity_reasoning_parser import IdentityReasoningParser

15
16
17
18
19
if TYPE_CHECKING:
    from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
    from vllm.entrypoints.openai.engine.protocol import DeltaMessage
    from vllm.entrypoints.openai.responses.protocol import ResponsesRequest

20
21
22
23
24
25
26
27
28
29
30
31
logger = init_logger(__name__)


class DeepSeekV3ReasoningParser(ReasoningParser):
    """
    V3 parser that delegates to either DeepSeekR1ReasoningParser or
    IdentityReasoningParser based on `thinking` and `separate_reasoning`.
    """

    def __init__(self, tokenizer: PreTrainedTokenizerBase, *args, **kwargs):
        super().__init__(tokenizer, *args, **kwargs)

32
33
34
        chat_kwargs = kwargs.get("chat_template_kwargs", {}) or {}
        thinking = bool(chat_kwargs.get("thinking", False))
        enable_thinking = bool(chat_kwargs.get("enable_thinking", False))
35
        thinking = thinking or enable_thinking
36

37
        self._parser: ReasoningParser
38
39
40
41
42
        if thinking:
            self._parser = DeepSeekR1ReasoningParser(tokenizer, *args, **kwargs)
        else:
            self._parser = IdentityReasoningParser(tokenizer, *args, **kwargs)

43
44
45
46
47
48
49
50
    @property
    def reasoning_start_str(self) -> str | None:
        return self._parser.reasoning_start_str

    @property
    def reasoning_end_str(self) -> str | None:
        return self._parser.reasoning_end_str

51
52
53
    def is_reasoning_end(self, input_ids: Sequence[int]) -> bool:
        return self._parser.is_reasoning_end(input_ids)

54
    def is_reasoning_end_streaming(
55
        self, input_ids: Sequence[int], delta_ids: Iterable[int]
56
57
58
    ) -> bool:
        return self._parser.is_reasoning_end_streaming(input_ids, delta_ids)

59
60
61
    def extract_content_ids(self, input_ids: list[int]) -> list[int]:
        return self._parser.extract_content_ids(input_ids)

62
    def extract_reasoning(
63
        self, model_output: str, request: "ChatCompletionRequest | ResponsesRequest"
64
    ) -> tuple[str | None, str | None]:
65
        return self._parser.extract_reasoning(model_output, request)
66

67
    def extract_reasoning_streaming(
68
69
70
71
72
73
74
        self,
        previous_text: str,
        current_text: str,
        delta_text: str,
        previous_token_ids: Sequence[int],
        current_token_ids: Sequence[int],
        delta_token_ids: Sequence[int],
75
    ) -> "DeltaMessage | None":
76
        return self._parser.extract_reasoning_streaming(
77
78
79
80
81
82
83
            previous_text,
            current_text,
            delta_text,
            previous_token_ids,
            current_token_ids,
            delta_token_ids,
        )
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99


class DeepSeekV3ReasoningWithThinkingParser(DeepSeekV3ReasoningParser):
    """
    DeepSeekV3ReasoningParser that defaults to thinking mode.
    """

    def __init__(self, tokenizer: PreTrainedTokenizerBase, *args, **kwargs):
        chat_kwargs = kwargs.get("chat_template_kwargs", {}) or {}
        thinking = chat_kwargs.get("thinking", None)
        enable_thinking = chat_kwargs.get("enable_thinking", None)
        if thinking is None and enable_thinking is None:
            chat_kwargs["thinking"] = True
            chat_kwargs["enable_thinking"] = True
            kwargs["chat_template_kwargs"] = chat_kwargs
        super().__init__(tokenizer, *args, **kwargs)