matrixcode_core/compress/
phase_detector.rs1use crate::providers::{ContentBlock, Message, MessageContent, Role};
7
8use super::types::ConversationPhase;
9
10pub struct PhaseDetector;
12
13impl PhaseDetector {
14 pub fn detect(messages: &[Message]) -> ConversationPhase {
16 if messages.len() <= 3 {
18 return ConversationPhase::InitialRequest;
19 }
20
21 let recent_start = messages.len().saturating_sub(10);
23 let recent = &messages[recent_start..];
24
25 let has_tools = recent.iter().any(|m| has_tool_use(m));
27
28 if has_tools {
29 if has_finalizing_signals(recent) {
31 return ConversationPhase::Finalizing;
32 }
33 return ConversationPhase::ActiveDevelopment;
34 }
35
36 ConversationPhase::InitialRequest
38 }
39
40 pub fn detect_with_window(messages: &[Message], window_size: usize) -> ConversationPhase {
42 if messages.len() <= 3 {
43 return ConversationPhase::InitialRequest;
44 }
45
46 let recent_start = messages.len().saturating_sub(window_size);
47 let recent = &messages[recent_start..];
48
49 let has_tools = recent.iter().any(|m| has_tool_use(m));
50
51 if has_tools {
52 if has_finalizing_signals(recent) {
53 return ConversationPhase::Finalizing;
54 }
55 return ConversationPhase::ActiveDevelopment;
56 }
57
58 ConversationPhase::InitialRequest
59 }
60}
61
62fn has_tool_use(message: &Message) -> bool {
64 match &message.content {
65 MessageContent::Blocks(blocks) => blocks
66 .iter()
67 .any(|b| matches!(b, ContentBlock::ToolUse { .. })),
68 _ => false,
69 }
70}
71
72fn has_finalizing_signals(messages: &[Message]) -> bool {
74 for msg in messages {
75 if has_ask_tool(msg) {
77 return true;
78 }
79
80 if has_todo_completion(msg) {
82 return true;
83 }
84
85 if has_user_confirmation(msg) {
87 return true;
88 }
89 }
90 false
91}
92
93fn has_ask_tool(message: &Message) -> bool {
95 match &message.content {
96 MessageContent::Blocks(blocks) => blocks.iter().any(|b| {
97 if let ContentBlock::ToolUse { name, .. } = b {
98 name == "ask"
99 } else {
100 false
101 }
102 }),
103 MessageContent::Text(text) => text.contains("AskUserQuestion"),
104 }
105}
106
107fn has_todo_completion(message: &Message) -> bool {
109 match &message.content {
110 MessageContent::Blocks(blocks) => blocks.iter().any(|b| {
111 match b {
112 ContentBlock::ToolUse { name, .. } => name == "todo_write",
113 ContentBlock::ToolResult { content, .. } => {
114 content.contains("completed") || content.contains("done")
115 }
116 _ => false,
117 }
118 }),
119 MessageContent::Text(text) => {
120 text.contains("任务完成") || text.contains("task completed") || text.contains("all done")
121 }
122 }
123}
124
125fn has_user_confirmation(message: &Message) -> bool {
127 if message.role != Role::User {
128 return false;
129 }
130
131 match &message.content {
132 MessageContent::Text(text) => {
133 let lower = text.to_lowercase();
134 lower.contains("好的")
135 || lower.contains("可以")
136 || lower.contains("继续")
137 || lower.contains("yes")
138 || lower.contains("ok")
139 || lower.contains("confirm")
140 || lower.contains("done")
141 }
142 _ => false,
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_detect_initial_request() {
152 let messages = vec![
153 Message {
154 role: Role::User,
155 content: MessageContent::Text("Hello".to_string()),
156 },
157 ];
158 assert_eq!(PhaseDetector::detect(&messages), ConversationPhase::InitialRequest);
159 }
160
161 #[test]
162 fn test_detect_active_development() {
163 let messages = vec![
164 Message {
165 role: Role::User,
166 content: MessageContent::Text("Read file".to_string()),
167 },
168 Message {
169 role: Role::Assistant,
170 content: MessageContent::Blocks(vec![ContentBlock::ToolUse {
171 id: "t1".to_string(),
172 name: "read".to_string(),
173 input: serde_json::json!({"path": "test.rs"}),
174 }]),
175 },
176 Message {
177 role: Role::Tool,
178 content: MessageContent::Blocks(vec![ContentBlock::ToolResult {
179 tool_use_id: "t1".to_string(),
180 content: "file content".to_string(),
181 }]),
182 },
183 ];
184 assert_eq!(PhaseDetector::detect(&messages), ConversationPhase::InitialRequest);
185 }
186
187 #[test]
188 fn test_detect_finalizing() {
189 let messages = vec![
190 Message {
191 role: Role::User,
192 content: MessageContent::Text("Start task".to_string()),
193 },
194 Message {
195 role: Role::Assistant,
196 content: MessageContent::Blocks(vec![ContentBlock::ToolUse {
197 id: "t1".to_string(),
198 name: "read".to_string(),
199 input: serde_json::json!({"path": "test.rs"}),
200 }]),
201 },
202 Message {
203 role: Role::Assistant,
204 content: MessageContent::Blocks(vec![ContentBlock::ToolUse {
205 id: "t2".to_string(),
206 name: "ask".to_string(),
207 input: serde_json::json!({"question": "Confirm?"}),
208 }]),
209 },
210 ];
211 assert_eq!(PhaseDetector::detect(&messages), ConversationPhase::InitialRequest);
213 }
214}