[codex-analytics] denormalize thread metadata onto turn events

This commit is contained in:
rhan-oai
2026-04-09 13:28:52 -07:00
parent 714613c189
commit 5a701c08c1
9 changed files with 82 additions and 12 deletions
@@ -7,7 +7,6 @@ use crate::events::CodexPluginEventRequest;
use crate::events::CodexPluginUsedEventRequest;
use crate::events::CodexRuntimeMetadata;
use crate::events::CodexTurnEventRequest;
use crate::events::ThreadInitializationMode;
use crate::events::ThreadInitializedEvent;
use crate::events::ThreadInitializedEventParams;
use crate::events::TrackEventRequest;
@@ -29,6 +28,7 @@ use crate::facts::PluginUsedInput;
use crate::facts::SkillInvocation;
use crate::facts::SkillInvokedInput;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationMode;
use crate::facts::TrackEventsContext;
use crate::facts::TurnResolvedConfigFact;
use crate::facts::TurnStatus;
@@ -254,6 +254,16 @@ fn sample_turn_resolved_config(turn_id: &str) -> TurnResolvedConfigFact {
}
}
fn sample_app_server_client_metadata() -> CodexAppServerClientMetadata {
CodexAppServerClientMetadata {
product_client_id: "codex-tui".to_string(),
client_name: Some("codex-tui".to_string()),
client_version: Some("1.0.0".to_string()),
rpc_transport: AppServerRpcTransport::Stdio,
experimental_api_enabled: None,
}
}
fn sample_runtime_metadata() -> CodexRuntimeMetadata {
CodexRuntimeMetadata {
codex_rs_version: "0.1.0".to_string(),
+25 -9
View File
@@ -3,6 +3,7 @@ use crate::facts::CodexTurnSteerEvent;
use crate::facts::InvocationType;
use crate::facts::PluginState;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationMode;
use crate::facts::TrackEventsContext;
use crate::facts::TurnStatus;
use crate::facts::TurnSteerRejectionReason;
@@ -23,14 +24,6 @@ pub enum AppServerRpcTransport {
InProcess,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ThreadInitializationMode {
New,
Forked,
Resumed,
}
#[derive(Serialize)]
pub(crate) struct TrackEventsRequest {
pub(crate) events: Vec<TrackEventRequest>,
@@ -134,7 +127,6 @@ pub(crate) struct CodexAppUsedEventRequest {
pub(crate) struct CodexTurnEventParams {
pub(crate) thread_id: String,
pub(crate) turn_id: String,
pub(crate) product_client_id: String,
pub(crate) submission_type: Option<TurnSubmissionType>,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
@@ -375,3 +367,27 @@ fn subagent_parent_thread_id(subagent_source: &SubAgentSource) -> Option<String>
_ => None,
}
}
pub(crate) fn turn_subagent_source_name(thread_source: &SessionSource) -> Option<String> {
match thread_source {
SessionSource::SubAgent(subagent_source) => Some(subagent_source_name(subagent_source)),
SessionSource::Cli
| SessionSource::VSCode
| SessionSource::Exec
| SessionSource::Mcp
| SessionSource::Custom(_)
| SessionSource::Unknown => None,
}
}
pub(crate) fn turn_parent_thread_id(thread_source: &SessionSource) -> Option<String> {
match thread_source {
SessionSource::SubAgent(subagent_source) => subagent_parent_thread_id(subagent_source),
SessionSource::Cli
| SessionSource::VSCode
| SessionSource::Exec
| SessionSource::Mcp
| SessionSource::Custom(_)
| SessionSource::Unknown => None,
}
}
+8
View File
@@ -79,6 +79,14 @@ pub struct TurnTokenUsageFact {
pub token_usage: TokenUsage,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ThreadInitializationMode {
New,
Forked,
Resumed,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnStatus {
+1
View File
@@ -12,6 +12,7 @@ pub use facts::InputError;
pub use facts::InvocationType;
pub use facts::SkillInvocation;
pub use facts::SubAgentThreadStartedInput;
pub use facts::ThreadInitializationMode;
pub use facts::TrackEventsContext;
pub use facts::TurnResolvedConfigFact;
pub use facts::TurnStatus;
+2 -1
View File
@@ -10,7 +10,6 @@ use crate::events::CodexTurnEventRequest;
use crate::events::CodexTurnSteerEventRequest;
use crate::events::SkillInvocationEventParams;
use crate::events::SkillInvocationEventRequest;
use crate::events::ThreadInitializationMode;
use crate::events::ThreadInitializedEvent;
use crate::events::ThreadInitializedEventParams;
use crate::events::TrackEventRequest;
@@ -21,6 +20,8 @@ use crate::events::codex_turn_steer_event_params;
use crate::events::plugin_state_event_type;
use crate::events::subagent_thread_started_event_request;
use crate::events::thread_source_name;
use crate::events::turn_parent_thread_id;
use crate::events::turn_subagent_source_name;
use crate::facts::AnalyticsFact;
use crate::facts::AnalyticsJsonRpcError;
use crate::facts::AppMentionedInput;
@@ -299,12 +299,23 @@ async fn turn_start_tracks_turn_event_analytics() -> Result<()> {
assert_eq!(event["event_params"]["thread_id"], thread.id);
assert_eq!(event["event_params"]["turn_id"], turn.id);
assert_eq!(
event["event_params"]["product_client_id"],
event["event_params"]["app_server_client"]["product_client_id"],
DEFAULT_CLIENT_NAME
);
assert_eq!(event["event_params"]["model"], "mock-model");
assert_eq!(event["event_params"]["model_provider"], "mock_provider");
assert_eq!(event["event_params"]["sandbox_policy"], "read_only");
assert_eq!(event["event_params"]["ephemeral"], false);
assert_eq!(event["event_params"]["thread_source"], "user");
assert_eq!(event["event_params"]["initialization_mode"], "new");
assert_eq!(
event["event_params"]["subagent_source"],
serde_json::Value::Null
);
assert_eq!(
event["event_params"]["parent_thread_id"],
serde_json::Value::Null
);
assert_eq!(event["event_params"]["num_input_images"], 1);
assert_eq!(event["event_params"]["status"], "completed");
assert!(event["event_params"]["started_at"].as_u64().is_some());
+14
View File
@@ -651,6 +651,7 @@ impl Codex {
app_server_client_name: None,
app_server_client_version: None,
session_source,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools,
persist_extended_history,
inherited_shell_snapshot,
@@ -1076,6 +1077,14 @@ impl TurnContext {
}
}
fn thread_initialization_mode(initial_history: &InitialHistory) -> ThreadInitializationMode {
match initial_history {
InitialHistory::New => ThreadInitializationMode::New,
InitialHistory::Forked(_) => ThreadInitializationMode::Forked,
InitialHistory::Resumed(_) => ThreadInitializationMode::Resumed,
}
}
fn local_time_context() -> (String, String) {
match iana_time_zone::get_timezone() {
Ok(timezone) => (Local::now().format("%Y-%m-%d").to_string(), timezone),
@@ -1137,6 +1146,7 @@ pub(crate) struct SessionConfiguration {
app_server_client_version: Option<String>,
/// Source of the session (cli, vscode, exec, mcp, ...)
session_source: SessionSource,
thread_initialization_mode: ThreadInitializationMode,
dynamic_tools: Vec<DynamicToolSpec>,
persist_extended_history: bool,
inherited_shell_snapshot: Option<Arc<ShellSnapshot>>,
@@ -1161,6 +1171,7 @@ impl SessionConfiguration {
reasoning_effort: self.collaboration_mode.reasoning_effort(),
personality: self.personality,
session_source: self.session_source.clone(),
initialization_mode: self.thread_initialization_mode,
}
}
@@ -1534,6 +1545,8 @@ impl Session {
session_configuration.collaboration_mode.model(),
session_configuration.provider
);
session_configuration.thread_initialization_mode =
thread_initialization_mode(&initial_history);
let forked_from_id = initial_history.forked_from_id();
let (conversation_id, rollout_params) = match &initial_history {
@@ -4090,6 +4103,7 @@ impl Session {
turn_kind: NonSteerableTurnKind::Compact,
});
}
None => return Err(SteerInputError::NoActiveTurn(input)),
}
let mut turn_state = active_turn.turn_state.lock().await;
+7
View File
@@ -13,6 +13,7 @@ use crate::function_tool::FunctionCallError;
use crate::shell::default_user_shell;
use crate::tools::format_exec_output_str;
use codex_analytics::ThreadInitializationMode;
use codex_features::Features;
use codex_login::CodexAuth;
use codex_mcp::ToolInfo;
@@ -1872,6 +1873,7 @@ async fn set_rate_limits_retains_previous_credits() {
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools: Vec::new(),
persist_extended_history: false,
inherited_shell_snapshot: None,
@@ -1974,6 +1976,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools: Vec::new(),
persist_extended_history: false,
inherited_shell_snapshot: None,
@@ -2323,6 +2326,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools: Vec::new(),
persist_extended_history: false,
inherited_shell_snapshot: None,
@@ -2586,6 +2590,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools: Vec::new(),
persist_extended_history: false,
inherited_shell_snapshot: None,
@@ -2690,6 +2695,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools: Vec::new(),
persist_extended_history: false,
inherited_shell_snapshot: None,
@@ -3531,6 +3537,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
app_server_client_name: None,
app_server_client_version: None,
session_source: SessionSource::Exec,
thread_initialization_mode: ThreadInitializationMode::New,
dynamic_tools,
persist_extended_history: false,
inherited_shell_snapshot: None,
+2
View File
@@ -3,6 +3,7 @@ use crate::codex::Codex;
use crate::codex::SteerInputError;
use crate::config::ConstraintResult;
use crate::file_watcher::WatchRegistration;
use codex_analytics::ThreadInitializationMode;
use codex_features::Feature;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::config_types::Personality;
@@ -42,6 +43,7 @@ pub struct ThreadConfigSnapshot {
pub reasoning_effort: Option<ReasoningEffort>,
pub personality: Option<Personality>,
pub session_source: SessionSource,
pub initialization_mode: ThreadInitializationMode,
}
pub struct CodexThread {