MCPcopy Create free account
hub / github.com/AI45Lab/Code / prune_tool_outputs

Function prune_tool_outputs

core/src/compaction.rs:157–224  ·  view source on GitHub ↗

Prune large tool outputs from messages to reclaim context space. Iterates backward from recent messages, protecting the first `TOOL_OUTPUT_PROTECT_TOKENS` worth of tool output tokens. Only prunes outputs that would save at least `MIN_PRUNE_SAVINGS_TOKENS`. Returns `Some(pruned_messages)` if any outputs were pruned, or `None` if no pruning was needed.

(messages: &[Message])

Source from the content-addressed store, hash-verified

155/// Returns `Some(pruned_messages)` if any outputs were pruned, or `None`
156/// if no pruning was needed.
157pub(crate) fn prune_tool_outputs(messages: &[Message]) -> Option<Vec<Message>> {
158 // First pass: estimate total tool output tokens (backward)
159 let mut tool_outputs: Vec<(usize, usize, usize)> = Vec::new(); // (msg_idx, block_idx, token_count)
160
161 for (msg_idx, msg) in messages.iter().enumerate() {
162 for (block_idx, block) in msg.content.iter().enumerate() {
163 if let ContentBlock::ToolResult { content, .. } = block {
164 let text = content.as_text();
165 let token_count = estimate_tokens(&text);
166 if token_count > 0 {
167 tool_outputs.push((msg_idx, block_idx, token_count));
168 }
169 }
170 }
171 }
172
173 if tool_outputs.is_empty() {
174 return None;
175 }
176
177 // Calculate total tool output tokens
178 let total_tool_tokens: usize = tool_outputs.iter().map(|(_, _, t)| *t).sum();
179
180 // If total is small, no pruning needed
181 if total_tool_tokens <= TOOL_OUTPUT_PROTECT_TOKENS {
182 return None;
183 }
184
185 // Iterate from oldest to newest, protecting the most recent outputs
186 // We prune old outputs first, keeping recent ones intact
187 let mut protected_tokens = 0usize;
188 let mut to_prune: Vec<(usize, usize)> = Vec::new();
189 let mut savings = 0usize;
190
191 // Walk backward (newest first) to protect recent outputs
192 for &(msg_idx, block_idx, token_count) in tool_outputs.iter().rev() {
193 if protected_tokens < TOOL_OUTPUT_PROTECT_TOKENS {
194 protected_tokens += token_count;
195 } else {
196 to_prune.push((msg_idx, block_idx));
197 savings += token_count;
198 }
199 }
200
201 // Only prune if savings are significant
202 if savings < MIN_PRUNE_SAVINGS_TOKENS {
203 return None;
204 }
205
206 // Apply pruning
207 let mut pruned = messages.to_vec();
208 for (msg_idx, block_idx) in &to_prune {
209 if let Some(msg) = pruned.get_mut(*msg_idx) {
210 if let Some(ContentBlock::ToolResult { content, .. }) = msg.content.get_mut(*block_idx)
211 {
212 *content = ToolResultContentField::Text(PRUNED_MARKER.to_string());
213 }
214 }

Callers 3

maybe_auto_compactMethod · 0.85

Calls 3

as_textMethod · 0.80
estimate_tokensFunction · 0.70
is_emptyMethod · 0.45

Tested by 2