use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum CompactStrategy {
#[default]
Auto,
Reactive,
Manual,
SessionMemory,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactConfig {
pub auto_compact_enabled: bool,
pub auto_compact_buffer_tokens: u32,
pub warning_buffer_tokens: u32,
pub error_buffer_tokens: u32,
pub manual_compact_buffer_tokens: u32,
pub strategy: CompactStrategy,
pub micro_compact_interval: u32,
pub post_compact_max_files_to_restore: usize,
pub post_compact_token_budget: u32,
}
impl Default for CompactConfig {
fn default() -> Self {
Self {
auto_compact_enabled: true,
auto_compact_buffer_tokens: 13_000, warning_buffer_tokens: 20_000, error_buffer_tokens: 20_000, manual_compact_buffer_tokens: 3_000, strategy: CompactStrategy::Auto,
micro_compact_interval: 10, post_compact_max_files_to_restore: 5, post_compact_token_budget: 50_000, }
}
}
impl CompactConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_auto_compact(mut self, enabled: bool) -> Self {
self.auto_compact_enabled = enabled;
self
}
pub fn with_strategy(mut self, strategy: CompactStrategy) -> Self {
self.strategy = strategy;
self
}
pub fn with_buffer_tokens(mut self, tokens: u32) -> Self {
self.auto_compact_buffer_tokens = tokens;
self
}
}
impl CompactConfig {
pub fn get_auto_compact_threshold(&self, context_window: u32) -> u32 {
context_window.saturating_sub(self.auto_compact_buffer_tokens)
}
pub fn get_warning_threshold(&self, context_window: u32) -> u32 {
context_window.saturating_sub(self.warning_buffer_tokens)
}
pub fn get_error_threshold(&self, context_window: u32) -> u32 {
context_window.saturating_sub(self.error_buffer_tokens)
}
pub fn get_manual_compact_threshold(&self, context_window: u32) -> u32 {
context_window.saturating_sub(self.manual_compact_buffer_tokens)
}
pub fn should_compact(&self, current_tokens: u32, context_window: u32) -> bool {
if !self.auto_compact_enabled {
return false;
}
let threshold = self.get_auto_compact_threshold(context_window);
current_tokens >= threshold
}
pub fn is_at_warning_level(&self, current_tokens: u32, context_window: u32) -> bool {
current_tokens >= self.get_warning_threshold(context_window)
}
pub fn is_at_error_level(&self, current_tokens: u32, context_window: u32) -> bool {
current_tokens >= self.get_error_threshold(context_window)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = CompactConfig::default();
assert!(config.auto_compact_enabled);
assert_eq!(config.auto_compact_buffer_tokens, 13_000);
assert_eq!(config.warning_buffer_tokens, 20_000);
}
#[test]
fn test_threshold_calculation() {
let config = CompactConfig::default();
let context_window = 200_000;
assert_eq!(config.get_auto_compact_threshold(context_window), 187_000);
assert_eq!(config.get_warning_threshold(context_window), 180_000);
assert_eq!(config.get_error_threshold(context_window), 180_000);
}
#[test]
fn test_should_compact() {
let config = CompactConfig::default();
let context_window = 200_000;
assert!(!config.should_compact(150_000, context_window));
assert!(config.should_compact(190_000, context_window));
let config_disabled = CompactConfig::new().with_auto_compact(false);
assert!(!config_disabled.should_compact(190_000, context_window));
}
}