agent_works/compression/config.rs
1//! Compression configuration.
2
3/// Tuning knobs for context compression.
4///
5/// Controls when and how the conversation history is compressed to fit within
6/// the model's context window. The compressor keeps the most recent N messages
7/// intact (including tool results) and summarises older history via LLM.
8///
9/// # Defaults
10///
11/// | Field | Default | Purpose |
12/// |-------|---------|---------|
13/// | `enabled` | `true` | Master switch |
14/// | `trigger_tokens` | 160 000 | Skip compression when estimated tokens are below this |
15/// | `keep_recent_messages` | 16 | Number of most-recent messages kept verbatim |
16/// | `max_summary_chars` | 10 240 (10 KB) | Hard cap on the generated summary length |
17/// | `max_transcript_chars` | 20 480 (20 KB) | Max chars of old messages sent to the summarizer |
18#[derive(Clone, Debug)]
19pub struct CompressionConfig {
20 /// Master switch. When `false`, compression is a no-op.
21 pub enabled: bool,
22 /// Compress when the estimated token count of the message list exceeds this.
23 /// Set lower than `context_window` so compression fires before the window
24 /// manager's blunt trim kicks in.
25 pub trigger_tokens: usize,
26 /// Always keep the most recent N messages intact (the agent's working
27 /// context, including tool results). Older messages are summarised.
28 pub keep_recent_messages: usize,
29 /// Hard cap (in chars) on the summary the summarizer LLM may produce.
30 /// Output exceeding this is truncated (front 80 % + rear 20 %).
31 pub max_summary_chars: usize,
32 /// Max chars of the old-message transcript handed to the summarizer.
33 pub max_transcript_chars: usize,
34}
35
36impl Default for CompressionConfig {
37 fn default() -> Self {
38 Self {
39 enabled: true,
40 trigger_tokens: 160_000,
41 keep_recent_messages: 16,
42 max_summary_chars: 10 * 1024, // 10 KB
43 max_transcript_chars: 20 * 1024, // 20 KB
44 }
45 }
46}
47
48// ── Builder methods ──────────────────────────────────────────────────────────
49
50impl CompressionConfig {
51 /// Create a config with everything at default, then customise via builder.
52 pub fn builder() -> Self {
53 Self::default()
54 }
55
56 pub fn with_enabled(mut self, enabled: bool) -> Self {
57 self.enabled = enabled;
58 self
59 }
60
61 pub fn with_trigger_tokens(mut self, tokens: usize) -> Self {
62 self.trigger_tokens = tokens;
63 self
64 }
65
66 pub fn with_keep_recent_messages(mut self, n: usize) -> Self {
67 self.keep_recent_messages = n;
68 self
69 }
70
71 pub fn with_max_summary_chars(mut self, chars: usize) -> Self {
72 self.max_summary_chars = chars;
73 self
74 }
75
76 pub fn with_max_transcript_chars(mut self, chars: usize) -> Self {
77 self.max_transcript_chars = chars;
78 self
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_default_values() {
88 let cfg = CompressionConfig::default();
89 assert!(cfg.enabled);
90 assert_eq!(cfg.trigger_tokens, 160_000);
91 assert_eq!(cfg.keep_recent_messages, 16);
92 assert_eq!(cfg.max_summary_chars, 10 * 1024);
93 assert_eq!(cfg.max_transcript_chars, 20 * 1024);
94 }
95
96 #[test]
97 fn test_builder_chaining() {
98 let cfg = CompressionConfig::builder()
99 .with_enabled(false)
100 .with_trigger_tokens(50_000)
101 .with_keep_recent_messages(20)
102 .with_max_summary_chars(8_000)
103 .with_max_transcript_chars(16_000);
104
105 assert!(!cfg.enabled);
106 assert_eq!(cfg.trigger_tokens, 50_000);
107 assert_eq!(cfg.keep_recent_messages, 20);
108 assert_eq!(cfg.max_summary_chars, 8_000);
109 assert_eq!(cfg.max_transcript_chars, 16_000);
110 }
111}