Files
codex/codex-rs/analytics/src/turn_critical_path.rs
T
taohong-oai db14d7508d Report auto-review overhead in turn critical-path analytics (#59260)
## What changed

Track Guardian compaction and review turn execution within each tool's auto-review interval, and expose `critical_path_auto_review_overhead_ms` in turn analytics. This measures the portion of `auto_review_ms` outside observed Guardian turn execution; it is a subset, not an additional duration.

Freeze review observations when the review or tool invocation ends. Report unknown overhead as `null`, including when denial reconsideration runs an unobserved follow-up turn.

## Testing

Add coverage for shared compaction and review observations, cached reviews, unfinished reviews, reused tool call IDs, and execution timing with child critical-path capture enabled or disabled. Extend analytics and app-server tests to check the new field.

GitOrigin-RevId: a63dec2a75571a3167c8c3f0c1d21e11e4a8d050
2026-11-03 23:49:01 +00:00

85 lines
3.2 KiB
Rust

//! Turn critical-path facts and their nullable analytics fields.
//! Missing capture never becomes a zero-duration measurement.
use serde::Serialize;
/// Capture coverage for one finalized turn, independent of its completion status.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
pub enum TurnCriticalPath {
#[default]
Disabled,
Incomplete,
Complete(TurnCriticalPathDurations),
}
impl TurnCriticalPath {
pub fn status(&self) -> &'static str {
match self {
Self::Disabled => "disabled",
Self::Incomplete => "incomplete",
Self::Complete(_) => "complete",
}
}
pub fn durations(&self) -> Option<&TurnCriticalPathDurations> {
match self {
Self::Complete(durations) => Some(durations),
Self::Disabled | Self::Incomplete => None,
}
}
}
/// Category totals over the server turn interval, truncated to milliseconds.
/// Inference is the compatibility total of sampling and compaction, not another leaf.
/// Independent truncation can leave the seven leaf categories up to six milliseconds
/// below duration_ms, and sampling_ms + compaction_ms up to one below inference_ms.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct TurnCriticalPathDurations {
pub duration_ms: u64,
pub inference_ms: u64,
pub sampling_ms: u64,
pub compaction_ms: u64,
pub auto_review_ms: u64,
/// Portion of auto_review_ms outside observed Guardian turn execution; not additive.
pub auto_review_overhead_ms: Option<u64>,
pub user_wait_ms: u64,
pub tools_ms: u64,
pub overhead_ms: u64,
pub unknown_ms: u64,
}
#[derive(Serialize)]
pub(crate) struct CriticalPathEventParams {
critical_path_status: &'static str,
critical_path_duration_ms: Option<u64>,
critical_path_inference_ms: Option<u64>,
critical_path_sampling_ms: Option<u64>,
critical_path_compaction_ms: Option<u64>,
critical_path_auto_review_ms: Option<u64>,
critical_path_auto_review_overhead_ms: Option<u64>,
critical_path_user_wait_ms: Option<u64>,
critical_path_tools_ms: Option<u64>,
critical_path_overhead_ms: Option<u64>,
critical_path_unknown_ms: Option<u64>,
}
impl From<TurnCriticalPath> for CriticalPathEventParams {
fn from(path: TurnCriticalPath) -> Self {
let durations = path.durations();
Self {
critical_path_status: path.status(),
critical_path_duration_ms: durations.map(|value| value.duration_ms),
critical_path_inference_ms: durations.map(|value| value.inference_ms),
critical_path_sampling_ms: durations.map(|value| value.sampling_ms),
critical_path_compaction_ms: durations.map(|value| value.compaction_ms),
critical_path_auto_review_ms: durations.map(|value| value.auto_review_ms),
critical_path_auto_review_overhead_ms: durations
.and_then(|value| value.auto_review_overhead_ms),
critical_path_user_wait_ms: durations.map(|value| value.user_wait_ms),
critical_path_tools_ms: durations.map(|value| value.tools_ms),
critical_path_overhead_ms: durations.map(|value| value.overhead_ms),
critical_path_unknown_ms: durations.map(|value| value.unknown_ms),
}
}
}