Files
codex/codex-rs/core/src/tasks/mod_tests.rs
T
jif-oai 9271e84b79 feat: add manual and remote_v2 tags to compaction metric (#24608)
## Why
`codex.task.compact` only distinguished `local` vs `remote`, which made
it hard to answer simple counter questions in Statsig. Manual `/compact`
and automatic compaction were collapsed together, and the legacy remote
path was also collapsed with `remote_compaction_v2`.

## What Changed
- route `codex.task.compact` through a shared helper in
`core/src/tasks/mod.rs`
- add a `manual=true|false` tag so manual and automatic compaction can
be counted separately
- split the remote tag into `remote` and `remote_v2`
- emit the metric from the inline auto-compaction path in
`core/src/session/turn.rs` as well as the manual `CompactTask` path in
`core/src/tasks/compact.rs`
- add focused unit coverage for the new tag shapes in
`core/src/tasks/mod_tests.rs`

## Verification
- added unit coverage in `core/src/tasks/mod_tests.rs` covering manual
`remote_v2` tags and automatic `local` tags
2026-05-26 18:47:42 +02:00

225 lines
6.7 KiB
Rust

use super::TASK_COMPACT_METRIC;
use super::emit_compact_metric;
use super::emit_turn_memory_metric;
use super::emit_turn_network_proxy_metric;
use codex_otel::MetricsClient;
use codex_otel::MetricsConfig;
use codex_otel::SessionTelemetry;
use codex_otel::TURN_MEMORY_METRIC;
use codex_otel::TURN_NETWORK_PROXY_METRIC;
use codex_protocol::ThreadId;
use codex_protocol::protocol::SessionSource;
use opentelemetry::KeyValue;
use opentelemetry_sdk::metrics::InMemoryMetricExporter;
use opentelemetry_sdk::metrics::data::AggregatedMetrics;
use opentelemetry_sdk::metrics::data::Metric;
use opentelemetry_sdk::metrics::data::MetricData;
use opentelemetry_sdk::metrics::data::ResourceMetrics;
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;
fn test_session_telemetry() -> SessionTelemetry {
let exporter = InMemoryMetricExporter::default();
let metrics = MetricsClient::new(
MetricsConfig::in_memory("test", "codex-core", env!("CARGO_PKG_VERSION"), exporter)
.with_runtime_reader(),
)
.expect("in-memory metrics client");
SessionTelemetry::new(
ThreadId::new(),
"gpt-5.4",
"gpt-5.4",
/*account_id*/ None,
/*account_email*/ None,
/*auth_mode*/ None,
"test_originator".to_string(),
/*log_user_prompts*/ false,
"tty".to_string(),
SessionSource::Cli,
)
.with_metrics_without_metadata_tags(metrics)
}
fn find_metric<'a>(resource_metrics: &'a ResourceMetrics, name: &str) -> &'a Metric {
for scope_metrics in resource_metrics.scope_metrics() {
for metric in scope_metrics.metrics() {
if metric.name() == name {
return metric;
}
}
}
panic!("metric {name} missing");
}
fn attributes_to_map<'a>(
attributes: impl Iterator<Item = &'a KeyValue>,
) -> BTreeMap<String, String> {
attributes
.map(|kv| (kv.key.as_str().to_string(), kv.value.as_str().to_string()))
.collect()
}
fn metric_point(resource_metrics: &ResourceMetrics, name: &str) -> (BTreeMap<String, String>, u64) {
let metric = find_metric(resource_metrics, name);
match metric.data() {
AggregatedMetrics::U64(data) => match data {
MetricData::Sum(sum) => {
let points: Vec<_> = sum.data_points().collect();
assert_eq!(points.len(), 1);
let point = points[0];
(attributes_to_map(point.attributes()), point.value())
}
_ => panic!("unexpected counter aggregation"),
},
_ => panic!("unexpected counter data type"),
}
}
#[test]
fn emit_turn_network_proxy_metric_records_active_turn() {
let session_telemetry = test_session_telemetry();
emit_turn_network_proxy_metric(
&session_telemetry,
/*network_proxy_active*/ true,
("tmp_mem_enabled", "true"),
);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("active".to_string(), "true".to_string()),
("tmp_mem_enabled".to_string(), "true".to_string()),
])
);
}
#[test]
fn emit_turn_network_proxy_metric_records_inactive_turn() {
let session_telemetry = test_session_telemetry();
emit_turn_network_proxy_metric(
&session_telemetry,
/*network_proxy_active*/ false,
("tmp_mem_enabled", "false"),
);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("active".to_string(), "false".to_string()),
("tmp_mem_enabled".to_string(), "false".to_string()),
])
);
}
#[test]
fn emit_turn_memory_metric_records_read_allowed_with_citations() {
let session_telemetry = test_session_telemetry();
emit_turn_memory_metric(
&session_telemetry,
/*feature_enabled*/ true,
/*config_enabled*/ true,
/*has_citations*/ true,
);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TURN_MEMORY_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("config_use_memories".to_string(), "true".to_string()),
("feature_enabled".to_string(), "true".to_string()),
("has_citations".to_string(), "true".to_string()),
("read_allowed".to_string(), "true".to_string()),
])
);
}
#[test]
fn emit_turn_memory_metric_records_config_disabled_without_citations() {
let session_telemetry = test_session_telemetry();
emit_turn_memory_metric(
&session_telemetry,
/*feature_enabled*/ true,
/*config_enabled*/ false,
/*has_citations*/ false,
);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TURN_MEMORY_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("config_use_memories".to_string(), "false".to_string()),
("feature_enabled".to_string(), "true".to_string()),
("has_citations".to_string(), "false".to_string()),
("read_allowed".to_string(), "false".to_string()),
])
);
}
#[test]
fn emit_compact_metric_records_manual_remote_v2() {
let session_telemetry = test_session_telemetry();
emit_compact_metric(&session_telemetry, "remote_v2", /*manual*/ true);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TASK_COMPACT_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("manual".to_string(), "true".to_string()),
("type".to_string(), "remote_v2".to_string()),
])
);
}
#[test]
fn emit_compact_metric_records_auto_local() {
let session_telemetry = test_session_telemetry();
emit_compact_metric(&session_telemetry, "local", /*manual*/ false);
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot, TASK_COMPACT_METRIC);
assert_eq!(value, 1);
assert_eq!(
attrs,
BTreeMap::from([
("manual".to_string(), "false".to_string()),
("type".to_string(), "local".to_string()),
])
);
}