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