Skip to main content

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}