[codex-analytics] add compaction analytics event (#17155)

- event for compaction analytics
- introduces thread-connection and thread metadata caches for data
denormalization, expected to be useful for denormalization onto core
emitted events in general
- threads analytics event client into core (mirrors approved
implementation in #16640)
- denormalizes key thread metadata: thread_source, subagent_source,
parent_thread_id, as well as app-server client and runtime metadata)
- compaction strategy defaults to memento, forward compatible with
expected prefill_compaction strategy

1. Manual standalone compact, local
`INFO | 2026-04-09 17:35:50 | codex_backend.routers.analytics_events |
analytics_events.track_analytics_events:526 | Tracked
codex_compaction_event event params={'thread_id':
'019d74d0-5cfb-70c0-bef9-165c3bf9b2df', 'turn_id':
'019d74d0-d7f6-7c81-acc6-aae2030243d6', 'product_surface': 'codex',
'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name':
'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process',
'experimental_api_enabled': True}, 'runtime': {'codex_rs_version':
'0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0',
'runtime_arch': 'aarch64'}, 'trigger': 'manual', 'reason':
'user_requested', 'implementation': 'responses', 'phase':
'standalone_turn', 'strategy': 'memento', 'status': 'completed',
'active_context_tokens_before': 20170, 'active_context_tokens_after':
4830, 'started_at': 1775781337, 'completed_at': 1775781350,
'thread_source': 'user', 'subagent_source': None, 'parent_thread_id':
None, 'error': None, 'duration_ms': 13524} | `

2. Auto pre-turn compact, local
`INFO | 2026-04-09 17:37:30 | codex_backend.routers.analytics_events |
analytics_events.track_analytics_events:526 | Tracked
codex_compaction_event event params={'thread_id':
'019d74d2-45ef-71d1-9c93-23cc0c13d988', 'turn_id':
'019d74d2-7b42-7372-9f0e-c0da3f352328', 'product_surface': 'codex',
'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name':
'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process',
'experimental_api_enabled': True}, 'runtime': {'codex_rs_version':
'0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0',
'runtime_arch': 'aarch64'}, 'trigger': 'auto', 'reason':
'context_limit', 'implementation': 'responses', 'phase': 'pre_turn',
'strategy': 'memento', 'status': 'completed',
'active_context_tokens_before': 20063, 'active_context_tokens_after':
4822, 'started_at': 1775781444, 'completed_at': 1775781449,
'thread_source': 'user', 'subagent_source': None, 'parent_thread_id':
None, 'error': None, 'duration_ms': 5497} | `

3. Auto mid-turn compact, local
`INFO | 2026-04-09 17:38:28 | codex_backend.routers.analytics_events |
analytics_events.track_analytics_events:526 | Tracked
codex_compaction_event event params={'thread_id':
'019d74d3-212f-7a20-8c0a-4816a978675e', 'turn_id':
'019d74d3-3ee1-7462-89f6-2ffbeefcd5e3', 'product_surface': 'codex',
'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name':
'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process',
'experimental_api_enabled': True}, 'runtime': {'codex_rs_version':
'0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0',
'runtime_arch': 'aarch64'}, 'trigger': 'auto', 'reason':
'context_limit', 'implementation': 'responses', 'phase': 'mid_turn',
'strategy': 'memento', 'status': 'completed',
'active_context_tokens_before': 20325, 'active_context_tokens_after':
14641, 'started_at': 1775781500, 'completed_at': 1775781508,
'thread_source': 'user', 'subagent_source': None, 'parent_thread_id':
None, 'error': None, 'duration_ms': 7507} | `

4. Remote /responses/compact, manual standalone
`INFO | 2026-04-09 17:40:20 | codex_backend.routers.analytics_events |
analytics_events.track_analytics_events:526 | Tracked
codex_compaction_event event params={'thread_id':
'019d74d4-7a11-78a1-89f7-0535a1149416', 'turn_id':
'019d74d4-e087-7183-9c20-b1e40b7578c0', 'product_surface': 'codex',
'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name':
'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process',
'experimental_api_enabled': True}, 'runtime': {'codex_rs_version':
'0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0',
'runtime_arch': 'aarch64'}, 'trigger': 'manual', 'reason':
'user_requested', 'implementation': 'responses_compact', 'phase':
'standalone_turn', 'strategy': 'memento', 'status': 'completed',
'active_context_tokens_before': 23461, 'active_context_tokens_after':
6171, 'started_at': 1775781601, 'completed_at': 1775781620,
'thread_source': 'user', 'subagent_source': None, 'parent_thread_id':
None, 'error': None, 'duration_ms': 18971} | `
This commit is contained in:
rhan-oai
2026-04-10 13:03:54 -07:00
committed by GitHub
parent 029fc63d13
commit 5779be314a
19 changed files with 717 additions and 25 deletions

View File

@@ -2,6 +2,7 @@ use crate::events::AppServerRpcTransport;
use crate::events::CodexAppMentionedEventRequest;
use crate::events::CodexAppServerClientMetadata;
use crate::events::CodexAppUsedEventRequest;
use crate::events::CodexCompactionEventRequest;
use crate::events::CodexPluginEventRequest;
use crate::events::CodexPluginUsedEventRequest;
use crate::events::CodexRuntimeMetadata;
@@ -12,14 +13,18 @@ use crate::events::ThreadInitializedEvent;
use crate::events::ThreadInitializedEventParams;
use crate::events::TrackEventRequest;
use crate::events::codex_app_metadata;
use crate::events::codex_compaction_event_params;
use crate::events::codex_plugin_metadata;
use crate::events::codex_plugin_used_metadata;
use crate::events::plugin_state_event_type;
use crate::events::subagent_parent_thread_id;
use crate::events::subagent_source_name;
use crate::events::subagent_thread_started_event_request;
use crate::events::thread_source_name;
use crate::facts::AnalyticsFact;
use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
use crate::facts::CodexCompactionEvent;
use crate::facts::CustomAnalyticsFact;
use crate::facts::PluginState;
use crate::facts::PluginStateChangedInput;
@@ -40,6 +45,8 @@ use std::path::Path;
#[derive(Default)]
pub(crate) struct AnalyticsReducer {
connections: HashMap<u64, ConnectionState>,
thread_connections: HashMap<String, u64>,
thread_metadata: HashMap<String, ThreadMetadataState>,
}
struct ConnectionState {
@@ -47,6 +54,35 @@ struct ConnectionState {
runtime: CodexRuntimeMetadata,
}
#[derive(Clone)]
struct ThreadMetadataState {
thread_source: Option<&'static str>,
subagent_source: Option<String>,
parent_thread_id: Option<String>,
}
impl ThreadMetadataState {
fn from_session_source(session_source: &SessionSource) -> Self {
let (subagent_source, parent_thread_id) = match session_source {
SessionSource::SubAgent(subagent_source) => (
Some(subagent_source_name(subagent_source)),
subagent_parent_thread_id(subagent_source),
),
SessionSource::Cli
| SessionSource::VSCode
| SessionSource::Exec
| SessionSource::Mcp
| SessionSource::Custom(_)
| SessionSource::Unknown => (None, None),
};
Self {
thread_source: thread_source_name(session_source),
subagent_source,
parent_thread_id,
}
}
}
impl AnalyticsReducer {
pub(crate) async fn ingest(&mut self, input: AnalyticsFact, out: &mut Vec<TrackEventRequest>) {
match input {
@@ -81,6 +117,9 @@ impl AnalyticsReducer {
CustomAnalyticsFact::SubAgentThreadStarted(input) => {
self.ingest_subagent_thread_started(input, out);
}
CustomAnalyticsFact::Compaction(input) => {
self.ingest_compaction(*input, out);
}
CustomAnalyticsFact::SkillInvoked(input) => {
self.ingest_skill_invoked(input, out).await;
}
@@ -254,27 +293,74 @@ impl AnalyticsReducer {
_ => return,
};
let thread_source: SessionSource = thread.source.into();
let thread_id = thread.id;
let Some(connection_state) = self.connections.get(&connection_id) else {
return;
};
let thread_metadata = ThreadMetadataState::from_session_source(&thread_source);
self.thread_connections
.insert(thread_id.clone(), connection_id);
self.thread_metadata
.insert(thread_id.clone(), thread_metadata.clone());
out.push(TrackEventRequest::ThreadInitialized(
ThreadInitializedEvent {
event_type: "codex_thread_initialized",
event_params: ThreadInitializedEventParams {
thread_id: thread.id,
thread_id,
app_server_client: connection_state.app_server_client.clone(),
runtime: connection_state.runtime.clone(),
model,
ephemeral: thread.ephemeral,
thread_source: thread_source_name(&thread_source),
thread_source: thread_metadata.thread_source,
initialization_mode,
subagent_source: None,
parent_thread_id: None,
subagent_source: thread_metadata.subagent_source,
parent_thread_id: thread_metadata.parent_thread_id,
created_at: u64::try_from(thread.created_at).unwrap_or_default(),
},
},
));
}
fn ingest_compaction(&mut self, input: CodexCompactionEvent, out: &mut Vec<TrackEventRequest>) {
let Some(connection_id) = self.thread_connections.get(&input.thread_id) else {
tracing::warn!(
thread_id = %input.thread_id,
turn_id = %input.turn_id,
"dropping compaction analytics event: missing thread connection metadata"
);
return;
};
let Some(connection_state) = self.connections.get(connection_id) else {
tracing::warn!(
thread_id = %input.thread_id,
turn_id = %input.turn_id,
connection_id,
"dropping compaction analytics event: missing connection metadata"
);
return;
};
let Some(thread_metadata) = self.thread_metadata.get(&input.thread_id) else {
tracing::warn!(
thread_id = %input.thread_id,
turn_id = %input.turn_id,
"dropping compaction analytics event: missing thread lifecycle metadata"
);
return;
};
out.push(TrackEventRequest::Compaction(Box::new(
CodexCompactionEventRequest {
event_type: "codex_compaction_event",
event_params: codex_compaction_event_params(
input,
connection_state.app_server_client.clone(),
connection_state.runtime.clone(),
thread_metadata.thread_source,
thread_metadata.subagent_source.clone(),
thread_metadata.parent_thread_id.clone(),
),
},
)));
}
}
pub(crate) fn skill_id_for_local_skill(