main
py 125 lines 5.05 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from dataclasses import field
5
6 from vllm.config.model import ModelConfig
7 from vllm.config.utils import config
8 from vllm.reasoning import ReasoningParserManager
9 from vllm.tokenizers import cached_tokenizer_from_config
10
11
12 @config
13 class ReasoningConfig:
14 """Configuration for reasoning models.
15
16 Set `reasoning_start_str` and `reasoning_end_str` to the strings used to
17 enter and forcibly terminate reasoning. The end string may include a
18 transition phrase before the parser's natural reasoning end marker. Token
19 IDs are derived automatically by `initialize_token_ids`.
20 """
21
22 reasoning_parser: str = ""
23 """The name of the ReasoningParser to use for this model."""
24 reasoning_start_str: str = ""
25 """String that indicates the start of reasoning."""
26 reasoning_end_str: str = ""
27 """String forced when the thinking budget is exhausted."""
28
29 _reasoning_start_token_ids: list[int] | None = field(
30 default=None, init=False, repr=False
31 )
32 """Private backing field for `reasoning_start_token_ids`. Set by
33 `initialize_token_ids`. Not intended to be configured directly."""
34 _reasoning_end_token_ids: list[int] | None = field(
35 default=None, init=False, repr=False
36 )
37 """Private backing field for forced reasoning end token IDs."""
38 _natural_reasoning_end_token_ids: list[int] | None = field(
39 default=None, init=False, repr=False
40 )
41 """Token IDs that naturally terminate reasoning, as defined by the parser."""
42
43 _enabled: bool = field(default=False, init=False, repr=False)
44 """Private field indicating whether reasoning token IDs have been initialized.
45 Set to True by `initialize_token_ids` once token IDs are initialized."""
46
47 @property
48 def enabled(self) -> bool:
49 """Returns True if reasoning is enabled (i.e. if token IDs have been
50 initialized), False otherwise."""
51 return self._enabled
52
53 @property
54 def reasoning_start_token_ids(self) -> list[int] | None:
55 """Token IDs derived from `reasoning_start_str`. Set automatically by
56 `initialize_token_ids`. Not intended to be configured directly."""
57 return self._reasoning_start_token_ids
58
59 @property
60 def reasoning_end_token_ids(self) -> list[int] | None:
61 """Token IDs forced when the thinking budget is exhausted."""
62 return self._reasoning_end_token_ids
63
64 @property
65 def natural_reasoning_end_token_ids(self) -> list[int] | None:
66 """Token IDs that indicate the model naturally ended reasoning."""
67 return self._natural_reasoning_end_token_ids
68
69 def initialize_token_ids(self, model_config: ModelConfig) -> None:
70 """Initialize reasoning token IDs from strings using the tokenizer."""
71 if (
72 self._reasoning_start_token_ids is not None
73 and self._reasoning_end_token_ids is not None
74 and self._natural_reasoning_end_token_ids is not None
75 ):
76 self._enabled = True
77 return # Already initialized
78
79 tokenizer = cached_tokenizer_from_config(model_config=model_config)
80 reasoning_start_str = self.reasoning_start_str
81 reasoning_end_str = self.reasoning_end_str
82 natural_reasoning_end_str = ""
83 if self.reasoning_parser:
84 parser_cls = ReasoningParserManager.get_reasoning_parser(
85 self.reasoning_parser
86 )
87 reasoning_parser = parser_cls(tokenizer)
88 start_token = reasoning_parser.reasoning_start_str
89 if start_token and not reasoning_start_str:
90 reasoning_start_str = start_token
91
92 end_token = reasoning_parser.reasoning_end_str
93 if end_token and not reasoning_end_str:
94 reasoning_end_str = end_token
95 natural_reasoning_end_str = end_token or ""
96
97 if not natural_reasoning_end_str:
98 natural_reasoning_end_str = reasoning_end_str
99
100 if not reasoning_start_str or not reasoning_end_str:
101 # If we don't have valid strings to tokenize,
102 # we can't initialize the token IDs.
103 return
104 self._reasoning_start_token_ids = tokenizer.encode(
105 reasoning_start_str, add_special_tokens=False
106 )
107 self._reasoning_end_token_ids = tokenizer.encode(
108 reasoning_end_str, add_special_tokens=False
109 )
110 self._natural_reasoning_end_token_ids = tokenizer.encode(
111 natural_reasoning_end_str, add_special_tokens=False
112 )
113
114 if (
115 not self._reasoning_start_token_ids
116 or not self._reasoning_end_token_ids
117 or not self._natural_reasoning_end_token_ids
118 ):
119 raise ValueError(
120 f"ReasoningConfig: failed to tokenize reasoning strings: "
121 f"reasoning_start_str='{self.reasoning_start_str}', "
122 f"reasoning_end_str='{self.reasoning_end_str}'. "
123 "Ensure the strings are valid tokens in the model's vocabulary."
124 )
125 self._enabled = True