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