Report code mode host request durations (#41452)

## Why

Code mode wall time should measure the host operation itself, without including
client-side response delays or idle time between requests.

## What changed

- Measure each execute, wait, and terminate request in the code mode host.
- Carry the duration through the stdio and gRPC protocols and use it for
  model-visible wall time.
- Emit a structured `codex.code_mode.host_timing` event correlated with the
  conversation, turn, tool call, and cell.

## Testing

- Cover successful and failed execution timing, delayed response reads,
  repeated waits, termination, and missing cells across stdio and gRPC.
- Verify timing survives protocol serialization and is reflected in app-server
  model output and structured telemetry.

GitOrigin-RevId: d24af30c3fc5820521b4beba1f9970714dad6482
This commit is contained in:
Adam Perry @ OpenAI
2026-08-29 02:52:58 +00:00
committed by copyberry
parent 75388bf321
commit 48e22a5fa0
34 changed files with 1398 additions and 259 deletions
@@ -347,6 +347,7 @@ fn runtime_response(
runtime::CellEvent::Yielded { content_items } => Ok(RuntimeResponse::Yielded {
cell_id: cell_id.clone(),
content_items: content_items.into_iter().map(output_item).collect(),
code_mode_host_duration: None,
}),
runtime::CellEvent::Completed {
content_items,
@@ -355,10 +356,12 @@ fn runtime_response(
cell_id: cell_id.clone(),
content_items: content_items.into_iter().map(output_item).collect(),
error_text,
code_mode_host_duration: None,
}),
runtime::CellEvent::Terminated { content_items } => Ok(RuntimeResponse::Terminated {
cell_id: cell_id.clone(),
content_items: content_items.into_iter().map(output_item).collect(),
code_mode_host_duration: None,
}),
runtime::CellEvent::Pending { .. } => {
Err("cell returned a pending frontier unexpectedly".to_string())
@@ -391,6 +394,7 @@ fn missing_cell_response(cell_id: CellId) -> RuntimeResponse {
error_text: Some(format!("exec cell {cell_id} not found")),
cell_id,
content_items: Vec::new(),
code_mode_host_duration: None,
}
}
@@ -199,6 +199,7 @@ async fn yields_and_resumes() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "before".to_string(),
@@ -214,6 +215,7 @@ async fn yields_and_resumes() {
.await
.unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "after".to_string(),
@@ -261,6 +263,7 @@ text("after");
.unwrap(),
WaitToPendingOutcome::LiveCell(ExecuteToPendingOutcome::Completed(
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "after".to_string(),
@@ -284,6 +287,7 @@ async fn observed_natural_completion_wins_over_termination() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -318,6 +322,7 @@ async fn observed_natural_completion_wins_over_termination() {
assert_eq!(
service.terminate(cell_id("1")).await.unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -345,6 +350,7 @@ async fn termination_cancels_pending_callbacks_before_responding() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -352,6 +358,7 @@ async fn termination_cancels_pending_callbacks_before_responding() {
assert_eq!(
service.terminate(cell_id("1")).await.unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -415,6 +422,7 @@ async fn repeated_termination_is_rejected_while_callback_cleanup_is_pending() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -438,6 +446,7 @@ async fn repeated_termination_is_rejected_while_callback_cleanup_is_pending() {
assert_eq!(
first_termination.await.unwrap().unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -459,6 +468,7 @@ async fn second_observer_is_rejected_without_displacing_the_first() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -482,6 +492,7 @@ async fn second_observer_is_rejected_without_displacing_the_first() {
);
let terminated = RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
};
@@ -513,6 +524,7 @@ async fn natural_completion_cleans_up_callbacks_before_responding() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -80,6 +80,7 @@ async fn execute_waits_for_nested_tool_during_yield_grace() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -118,6 +119,7 @@ async fn execute_and_wait_clamp_yield_grace_without_stopping_the_cell() {
assert_eq!(
initial_response.await.unwrap().unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -136,6 +138,7 @@ async fn execute_and_wait_clamp_yield_grace_without_stopping_the_cell() {
assert_eq!(
wait_response.await.unwrap().unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -153,6 +156,7 @@ async fn execute_and_wait_clamp_yield_grace_without_stopping_the_cell() {
assert_eq!(
completion.await.unwrap().unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -198,6 +202,7 @@ async fn wait_waits_for_nested_tool_during_yield_grace() {
assert_eq!(
response.unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -242,6 +247,7 @@ async fn zero_yield_limit_is_immediate_and_scoped_to_its_session() {
assert_eq!(
zero_response.await.unwrap().unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -258,6 +264,7 @@ async fn zero_yield_limit_is_immediate_and_scoped_to_its_session() {
assert_eq!(
zero_wait.await.unwrap().unwrap(),
WaitOutcome::LiveCell(RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -268,6 +275,7 @@ async fn zero_yield_limit_is_immediate_and_scoped_to_its_session() {
assert_eq!(
limited_response.await.unwrap().unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -389,6 +397,7 @@ async fn synchronous_exit_returns_successfully() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "before".to_string(),
@@ -435,6 +444,7 @@ async fn stored_values_are_shared_between_cells_but_not_sessions() {
assert_eq!(
write_response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: None,
@@ -443,6 +453,7 @@ async fn stored_values_are_shared_between_cells_but_not_sessions() {
assert_eq!(
same_session,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("2"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "visible".to_string(),
@@ -453,6 +464,7 @@ async fn stored_values_are_shared_between_cells_but_not_sessions() {
assert_eq!(
other_session,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "undefined".to_string(),
@@ -476,6 +488,7 @@ async fn shutdown_interrupts_cpu_bound_cells() {
assert_eq!(
cell.initial_response().await.unwrap(),
RuntimeResponse::Yielded {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
}
@@ -517,6 +530,7 @@ async fn execute_to_pending_returns_completed_for_synchronous_results() {
assert_eq!(
response,
ExecuteToPendingOutcome::Completed(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -558,6 +572,7 @@ async fn execute_to_pending_returns_once_the_runtime_is_quiescent() {
assert_eq!(
termination,
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -598,6 +613,7 @@ await Promise.all([
assert_eq!(
termination,
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -662,6 +678,7 @@ await Promise.all([
assert_eq!(
termination,
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -725,6 +742,7 @@ await new Promise(() => {});
assert_eq!(
termination,
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
})
@@ -775,6 +793,7 @@ text("done");
resumed_response,
WaitToPendingOutcome::LiveCell(ExecuteToPendingOutcome::Completed(
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "done".to_string(),
@@ -920,6 +939,7 @@ async fn v8_console_is_not_exposed_on_global_this() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "false".to_string(),
@@ -960,6 +980,7 @@ text(value);
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "jeudi 2 janvier \u{e0} 03:04:05".to_string(),
@@ -999,6 +1020,7 @@ text(formatter.format(new Date("2025-01-02T03:04:05Z")));
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: "jeudi 2 janvier \u{e0} 03:04:05".to_string(),
@@ -1034,6 +1056,7 @@ text(JSON.stringify(returnsUndefined));
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![
FunctionCallOutputContentItem::InputText {
@@ -1072,6 +1095,7 @@ async fn text_helper_serializes_objects() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputText {
text: r#"{"json":true}"#.to_string(),
@@ -1115,6 +1139,7 @@ text(circular);
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(error_text),
@@ -1149,6 +1174,7 @@ audio({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![
FunctionCallOutputContentItem::InputAudio {
@@ -1184,6 +1210,7 @@ async fn audio_helper_rejects_non_data_urls() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(
@@ -1220,6 +1247,7 @@ image({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==".to_string(),
@@ -1253,6 +1281,7 @@ generatedImage({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![
FunctionCallOutputContentItem::InputImage {
@@ -1294,6 +1323,7 @@ image(
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,AAA".to_string(),
@@ -1332,6 +1362,7 @@ image(
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==".to_string(),
@@ -1365,6 +1396,7 @@ image({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: vec![FunctionCallOutputContentItem::InputImage {
image_url: "data:image/png;base64,AAA".to_string(),
@@ -1400,6 +1432,7 @@ async fn image_helpers_reject_remote_urls() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(
@@ -1434,6 +1467,7 @@ async fn image_helpers_reject_invalid_image_outputs() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(
@@ -1468,6 +1502,7 @@ image({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some("image detail must be one of: auto, low, high, original".to_string()),
@@ -1505,6 +1540,7 @@ image({
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("1"),
content_items: Vec::new(),
error_text: Some(
@@ -1529,6 +1565,7 @@ async fn wait_reports_missing_cell_separately_from_runtime_results() {
assert_eq!(
response,
WaitOutcome::MissingCell(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id("missing"),
content_items: Vec::new(),
error_text: Some("exec cell missing not found".to_string()),
+1
View File
@@ -29,6 +29,7 @@ async fn code_mode_runs_with_jit_disabled() {
assert_eq!(
response,
RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id,
content_items: Vec::new(),
error_text: None,