agent_works/compression/
policy.rs1use std::io::BufRead;
19
20use async_trait::async_trait;
21
22#[async_trait]
32pub trait CompressionPolicy: Send + Sync {
33 async fn should_compress(&self, tokens_before: usize, msg_count: usize) -> bool;
43}
44
45pub struct AutoCompressionPolicy;
51
52#[async_trait]
53impl CompressionPolicy for AutoCompressionPolicy {
54 async fn should_compress(&self, _tokens_before: usize, _msg_count: usize) -> bool {
55 true
56 }
57}
58
59pub struct UserConfirmationPolicy {
67 reader: std::sync::Mutex<Box<dyn BufRead + Send>>,
68}
69
70impl UserConfirmationPolicy {
71 pub fn new(reader: impl BufRead + Send + 'static) -> Self {
73 Self {
74 reader: std::sync::Mutex::new(Box::new(reader)),
75 }
76 }
77
78 pub fn stdin() -> Self {
80 Self::new(std::io::BufReader::new(std::io::stdin()))
81 }
82}
83
84#[async_trait]
85impl CompressionPolicy for UserConfirmationPolicy {
86 async fn should_compress(&self, tokens_before: usize, msg_count: usize) -> bool {
87 println!(
88 "\n⏳ Compression needed (~{} tokens, {} messages). Proceed? (y/n): ",
89 tokens_before, msg_count
90 );
91 let mut reader = self.reader.lock().unwrap();
92 let mut input = String::new();
93 reader.read_line(&mut input).unwrap_or(0);
94 input.trim().to_lowercase() == "y"
95 }
96}
97
98pub struct RateLimitPolicy {
103 min_interval: std::time::Duration,
104 last_compression: std::sync::Mutex<Option<std::time::Instant>>,
105}
106
107impl RateLimitPolicy {
108 pub fn new(min_interval: std::time::Duration) -> Self {
110 Self {
111 min_interval,
112 last_compression: std::sync::Mutex::new(None),
113 }
114 }
115}
116
117#[async_trait]
118impl CompressionPolicy for RateLimitPolicy {
119 async fn should_compress(&self, _tokens_before: usize, _msg_count: usize) -> bool {
120 let mut last = self.last_compression.lock().unwrap();
121 if let Some(last_time) = *last
122 && last_time.elapsed() < self.min_interval
123 {
124 return false; }
126 *last = Some(std::time::Instant::now());
127 true
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[tokio::test]
136 async fn test_auto_policy_always_returns_true() {
137 let policy = AutoCompressionPolicy;
138 assert!(policy.should_compress(0, 0).await);
139 assert!(policy.should_compress(999_999, 1000).await);
140 }
141
142 #[tokio::test]
143 async fn test_rate_limit_policy_allows_first_call() {
144 let policy = RateLimitPolicy::new(std::time::Duration::from_secs(60));
145 assert!(policy.should_compress(4000, 10).await);
146 }
147
148 #[tokio::test]
149 async fn test_rate_limit_policy_blocks_second_call_within_interval() {
150 let policy = RateLimitPolicy::new(std::time::Duration::from_secs(60));
151 assert!(policy.should_compress(4000, 10).await);
152 assert!(!policy.should_compress(4000, 10).await);
154 }
155
156 #[tokio::test]
157 async fn test_rate_limit_policy_allows_after_interval() {
158 let policy = RateLimitPolicy::new(std::time::Duration::from_millis(20));
159 assert!(policy.should_compress(4000, 10).await);
160 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
162 assert!(policy.should_compress(4000, 10).await);
163 }
164
165 #[tokio::test]
166 async fn test_rate_limit_policy_tracks_last_compression_time() {
167 let policy = RateLimitPolicy::new(std::time::Duration::from_millis(20));
168
169 assert!(policy.should_compress(1000, 5).await);
171
172 assert!(!policy.should_compress(2000, 10).await);
174
175 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
177 assert!(policy.should_compress(3000, 15).await);
178
179 assert!(!policy.should_compress(4000, 20).await);
181 }
182
183 #[tokio::test]
186 async fn test_user_confirmation_accepts_y() {
187 let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"y\n".to_vec()));
188 assert!(policy.should_compress(4000, 10).await);
189 }
190
191 #[tokio::test]
192 async fn test_user_confirmation_accepts_uppercase_y() {
193 let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"Y\n".to_vec()));
194 assert!(policy.should_compress(4000, 10).await);
195 }
196
197 #[tokio::test]
198 async fn test_user_confirmation_rejects_n() {
199 let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"n\n".to_vec()));
200 assert!(!policy.should_compress(4000, 10).await);
201 }
202
203 #[tokio::test]
204 async fn test_user_confirmation_rejects_empty() {
205 let policy = UserConfirmationPolicy::new(std::io::Cursor::new(b"\n".to_vec()));
206 assert!(!policy.should_compress(4000, 10).await);
207 }
208}