feat: add metric to track the number of turns with memory usage (#18662)

Add a metric `codex.turn.memory` to know if a turn used memories or not.
This is not part of the other turn metrics as a label to limit
cardinality
This commit is contained in:
jif-oai
2026-04-20 14:31:22 +01:00
committed by GitHub
parent 1c24347772
commit 2c59806fe0
8 changed files with 138 additions and 26 deletions
+32
View File
@@ -35,6 +35,7 @@ use codex_login::AuthManager;
use codex_models_manager::manager::ModelsManager;
use codex_otel::SessionTelemetry;
use codex_otel::TURN_E2E_DURATION_METRIC;
use codex_otel::TURN_MEMORY_METRIC;
use codex_otel::TURN_NETWORK_PROXY_METRIC;
use codex_otel::TURN_TOKEN_USAGE_METRIC;
use codex_otel::TURN_TOOL_CALL_METRIC;
@@ -96,6 +97,29 @@ fn emit_turn_network_proxy_metric(
);
}
fn emit_turn_memory_metric(
session_telemetry: &SessionTelemetry,
feature_enabled: bool,
config_enabled: bool,
has_citations: bool,
) {
let read_allowed = feature_enabled && config_enabled;
session_telemetry.counter(
TURN_MEMORY_METRIC,
/*inc*/ 1,
&[
("read_allowed", bool_tag(read_allowed)),
("feature_enabled", bool_tag(feature_enabled)),
("config_use_memories", bool_tag(config_enabled)),
("has_citations", bool_tag(has_citations)),
],
);
}
fn bool_tag(value: bool) -> &'static str {
if value { "true" } else { "false" }
}
/// Thin wrapper that exposes the parts of [`Session`] task runners need.
#[derive(Clone)]
pub(crate) struct SessionTaskContext {
@@ -409,6 +433,7 @@ impl Session {
let mut pending_input = Vec::<ResponseInputItem>::new();
let mut should_clear_active_turn = false;
let mut token_usage_at_turn_start = None;
let mut turn_had_memory_citation = false;
let mut turn_tool_calls = 0_u64;
let turn_state = {
let mut active = self.active_turn.lock().await;
@@ -428,6 +453,7 @@ impl Session {
if let Some(turn_state) = turn_state {
let mut ts = turn_state.lock().await;
pending_input = ts.take_pending_input();
turn_had_memory_citation = ts.has_memory_citation;
turn_tool_calls = ts.tool_calls;
token_usage_at_turn_start = Some(ts.token_usage_at_turn_start.clone());
}
@@ -531,6 +557,12 @@ impl Session {
&[("token_type", "reasoning_output"), tmp_mem],
);
}
emit_turn_memory_metric(
&self.services.session_telemetry,
turn_context.features.enabled(Feature::MemoryTool),
turn_context.config.memories.use_memories,
turn_had_memory_citation,
);
let (completed_at, duration_ms) = turn_context
.turn_timing_state
.completed_at_and_duration_ms()
+62 -4
View File
@@ -1,7 +1,9 @@
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;
@@ -55,8 +57,8 @@ fn attributes_to_map<'a>(
.collect()
}
fn metric_point(resource_metrics: &ResourceMetrics) -> (BTreeMap<String, String>, u64) {
let metric = find_metric(resource_metrics, TURN_NETWORK_PROXY_METRIC);
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) => {
@@ -84,7 +86,7 @@ fn emit_turn_network_proxy_metric_records_active_turn() {
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot);
let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC);
assert_eq!(value, 1);
assert_eq!(
@@ -109,7 +111,7 @@ fn emit_turn_network_proxy_metric_records_inactive_turn() {
let snapshot = session_telemetry
.snapshot_metrics()
.expect("runtime metrics snapshot");
let (attrs, value) = metric_point(&snapshot);
let (attrs, value) = metric_point(&snapshot, TURN_NETWORK_PROXY_METRIC);
assert_eq!(value, 1);
assert_eq!(
@@ -120,3 +122,59 @@ fn emit_turn_network_proxy_metric_records_inactive_turn() {
])
);
}
#[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()),
])
);
}