reasoning_parser/parsers/
nano_v3.rs1use crate::{
12 parsers::BaseReasoningParser,
13 traits::{ParseError, ParserConfig, ParserResult, ReasoningParser, DEFAULT_MAX_BUFFER_SIZE},
14};
15
16pub struct NanoV3Parser {
18 base: BaseReasoningParser,
19}
20
21impl NanoV3Parser {
22 pub fn new() -> Self {
24 let config = ParserConfig {
25 think_start_token: "<think>".to_string(),
26 think_end_token: "</think>".to_string(),
27 stream_reasoning: true,
28 max_buffer_size: DEFAULT_MAX_BUFFER_SIZE,
29 always_in_reasoning: false,
30 };
31
32 Self {
33 base: BaseReasoningParser::new(config).with_model_type("nano_v3".to_string()),
34 }
35 }
36}
37
38impl Default for NanoV3Parser {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl ReasoningParser for NanoV3Parser {
45 fn detect_and_parse_reasoning(&mut self, text: &str) -> Result<ParserResult, ParseError> {
46 self.base.detect_and_parse_reasoning(text)
47 }
48
49 fn parse_reasoning_streaming_incremental(
50 &mut self,
51 text: &str,
52 ) -> Result<ParserResult, ParseError> {
53 self.base.parse_reasoning_streaming_incremental(text)
54 }
55
56 fn reset(&mut self) {
57 self.base.reset();
58 }
59
60 fn model_type(&self) -> &str {
61 self.base.model_type()
62 }
63
64 fn is_in_reasoning(&self) -> bool {
65 self.base.is_in_reasoning()
66 }
67
68 fn mark_reasoning_started(&mut self) {
69 self.base.mark_reasoning_started();
70 }
71
72 fn mark_think_start_stripped(&mut self) {
73 self.base.mark_think_start_stripped();
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_nano_v3_initial_state() {
83 let mut parser = NanoV3Parser::new();
84
85 let result = parser
87 .detect_and_parse_reasoning("This is normal content")
88 .unwrap();
89 assert_eq!(result.normal_text, "This is normal content");
90 assert_eq!(result.reasoning_text, "");
91 }
92
93 #[test]
94 fn test_nano_v3_with_mark_reasoning_started() {
95 let mut parser = NanoV3Parser::new();
96 parser.mark_reasoning_started();
97
98 let result = parser
100 .detect_and_parse_reasoning("reasoning content</think>answer")
101 .unwrap();
102 assert_eq!(result.normal_text, "answer");
103 assert_eq!(result.reasoning_text, "reasoning content");
104 }
105
106 #[test]
107 fn test_nano_v3_with_both_tokens() {
108 let mut parser = NanoV3Parser::new();
109
110 let result = parser
111 .detect_and_parse_reasoning("<think>reasoning content</think>answer")
112 .unwrap();
113 assert_eq!(result.normal_text, "answer");
114 assert_eq!(result.reasoning_text, "reasoning content");
115 }
116
117 #[test]
118 fn test_nano_v3_streaming() {
119 let mut parser = NanoV3Parser::new();
120 parser.mark_reasoning_started();
121
122 let result1 = parser
123 .parse_reasoning_streaming_incremental("reasoning text ")
124 .unwrap();
125 assert_eq!(result1.normal_text, "");
126 assert_eq!(result1.reasoning_text, "reasoning text ");
127
128 let result2 = parser
129 .parse_reasoning_streaming_incremental("more reasoning</think>answer")
130 .unwrap();
131 assert_eq!(result2.normal_text, "answer");
132 assert_eq!(result2.reasoning_text, "more reasoning");
133 }
134
135 #[test]
136 fn test_model_type() {
137 let parser = NanoV3Parser::new();
138 assert_eq!(parser.model_type(), "nano_v3");
139 }
140}