reasoning_parser/parsers/
kimi.rs1use crate::{
5 parsers::BaseReasoningParser,
6 traits::{ParseError, ParserConfig, ParserResult, ReasoningParser, DEFAULT_MAX_BUFFER_SIZE},
7};
8
9pub struct KimiParser {
14 base: BaseReasoningParser,
15}
16
17impl KimiParser {
18 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: DEFAULT_MAX_BUFFER_SIZE,
25 always_in_reasoning: false,
26 };
27
28 Self {
29 base: BaseReasoningParser::new(config).with_model_type("kimi".to_string()),
30 }
31 }
32}
33
34impl Default for KimiParser {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl ReasoningParser for KimiParser {
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 fn mark_reasoning_started(&mut self) {
65 self.base.mark_reasoning_started();
66 }
67
68 fn mark_think_start_stripped(&mut self) {
69 self.base.mark_think_start_stripped();
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_kimi_initial_state() {
79 let mut parser = KimiParser::new();
80
81 let result = parser
83 .detect_and_parse_reasoning("This is normal content")
84 .unwrap();
85 assert_eq!(result.normal_text, "This is normal content");
86 assert_eq!(result.reasoning_text, "");
87 }
88
89 #[test]
90 fn test_kimi_with_unicode_tokens() {
91 let mut parser = KimiParser::new();
92
93 let result = parser
95 .detect_and_parse_reasoning("◁think▷reasoning content◁/think▷answer")
96 .unwrap();
97 assert_eq!(result.normal_text, "answer");
98 assert_eq!(result.reasoning_text, "reasoning content");
99 }
100
101 #[test]
102 fn test_kimi_partial_unicode() {
103 let mut parser = KimiParser::new();
104
105 let result1 = parser
106 .parse_reasoning_streaming_incremental("◁thi")
107 .unwrap();
108 assert_eq!(result1.normal_text, "");
109 assert_eq!(result1.reasoning_text, "");
110
111 let result2 = parser
113 .parse_reasoning_streaming_incremental("nk▷reasoning")
114 .unwrap();
115 assert_eq!(result2.normal_text, "");
116 assert_eq!(result2.reasoning_text, "reasoning");
117 }
118
119 #[test]
120 fn test_kimi_streaming() {
121 let mut parser = KimiParser::new();
122
123 let result1 = parser
125 .parse_reasoning_streaming_incremental("normal ")
126 .unwrap();
127 assert_eq!(result1.normal_text, "normal ");
128 assert_eq!(result1.reasoning_text, "");
129
130 let result2 = parser
132 .parse_reasoning_streaming_incremental("◁think▷thinking")
133 .unwrap();
134 assert_eq!(result2.normal_text, "");
135 assert_eq!(result2.reasoning_text, "thinking");
136
137 let result3 = parser
139 .parse_reasoning_streaming_incremental("◁/think▷answer")
140 .unwrap();
141 assert_eq!(result3.normal_text, "answer");
142 assert_eq!(result3.reasoning_text, ""); }
144
145 #[test]
146 fn test_model_type() {
147 let parser = KimiParser::new();
148 assert_eq!(parser.model_type(), "kimi");
149 }
150}