matrixcode_core/compress/
config.rs1use anyhow::Result;
4
5pub const DEFAULT_COMPRESSION_THRESHOLD: f64 = 0.5;
12
13pub const MIN_MESSAGES_TO_KEEP: usize = 20;
16
17pub const DEFAULT_TARGET_RATIO: f64 = 0.4;
19
20pub const DEFAULT_COMPRESSOR_MODEL: &str = "claude-3-5-haiku-20241022";
22
23pub fn format_tokens(n: u32) -> String {
29 if n < 1_000 {
30 n.to_string()
31 } else if n < 10_000 {
32 format!("{:.1}K", n as f64 / 1_000.0)
33 } else {
34 format!("{:.0}K", n as f64 / 1_000.0)
35 }
36}
37
38#[derive(Debug, Clone, Default)]
44pub struct CompressionBias {
45 pub preserve_tools: bool,
47 pub preserve_thinking: bool,
49 pub preserve_user_questions: bool,
51 pub compact_long_outputs: bool,
53 pub aggressive: bool,
55 pub preserve_keywords: Vec<String>,
57}
58
59impl CompressionBias {
60 pub fn balanced() -> Self {
62 Self {
63 preserve_tools: true,
64 preserve_thinking: false,
65 preserve_user_questions: true,
66 compact_long_outputs: false,
67 aggressive: false,
68 preserve_keywords: vec![
69 "决定".to_string(),
70 "decision".to_string(),
71 "重要".to_string(),
72 "important".to_string(),
73 "关键".to_string(),
74 "key".to_string(),
75 ],
76 }
77 }
78
79 pub fn preserve_important() -> Self {
81 Self {
82 preserve_tools: true,
83 preserve_thinking: true,
84 preserve_user_questions: true,
85 compact_long_outputs: true,
86 aggressive: false,
87 preserve_keywords: vec![
88 "决定".to_string(),
89 "decision".to_string(),
90 "重要".to_string(),
91 "important".to_string(),
92 "关键".to_string(),
93 "key".to_string(),
94 "完成".to_string(),
95 "done".to_string(),
96 "成功".to_string(),
97 "success".to_string(),
98 ],
99 }
100 }
101
102 pub fn aggressive() -> Self {
104 Self {
105 preserve_tools: false,
106 preserve_thinking: false,
107 preserve_user_questions: false,
108 compact_long_outputs: false,
109 aggressive: true,
110 preserve_keywords: vec![],
111 }
112 }
113
114 pub fn tool_focused() -> Self {
116 Self {
117 preserve_tools: true,
118 preserve_thinking: false,
119 preserve_user_questions: false,
120 compact_long_outputs: false,
121 aggressive: false,
122 preserve_keywords: vec![
123 "工具".to_string(),
124 "tool".to_string(),
125 "执行".to_string(),
126 "execute".to_string(),
127 "文件".to_string(),
128 "file".to_string(),
129 ],
130 }
131 }
132
133 pub fn parse(spec: &str) -> Result<Self> {
135 let spec = spec.trim().to_lowercase();
136
137 if spec == "balanced" || spec == "default" || spec.is_empty() {
138 return Ok(Self::balanced());
139 }
140 if spec == "aggressive" {
141 return Ok(Self::aggressive());
142 }
143 if spec == "preserve_important" || spec == "important" {
144 return Ok(Self::preserve_important());
145 }
146 if spec == "tool_focused" || spec == "tools" {
147 return Ok(Self::tool_focused());
148 }
149
150 let mut bias = Self::default();
152
153 for part in spec.split_whitespace() {
154 if let Some(preserve_list) = part.strip_prefix("preserve:") {
155 for item in preserve_list.split(',') {
156 match item.trim() {
157 "tools" | "tool" => bias.preserve_tools = true,
158 "thinking" | "think" => bias.preserve_thinking = true,
159 "user" | "questions" => bias.preserve_user_questions = true,
160 "compact" | "long" => bias.compact_long_outputs = true,
161 _ => {}
162 }
163 }
164 } else if let Some(keyword_list) = part.strip_prefix("keywords:") {
165 bias.preserve_keywords = keyword_list
166 .split(',')
167 .map(|k| k.trim().to_string())
168 .filter(|k| !k.is_empty())
169 .collect();
170 } else if part == "aggressive" {
171 bias.aggressive = true;
172 }
173 }
174
175 Ok(bias)
176 }
177
178 pub fn format(&self) -> String {
180 let mut parts: Vec<String> = Vec::new();
181
182 if self.preserve_tools {
183 parts.push("tools".to_string());
184 }
185 if self.preserve_thinking {
186 parts.push("thinking".to_string());
187 }
188 if self.preserve_user_questions {
189 parts.push("user".to_string());
190 }
191 if self.compact_long_outputs {
192 parts.push("compact".to_string());
193 }
194 if self.aggressive {
195 parts.push("aggressive".to_string());
196 }
197
198 if !self.preserve_keywords.is_empty() {
199 parts.push(format!("keywords:{}", self.preserve_keywords.join(",")));
200 }
201
202 if parts.is_empty() {
203 "default".to_string()
204 } else {
205 parts.join(", ")
206 }
207 }
208}
209
210#[derive(Debug, Clone)]
216pub struct CompressionConfig {
217 pub threshold: f64,
219 pub target_ratio: f64,
221 pub min_preserve_messages: usize,
223 pub use_summarization: bool,
225 pub compressor_model: Option<String>,
227 pub bias: CompressionBias,
229}
230
231impl Default for CompressionConfig {
232 fn default() -> Self {
233 Self {
234 threshold: DEFAULT_COMPRESSION_THRESHOLD,
235 target_ratio: DEFAULT_TARGET_RATIO,
236 min_preserve_messages: MIN_MESSAGES_TO_KEEP,
237 use_summarization: true,
238 compressor_model: None,
239 bias: CompressionBias::balanced(),
240 }
241 }
242}
243
244impl CompressionConfig {
245 pub fn compressor_model_name(&self) -> &str {
247 self.compressor_model
248 .as_deref()
249 .unwrap_or(DEFAULT_COMPRESSOR_MODEL)
250 }
251}