vtcode_core/tools/summarizers/
mod.rs1use anyhow::Result;
15
16use crate::utils::tokens::{estimate_tokens, truncate_to_tokens};
17
18pub mod execution;
19pub mod file_ops;
20pub mod search;
21
22#[derive(Debug, Clone)]
24pub struct SavingsEstimate {
25 pub llm_tokens: usize,
27 pub ui_tokens: usize,
29 pub savings_percent: f32,
31}
32
33use std::borrow::Cow;
34
35pub(super) fn truncate_line<'a>(line: &'a str, max_len: usize) -> Cow<'a, str> {
39 if line.len() <= max_len {
40 Cow::Borrowed(line)
41 } else {
42 let prefix = vtcode_commons::formatting::truncate_utf8_prefix(line, max_len.saturating_sub(3));
43 Cow::Owned(format!("{prefix}..."))
44 }
45}
46
47pub trait Summarizer {
52 fn summarize(&self, full_output: &str, metadata: Option<&serde_json::Value>) -> Result<String>;
61
62 fn estimate_savings(&self, full_output: &str, summary: &str) -> SavingsEstimate {
64 let ui_tokens = estimate_tokens(full_output);
65 let llm_tokens = estimate_tokens(summary);
66 let savings = ui_tokens.saturating_sub(llm_tokens);
67 let savings_percent = if ui_tokens > 0 {
68 (savings as f32 / ui_tokens as f32) * 100.0
69 } else {
70 0.0
71 };
72 SavingsEstimate { llm_tokens, ui_tokens, savings_percent }
73 }
74}
75
76pub fn extract_key_info(text: &str, max_lines: usize) -> String {
80 let mut lines: Vec<&str> = Vec::with_capacity(max_lines.min(32));
81 let mut total_lines = 0usize;
82 for line in text.lines() {
83 total_lines += 1;
84 if lines.len() < max_lines {
85 lines.push(line);
86 }
87 }
88
89 if total_lines > max_lines {
90 format!("{}\n[...{} more lines]", lines.join("\n"), total_lines - max_lines)
91 } else {
92 lines.join("\n")
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_estimate_tokens() {
102 assert_eq!(estimate_tokens("Hello world"), 2);
104 assert_eq!(estimate_tokens(""), 0);
105 assert_eq!(estimate_tokens("a".repeat(1000).as_str()), 125);
107 }
108
109 #[test]
110 fn test_truncate_to_tokens() {
111 let text = "a".repeat(1000);
112 let truncated = truncate_to_tokens(&text, 50); assert!(truncated.len() <= 500); assert!(truncated.len() < text.len());
117 }
118
119 #[test]
120 fn test_truncate_short_text() {
121 let text = "Short text";
122 let truncated = truncate_to_tokens(text, 100);
123 assert_eq!(truncated, text);
124 }
125
126 #[test]
127 fn test_extract_key_info() {
128 let text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5";
129 let extracted = extract_key_info(text, 3);
130 assert!(extracted.contains("Line 1"));
131 assert!(extracted.contains("Line 3"));
132 assert!(extracted.contains("[...2 more lines]"));
133 }
134
135 #[test]
136 fn test_extract_key_info_exact() {
137 let text = "Line 1\nLine 2\nLine 3";
138 let extracted = extract_key_info(text, 3);
139 assert_eq!(extracted, text);
140 assert!(!extracted.contains("more lines"));
141 }
142}