lean_ctx/core/ocla/builtin/
compression_provider.rs1use std::collections::BTreeMap;
8use std::path::{Path, PathBuf};
9use std::sync::OnceLock;
10
11use crate::core::compressor;
12use crate::core::config::Config;
13use crate::core::ocla::OclaError;
14use crate::core::ocla::content_port::CompressionContentPort;
15use crate::core::ocla::traits::{CompressionProvider, OclaService};
16use crate::core::ocla::types::{
17 CompressionRequest, CompressionResult, OCLA_API_VERSION, OclaCapability, OclaCapabilityKind,
18 OclaCapabilityStatus, OclaResult,
19};
20use crate::core::ocla_bus::{self, OclaEvent};
21use crate::core::tokens;
22
23static DEFAULT_PORT: OnceLock<Option<CompressionContentPort>> = OnceLock::new();
24
25fn port_from_project_root(root_str: Option<String>) -> Option<CompressionContentPort> {
26 let root_str = root_str?;
27 if root_str.trim().is_empty() || root_str == "." {
28 return None;
29 }
30 let root = PathBuf::from(root_str);
31 let mut component_path = PathBuf::new();
32 for component in root.components() {
33 component_path.push(component.as_os_str());
34 if component_path
35 .symlink_metadata()
36 .ok()?
37 .file_type()
38 .is_symlink()
39 {
40 return None;
41 }
42 }
43 if !root.symlink_metadata().ok()?.is_dir() {
44 return None;
45 }
46 let canonical = root.canonicalize().ok()?;
47 CompressionContentPort::new(canonical)
48}
49
50fn try_default_port() -> Option<&'static CompressionContentPort> {
51 DEFAULT_PORT
52 .get_or_init(|| port_from_project_root(Config::find_project_root()))
53 .as_ref()
54}
55
56pub struct BuiltinCompressionProvider;
57
58impl BuiltinCompressionProvider {
59 pub fn new() -> Self {
60 Self
61 }
62
63 pub fn compress_with_port(
64 &self,
65 request: CompressionRequest,
66 port: &CompressionContentPort,
67 ) -> OclaResult<CompressionResult> {
68 if request.source_tokens == 0 {
69 return Err(OclaError::InvalidRequest(
70 "source_tokens must be > 0".into(),
71 ));
72 }
73 if request.target_tokens == 0 {
74 return Err(OclaError::InvalidRequest(
75 "target_tokens must be > 0".into(),
76 ));
77 }
78
79 if !request.source_ref.starts_with("file:") {
80 return Err(OclaError::InvalidRequest(format!(
81 "only file: refs supported, got: {}",
82 request.source_ref.split(':').next().unwrap_or("unknown")
83 )));
84 }
85
86 let bytes = port.resolve(&request.source_ref)?;
87
88 let source_text = std::str::from_utf8(&bytes)
89 .map_err(|_| OclaError::InvalidRequest("source is not valid UTF-8".into()))?;
90
91 let ext = request
92 .source_ref
93 .strip_prefix("file:")
94 .and_then(|p| Path::new(p).extension())
95 .and_then(|e| e.to_str());
96
97 let compressed = compressor::aggressive_compress(source_text, ext);
98 let delivered_tokens = tokens::count_tokens(&compressed) as u64;
99
100 if delivered_tokens >= request.source_tokens {
101 return Err(OclaError::InvalidRequest(
102 "compression produced no gain (output >= source)".into(),
103 ));
104 }
105
106 if delivered_tokens > request.target_tokens {
107 return Err(OclaError::InvalidRequest(format!(
108 "compressed output ({delivered_tokens}) exceeds target ({})",
109 request.target_tokens
110 )));
111 }
112
113 let ref_key = port.persist(compressed.as_bytes())?;
114
115 ocla_bus::emit(OclaEvent::CompressionApplied {
116 path: Some(request.source_ref.clone()),
117 before_tokens: request.source_tokens,
118 after_tokens: delivered_tokens,
119 strategy: "aggressive_compress".to_string(),
120 });
121
122 Ok(CompressionResult {
123 delivered_ref: ref_key,
124 delivered_tokens,
125 recovery_ref: Some(request.source_ref),
126 })
127 }
128}
129
130impl Default for BuiltinCompressionProvider {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl OclaService for BuiltinCompressionProvider {
137 fn capability(&self) -> OclaCapability {
138 if try_default_port().is_some() {
139 OclaCapability::available(OclaCapabilityKind::CompressionProvider)
140 } else {
141 OclaCapability {
142 kind: OclaCapabilityKind::CompressionProvider,
143 api_version: OCLA_API_VERSION.to_string(),
144 status: OclaCapabilityStatus::Unavailable,
145 limits: BTreeMap::new(),
146 }
147 }
148 }
149}
150
151impl CompressionProvider for BuiltinCompressionProvider {
152 fn compress(&self, request: CompressionRequest) -> OclaResult<CompressionResult> {
153 let port = try_default_port().ok_or_else(|| {
154 OclaError::InvalidRequest("compression unavailable: no valid project root".into())
155 })?;
156 self.compress_with_port(request, port)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::core::ocla::types::OclaRequestContext;
164 use std::fs;
165
166 fn ctx() -> OclaRequestContext {
167 OclaRequestContext {
168 request_id: "r1".into(),
169 session_id: "s1".into(),
170 agent_id: "agent-test".into(),
171 content_ref: "blake3:test".into(),
172 tenant_id: None,
173 trace_id: "tr-unit".into(),
174 }
175 }
176
177 #[test]
178 fn compress_rejects_non_file_ref() {
179 let dir = tempfile::tempdir().unwrap();
180 let root = dir.path().canonicalize().unwrap();
181 let port = CompressionContentPort::new(&root).unwrap();
182 let provider = BuiltinCompressionProvider::new();
183 let err = provider
184 .compress_with_port(
185 CompressionRequest {
186 context: ctx(),
187 source_ref: "mem:buffer-123".into(),
188 source_tokens: 1000,
189 target_tokens: 300,
190 quality_policy_ref: None,
191 },
192 &port,
193 )
194 .unwrap_err();
195 assert!(err.to_string().contains("only file: refs"));
196 }
197
198 #[test]
199 fn compress_rejects_zero_source_tokens() {
200 let dir = tempfile::tempdir().unwrap();
201 let root = dir.path().canonicalize().unwrap();
202 let port = CompressionContentPort::new(&root).unwrap();
203 let provider = BuiltinCompressionProvider::new();
204 let err = provider
205 .compress_with_port(
206 CompressionRequest {
207 context: ctx(),
208 source_ref: "file:test.rs".into(),
209 source_tokens: 0,
210 target_tokens: 300,
211 quality_policy_ref: None,
212 },
213 &port,
214 )
215 .unwrap_err();
216 assert!(err.to_string().contains("source_tokens"));
217 }
218
219 #[test]
220 fn compress_rejects_zero_target_tokens() {
221 let dir = tempfile::tempdir().unwrap();
222 let root = dir.path().canonicalize().unwrap();
223 let port = CompressionContentPort::new(&root).unwrap();
224 let provider = BuiltinCompressionProvider::new();
225 let err = provider
226 .compress_with_port(
227 CompressionRequest {
228 context: ctx(),
229 source_ref: "file:test.rs".into(),
230 source_tokens: 100,
231 target_tokens: 0,
232 quality_policy_ref: None,
233 },
234 &port,
235 )
236 .unwrap_err();
237 assert!(err.to_string().contains("target_tokens"));
238 }
239
240 #[test]
241 fn compress_with_real_file_returns_blake3_ref() {
242 let dir = tempfile::tempdir().unwrap();
243 let root = dir.path().canonicalize().unwrap();
244 let content = "use std::collections::HashMap;\n\
245 use std::io::{self, Read, Write, BufReader, BufWriter};\n\
246 use std::fs::File;\n\n\
247 /// Verbose doc that should compress well.\n\
248 /// Another line of documentation.\n\
249 /// Even more verbose documentation here.\n\
250 fn main() -> io::Result<()> {\n \
251 // Initialize the hashmap for storing key-value pairs\n \
252 let mut hash_map_instance: HashMap<String, String> = HashMap::new();\n \
253 // Insert the first key-value pair into the hashmap\n \
254 hash_map_instance.insert(String::from(\"key_one\"), String::from(\"value_one\"));\n \
255 // Insert the second key-value pair into the hashmap\n \
256 hash_map_instance.insert(String::from(\"key_two\"), String::from(\"value_two\"));\n \
257 // Insert the third key-value pair into the hashmap\n \
258 hash_map_instance.insert(String::from(\"key_three\"), String::from(\"value_three\"));\n \
259 // Iterate over all key-value pairs and print them\n \
260 for (key_variable, value_variable) in hash_map_instance.iter() {\n \
261 // Print the current key and value\n \
262 println!(\"Key: {}, Value: {}\", key_variable, value_variable);\n \
263 }\n \
264 // Open a file for reading\n \
265 let input_file_handle: File = File::open(\"input.txt\")?;\n \
266 // Create a buffered reader\n \
267 let mut buffered_reader_instance: BufReader<File> = BufReader::new(input_file_handle);\n \
268 // Read all contents into a string\n \
269 let mut file_contents_buffer: String = String::new();\n \
270 buffered_reader_instance.read_to_string(&mut file_contents_buffer)?;\n \
271 // Print the length\n \
272 println!(\"Read {} bytes from input file\", file_contents_buffer.len());\n \
273 Ok(())\n\
274 }\n";
275 fs::write(root.join("test.rs"), content).unwrap();
276
277 let port = CompressionContentPort::new(&root).unwrap();
278 let provider = BuiltinCompressionProvider::new();
279 let source_tokens = tokens::count_tokens(content) as u64;
280
281 let result = provider.compress_with_port(
282 CompressionRequest {
283 context: ctx(),
284 source_ref: "file:test.rs".into(),
285 source_tokens,
286 target_tokens: source_tokens,
287 quality_policy_ref: None,
288 },
289 &port,
290 );
291
292 let r = result.unwrap();
293 assert!(r.delivered_ref.starts_with("blake3:"));
294 assert!(r.delivered_tokens < source_tokens);
295 assert_eq!(r.recovery_ref, Some("file:test.rs".into()));
296 let delivered = port.retrieve(&r.delivered_ref).unwrap();
297 assert!(delivered.len() < content.len());
298 }
299
300 #[test]
301 fn compress_propagates_resolve_error() {
302 let dir = tempfile::tempdir().unwrap();
303 let root = dir.path().canonicalize().unwrap();
304 let port = CompressionContentPort::new(&root).unwrap();
305 let provider = BuiltinCompressionProvider::new();
306 let err = provider
307 .compress_with_port(
308 CompressionRequest {
309 context: ctx(),
310 source_ref: "file:nonexistent.rs".into(),
311 source_tokens: 100,
312 target_tokens: 50,
313 quality_policy_ref: None,
314 },
315 &port,
316 )
317 .unwrap_err();
318 let msg = err.to_string();
319 assert!(
320 msg.contains("resolve")
321 || msg.contains("No such file")
322 || msg.contains("not found")
323 || msg.contains("cannot find"),
324 "unexpected error: {msg}"
325 );
326 }
327
328 #[test]
329 fn compress_empty_file_persists_empty_output() {
330 let dir = tempfile::tempdir().unwrap();
331 let root = dir.path().canonicalize().unwrap();
332 fs::write(root.join("empty.rs"), b"").unwrap();
333 let port = CompressionContentPort::new(&root).unwrap();
334
335 let result = BuiltinCompressionProvider::new()
336 .compress_with_port(
337 CompressionRequest {
338 context: ctx(),
339 source_ref: "file:empty.rs".into(),
340 source_tokens: 1,
341 target_tokens: 1,
342 quality_policy_ref: None,
343 },
344 &port,
345 )
346 .unwrap();
347
348 assert_eq!(result.delivered_tokens, 0);
349 assert_eq!(result.recovery_ref, Some("file:empty.rs".into()));
350 assert!(port.retrieve(&result.delivered_ref).unwrap().is_empty());
351 }
352
353 #[test]
354 fn no_gain_rejects_before_persisting() {
355 let dir = tempfile::tempdir().unwrap();
356 let root = dir.path().canonicalize().unwrap();
357 let content = "let value = 1;\n";
358 fs::write(root.join("small.rs"), content).unwrap();
359 let compressed = compressor::aggressive_compress(content, Some("rs"));
360 let source_tokens = tokens::count_tokens(&compressed) as u64;
361 assert!(source_tokens > 0);
362 let ref_key = format!("blake3:{}", blake3::hash(compressed.as_bytes()).to_hex());
363 let port = CompressionContentPort::new(&root).unwrap();
364
365 let err = BuiltinCompressionProvider::new()
366 .compress_with_port(
367 CompressionRequest {
368 context: ctx(),
369 source_ref: "file:small.rs".into(),
370 source_tokens,
371 target_tokens: 100,
372 quality_policy_ref: None,
373 },
374 &port,
375 )
376 .unwrap_err();
377
378 assert!(err.to_string().contains("no gain"));
379 assert!(port.retrieve(&ref_key).is_err());
380 }
381
382 #[test]
383 fn target_cap_rejects_before_persisting() {
384 let dir = tempfile::tempdir().unwrap();
385 let root = dir.path().canonicalize().unwrap();
386 let content = "// repeated documentation\nfn main() {\n let value = 1;\n}\n";
387 fs::write(root.join("target.rs"), content).unwrap();
388 let compressed = compressor::aggressive_compress(content, Some("rs"));
389 let delivered_tokens = tokens::count_tokens(&compressed) as u64;
390 assert!(delivered_tokens > 1);
391 let ref_key = format!("blake3:{}", blake3::hash(compressed.as_bytes()).to_hex());
392 let port = CompressionContentPort::new(&root).unwrap();
393
394 let err = BuiltinCompressionProvider::new()
395 .compress_with_port(
396 CompressionRequest {
397 context: ctx(),
398 source_ref: "file:target.rs".into(),
399 source_tokens: tokens::count_tokens(content) as u64,
400 target_tokens: delivered_tokens - 1,
401 quality_policy_ref: None,
402 },
403 &port,
404 )
405 .unwrap_err();
406
407 assert!(err.to_string().contains("exceeds target"));
408 assert!(port.retrieve(&ref_key).is_err());
409 }
410
411 #[cfg(unix)]
412 #[test]
413 fn project_root_symlink_is_unavailable() {
414 let dir = tempfile::tempdir().unwrap();
415 let link = dir.path().join("root-link");
416 std::os::unix::fs::symlink(dir.path(), &link).unwrap();
417 assert!(port_from_project_root(Some(link.to_string_lossy().into_owned())).is_none());
418 }
419
420 #[test]
421 fn project_root_empty_or_dot_is_unavailable() {
422 assert!(port_from_project_root(Some(String::new())).is_none());
423 assert!(port_from_project_root(Some(".".into())).is_none());
424 }
425
426 #[test]
427 fn capability_reflects_root_availability() {
428 let provider = BuiltinCompressionProvider::new();
429 let cap = provider.capability();
430 assert_eq!(cap.kind, OclaCapabilityKind::CompressionProvider);
431 }
432}