reasoning_parser/parsers/
step3.rs

1// Step3 specific reasoning parser.
2// Uses the same format as DeepSeek-R1 but has its own implementation for debugging.
3
4use crate::{
5    parsers::BaseReasoningParser,
6    traits::{ParseError, ParserConfig, ParserResult, ReasoningParser},
7};
8
9/// Step3 reasoning parser.
10///
11/// This parser uses the same format as DeepSeek-R1 (<think>...</think>) but has
12/// its own implementation for better debugging and potential future customization.
13pub struct Step3Parser {
14    base: BaseReasoningParser,
15}
16
17impl Step3Parser {
18    /// Create a new Step3 parser.
19    pub fn new() -> Self {
20        let config = ParserConfig {
21            think_start_token: "<think>".to_string(),
22            think_end_token: "</think>".to_string(),
23            stream_reasoning: true,
24            max_buffer_size: 65536,
25            initial_in_reasoning: true, // Assumes reasoning from start like DeepSeek-R1
26        };
27
28        Self {
29            base: BaseReasoningParser::new(config).with_model_type("step3".to_string()),
30        }
31    }
32}
33
34impl Default for Step3Parser {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40impl ReasoningParser for Step3Parser {
41    fn detect_and_parse_reasoning(&mut self, text: &str) -> Result<ParserResult, ParseError> {
42        self.base.detect_and_parse_reasoning(text)
43    }
44
45    fn parse_reasoning_streaming_incremental(
46        &mut self,
47        text: &str,
48    ) -> Result<ParserResult, ParseError> {
49        self.base.parse_reasoning_streaming_incremental(text)
50    }
51
52    fn reset(&mut self) {
53        self.base.reset()
54    }
55
56    fn model_type(&self) -> &str {
57        self.base.model_type()
58    }
59
60    fn is_in_reasoning(&self) -> bool {
61        self.base.is_in_reasoning()
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_step3_initial_state() {
71        let mut parser = Step3Parser::new();
72
73        // Should treat text as reasoning even without start token
74        let result = parser
75            .detect_and_parse_reasoning("This is reasoning content")
76            .unwrap();
77        assert_eq!(result.normal_text, "");
78        assert_eq!(result.reasoning_text, "This is reasoning content");
79    }
80
81    #[test]
82    fn test_step3_with_end_token() {
83        let mut parser = Step3Parser::new();
84
85        // Should handle text with end token
86        let result = parser
87            .detect_and_parse_reasoning("reasoning content</think>answer")
88            .unwrap();
89        assert_eq!(result.normal_text, "answer");
90        assert_eq!(result.reasoning_text, "reasoning content");
91    }
92
93    #[test]
94    fn test_step3_with_both_tokens() {
95        let mut parser = Step3Parser::new();
96
97        // Should handle both start and end tokens
98        let result = parser
99            .detect_and_parse_reasoning("<think>reasoning content</think>answer")
100            .unwrap();
101        assert_eq!(result.normal_text, "answer");
102        assert_eq!(result.reasoning_text, "reasoning content");
103    }
104
105    #[test]
106    fn test_step3_streaming() {
107        let mut parser = Step3Parser::new();
108
109        // First chunk - treated as reasoning (initial_in_reasoning=true)
110        let result1 = parser
111            .parse_reasoning_streaming_incremental("reasoning text ")
112            .unwrap();
113        assert_eq!(result1.normal_text, "");
114        assert_eq!(result1.reasoning_text, "reasoning text ");
115
116        // Second chunk - continues reasoning until end token
117        let result2 = parser
118            .parse_reasoning_streaming_incremental("more reasoning</think>answer")
119            .unwrap();
120        assert_eq!(result2.normal_text, "answer");
121        assert_eq!(result2.reasoning_text, "more reasoning");
122    }
123
124    #[test]
125    fn test_model_type() {
126        let parser = Step3Parser::new();
127        assert_eq!(parser.model_type(), "step3");
128    }
129}