mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
Add code-mode output truncation protocol contracts (#57272)
## What changed Add session options for client- or server-owned output truncation and optional `max_output_tokens` budgets for wait and terminate observations. Introduce `TerminateRequest` and update session implementations and callers to use it. Extend the stdio and gRPC schemas with optional truncation settings and budgets, and define the `bounded-output-v1` capability. Existing sessions retain client-side truncation; providers reject server mode by default. This change defines the contracts without enabling server-side truncation. ## Testing Extend protocol serialization coverage for `outputTruncation: "server"` while preserving existing wire shapes when optional fields are absent. GitOrigin-RevId: 22145897ea16d385c5ba9e7549109f7a669fceb4
This commit is contained in:
@@ -15,6 +15,7 @@ use codex_code_mode_protocol::FunctionCallOutputContentItem;
|
||||
use codex_code_mode_protocol::ImageDetail;
|
||||
use codex_code_mode_protocol::RuntimeResponse;
|
||||
use codex_code_mode_protocol::StartedCell;
|
||||
use codex_code_mode_protocol::TerminateRequest;
|
||||
use codex_code_mode_protocol::WaitOutcome;
|
||||
use codex_code_mode_protocol::WaitRequest;
|
||||
use codex_code_mode_protocol::WaitToPendingOutcome;
|
||||
@@ -129,6 +130,7 @@ impl InProcessCodeModeSession {
|
||||
preempt: Option<CancellationToken>,
|
||||
) -> CodeModeSessionResultFuture<'static, WaitOutcome> {
|
||||
let WaitRequest {
|
||||
max_output_tokens: _,
|
||||
cell_id,
|
||||
yield_time_ms,
|
||||
} = request;
|
||||
@@ -158,7 +160,8 @@ impl InProcessCodeModeSession {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn terminate(&self, cell_id: CellId) -> Result<WaitOutcome, String> {
|
||||
pub async fn terminate(&self, request: TerminateRequest) -> Result<WaitOutcome, String> {
|
||||
let cell_id = request.cell_id;
|
||||
match self.runtime.terminate(&runtime_cell_id(&cell_id)).await {
|
||||
Ok(event) => Ok(WaitOutcome::LiveCell(runtime_response(&cell_id, event)?)),
|
||||
Err(runtime::Error::MissingCell(_) | runtime::Error::ClosedCell(_)) => {
|
||||
@@ -239,8 +242,11 @@ impl CodeModeSession for InProcessCodeModeSession {
|
||||
Box::pin(InProcessCodeModeSession::wait(self, request, preempt))
|
||||
}
|
||||
|
||||
fn terminate<'a>(&'a self, cell_id: CellId) -> CodeModeSessionResultFuture<'a, WaitOutcome> {
|
||||
Box::pin(InProcessCodeModeSession::terminate(self, cell_id))
|
||||
fn terminate<'a>(
|
||||
&'a self,
|
||||
request: TerminateRequest,
|
||||
) -> CodeModeSessionResultFuture<'a, WaitOutcome> {
|
||||
Box::pin(InProcessCodeModeSession::terminate(self, request))
|
||||
}
|
||||
|
||||
fn shutdown<'a>(&'a self) -> CodeModeSessionResultFuture<'a, ()> {
|
||||
|
||||
@@ -149,6 +149,7 @@ async fn audio_helper_bounds_wav_data_and_preserves_outputs_after_yield() {
|
||||
let actual = service
|
||||
.wait(
|
||||
WaitRequest {
|
||||
max_output_tokens: None,
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
},
|
||||
|
||||
@@ -223,6 +223,7 @@ async fn yielded_cells_retain_their_own_delegate_until_closed() {
|
||||
let actual = service
|
||||
.wait(
|
||||
WaitRequest {
|
||||
max_output_tokens: None,
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
},
|
||||
@@ -253,7 +254,7 @@ async fn yielded_cells_retain_their_own_delegate_until_closed() {
|
||||
);
|
||||
assert!(weak_a.upgrade().is_none());
|
||||
|
||||
service.terminate(cell_id("2")).await.unwrap();
|
||||
service.terminate(cell_id("2").into()).await.unwrap();
|
||||
assert_eq!(
|
||||
next_event(&mut events_b).await,
|
||||
DelegateEvent::ToolCancelled
|
||||
@@ -293,6 +294,7 @@ async fn yields_and_resumes() {
|
||||
let actual = service
|
||||
.wait(
|
||||
WaitRequest {
|
||||
max_output_tokens: None,
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
},
|
||||
@@ -418,7 +420,7 @@ async fn observed_natural_completion_wins_over_termination() {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let actual = service.terminate(cell_id("1")).await.unwrap();
|
||||
let actual = service.terminate(cell_id("1").into()).await.unwrap();
|
||||
let result_buffered_for = actual.result_buffered_for().unwrap();
|
||||
assert!(!result_buffered_for.is_zero());
|
||||
assert_eq!(
|
||||
@@ -461,7 +463,7 @@ async fn termination_cancels_pending_callbacks_before_responding() {
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
service.terminate(cell_id("1")).await.unwrap(),
|
||||
service.terminate(cell_id("1").into()).await.unwrap(),
|
||||
WaitOutcome::LiveCell(RuntimeResponse::Terminated {
|
||||
code_mode_host_duration: None,
|
||||
cell_id: cell_id("1"),
|
||||
@@ -541,13 +543,13 @@ async fn repeated_termination_is_rejected_while_callback_cleanup_is_pending() {
|
||||
|
||||
let terminating_service = Arc::clone(&service);
|
||||
let first_termination =
|
||||
tokio::spawn(async move { terminating_service.terminate(cell_id("1")).await });
|
||||
tokio::spawn(async move { terminating_service.terminate(cell_id("1").into()).await });
|
||||
assert_eq!(
|
||||
next_event(&mut events_rx).await,
|
||||
DelegateEvent::NotificationCancelled
|
||||
);
|
||||
|
||||
let repeated_termination = service.terminate(cell_id("1")).await;
|
||||
let repeated_termination = service.terminate(cell_id("1").into()).await;
|
||||
delegate.release_notification();
|
||||
|
||||
assert_eq!(
|
||||
@@ -594,6 +596,7 @@ async fn second_observer_is_rejected_without_displacing_the_first() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -604,6 +607,7 @@ async fn second_observer_is_rejected_without_displacing_the_first() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -618,7 +622,7 @@ async fn second_observer_is_rejected_without_displacing_the_first() {
|
||||
content_items: Vec::new(),
|
||||
};
|
||||
assert_eq!(
|
||||
service.terminate(cell_id("1")).await.unwrap(),
|
||||
service.terminate(cell_id("1").into()).await.unwrap(),
|
||||
WaitOutcome::LiveCell(terminated.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -98,6 +98,7 @@ async fn interrupt_yields_execute_and_wait_without_stopping_the_cell() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
Some(wait_signal),
|
||||
)
|
||||
@@ -125,6 +126,7 @@ async fn interrupt_yields_execute_and_wait_without_stopping_the_cell() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -221,6 +223,7 @@ async fn execute_and_wait_clamp_yield_grace_without_stopping_the_cell() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 10_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -243,6 +246,7 @@ async fn execute_and_wait_clamp_yield_grace_without_stopping_the_cell() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 10_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -291,6 +295,7 @@ async fn wait_waits_for_nested_tool_during_yield_grace() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 10_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -366,6 +371,7 @@ async fn zero_yield_limit_is_immediate_and_scoped_to_its_session() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 60_000,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
@@ -441,7 +447,7 @@ async fn tool_start_acknowledgement_reports_termination_before_invocation() {
|
||||
|
||||
let (startup, terminated) = tokio::join!(
|
||||
wait_until_tool_started(&delegate),
|
||||
service.terminate(cell_id("1")),
|
||||
service.terminate(cell_id("1").into()),
|
||||
);
|
||||
assert_eq!(startup, Err("cell closed before nested tool started"));
|
||||
let response = RuntimeResponse::Terminated {
|
||||
@@ -746,6 +752,7 @@ text("after exit");
|
||||
let actual = service
|
||||
.wait(
|
||||
WaitRequest {
|
||||
max_output_tokens: None,
|
||||
cell_id: cell_id("1"),
|
||||
yield_time_ms: 30_000,
|
||||
},
|
||||
@@ -1106,7 +1113,7 @@ async fn execute_to_pending_returns_once_the_runtime_is_quiescent() {
|
||||
}
|
||||
);
|
||||
|
||||
let termination = service.terminate(cell_id("1")).await.unwrap();
|
||||
let termination = service.terminate(cell_id("1").into()).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
termination,
|
||||
@@ -1150,7 +1157,7 @@ await Promise.all([
|
||||
}
|
||||
);
|
||||
|
||||
let termination = service.terminate(cell_id("1")).await.unwrap();
|
||||
let termination = service.terminate(cell_id("1").into()).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
termination,
|
||||
@@ -1218,7 +1225,7 @@ await Promise.all([
|
||||
})
|
||||
);
|
||||
|
||||
let termination = service.terminate(cell_id("1")).await.unwrap();
|
||||
let termination = service.terminate(cell_id("1").into()).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
termination,
|
||||
@@ -1285,7 +1292,7 @@ await new Promise(() => {});
|
||||
})
|
||||
);
|
||||
|
||||
let termination = service.terminate(cell_id("1")).await.unwrap();
|
||||
let termination = service.terminate(cell_id("1").into()).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
termination,
|
||||
@@ -2132,6 +2139,7 @@ async fn wait_reports_missing_cell_separately_from_runtime_results() {
|
||||
WaitRequest {
|
||||
cell_id: cell_id("missing"),
|
||||
yield_time_ms: 1,
|
||||
max_output_tokens: None,
|
||||
},
|
||||
/*preempt*/ None,
|
||||
)
|
||||
|
||||
@@ -242,7 +242,7 @@ async fn terminates_helpers_waiting_for_unsettled_inputs() {
|
||||
assert_eq!(
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(/*secs*/ 5),
|
||||
session.terminate(cell_id.clone())
|
||||
session.terminate(cell_id.clone().into())
|
||||
)
|
||||
.await
|
||||
.expect("termination timed out")
|
||||
|
||||
Reference in New Issue
Block a user