agent: Truncate bash tool output (#28291)

The bash tool will now truncate its output to 8192 bytes (or the last
newline before that).

We also added a global limit for any tool that produces a clearly large
output that wouldn't fit the context window.

Release Notes:

- agent: Truncate bash tool output

---------

Co-authored-by: Michael Sloan <mgsloan@gmail.com>
This commit is contained in:
Agus Zubiaga 2025-04-08 10:55:35 -06:00 committed by GitHub
parent 1774cad933
commit 85c5d8af3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 164 additions and 16 deletions

View file

@ -145,6 +145,66 @@ pub fn truncate_lines_and_trailoff(s: &str, max_lines: usize) -> String {
}
}
/// Truncates the string at a character boundary, such that the result is less than `max_bytes` in
/// length.
pub fn truncate_to_byte_limit(s: &str, max_bytes: usize) -> &str {
if s.len() < max_bytes {
return s;
}
for i in (0..max_bytes).rev() {
if s.is_char_boundary(i) {
return &s[..i];
}
}
""
}
/// Takes a prefix of complete lines which fit within the byte limit. If the first line is longer
/// than the limit, truncates at a character boundary.
pub fn truncate_lines_to_byte_limit(s: &str, max_bytes: usize) -> &str {
if s.len() < max_bytes {
return s;
}
for i in (0..max_bytes).rev() {
if s.is_char_boundary(i) {
if s.as_bytes()[i] == b'\n' {
// Since the i-th character is \n, valid to slice at i + 1.
return &s[..i + 1];
}
}
}
truncate_to_byte_limit(s, max_bytes)
}
#[test]
fn test_truncate_lines_to_byte_limit() {
let text = "Line 1\nLine 2\nLine 3\nLine 4";
// Limit that includes all lines
assert_eq!(truncate_lines_to_byte_limit(text, 100), text);
// Exactly the first line
assert_eq!(truncate_lines_to_byte_limit(text, 7), "Line 1\n");
// Limit between lines
assert_eq!(truncate_lines_to_byte_limit(text, 13), "Line 1\n");
assert_eq!(truncate_lines_to_byte_limit(text, 20), "Line 1\nLine 2\n");
// Limit before first newline
assert_eq!(truncate_lines_to_byte_limit(text, 6), "Line ");
// Test with non-ASCII characters
let text_utf8 = "Line 1\nLíne 2\nLine 3";
assert_eq!(
truncate_lines_to_byte_limit(text_utf8, 15),
"Line 1\nLíne 2\n"
);
}
pub fn post_inc<T: From<u8> + AddAssign<T> + Copy>(value: &mut T) -> T {
let prev = *value;
*value += T::from(1);