Files
codex/codex-rs/code-mode-host/src/peer_tests.rs
Adam Perry @ OpenAI 48e22a5fa0 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
2026-08-29 02:52:58 +00:00

99 lines
3.2 KiB
Rust

use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
use codex_code_mode_protocol::CellId;
use codex_code_mode_protocol::RuntimeResponse;
use codex_code_mode_protocol::StartedCell;
use codex_code_mode_protocol::host::DelegateRequest;
use codex_code_mode_protocol::host::RequestId;
use codex_code_mode_protocol::host::SessionId;
use pretty_assertions::assert_eq;
use tokio::sync::Semaphore;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use tokio::sync::oneshot::error::TryRecvError;
use tokio_util::sync::CancellationToken;
use super::HostPeer;
use super::MAX_PENDING_DELEGATE_CALLS;
fn session_id(value: &str) -> SessionId {
SessionId::new(value).expect("session ID")
}
#[tokio::test]
async fn start_cell_reports_when_initial_response_is_enqueued() {
let (outgoing_tx, mut outgoing_rx) = mpsc::channel(/*max_capacity*/ 4);
let peer = Arc::new(HostPeer::new(outgoing_tx));
let cell_id = CellId::new("cell-1".to_string());
let (response_tx, response_rx) = oneshot::channel();
let started = StartedCell::new(cell_id.clone(), response_rx);
let active_cell_permits = Arc::new(Semaphore::new(/*permits*/ 1));
let active_cell_permit = Arc::clone(&active_cell_permits)
.try_acquire_owned()
.expect("active cell permit");
let mut initial_response_sent = peer.start_cell(
session_id("session-1"),
RequestId::new(/*value*/ 1),
started,
active_cell_permit,
Instant::now(),
);
assert_eq!(initial_response_sent.try_recv(), Err(TryRecvError::Empty));
response_tx
.send(RuntimeResponse::Result {
code_mode_host_duration: None,
cell_id: cell_id.clone(),
content_items: Vec::new(),
error_text: None,
})
.expect("initial response receiver");
initial_response_sent
.await
.expect("initial response completion");
outgoing_rx.recv().await.expect("initial response frame");
assert_eq!(active_cell_permits.available_permits(), 0);
peer.close_cell(session_id("session-1"), cell_id);
let permit = tokio::time::timeout(
Duration::from_secs(1),
Arc::clone(&active_cell_permits).acquire_owned(),
)
.await
.expect("cell permit should be released")
.expect("cell permit semaphore should remain open");
drop(permit);
}
#[tokio::test]
async fn pending_delegate_limit_rejects_call_without_disconnecting() {
let (outgoing_tx, _outgoing_rx) = mpsc::channel(/*max_capacity*/ 1);
let peer = Arc::new(HostPeer::new(outgoing_tx));
let permits = Arc::clone(&peer.delegate_permits)
.acquire_many_owned(MAX_PENDING_DELEGATE_CALLS as u32)
.await
.expect("delegate permits");
let result = peer
.call(
session_id("session-1"),
DelegateRequest::Notify {
call_id: "call-1".to_string(),
cell_id: CellId::new("cell-1".to_string()).into(),
text: "hello".to_string(),
},
CancellationToken::new(),
)
.await;
assert_eq!(
result,
Err("code-mode host has too many pending delegate calls".to_string())
);
assert!(!peer.is_disconnected());
drop(permits);
}