mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Cap persisted command output in paginated history at 64 KiB (#50427)
## Why Paginated history previously persisted completed commands' full `aggregated_output`, allowing large command outputs to inflate stored history. ## What changed - Limit persisted command `aggregated_output` to 64 KiB, including a truncation marker. Preserve the beginning and end of the output without splitting UTF-8 characters. - Apply the shared persistence policy to rollout recording, thread-store appends, and legacy-to-paginated history migration. - Measure persisted payload bytes after truncation so persistence metrics reflect the stored representation. ## Testing Add coverage for the byte limit, UTF-8 boundaries, unchanged inputs that fit, and markers that exceed the budget. Verify capped output in paginated thread history and migrated legacy history, and verify persistence metrics use the truncated payload size. GitOrigin-RevId: 01d420d396ad34b42dc5dc208c0bf88d890639ee
This commit is contained in:
@@ -7,6 +7,7 @@ pub use truncate::approx_bytes_for_tokens;
|
||||
pub use truncate::approx_token_count;
|
||||
pub use truncate::approx_tokens_from_byte_count;
|
||||
pub use truncate::truncate_middle_chars;
|
||||
pub use truncate::truncate_middle_with_marker;
|
||||
pub use truncate::truncate_middle_with_token_budget;
|
||||
|
||||
// Truncate a &str to a byte budget at a char boundary (prefix)
|
||||
|
||||
@@ -3,11 +3,35 @@
|
||||
|
||||
const APPROX_BYTES_PER_TOKEN: usize = 4;
|
||||
|
||||
/// Truncate a string to `max_bytes` using a character-count marker.
|
||||
/// Retain at most `max_bytes` from a string's ends and add a character-count
|
||||
/// marker outside that budget.
|
||||
///
|
||||
/// Use [`truncate_middle_with_marker`] when the complete result must fit a hard
|
||||
/// byte limit.
|
||||
pub fn truncate_middle_chars(s: &str, max_bytes: usize) -> String {
|
||||
truncate_with_byte_estimate(s, max_bytes, /*use_tokens*/ false)
|
||||
}
|
||||
|
||||
/// Truncate the middle of a UTF-8 string so the result, including `marker`, is
|
||||
/// at most `max_bytes` bytes.
|
||||
///
|
||||
/// Returns the original string when it already fits. If the marker itself does
|
||||
/// not fit, returns the longest original prefix that does.
|
||||
pub fn truncate_middle_with_marker(s: &str, max_bytes: usize, marker: &str) -> String {
|
||||
if s.len() <= max_bytes {
|
||||
return s.to_string();
|
||||
}
|
||||
|
||||
let Some(content_budget) = max_bytes.checked_sub(marker.len()) else {
|
||||
return s[..s.floor_char_boundary(max_bytes)].to_string();
|
||||
};
|
||||
let (left_budget, right_budget) = split_budget(content_budget);
|
||||
let prefix_end = s.floor_char_boundary(left_budget);
|
||||
let suffix_start = s.ceil_char_boundary(s.len().saturating_sub(right_budget));
|
||||
|
||||
assemble_truncated_output(&s[..prefix_end], &s[suffix_start..], marker)
|
||||
}
|
||||
|
||||
/// Truncate the middle of a UTF-8 string to at most `max_tokens` approximate
|
||||
/// tokens, preserving the beginning and the end. Returns the possibly
|
||||
/// truncated string and `Some(original_token_count)` if truncation occurred;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::split_string;
|
||||
use super::truncate_middle_chars;
|
||||
use super::truncate_middle_with_marker;
|
||||
use super::truncate_middle_with_token_budget;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -129,3 +130,36 @@ fn truncate_middle_bytes_handles_utf8_content() {
|
||||
let out = truncate_middle_chars(s, /*max_bytes*/ 20);
|
||||
assert_eq!(out, "😀😀…21 chars truncated…with text\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_middle_with_marker_enforces_the_total_byte_limit() {
|
||||
let input = format!("head{}tail", "x".repeat(100));
|
||||
assert_eq!(
|
||||
truncate_middle_with_marker(&input, /*max_bytes*/ 32, "<truncated>"),
|
||||
"headxxxxxx<truncated>xxxxxxxtail"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_middle_with_marker_preserves_utf8_boundaries() {
|
||||
assert_eq!(
|
||||
truncate_middle_with_marker("head🦀🦀🦀🦀🦀tail", /*max_bytes*/ 18, "..."),
|
||||
"head...🦀tail"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_middle_with_marker_returns_input_that_fits() {
|
||||
assert_eq!(
|
||||
truncate_middle_with_marker("short", /*max_bytes*/ 5, "..."),
|
||||
"short"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_middle_with_marker_keeps_a_prefix_when_the_marker_does_not_fit() {
|
||||
assert_eq!(
|
||||
truncate_middle_with_marker("🦀abc", /*max_bytes*/ 4, "marker"),
|
||||
"🦀"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user