mirror of
https://github.com/openai/codex.git
synced 2026-09-15 12:08:01 +00:00
Simplify turn duration metric tagging
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::Notify;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -19,14 +20,12 @@ use crate::tasks::SessionTask;
|
||||
pub(crate) struct ActiveTurn {
|
||||
pub(crate) tasks: IndexMap<String, RunningTask>,
|
||||
pub(crate) turn_state: Arc<Mutex<TurnState>>,
|
||||
pub(crate) started_at: Instant,
|
||||
}
|
||||
|
||||
impl Default for ActiveTurn {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tasks: IndexMap::new(),
|
||||
turn_state: Arc::new(Mutex::new(TurnState::default())),
|
||||
}
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +47,14 @@ pub(crate) struct RunningTask {
|
||||
}
|
||||
|
||||
impl ActiveTurn {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
tasks: IndexMap::new(),
|
||||
turn_state: Arc::new(Mutex::new(TurnState::default())),
|
||||
started_at: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_task(&mut self, task: RunningTask) {
|
||||
let sub_id = task.turn_context.sub_id.clone();
|
||||
self.tasks.insert(sub_id, task);
|
||||
|
||||
@@ -168,9 +168,11 @@ impl Session {
|
||||
last_agent_message: Option<String>,
|
||||
) {
|
||||
let mut active = self.active_turn.lock().await;
|
||||
let mut completed_turn = None;
|
||||
let should_close_processes = if let Some(at) = active.as_mut()
|
||||
&& at.remove_task(&turn_context.sub_id)
|
||||
{
|
||||
completed_turn = Some(at.started_at);
|
||||
*active = None;
|
||||
true
|
||||
} else {
|
||||
@@ -180,13 +182,20 @@ impl Session {
|
||||
if should_close_processes {
|
||||
self.close_unified_exec_processes().await;
|
||||
}
|
||||
if let Some(started_at) = completed_turn {
|
||||
self.services.otel_manager.record_duration(
|
||||
"codex.turn.end_to_end_duration",
|
||||
started_at.elapsed(),
|
||||
&[],
|
||||
);
|
||||
}
|
||||
let event = EventMsg::TurnComplete(TurnCompleteEvent { last_agent_message });
|
||||
self.send_event(turn_context.as_ref(), event).await;
|
||||
}
|
||||
|
||||
async fn register_new_active_task(&self, task: RunningTask) {
|
||||
let mut active = self.active_turn.lock().await;
|
||||
let mut turn = ActiveTurn::default();
|
||||
let mut turn = ActiveTurn::new();
|
||||
turn.add_task(task);
|
||||
*active = Some(turn);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::harness::attributes_to_map;
|
||||
use crate::harness::build_metrics_with_defaults;
|
||||
use crate::harness::find_metric;
|
||||
use crate::harness::histogram_data;
|
||||
use crate::harness::latest_metrics;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_otel::OtelManager;
|
||||
@@ -11,6 +12,7 @@ use opentelemetry_sdk::metrics::data::AggregatedMetrics;
|
||||
use opentelemetry_sdk::metrics::data::MetricData;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
// Ensures OtelManager attaches metadata tags when forwarding metrics.
|
||||
#[test]
|
||||
@@ -102,3 +104,62 @@ fn manager_allows_disabling_metadata_tags() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Ensures duration metrics from OtelManager emit histograms with metadata tags.
|
||||
#[test]
|
||||
fn manager_records_turn_end_to_end_duration_histogram() -> Result<()> {
|
||||
let (metrics, exporter) = build_metrics_with_defaults(&[("service", "codex-cli")])?;
|
||||
let manager = OtelManager::new(
|
||||
ThreadId::new(),
|
||||
"gpt-5.1",
|
||||
"gpt-5.1",
|
||||
Some("account-id".to_string()),
|
||||
None,
|
||||
Some(AuthMode::ApiKey),
|
||||
true,
|
||||
"tty".to_string(),
|
||||
SessionSource::Cli,
|
||||
)
|
||||
.with_metrics(metrics);
|
||||
|
||||
manager.record_duration(
|
||||
"codex.turn.end_to_end_duration",
|
||||
Duration::from_millis(120),
|
||||
&[],
|
||||
);
|
||||
manager.shutdown_metrics()?;
|
||||
|
||||
let resource_metrics = latest_metrics(&exporter);
|
||||
let (_bounds, bucket_counts, sum, count) =
|
||||
histogram_data(&resource_metrics, "codex.turn.end_to_end_duration");
|
||||
assert_eq!(bucket_counts.iter().sum::<u64>(), 1);
|
||||
assert_eq!(sum, 120.0);
|
||||
assert_eq!(count, 1);
|
||||
|
||||
let metric = find_metric(&resource_metrics, "codex.turn.end_to_end_duration")
|
||||
.expect("duration metric missing");
|
||||
let attrs = match metric.data() {
|
||||
AggregatedMetrics::F64(data) => match data {
|
||||
MetricData::Histogram(histogram) => {
|
||||
let points: Vec<_> = histogram.data_points().collect();
|
||||
assert_eq!(points.len(), 1);
|
||||
attributes_to_map(points[0].attributes())
|
||||
}
|
||||
_ => panic!("unexpected histogram aggregation"),
|
||||
},
|
||||
_ => panic!("unexpected metric data type"),
|
||||
};
|
||||
|
||||
let expected = BTreeMap::from([
|
||||
(
|
||||
"app.version".to_string(),
|
||||
env!("CARGO_PKG_VERSION").to_string(),
|
||||
),
|
||||
("auth_mode".to_string(), AuthMode::ApiKey.to_string()),
|
||||
("model".to_string(), "gpt-5.1".to_string()),
|
||||
("service".to_string(), "codex-cli".to_string()),
|
||||
]);
|
||||
assert_eq!(attrs, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user