talos_agent/compaction/policy.rs
1use super::constants::{
2 CIRCUIT_BREAKER_THRESHOLD, COLLAPSE_TURN_THRESHOLD, MAX_TOOL_RESULT_CHARS, PRESERVED_TURNS,
3 TRIM_TURN_THRESHOLD,
4};
5
6/// Configurable compaction policy documenting threshold math and limit sources.
7///
8/// All thresholds have documented defaults and source precedence. This struct
9/// makes the compaction decision boundary explicit and testable (MEM-005-A).
10#[derive(Debug, Clone)]
11pub struct CompactionPolicy {
12 /// Fraction of model_limit that triggers compaction (default: 0.8).
13 pub trigger_threshold: f32,
14 /// Maximum characters per tool result before budget truncation (default: 4000).
15 pub max_tool_result_chars: usize,
16 /// Number of recent turns preserved verbatim, never compacted by layers 4/5 (default: 10).
17 pub preserved_turns: usize,
18 /// Turn threshold for trim layer — tool results older than this are emptied (default: 20).
19 pub trim_turn_threshold: usize,
20 /// Turn threshold for collapse layer — turns older than this are summarized (default: 10).
21 pub collapse_turn_threshold: usize,
22 /// Maximum consecutive failures before circuit breaker trips (default: 3).
23 pub circuit_breaker_threshold: usize,
24 /// Tokens reserved for model output, reducing effective context budget (placeholder, default: 0).
25 pub output_reserve: u32,
26}
27
28impl Default for CompactionPolicy {
29 fn default() -> Self {
30 Self {
31 trigger_threshold: 0.8,
32 max_tool_result_chars: MAX_TOOL_RESULT_CHARS,
33 preserved_turns: PRESERVED_TURNS,
34 trim_turn_threshold: TRIM_TURN_THRESHOLD,
35 collapse_turn_threshold: COLLAPSE_TURN_THRESHOLD,
36 circuit_breaker_threshold: CIRCUIT_BREAKER_THRESHOLD,
37 output_reserve: 0,
38 }
39 }
40}
41
42impl CompactionPolicy {
43 /// Returns the token threshold at which compaction triggers.
44 ///
45 /// Source precedence: `model_limit * trigger_threshold - output_reserve`.
46 #[must_use]
47 pub fn trigger_tokens(&self, model_limit: u32) -> u32 {
48 let raw = (model_limit as f32 * self.trigger_threshold) as u32;
49 raw.saturating_sub(self.output_reserve)
50 }
51}