use crate::types::Message;
pub const TIME_BASED_MC_CLEARED_MESSAGE: &str = "[Old tool result content cleared]";
const IMAGE_MAX_TOKEN_SIZE: usize = 2000;
const MAX_TOOL_RESULT_CHARS: usize = 16_000;
const MAX_GLOB_RESULTS: usize = 100;
pub fn truncate_tool_result_content(content: &str, tool_name: &str) -> String {
if tool_name == "Glob" {
let total_lines = content.lines().count();
if total_lines <= MAX_GLOB_RESULTS {
return content.to_string();
}
let lines: Vec<&str> = content.lines().take(MAX_GLOB_RESULTS).collect();
let truncated = lines.join("\n");
return format!(
"{}\n\n... ({} more files not shown. Use more specific glob patterns to reduce results)",
truncated,
total_lines.saturating_sub(MAX_GLOB_RESULTS)
);
}
if content.len() <= MAX_TOOL_RESULT_CHARS {
return content.to_string();
}
let chars: Vec<char> = content.chars().take(MAX_TOOL_RESULT_CHARS).collect();
format!(
"{}\n\n... (truncated {} characters)",
chars.into_iter().collect::<String>(),
content.len().saturating_sub(MAX_TOOL_RESULT_CHARS)
)
}
pub fn microcompact_messages(messages: &mut [Message]) {
for msg in messages.iter_mut() {
if let crate::types::MessageRole::Tool = msg.role {
if msg.content.len() > MAX_TOOL_RESULT_CHARS {
let truncated = truncate_tool_result_content(&msg.content, "Tool");
msg.content = truncated;
}
}
}
}
pub fn calculate_tool_result_tokens(content: &str) -> usize {
content.len() / 4
}
pub fn needs_microcompact(messages: &[Message], threshold: usize) -> bool {
let total_tool_chars: usize = messages
.iter()
.filter(|m| m.role == crate::types::MessageRole::Tool)
.map(|m| m.content.len())
.sum();
let estimated_tokens = total_tool_chars / 4;
estimated_tokens > threshold
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_tool_result_small() {
let content = "small content";
let result = truncate_tool_result_content(content, "Read");
assert_eq!(result, "small content");
}
#[test]
fn test_truncate_tool_result_large() {
let content = "x".repeat(20000);
let result = truncate_tool_result_content(&content, "Read");
assert!(result.len() < content.len());
assert!(result.contains("truncated"));
}
#[test]
fn test_truncate_glob_results() {
let files: Vec<String> = (0..200).map(|i| format!("file{}.txt", i)).collect();
let content = files.join("\n");
let result = truncate_tool_result_content(&content, "Glob");
assert!(result.len() < content.len());
assert!(result.contains("not shown"));
}
#[test]
fn test_calculate_tool_result_tokens() {
let content = "test content";
let tokens = calculate_tool_result_tokens(content);
assert!(tokens > 0);
}
}