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
@@ -463,6 +463,7 @@ fn host_to_client_v1_variants_are_pinned() {
request_id(/*value*/ 3),
HostResponse::WaitCompleted {
outcome: WireWaitOutcome::LiveCell(WireRuntimeResponse::Yielded {
code_mode_host_duration_ns: 0,
cell_id: cell_id("cell-1"),
content_items: content_items(),
}),
@@ -474,6 +475,7 @@ fn host_to_client_v1_variants_are_pinned() {
"Yielded": {
"cell_id": "cell-1",
"content_items": content_items_json(),
"code_mode_host_duration_ns": 0,
},
},
},
@@ -483,6 +485,7 @@ fn host_to_client_v1_variants_are_pinned() {
request_id(/*value*/ 4),
HostResponse::WaitCompleted {
outcome: WireWaitOutcome::MissingCell(WireRuntimeResponse::Result {
code_mode_host_duration_ns: 0,
cell_id: cell_id("missing-cell"),
content_items: Vec::new(),
error_text: Some("cell not found".to_string()),
@@ -496,6 +499,7 @@ fn host_to_client_v1_variants_are_pinned() {
"cell_id": "missing-cell",
"content_items": [],
"error_text": "cell not found",
"code_mode_host_duration_ns": 0,
},
},
},
@@ -540,6 +544,7 @@ fn host_to_client_v1_variants_are_pinned() {
id: request_id(/*value*/ 7),
result: WireResult::Ok {
value: WireRuntimeResponse::Terminated {
code_mode_host_duration_ns: 0,
cell_id: cell_id("cell-1"),
content_items: Vec::new(),
},
@@ -551,7 +556,11 @@ fn host_to_client_v1_variants_are_pinned() {
"result": {
"status": "ok",
"value": {
"Terminated": { "cell_id": "cell-1", "content_items": [] },
"Terminated": {
"cell_id": "cell-1",
"content_items": [],
"code_mode_host_duration_ns": 0,
},
},
},
}),
@@ -798,6 +807,7 @@ fn every_nested_v1_object_rejects_unknown_fields() {
"Yielded": {
"cell_id": "cell-1",
"content_items": [],
"code_mode_host_duration_ns": 0,
"unexpected": true,
},
}))
+63 -23
View File
@@ -1,4 +1,5 @@
use std::num::TryFromIntError;
use std::time::Duration;
use codex_protocol::ToolName;
use serde::Deserialize;
@@ -12,6 +13,7 @@ use crate::CodeModeToolKind;
use crate::ExecuteRequest;
use crate::FunctionCallOutputContentItem;
use crate::ImageDetail;
use crate::MissingCodeModeHostDuration;
use crate::RuntimeResponse;
use crate::ToolDefinition;
use crate::WaitOutcome;
@@ -312,51 +314,81 @@ impl From<WireContentItem> for FunctionCallOutputContentItem {
}
/// Runtime output returned over the V1 host connection.
///
/// Host time is required and covers this request, not the cell lifetime. The
/// app-server and host run at the same version; no negotiation is needed.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub enum WireRuntimeResponse {
Yielded {
cell_id: WireCellId,
content_items: Vec<WireContentItem>,
code_mode_host_duration_ns: u64,
},
Terminated {
cell_id: WireCellId,
content_items: Vec<WireContentItem>,
code_mode_host_duration_ns: u64,
},
Result {
cell_id: WireCellId,
content_items: Vec<WireContentItem>,
error_text: Option<String>,
code_mode_host_duration_ns: u64,
},
}
impl From<RuntimeResponse> for WireRuntimeResponse {
fn from(value: RuntimeResponse) -> Self {
match value {
impl TryFrom<RuntimeResponse> for WireRuntimeResponse {
type Error = MissingCodeModeHostDuration;
/// Preserves the response's timing; the host handler must record it first.
fn try_from(value: RuntimeResponse) -> Result<Self, Self::Error> {
Ok(match value {
RuntimeResponse::Yielded {
cell_id,
content_items,
} => Self::Yielded {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
},
code_mode_host_duration,
} => {
let code_mode_host_duration =
code_mode_host_duration.ok_or(MissingCodeModeHostDuration)?;
Self::Yielded {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
code_mode_host_duration_ns: u64::try_from(code_mode_host_duration.as_nanos())
.unwrap_or(u64::MAX),
}
}
RuntimeResponse::Terminated {
cell_id,
content_items,
} => Self::Terminated {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
},
code_mode_host_duration,
} => {
let code_mode_host_duration =
code_mode_host_duration.ok_or(MissingCodeModeHostDuration)?;
Self::Terminated {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
code_mode_host_duration_ns: u64::try_from(code_mode_host_duration.as_nanos())
.unwrap_or(u64::MAX),
}
}
RuntimeResponse::Result {
cell_id,
content_items,
error_text,
} => Self::Result {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
error_text,
},
}
code_mode_host_duration,
} => {
let code_mode_host_duration =
code_mode_host_duration.ok_or(MissingCodeModeHostDuration)?;
Self::Result {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
error_text,
code_mode_host_duration_ns: u64::try_from(code_mode_host_duration.as_nanos())
.unwrap_or(u64::MAX),
}
}
})
}
}
@@ -366,25 +398,31 @@ impl From<WireRuntimeResponse> for RuntimeResponse {
WireRuntimeResponse::Yielded {
cell_id,
content_items,
code_mode_host_duration_ns,
} => Self::Yielded {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
code_mode_host_duration: Some(Duration::from_nanos(code_mode_host_duration_ns)),
},
WireRuntimeResponse::Terminated {
cell_id,
content_items,
code_mode_host_duration_ns,
} => Self::Terminated {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
code_mode_host_duration: Some(Duration::from_nanos(code_mode_host_duration_ns)),
},
WireRuntimeResponse::Result {
cell_id,
content_items,
error_text,
code_mode_host_duration_ns,
} => Self::Result {
cell_id: cell_id.into(),
content_items: content_items.into_iter().map(Into::into).collect(),
error_text,
code_mode_host_duration: Some(Duration::from_nanos(code_mode_host_duration_ns)),
},
}
}
@@ -398,12 +436,14 @@ pub enum WireWaitOutcome {
MissingCell(WireRuntimeResponse),
}
impl From<WaitOutcome> for WireWaitOutcome {
fn from(value: WaitOutcome) -> Self {
match value {
WaitOutcome::LiveCell(response) => Self::LiveCell(response.into()),
WaitOutcome::MissingCell(response) => Self::MissingCell(response.into()),
}
impl TryFrom<WaitOutcome> for WireWaitOutcome {
type Error = MissingCodeModeHostDuration;
fn try_from(value: WaitOutcome) -> Result<Self, Self::Error> {
Ok(match value {
WaitOutcome::LiveCell(response) => Self::LiveCell(response.try_into()?),
WaitOutcome::MissingCell(response) => Self::MissingCell(response.try_into()?),
})
}
}