Refine additional-context byte histogram buckets (#58939)

## Why

The previous histogram placed post-truncation values just over 4,000 bytes in a bucket ending at 8,000 bytes, obscuring their size near the truncation cap.

## What changed

Use logarithmic boundaries through 4 MiB for `codex.additional_context.value_bytes`, with an explicit 4 KiB boundary near the truncation cap.

## Testing

Add a regression test verifying that post-truncation samples between 4,024 and 4,043 bytes land in buckets ending at or below 4 KiB while preserving their sum and count.

GitOrigin-RevId: af268fc0de3d5ceb648ce51fced3918317f2457a
This commit is contained in:
Alex Kira
2026-11-02 18:05:13 +00:00
committed by copyberry
parent 62bf900680
commit 1a604d58da
3 changed files with 44 additions and 18 deletions
@@ -120,7 +120,7 @@ impl AdditionalContextStore {
telemetry.histogram_with_boundaries(
ADDITIONAL_CONTEXT_VALUE_BYTES_METRIC,
i64::try_from(bytes).unwrap_or(i64::MAX),
ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS,
ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS.as_slice(),
&size_tags,
);
}
+10 -17
View File
@@ -75,23 +75,16 @@ pub const THREAD_STARTED_METRIC: &str = "codex.thread.started";
pub const ADDITIONAL_CONTEXT_COUNT_METRIC: &str = "codex.additional_context";
/// UTF-8 value bytes before/after truncation, excluding message wrappers; after includes the marker.
pub const ADDITIONAL_CONTEXT_VALUE_BYTES_METRIC: &str = "codex.additional_context.value_bytes";
pub const ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS: &[f64] = &[
0.0,
256.0,
512.0,
1_000.0,
2_000.0,
4_000.0,
8_000.0,
16_000.0,
32_000.0,
64_000.0,
128_000.0,
256_000.0,
512_000.0,
1_024_000.0,
4_096_000.0,
];
/// Logarithmic byte buckets through 4 MiB, with a 4 KiB boundary near the truncation cap.
pub static ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS: LazyLock<Vec<f64>> = LazyLock::new(|| {
let mut buckets = context_log_buckets(/*max_exponent*/ 22.0)
.into_iter()
.chain([4096.0])
.collect::<Vec<_>>();
buckets.sort_by(f64::total_cmp);
buckets.dedup();
buckets
});
pub const THREAD_SKILLS_ENABLED_TOTAL_METRIC: &str = "codex.thread.skills.enabled_total";
pub const THREAD_SKILLS_KEPT_TOTAL_METRIC: &str = "codex.thread.skills.kept_total";
pub const THREAD_SKILLS_DESCRIPTION_TRUNCATED_CHARS_METRIC: &str =
+33
View File
@@ -3,6 +3,8 @@ use crate::harness::build_metrics_with_defaults;
use crate::harness::find_metric;
use crate::harness::histogram_data;
use crate::harness::latest_metrics;
use codex_otel::ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS;
use codex_otel::ADDITIONAL_CONTEXT_VALUE_BYTES_METRIC;
use codex_otel::Result;
use codex_otel::THREAD_SKILLS_COUNT_METRIC_BUCKETS;
use codex_otel::THREAD_SKILLS_DESCRIPTION_TRUNCATED_CHARS_BUCKETS;
@@ -135,6 +137,37 @@ fn histogram_uses_explicit_bucket_boundaries() -> Result<()> {
Ok(())
}
#[test]
fn additional_context_histogram_bounds_truncated_observations() -> Result<()> {
let (metrics, exporter) = build_metrics_with_defaults(&[])?;
let samples = [4_024, 4_027, 4_043];
for value in samples {
metrics.histogram_with_boundaries(
ADDITIONAL_CONTEXT_VALUE_BYTES_METRIC,
value,
ADDITIONAL_CONTEXT_VALUE_BYTES_BUCKETS.as_slice(),
&[("stage", "after"), ("truncated", "true")],
)?;
}
metrics.shutdown()?;
let (bounds, counts, sum, count) = histogram_data(
&latest_metrics(&exporter),
ADDITIONAL_CONTEXT_VALUE_BYTES_METRIC,
);
assert_eq!((sum, count), (12_094.0, 3));
assert_eq!(counts.iter().sum::<u64>(), count);
assert_eq!(counts.last(), Some(&0));
for (upper_bound, bucket_count) in bounds.iter().zip(&counts) {
assert!(
*bucket_count == 0 || *upper_bound <= 4_096.0,
"post-truncation observations landed in a bucket ending at {upper_bound} bytes"
);
}
Ok(())
}
#[test]
fn context_histograms_preserve_zero_and_family_ranges() -> Result<()> {
let (metrics, exporter) = build_metrics_with_defaults(&[])?;