use g3_core::ContextWindow; use g3_providers::{Message, MessageRole}; #[test] fn test_thinning_thresholds() { let mut context = ContextWindow::new(10000); // At 0%, should not thin assert!(!context.should_thin()); // Simulate reaching 50% usage context.used_tokens = 5000; assert!(context.should_thin()); // After thinning at 50%, should not thin again until next threshold context.last_thinning_percentage = 50; assert!(!context.should_thin()); // At 60%, should thin again context.used_tokens = 6000; assert!(context.should_thin()); // After thinning at 60%, should not thin context.last_thinning_percentage = 60; assert!(!context.should_thin()); // At 70%, should thin context.used_tokens = 7000; assert!(context.should_thin()); // At 80%, should thin context.last_thinning_percentage = 70; context.used_tokens = 8000; assert!(context.should_thin()); // After 80%, should not thin (compaction takes over) context.last_thinning_percentage = 80; context.used_tokens = 8500; assert!(!context.should_thin()); } #[test] fn test_thin_context_basic() { let mut context = ContextWindow::new(10000); // Add some messages to the first third for i in 0..9 { if i % 2 == 0 { context.add_message(Message::new( MessageRole::Assistant, format!("Assistant message {}", i), )); } else { // Add tool results with varying sizes let content = if i == 1 { // Large tool result (> 1000 chars) format!("Tool result: {}", "x".repeat(1500)) } else if i == 3 { // Another large tool result format!("Tool result: {}", "y".repeat(2000)) } else { // Small tool result (< 1000 chars) format!("Tool result: small result {}", i) }; context.add_message(Message::new(MessageRole::User, content)); } } // Trigger thinning at 50% context.used_tokens = 5000; let (summary, _chars_saved) = context.thin_context(); println!("Thinning summary: {}", summary); // Should have thinned at least 1 large tool result in the first third assert!( summary.contains("1 tool result"), "Summary was: {}", summary ); assert!(summary.contains("50%")); // Check that the large tool results were replaced let first_third_end = context.conversation_history.len() / 3; for i in 0..first_third_end { if let Some(msg) = context.conversation_history.get(i) { if matches!(msg.role, MessageRole::User) && msg.content.starts_with("Tool result:") { if msg.content.len() > 1000 { panic!("Found un-thinned large tool result at index {}", i); } } } } } #[test] fn test_thin_write_file_tool_calls() { let mut context = ContextWindow::new(10000); // Add some messages including a write_file tool call with large content context.add_message(Message::new( MessageRole::User, "Please create a large file".to_string(), )); // Add an assistant message with a write_file tool call containing large content let large_content = "x".repeat(1500); let tool_call_json = format!( r#"{{"tool": "write_file", "args": {{"file_path": "test.txt", "content": "{}"}}}}"#, large_content ); context.add_message(Message::new( MessageRole::Assistant, format!("I'll create that file.\n\n{}", tool_call_json), )); context.add_message(Message::new( MessageRole::User, "Tool result: ✅ Successfully wrote 1500 lines".to_string(), )); // Add more messages to ensure we have enough for "first third" logic for i in 0..6 { context.add_message(Message::new( MessageRole::Assistant, format!("Response {}", i), )); } // Trigger thinning at 50% context.used_tokens = 5000; let (summary, _chars_saved) = context.thin_context(); println!("Thinning summary: {}", summary); // Should have thinned the write_file tool call assert!(summary.contains("tool call") || summary.contains("chars saved")); // Check that the large content was replaced with a file reference let first_third_end = context.conversation_history.len() / 3; for i in 0..first_third_end { if let Some(msg) = context.conversation_history.get(i) { if matches!(msg.role, MessageRole::Assistant) && msg.content.contains("write_file") { // The content should now reference an external file assert!(msg.content.contains(" 1000, "Message at index {} should not have been thinned", i ); } } } } }