reasoning_parser/parsers/
qwen3.rs1use crate::{
6 parsers::BaseReasoningParser,
7 traits::{ParseError, ParserConfig, ParserResult, ReasoningParser, DEFAULT_MAX_BUFFER_SIZE},
8};
9
10pub struct Qwen3Parser {
15 base: BaseReasoningParser,
16}
17
18impl Qwen3Parser {
19 pub fn new() -> Self {
21 let config = ParserConfig {
22 think_start_token: "<think>".to_string(),
23 think_end_token: "</think>".to_string(),
24 stream_reasoning: true,
25 max_buffer_size: DEFAULT_MAX_BUFFER_SIZE,
26 always_in_reasoning: false,
27 };
28
29 Self {
30 base: BaseReasoningParser::new(config).with_model_type("qwen3".to_string()),
31 }
32 }
33}
34
35impl Default for Qwen3Parser {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl ReasoningParser for Qwen3Parser {
42 fn detect_and_parse_reasoning(&mut self, text: &str) -> Result<ParserResult, ParseError> {
43 self.base.detect_and_parse_reasoning(text)
44 }
45
46 fn parse_reasoning_streaming_incremental(
47 &mut self,
48 text: &str,
49 ) -> Result<ParserResult, ParseError> {
50 self.base.parse_reasoning_streaming_incremental(text)
51 }
52
53 fn reset(&mut self) {
54 self.base.reset();
55 }
56
57 fn model_type(&self) -> &str {
58 self.base.model_type()
59 }
60
61 fn is_in_reasoning(&self) -> bool {
62 self.base.is_in_reasoning()
63 }
64
65 fn mark_reasoning_started(&mut self) {
66 self.base.mark_reasoning_started();
67 }
68
69 fn mark_think_start_stripped(&mut self) {
70 self.base.mark_think_start_stripped();
71 }
72}
73
74pub struct QwenThinkingParser {
78 base: BaseReasoningParser,
79}
80
81impl QwenThinkingParser {
82 pub fn new() -> Self {
84 let config = ParserConfig {
85 think_start_token: "<think>".to_string(),
86 think_end_token: "</think>".to_string(),
87 stream_reasoning: true,
88 max_buffer_size: DEFAULT_MAX_BUFFER_SIZE,
89 always_in_reasoning: true,
90 };
91
92 Self {
93 base: BaseReasoningParser::new(config).with_model_type("qwen_thinking".to_string()),
94 }
95 }
96}
97
98impl Default for QwenThinkingParser {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104impl ReasoningParser for QwenThinkingParser {
105 fn detect_and_parse_reasoning(&mut self, text: &str) -> Result<ParserResult, ParseError> {
106 self.base.detect_and_parse_reasoning(text)
107 }
108
109 fn parse_reasoning_streaming_incremental(
110 &mut self,
111 text: &str,
112 ) -> Result<ParserResult, ParseError> {
113 self.base.parse_reasoning_streaming_incremental(text)
114 }
115
116 fn reset(&mut self) {
117 self.base.reset();
118 }
119
120 fn model_type(&self) -> &str {
121 self.base.model_type()
122 }
123
124 fn is_in_reasoning(&self) -> bool {
125 self.base.is_in_reasoning()
126 }
127
128 fn mark_reasoning_started(&mut self) {
129 self.base.mark_reasoning_started();
130 }
131
132 fn mark_think_start_stripped(&mut self) {
133 self.base.mark_think_start_stripped();
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_qwen3_initial_state() {
143 let mut parser = Qwen3Parser::new();
144
145 let result = parser
147 .detect_and_parse_reasoning("This is normal content")
148 .unwrap();
149 assert_eq!(result.normal_text, "This is normal content");
150 assert_eq!(result.reasoning_text, "");
151 }
152
153 #[test]
154 fn test_qwen3_with_tokens() {
155 let mut parser = Qwen3Parser::new();
156
157 let result = parser
159 .detect_and_parse_reasoning("<think>reasoning</think>answer")
160 .unwrap();
161 assert_eq!(result.normal_text, "answer");
162 assert_eq!(result.reasoning_text, "reasoning");
163 }
164
165 #[test]
166 fn test_qwen_thinking_initial_state() {
167 let mut parser = QwenThinkingParser::new();
168
169 let result = parser
171 .detect_and_parse_reasoning("This is reasoning content")
172 .unwrap();
173 assert_eq!(result.normal_text, "");
174 assert_eq!(result.reasoning_text, "This is reasoning content");
175 }
176
177 #[test]
178 fn test_qwen3_streaming() {
179 let mut parser = Qwen3Parser::new();
180
181 let result1 = parser
183 .parse_reasoning_streaming_incremental("normal text ")
184 .unwrap();
185 assert_eq!(result1.normal_text, "normal text ");
186 assert_eq!(result1.reasoning_text, "");
187
188 let result2 = parser
190 .parse_reasoning_streaming_incremental("<think>reasoning")
191 .unwrap();
192 assert_eq!(result2.normal_text, "");
193 assert_eq!(result2.reasoning_text, "reasoning");
194 }
195
196 #[test]
197 fn test_model_types() {
198 let qwen3 = Qwen3Parser::new();
199 assert_eq!(qwen3.model_type(), "qwen3");
200
201 let qwen_thinking = QwenThinkingParser::new();
202 assert_eq!(qwen_thinking.model_type(), "qwen_thinking");
203 }
204}