matrixcode_core/agent/core/config.rs
1//! Agent configuration constants.
2//!
3//! This module defines configuration constants that were previously hardcoded
4//! throughout the agent module. By extracting these into a configuration struct,
5//! we enable:
6//! - Dynamic adjustment based on task complexity
7//! - User-configurable limits in config files
8//! - Better testability (can test edge cases)
9//! - Clearer documentation of defaults
10
11use crate::compress::CompressionConfig;
12use crate::tools::code_quality_hook::VerificationStrategy;
13
14/// Maximum number of iterations in the main loop.
15///
16/// This constant is used as the default for [`AgentConfig::max_iterations`].
17/// It is re-exported from the parent module for backward compatibility.
18///
19/// See [`AgentConfig::max_iterations`] for detailed documentation.
20pub const MAX_ITERATIONS: usize = 200;
21
22/// Agent runtime configuration.
23///
24/// Controls iteration limits, retry behavior, size thresholds, and compression settings.
25/// All defaults match the original hardcoded values for backward compatibility.
26#[derive(Debug, Clone)]
27pub struct AgentConfig {
28 /// Maximum number of iterations in the main loop.
29 ///
30 /// **Default**: 200
31 ///
32 /// **Why this value?**
33 /// - Sufficient for most common tasks (file edits, code review, simple builds)
34 /// - Prevents infinite loops and runaway operations
35 /// - Balances task completion with resource efficiency
36 ///
37 /// **What happens when limit is reached?**
38 /// - Agent stops execution gracefully
39 /// - User receives detailed warning message
40 /// - Can continue, break down task, or resume
41 ///
42 /// **Examples**:
43 /// - Simple task (edit file): ~5-10 iterations
44 /// - Medium task (refactor module): ~15-30 iterations
45 /// - Complex task (build system): ~40-50 iterations (may hit limit)
46 pub max_iterations: usize,
47
48 /// Maximum retry attempts for API calls.
49 ///
50 /// **Default**: 5
51 ///
52 /// **Why this value?**
53 /// - Handles transient network failures
54 /// - Balances resilience with response time
55 /// - Most failures resolve within 2-3 retries
56 pub max_retries: u32,
57
58 /// Delay between retry attempts (milliseconds).
59 ///
60 /// **Default**: 1000ms
61 ///
62 /// **Why this value?**
63 /// - Gives API server time to recover
64 /// - Not too long to frustrate users
65 /// - Often used with exponential backoff
66 pub retry_delay_ms: u64,
67
68 /// Maximum size for tool execution results (bytes).
69 ///
70 /// **Default**: 50,000 (50KB)
71 ///
72 /// **Why this value?**
73 /// - Prevents oversized responses from breaking API calls
74 /// - Fits within typical context window limits
75 /// - Most tool results are <10KB
76 ///
77 /// **Truncation behavior**:
78 /// - Results larger than this are truncated
79 /// - Truncation message added: "⚠️ Output truncated (X bytes total)"
80 /// - User can request specific portions if needed
81 pub max_tool_result_size: usize,
82
83 /// Maximum number of todo reminders per todo item.
84 ///
85 /// **Default**: 2
86 ///
87 /// **Why this value?**
88 /// - Prevents infinite loops when model doesn't update todo status
89 /// - Gives model multiple chances to notice pending work
90 /// - After limit, stops reminding (assumes model won't update)
91 pub max_todo_reminders: usize,
92
93 /// Iteration count threshold for warning user.
94 ///
95 /// **Default**: max_iterations - 10 (190)
96 ///
97 /// **Why this value?**
98 /// - Warns user 10 iterations before limit
99 /// - Gives time to decide: continue, break down task, or cancel
100 /// - Prevents surprising cutoff at exactly max_iterations
101 pub iteration_warning_threshold: usize,
102
103 /// Context compression configuration.
104 ///
105 /// Controls when and how to compress long conversations.
106 /// See [`CompressionConfig`] for details.
107 pub compression: CompressionConfig,
108
109 // === API Request Settings ===
110 /// Maximum tokens for API responses.
111 ///
112 /// **Default**: 4096 (from QUICK_ACTION_MAX_TOKENS)
113 pub max_tokens: u32,
114
115 /// Override for context window size.
116 ///
117 /// **Default**: None (use provider's default)
118 pub context_size_override: Option<u32>,
119
120 /// Enable thinking mode for extended reasoning.
121 ///
122 /// **Default**: false
123 pub think: bool,
124
125 /// Code verification strategy for write operations.
126 ///
127 /// Controls whether and when code quality checks run on edit/write/multi_edit.
128 /// - `None`: No verification
129 /// - `Post`: Verify after write (default)
130 /// - `Pre`: Verify before write, block if errors
131 /// - `PreQuick`: Quick syntax check before, full check after
132 pub verify_strategy: VerificationStrategy,
133
134 /// Project root path for code verification.
135 ///
136 /// Used to detect project type (Rust/Node.js/Python/Go) and run
137 /// appropriate verification commands (cargo check, tsc, etc.).
138 pub project_path: Option<std::path::PathBuf>,
139}
140
141impl Default for AgentConfig {
142 fn default() -> Self {
143 Self {
144 max_iterations: MAX_ITERATIONS,
145 max_retries: 5,
146 retry_delay_ms: 1000,
147 max_tool_result_size: 50_000,
148 max_todo_reminders: 2,
149 iteration_warning_threshold: MAX_ITERATIONS.saturating_sub(10),
150 compression: CompressionConfig::default(),
151 max_tokens: crate::constants::QUICK_ACTION_MAX_TOKENS,
152 context_size_override: None,
153 think: false,
154 verify_strategy: VerificationStrategy::default(),
155 project_path: None,
156 }
157 }
158}
159
160impl AgentConfig {
161 /// Create a new config with all settings.
162 pub fn new(
163 max_tokens: u32,
164 context_size_override: Option<u32>,
165 think: bool,
166 compression: CompressionConfig,
167 ) -> Self {
168 Self {
169 max_tokens,
170 context_size_override,
171 think,
172 compression,
173 ..Self::default()
174 }
175 }
176
177 /// Create a new config with verification strategy.
178 pub fn with_verify_strategy(
179 verify_strategy: VerificationStrategy,
180 project_path: Option<std::path::PathBuf>,
181 ) -> Self {
182 Self {
183 verify_strategy,
184 project_path,
185 ..Self::default()
186 }
187 }
188
189 /// Create a new config with custom max_iterations.
190 ///
191 /// Automatically adjusts iteration_warning_threshold to max_iterations - 10.
192 pub fn with_max_iterations(max_iterations: usize) -> Self {
193 Self {
194 max_iterations,
195 iteration_warning_threshold: max_iterations.saturating_sub(10),
196 ..Self::default()
197 }
198 }
199
200 /// Create a new config with custom compression settings.
201 pub fn with_compression(compression: CompressionConfig) -> Self {
202 Self {
203 compression,
204 ..Self::default()
205 }
206 }
207
208 /// Create a config optimized for simple tasks.
209 ///
210 /// Use for: file edits, quick queries, simple tool calls.
211 /// Characteristics: lower iteration limit, smaller result size.
212 pub fn simple_task() -> Self {
213 Self {
214 max_iterations: 50,
215 iteration_warning_threshold: 40,
216 max_tool_result_size: 10_000,
217 ..Self::default()
218 }
219 }
220
221 /// Create a config optimized for complex tasks.
222 ///
223 /// Use for: refactoring, build systems, multi-file changes.
224 /// Characteristics: higher iteration limit, larger result size.
225 pub fn complex_task() -> Self {
226 Self {
227 max_iterations: 300,
228 iteration_warning_threshold: 290,
229 max_tool_result_size: 100_000,
230 max_todo_reminders: 3,
231 ..Self::default()
232 }
233 }
234
235 // === Accessor methods ===
236
237 /// Get max tokens for API responses.
238 pub fn max_tokens(&self) -> u32 {
239 self.max_tokens
240 }
241
242 /// Get context size override.
243 pub fn context_size_override(&self) -> Option<u32> {
244 self.context_size_override
245 }
246
247 /// Get thinking mode flag.
248 pub fn think(&self) -> bool {
249 self.think
250 }
251
252 /// Get compression config.
253 pub fn compression_config(&self) -> &CompressionConfig {
254 &self.compression
255 }
256
257 /// Get mutable compression config.
258 pub fn compression_config_mut(&mut self) -> &mut CompressionConfig {
259 &mut self.compression
260 }
261
262 /// Get verification strategy.
263 pub fn verify_strategy(&self) -> VerificationStrategy {
264 self.verify_strategy
265 }
266
267 /// Get project path for verification.
268 pub fn project_path(&self) -> Option<&std::path::Path> {
269 self.project_path.as_deref()
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_default_config_matches_hardcoded_values() {
279 let config = AgentConfig::default();
280
281 // Verify all defaults match original hardcoded constants
282 assert_eq!(config.max_iterations, 200, "max_iterations default changed");
283 assert_eq!(config.max_retries, 5, "max_retries default changed");
284 assert_eq!(config.retry_delay_ms, 1000, "retry_delay_ms default changed");
285 assert_eq!(config.max_tool_result_size, 50_000, "max_tool_result_size default changed");
286 assert_eq!(config.max_todo_reminders, 2, "max_todo_reminders default changed");
287 assert_eq!(config.iteration_warning_threshold, 190, "iteration_warning_threshold default changed");
288 }
289
290 #[test]
291 fn test_with_max_iterations_adjusts_warning_threshold() {
292 let config = AgentConfig::with_max_iterations(100);
293
294 assert_eq!(config.max_iterations, 100);
295 assert_eq!(config.iteration_warning_threshold, 90, "warning threshold should be max - 10");
296 }
297
298 #[test]
299 fn test_with_max_iterations_handles_small_values() {
300 let config = AgentConfig::with_max_iterations(5);
301
302 // saturating_sub prevents negative threshold
303 assert_eq!(config.iteration_warning_threshold, 0, "threshold should use saturating_sub");
304 }
305
306 #[test]
307 fn test_simple_task_optimization() {
308 let config = AgentConfig::simple_task();
309
310 assert_eq!(config.max_iterations, 50, "simple tasks should have lower iteration limit");
311 assert_eq!(config.max_tool_result_size, 10_000, "simple tasks should have smaller result size");
312 }
313
314 #[test]
315 fn test_complex_task_optimization() {
316 let config = AgentConfig::complex_task();
317
318 assert_eq!(config.max_iterations, 300, "complex tasks should have higher iteration limit");
319 assert_eq!(config.max_tool_result_size, 100_000, "complex tasks should have larger result size");
320 assert_eq!(config.max_todo_reminders, 3, "complex tasks should allow more reminders");
321 }
322}