Decouple thread initialization profiling

This commit is contained in:
Ahmed Ibrahim
2026-06-08 12:21:43 -07:00
parent e58edbbef6
commit 133689b20f
14 changed files with 289 additions and 230 deletions
@@ -530,7 +530,6 @@ async fn ingest_rejected_turn_steer(
response: Box::new(sample_thread_resume_response(
"thread-2", /*ephemeral*/ false, "gpt-5",
)),
thread_initialization: None,
},
out,
)
@@ -604,7 +603,6 @@ async fn ingest_turn_prerequisites(
response: Box::new(sample_thread_start_response(
"thread-2", /*ephemeral*/ false, "gpt-5",
)),
thread_initialization: None,
},
out,
)
@@ -628,7 +626,6 @@ async fn ingest_turn_prerequisites(
connection_id: 7,
request_id: RequestId::Integer(3),
response: Box::new(sample_turn_start_response("turn-2")),
thread_initialization: None,
},
out,
)
@@ -695,7 +692,6 @@ async fn ingest_review_prerequisites(
response: Box::new(sample_thread_start_response(
"thread-1", /*ephemeral*/ false, "gpt-5",
)),
thread_initialization: None,
},
events,
)
@@ -1608,7 +1604,6 @@ async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialize
/*ephemeral*/ false,
"gpt-5",
)),
thread_initialization: None,
},
&mut events,
)
@@ -1653,7 +1648,6 @@ async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialize
response: Box::new(sample_thread_resume_response(
"thread-1", /*ephemeral*/ true, "gpt-5",
)),
thread_initialization: None,
},
&mut events,
)
@@ -1724,7 +1718,6 @@ async fn unrelated_client_requests_are_ignored_by_reducer() {
connection_id: 7,
request_id: RequestId::Integer(3),
response: Box::new(sample_turn_start_response("turn-2")),
thread_initialization: None,
},
&mut events,
)
@@ -1750,7 +1743,6 @@ async fn unrelated_client_responses_are_ignored_by_reducer() {
response: Box::new(ClientResponsePayload::ThreadArchive(
ThreadArchiveResponse {},
)),
thread_initialization: None,
},
&mut events,
)
@@ -1809,7 +1801,6 @@ async fn compaction_event_ingests_custom_fact() {
Some(AppServerThreadSource::Subagent),
Some(parent_thread_id.to_string()),
)),
thread_initialization: None,
},
&mut events,
)
@@ -1917,7 +1908,6 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
/*ephemeral*/ false,
"gpt-5",
)),
thread_initialization: None,
},
&mut events,
)
@@ -2419,7 +2409,6 @@ async fn item_review_summaries_do_not_cross_threads_with_reused_item_ids() {
response: Box::new(sample_thread_start_response(
"thread-2", /*ephemeral*/ false, "gpt-5",
)),
thread_initialization: None,
},
&mut events,
)
@@ -2707,7 +2696,6 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
/*ephemeral*/ false,
"gpt-5",
)),
thread_initialization: None,
},
&mut events,
)
@@ -3462,7 +3450,6 @@ async fn accepted_turn_steer_emits_expected_event() {
connection_id: 7,
request_id: RequestId::Integer(4),
response: Box::new(sample_turn_steer_response("turn-2")),
thread_initialization: None,
},
&mut out,
)
@@ -3634,7 +3621,6 @@ async fn turn_start_error_response_discards_pending_start_request() {
connection_id: 7,
request_id: RequestId::Integer(3),
response: Box::new(sample_turn_start_response("turn-2")),
thread_initialization: None,
},
&mut out,
)
@@ -3934,7 +3920,6 @@ async fn accepted_steers_increment_turn_steer_count() {
connection_id: 7,
request_id: RequestId::Integer(4),
response: Box::new(sample_turn_steer_response("turn-2")),
thread_initialization: None,
},
&mut out,
)
@@ -3982,7 +3967,6 @@ async fn accepted_steers_increment_turn_steer_count() {
connection_id: 7,
request_id: RequestId::Integer(6),
response: Box::new(sample_turn_steer_response("turn-2")),
thread_initialization: None,
},
&mut out,
)
+5 -3
View File
@@ -17,7 +17,7 @@ use crate::facts::PluginStateChangedInput;
use crate::facts::SkillInvocation;
use crate::facts::SkillInvokedInput;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationFact;
use crate::facts::ThreadInitializationResponseFact;
use crate::facts::TrackEventsContext;
use crate::facts::TurnCodexErrorFact;
use crate::facts::TurnProfileFact;
@@ -318,7 +318,6 @@ impl AnalyticsEventsClient {
connection_id: u64,
request_id: RequestId,
response: ClientResponsePayload,
thread_initialization: Option<ThreadInitializationFact>,
) {
if !matches!(
response,
@@ -334,10 +333,13 @@ impl AnalyticsEventsClient {
connection_id,
request_id,
response: Box::new(response),
thread_initialization,
});
}
pub fn track_thread_initialization_response(&self, fact: ThreadInitializationResponseFact) {
self.record_fact(AnalyticsFact::ThreadInitializationResponse(Box::new(fact)));
}
pub fn track_error_response(
&self,
connection_id: u64,
+1 -4
View File
@@ -252,9 +252,7 @@ fn track_response_only_enqueues_analytics_relevant_responses() {
(RequestId::Integer(4), sample_turn_start_response()),
(RequestId::Integer(5), sample_turn_steer_response()),
] {
client.track_response(
/*connection_id*/ 7, request_id, response, /*thread_initialization*/ None,
);
client.track_response(/*connection_id*/ 7, request_id, response);
assert!(matches!(
receiver.try_recv(),
Ok(AnalyticsFact::ClientResponse { .. })
@@ -265,7 +263,6 @@ fn track_response_only_enqueues_analytics_relevant_responses() {
/*connection_id*/ 7,
RequestId::Integer(6),
ClientResponsePayload::ThreadArchive(ThreadArchiveResponse {}),
/*thread_initialization*/ None,
);
assert!(matches!(receiver.try_recv(), Err(TryRecvError::Empty)));
}
+11 -3
View File
@@ -27,6 +27,7 @@ use codex_protocol::protocol::SkillScope;
use codex_protocol::protocol::SubAgentSource;
use codex_protocol::protocol::TokenUsage;
use codex_protocol::request_permissions::RequestPermissionsResponse;
use serde::Deserialize;
use serde::Serialize;
use std::path::PathBuf;
@@ -98,7 +99,7 @@ pub enum ThreadInitializationMode {
Resumed,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize)]
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct ThreadInitializationProfile {
pub duration_ms: u64,
pub app_server_duration_ms: u64,
@@ -117,11 +118,18 @@ pub struct ThreadInitializationProfile {
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ThreadInitializationFact {
pub struct CompletedThreadInitialization {
pub initialization_mode: ThreadInitializationMode,
pub profile: ThreadInitializationProfile,
}
#[derive(Clone)]
pub struct ThreadInitializationResponseFact {
pub connection_id: u64,
pub response: ClientResponsePayload,
pub initialization: CompletedThreadInitialization,
}
#[derive(Clone)]
pub struct TurnTokenUsageFact {
pub turn_id: String,
@@ -482,8 +490,8 @@ pub(crate) enum AnalyticsFact {
connection_id: u64,
request_id: RequestId,
response: Box<ClientResponsePayload>,
thread_initialization: Option<ThreadInitializationFact>,
},
ThreadInitializationResponse(Box<ThreadInitializationResponseFact>),
ErrorResponse {
connection_id: u64,
request_id: RequestId,
+2 -1
View File
@@ -31,14 +31,15 @@ pub use facts::CompactionReason;
pub use facts::CompactionStatus;
pub use facts::CompactionStrategy;
pub use facts::CompactionTrigger;
pub use facts::CompletedThreadInitialization;
pub use facts::HookRunFact;
pub use facts::InputError;
pub use facts::InvocationType;
pub use facts::SkillInvocation;
pub use facts::SubAgentThreadStartedInput;
pub use facts::ThreadInitializationFact;
pub use facts::ThreadInitializationMode;
pub use facts::ThreadInitializationProfile;
pub use facts::ThreadInitializationResponseFact;
pub use facts::TrackEventsContext;
pub use facts::TurnCodexErrorFact;
pub use facts::TurnProfile;
+74 -17
View File
@@ -62,6 +62,7 @@ use crate::facts::AnalyticsJsonRpcError;
use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
use crate::facts::CodexCompactionEvent;
use crate::facts::CompletedThreadInitialization;
use crate::facts::CustomAnalyticsFact;
use crate::facts::HookRunInput;
use crate::facts::PluginState;
@@ -69,9 +70,9 @@ use crate::facts::PluginStateChangedInput;
use crate::facts::PluginUsedInput;
use crate::facts::SkillInvokedInput;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationFact;
use crate::facts::ThreadInitializationMode;
use crate::facts::ThreadInitializationProfile;
use crate::facts::ThreadInitializationResponseFact;
use crate::facts::TurnCodexError;
use crate::facts::TurnCodexErrorFact;
use crate::facts::TurnProfile;
@@ -143,6 +144,30 @@ pub(crate) struct AnalyticsReducer {
item_review_summaries: HashMap<ToolItemKey, ItemReviewSummary>,
}
#[derive(Clone, Copy)]
struct ThreadInitializationEventDetails {
mode: ThreadInitializationMode,
profile: Option<ThreadInitializationProfile>,
}
impl ThreadInitializationEventDetails {
fn without_profile(mode: ThreadInitializationMode) -> Self {
Self {
mode,
profile: None,
}
}
}
impl From<CompletedThreadInitialization> for ThreadInitializationEventDetails {
fn from(initialization: CompletedThreadInitialization) -> Self {
Self {
mode: initialization.initialization_mode,
profile: Some(initialization.profile),
}
}
}
struct ConnectionState {
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
@@ -408,13 +433,14 @@ impl AnalyticsReducer {
connection_id,
request_id,
response,
thread_initialization,
} => {
if let Some(response) = response.into_client_response(request_id) {
self.ingest_response(connection_id, response, thread_initialization, out)
.await;
self.ingest_response(connection_id, response, out).await;
}
}
AnalyticsFact::ThreadInitializationResponse(input) => {
self.ingest_thread_initialization_response(*input, out);
}
AnalyticsFact::ErrorResponse {
connection_id,
request_id,
@@ -764,7 +790,6 @@ impl AnalyticsReducer {
&mut self,
connection_id: u64,
response: ClientResponse,
thread_initialization: Option<ThreadInitializationFact>,
out: &mut Vec<TrackEventRequest>,
) {
match response {
@@ -773,10 +798,9 @@ impl AnalyticsReducer {
connection_id,
response.thread,
response.model,
thread_initialization
.map(|initialization| initialization.initialization_mode)
.unwrap_or(ThreadInitializationMode::New),
thread_initialization.map(|initialization| initialization.profile),
ThreadInitializationEventDetails::without_profile(
ThreadInitializationMode::New,
),
out,
);
}
@@ -785,8 +809,9 @@ impl AnalyticsReducer {
connection_id,
response.thread,
response.model,
ThreadInitializationMode::Resumed,
thread_initialization.map(|initialization| initialization.profile),
ThreadInitializationEventDetails::without_profile(
ThreadInitializationMode::Resumed,
),
out,
);
}
@@ -795,8 +820,9 @@ impl AnalyticsReducer {
connection_id,
response.thread,
response.model,
ThreadInitializationMode::Forked,
thread_initialization.map(|initialization| initialization.profile),
ThreadInitializationEventDetails::without_profile(
ThreadInitializationMode::Forked,
),
out,
);
}
@@ -826,6 +852,38 @@ impl AnalyticsReducer {
}
}
fn ingest_thread_initialization_response(
&mut self,
input: ThreadInitializationResponseFact,
out: &mut Vec<TrackEventRequest>,
) {
let initialization = input.initialization.into();
match input.response {
ClientResponsePayload::ThreadStart(response) => self.emit_thread_initialized(
input.connection_id,
response.thread,
response.model,
initialization,
out,
),
ClientResponsePayload::ThreadResume(response) => self.emit_thread_initialized(
input.connection_id,
response.thread,
response.model,
initialization,
out,
),
ClientResponsePayload::ThreadFork(response) => self.emit_thread_initialized(
input.connection_id,
response.thread,
response.model,
initialization,
out,
),
_ => {}
}
}
fn ingest_server_request(&mut self, _connection_id: u64, request: ServerRequest) {
match request {
ServerRequest::CommandExecutionRequestApproval { request_id, params } => {
@@ -1199,8 +1257,7 @@ impl AnalyticsReducer {
connection_id: u64,
thread: codex_app_server_protocol::Thread,
model: String,
initialization_mode: ThreadInitializationMode,
profile: Option<ThreadInitializationProfile>,
initialization: ThreadInitializationEventDetails,
out: &mut Vec<TrackEventRequest>,
) {
let session_source: SessionSource = thread.source.into();
@@ -1236,12 +1293,12 @@ impl AnalyticsReducer {
model,
ephemeral: thread.ephemeral,
thread_source: thread_metadata.thread_source,
initialization_mode,
initialization_mode: initialization.mode,
subagent_source: thread_metadata.subagent_source.clone(),
parent_thread_id: thread_metadata.parent_thread_id,
forked_from_thread_id,
created_at: u64::try_from(thread.created_at).unwrap_or_default(),
profile,
profile: initialization.profile,
},
},
));