reasoning_parser/parsers/
cohere_cmd.rs1use crate::{
7 parsers::BaseReasoningParser,
8 traits::{ParseError, ParserConfig, ParserResult, ReasoningParser},
9};
10
11pub struct CohereCmdParser {
16 base: BaseReasoningParser,
17}
18
19impl CohereCmdParser {
20 pub fn new() -> Self {
22 let config = ParserConfig {
23 think_start_token: "<|START_THINKING|>".to_string(),
24 think_end_token: "<|END_THINKING|>".to_string(),
25 stream_reasoning: true,
26 max_buffer_size: 65536,
27 initial_in_reasoning: false, };
29
30 Self {
31 base: BaseReasoningParser::new(config).with_model_type("cohere_cmd".to_string()),
32 }
33 }
34}
35
36impl Default for CohereCmdParser {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl ReasoningParser for CohereCmdParser {
43 fn detect_and_parse_reasoning(&mut self, text: &str) -> Result<ParserResult, ParseError> {
44 self.base.detect_and_parse_reasoning(text)
45 }
46
47 fn parse_reasoning_streaming_incremental(
48 &mut self,
49 text: &str,
50 ) -> Result<ParserResult, ParseError> {
51 self.base.parse_reasoning_streaming_incremental(text)
52 }
53
54 fn reset(&mut self) {
55 self.base.reset()
56 }
57
58 fn model_type(&self) -> &str {
59 self.base.model_type()
60 }
61
62 fn is_in_reasoning(&self) -> bool {
63 self.base.is_in_reasoning()
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_cohere_cmd_no_reasoning() {
73 let mut parser = CohereCmdParser::new();
74
75 let result = parser
77 .detect_and_parse_reasoning("This is a normal response.")
78 .unwrap();
79 assert_eq!(result.normal_text, "This is a normal response.");
80 assert_eq!(result.reasoning_text, "");
81 }
82
83 #[test]
84 fn test_cohere_cmd_with_thinking() {
85 let mut parser = CohereCmdParser::new();
86
87 let result = parser
88 .detect_and_parse_reasoning(
89 "<|START_THINKING|>Let me analyze this step by step.<|END_THINKING|>The answer is 42.",
90 )
91 .unwrap();
92 assert_eq!(result.normal_text, "The answer is 42.");
93 assert_eq!(result.reasoning_text, "Let me analyze this step by step.");
94 }
95
96 #[test]
97 fn test_cohere_cmd_truncated_thinking() {
98 let mut parser = CohereCmdParser::new();
99
100 let result = parser
102 .detect_and_parse_reasoning("<|START_THINKING|>Analyzing the problem...")
103 .unwrap();
104 assert_eq!(result.normal_text, "");
105 assert_eq!(result.reasoning_text, "Analyzing the problem...");
106 }
107
108 #[test]
109 fn test_cohere_cmd_streaming() {
110 let mut parser = CohereCmdParser::new();
111
112 let result1 = parser
114 .parse_reasoning_streaming_incremental("<|START_THINKING|>Step 1: ")
115 .unwrap();
116 assert_eq!(result1.reasoning_text, "Step 1: ");
117 assert_eq!(result1.normal_text, "");
118
119 let result2 = parser
121 .parse_reasoning_streaming_incremental("Analyze inputs. ")
122 .unwrap();
123 assert_eq!(result2.reasoning_text, "Analyze inputs. ");
124 assert_eq!(result2.normal_text, "");
125
126 let result3 = parser
128 .parse_reasoning_streaming_incremental(
129 "Step 2: Check.<|END_THINKING|>Here's the answer.",
130 )
131 .unwrap();
132 assert_eq!(result3.reasoning_text, "Step 2: Check.");
133 assert_eq!(result3.normal_text, "Here's the answer.");
134 }
135
136 #[test]
137 fn test_cohere_cmd_streaming_partial_token() {
138 let mut parser = CohereCmdParser::new();
139
140 let result1 = parser
142 .parse_reasoning_streaming_incremental("<|START_")
143 .unwrap();
144 assert_eq!(result1.reasoning_text, "");
145 assert_eq!(result1.normal_text, "");
146
147 let result2 = parser
149 .parse_reasoning_streaming_incremental("THINKING|>reasoning")
150 .unwrap();
151 assert_eq!(result2.reasoning_text, "reasoning");
152 assert_eq!(result2.normal_text, "");
153 }
154
155 #[test]
156 fn test_cohere_cmd_reset() {
157 let mut parser = CohereCmdParser::new();
158
159 parser
161 .parse_reasoning_streaming_incremental("<|START_THINKING|>thinking<|END_THINKING|>done")
162 .unwrap();
163
164 parser.reset();
166 assert!(!parser.is_in_reasoning());
167
168 let result = parser
170 .detect_and_parse_reasoning("<|START_THINKING|>new<|END_THINKING|>text")
171 .unwrap();
172 assert_eq!(result.reasoning_text, "new");
173 assert_eq!(result.normal_text, "text");
174 }
175
176 #[test]
177 fn test_model_type() {
178 let parser = CohereCmdParser::new();
179 assert_eq!(parser.model_type(), "cohere_cmd");
180 }
181
182 #[test]
183 fn test_cohere_full_response_format() {
184 let mut parser = CohereCmdParser::new();
185
186 let input = r#"<|START_THINKING|>
188Let me analyze this step by step.
1891. First, I'll consider the question.
1902. Then, I'll formulate a response.
191<|END_THINKING|>
192<|START_RESPONSE|>The answer is 42.<|END_RESPONSE|>"#;
193
194 let result = parser.detect_and_parse_reasoning(input).unwrap();
195 assert!(result.reasoning_text.contains("step by step"));
196 assert!(result.normal_text.contains("START_RESPONSE"));
198 }
199
200 #[test]
201 fn test_cohere_cmd_empty_thinking() {
202 let mut parser = CohereCmdParser::new();
203
204 let result = parser
205 .detect_and_parse_reasoning("<|START_THINKING|><|END_THINKING|>The answer.")
206 .unwrap();
207 assert_eq!(result.normal_text, "The answer.");
208 assert_eq!(result.reasoning_text, "");
209 }
210
211 #[test]
212 fn test_cohere_cmd_unicode_in_thinking() {
213 let mut parser = CohereCmdParser::new();
214
215 let result = parser
216 .detect_and_parse_reasoning(
217 "<|START_THINKING|>分析这个问题 🤔 emoji test<|END_THINKING|>答案是42。",
218 )
219 .unwrap();
220 assert_eq!(result.reasoning_text, "分析这个问题 🤔 emoji test");
221 assert_eq!(result.normal_text, "答案是42。");
222 }
223
224 #[test]
225 fn test_cohere_cmd_angle_brackets_in_thinking() {
226 let mut parser = CohereCmdParser::new();
227
228 let result = parser
230 .detect_and_parse_reasoning(
231 "<|START_THINKING|>check if x < 10 and y > 5<|END_THINKING|>result",
232 )
233 .unwrap();
234 assert_eq!(result.reasoning_text, "check if x < 10 and y > 5");
235 assert_eq!(result.normal_text, "result");
236 }
237
238 #[test]
239 fn test_cohere_cmd_whitespace_only_thinking() {
240 let mut parser = CohereCmdParser::new();
241
242 let result = parser
243 .detect_and_parse_reasoning("<|START_THINKING|> \n\t <|END_THINKING|>answer")
244 .unwrap();
245 assert_eq!(result.reasoning_text, "");
247 assert_eq!(result.normal_text, "answer");
248 }
249}