Correlate code mode tool analytics with model responses (#36729)

## What changed

- Emit dynamic tool-call analytics for code mode `exec` and `wait` calls, including duration and terminal status.
- Associate code-mode cells and their nested tool calls with the originating and subsequent model response IDs.
- Buffer correlated tool events until the next response is known, while flushing pending events when turns, threads, or the analytics queue close.

## Testing

- Add reducer coverage for correlating `exec`, `wait`, and nested tool events across turns.
- Add an app-server test that verifies production analytics include the cell and response IDs for a code-mode `exec` call.

GitOrigin-RevId: 8b34ab59c1ad68ebbd57f9c8a05f19ab04df48a4
This commit is contained in:
jif
2026-08-03 10:19:04 +00:00
committed by copyberry
parent dae2122214
commit 155f1ca9e5
13 changed files with 797 additions and 35 deletions
+28
View File
@@ -2207,6 +2207,8 @@ async fn try_run_sampling_request(
)> = None;
let mut should_emit_turn_diff = false;
let mut should_emit_token_count = false;
const MAX_ANALYTICS_TOOL_CALL_IDS_PER_RESPONSE: usize = 256;
let mut analytics_tool_call_ids = Vec::new();
let reasoning_effort = turn_context.effective_reasoning_effort_for_tracing();
let plan_mode = turn_context.mode == ModeKind::Plan;
let mut assistant_message_stream_parsers = AssistantMessageStreamParsers::new(plan_mode);
@@ -2262,6 +2264,22 @@ async fn try_run_sampling_request(
ResponseEvent::Created => {}
ResponseEvent::OutputItemDone(mut item) => {
assign_missing_streamed_response_item_id(&mut item, active_item.as_ref());
if analytics_tool_call_ids.len() < MAX_ANALYTICS_TOOL_CALL_IDS_PER_RESPONSE {
let call_id = match &item {
ResponseItem::FunctionCall { call_id, .. }
| ResponseItem::CustomToolCall { call_id, .. } => Some(call_id.as_str()),
ResponseItem::ToolSearchCall { call_id, .. }
| ResponseItem::LocalShellCall { call_id, .. } => call_id.as_deref(),
ResponseItem::WebSearchCall { id, .. }
| ResponseItem::ImageGenerationCall { id, .. } => {
id.as_ref().map(codex_protocol::ResponseItemId::as_str)
}
_ => None,
};
if let Some(call_id) = call_id {
analytics_tool_call_ids.push(call_id.to_string());
}
}
if let Some((_, mut consumer)) = active_tool_argument_diff_consumer.take()
&& let Ok(Some(event)) = consumer.finish()
{
@@ -2493,6 +2511,16 @@ async fn try_run_sampling_request(
token_usage,
end_turn,
} => {
sess.services
.analytics_events_client
.track_code_mode_tool_call(
codex_analytics::CodeModeToolCallFact::SamplingResponseCompleted {
thread_id: sess.thread_id.to_string(),
turn_id: turn_context.sub_id.clone(),
response_id: response_id.clone(),
tool_call_ids: std::mem::take(&mut analytics_tool_call_ids),
},
);
flush_assistant_text_segments_all(
&sess,
&turn_context,