#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolOutputCompression {
pub model_content: String,
pub raw_content: Option<String>,
pub raw_flag: u8,
}
pub fn compress_tool_output(content: &str, threshold: usize) -> ToolOutputCompression {
if content.is_empty() || content.len() <= threshold {
return ToolOutputCompression {
model_content: content.to_string(),
raw_content: None,
raw_flag: 0,
};
}
let total = content.len();
let prefix_end = content
.char_indices()
.map(|(index, _)| index)
.take_while(|index| *index <= threshold)
.last()
.unwrap_or(0);
let summary = format!(
"{}\n... [truncated, {total} bytes total]",
&content[..prefix_end]
);
ToolOutputCompression {
model_content: summary,
raw_content: Some(content.to_string()),
raw_flag: 1,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn under_threshold_no_compression() {
let result = compress_tool_output("hello world", 100);
assert_eq!(result.model_content, "hello world");
assert_eq!(result.raw_content, None);
assert_eq!(result.raw_flag, 0);
}
#[test]
fn over_threshold_summary_and_raw() {
let content = "A".repeat(200);
let result = compress_tool_output(&content, 50);
assert_eq!(result.raw_flag, 1);
assert_eq!(result.raw_content, Some(content.clone()));
assert_eq!(
result.model_content,
format!("{}\n... [truncated, 200 bytes total]", "A".repeat(50))
);
}
#[test]
fn empty_content_no_compression() {
let result = compress_tool_output("", 100);
assert_eq!(result.model_content, "");
assert_eq!(result.raw_content, None);
assert_eq!(result.raw_flag, 0);
}
#[test]
fn exactly_at_threshold_no_compression() {
let content = "B".repeat(50);
let result = compress_tool_output(&content, 50);
assert_eq!(result.model_content, content);
assert_eq!(result.raw_content, None);
assert_eq!(result.raw_flag, 0);
}
#[test]
fn truncation_preserves_utf8_boundaries() {
let result = compress_tool_output("你好世界", 5);
assert_eq!(result.model_content, "你\n... [truncated, 12 bytes total]");
assert_eq!(result.raw_content.as_deref(), Some("你好世界"));
}
}