mirror of
https://github.com/openai/codex.git
synced 2026-10-21 05:12:46 +00:00
Add a realtime delegation acknowledgement control (#36413)
## What changed - Add the optional `delegationAckFiller` field to `thread/realtime/start`. - Forward explicit `true` or `false` values to V3 Frameless Bidi session payloads as `delegation.ack_filler`; leave the field absent when unspecified so the Realtime API default is preserved. - Document that V1 and V2 ignore this setting. ## Testing - Cover both boolean values in Frameless Bidi serialization and verify the WebRTC V3 session creation payload. GitOrigin-RevId: 9fe8efd14e42738c3e86e3e9a8af81d0f4521244
This commit is contained in:
committed by
copyberry
parent
4c219fdb1a
commit
6751b54cae
BIN
Binary file not shown.
@@ -3606,6 +3606,7 @@ mod tests {
|
||||
request_id: RequestId::Integer(9),
|
||||
params: v2::ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: Some(true),
|
||||
delegation_ack_filler: Some(false),
|
||||
flush_transcript_tail_on_session_end: Some(true),
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3648,6 +3649,7 @@ mod tests {
|
||||
"params": {
|
||||
"threadId": "thr_123",
|
||||
"clientManagedHandoffs": true,
|
||||
"delegationAckFiller": false,
|
||||
"flushTranscriptTailOnSessionEnd": true,
|
||||
"codexResponsesAsItems": null,
|
||||
"codexResponseItemPrefix": null,
|
||||
@@ -3690,6 +3692,7 @@ mod tests {
|
||||
request_id: RequestId::Integer(9),
|
||||
params: v2::ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3716,6 +3719,7 @@ mod tests {
|
||||
"params": {
|
||||
"threadId": "thr_123",
|
||||
"clientManagedHandoffs": null,
|
||||
"delegationAckFiller": null,
|
||||
"flushTranscriptTailOnSessionEnd": null,
|
||||
"codexResponsesAsItems": null,
|
||||
"codexResponseItemPrefix": null,
|
||||
@@ -3740,6 +3744,7 @@ mod tests {
|
||||
request_id: RequestId::Integer(9),
|
||||
params: v2::ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3766,6 +3771,7 @@ mod tests {
|
||||
"params": {
|
||||
"threadId": "thr_123",
|
||||
"clientManagedHandoffs": null,
|
||||
"delegationAckFiller": null,
|
||||
"flushTranscriptTailOnSessionEnd": null,
|
||||
"codexResponsesAsItems": null,
|
||||
"codexResponseItemPrefix": null,
|
||||
@@ -3990,6 +3996,7 @@ mod tests {
|
||||
request_id: RequestId::Integer(1),
|
||||
params: v2::ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -72,6 +72,10 @@ pub struct ThreadRealtimeStartParams {
|
||||
/// them automatically. Defaults to false.
|
||||
#[ts(optional = nullable)]
|
||||
pub client_managed_handoffs: Option<bool>,
|
||||
/// Controls whether a realtime V3 delegation produces an acknowledgement filler.
|
||||
/// Omitted values preserve the Realtime API's default behavior.
|
||||
#[ts(optional = nullable)]
|
||||
pub delegation_ack_filler: Option<bool>,
|
||||
/// Routes any transcript tail remaining at session end through Codex. Defaults to false.
|
||||
/// TODO: Remove this rollout knob once transcript-tail flushing is always enabled.
|
||||
#[ts(optional = nullable)]
|
||||
|
||||
@@ -4731,7 +4731,6 @@ fn realtime_start_omitted_initial_items_remain_none() {
|
||||
|
||||
assert_eq!(params.initial_items, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn realtime_start_deserializes_client_handoff_channel_prefixes() {
|
||||
let params = serde_json::from_value::<ThreadRealtimeStartParams>(json!({
|
||||
|
||||
@@ -180,7 +180,7 @@ Example with notification opt-out:
|
||||
- `thread/inject_items` — append raw Responses API items to a loaded thread’s model-visible history without starting a user turn; returns `{}` on success.
|
||||
- `turn/steer` — add user input to an already in-flight regular turn without starting a new turn; returns the active `turnId` that accepted the input. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Review and manual compaction turns reject `turn/steer`.
|
||||
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
|
||||
- `thread/realtime/start` — start a thread-scoped realtime session (experimental); pass `outputModality: "text"` or `outputModality: "audio"` to choose model output, optionally pass `model` and `version` to override configured realtime selection for this session only, pass `includeStartupContext: false` to omit Codex's generated startup context, and optionally pass `initialItems` to seed V3 with complete role-bearing text messages at session creation. Pass `realtimeStartInstructions` and `realtimeEndInstructions` to control the developer instructions given to the backing Codex model when this session starts and ends. Version `"v1"` uses legacy Bidi `conversation.handoff.*`, `"v2"` uses the Realtime Voice API, and `"v3"` preserves V1 Codex Voice behavior while using Frameless Bidi `delegation.*`. For V3 automatic Codex text, `codexResponseHandoffMode` accepts `"thinking"` (the default; all output uses channel-less thinking appends), `"commentary"` (all output uses the commentary channel), or `"bemTags"` (the raw BEM envelope selects the API channel: BEM `analysis` and `commentary` use `commentary`, while BEM `final` and unparsable output use `speakable`). The BEM envelope remains in the appended text for the frontend model to interpret. V1 and V2 ignore this setting. V3 handoffs do not prepend the legacy `"Agent Final Message"` label. Pass `clientManagedHandoffs: true` to disable automatic Codex response delivery so only the client's explicit append calls produce handoffs. Pass `codexResponsesAsItems: true` to send automatic Codex responses as realtime conversation items instead, and optionally pass `codexResponseItemPrefix` to prepend experiment instructions to those items. Returns `{}` and streams `thread/realtime/*` notifications. Omit `transport` for the websocket transport, or pass `{ "type": "webrtc", "sdp": "..." }` to create a Bidi WebRTC session from a browser-generated SDP offer; the remote answer SDP is emitted as `thread/realtime/sdp`. Conversation `version: "v2"` requests remain unsupported for WebRTC.
|
||||
- `thread/realtime/start` — start a thread-scoped realtime session (experimental); pass `outputModality: "text"` or `outputModality: "audio"` to choose model output, optionally pass `model` and `version` to override configured realtime selection for this session only, pass `includeStartupContext: false` to omit Codex's generated startup context, and optionally pass `initialItems` to seed V3 with complete role-bearing text messages at session creation. Pass `realtimeStartInstructions` and `realtimeEndInstructions` to control the developer instructions given to the backing Codex model when this session starts and ends. Version `"v1"` uses legacy Bidi `conversation.handoff.*`, `"v2"` uses the Realtime Voice API, and `"v3"` preserves V1 Codex Voice behavior while using Frameless Bidi `delegation.*`. For V3 automatic Codex text, `codexResponseHandoffMode` accepts `"thinking"` (the default; all output uses channel-less thinking appends), `"commentary"` (all output uses the commentary channel), or `"bemTags"` (the raw BEM envelope selects the API channel: BEM `analysis` and `commentary` use `commentary`, while BEM `final` and unparsable output use `speakable`). The BEM envelope remains in the appended text for the frontend model to interpret. V1 and V2 ignore this setting. For V3, pass `delegationAckFiller: false` to suppress the Realtime API's delegation acknowledgement filler or `true` to restore it; omitting the field preserves the Realtime API's default. V1 and V2 ignore `delegationAckFiller`. V3 handoffs do not prepend the legacy `"Agent Final Message"` label. Pass `clientManagedHandoffs: true` to disable automatic Codex response delivery so only the client's explicit append calls produce handoffs. Pass `codexResponsesAsItems: true` to send automatic Codex responses as realtime conversation items instead, and optionally pass `codexResponseItemPrefix` to prepend experiment instructions to those items. Returns `{}` and streams `thread/realtime/*` notifications. Omit `transport` for the websocket transport, or pass `{ "type": "webrtc", "sdp": "..." }` to create a Bidi WebRTC session from a browser-generated SDP offer; the remote answer SDP is emitted as `thread/realtime/sdp`. Conversation `version: "v2"` requests remain unsupported for WebRTC.
|
||||
- `thread/realtime/appendAudio` — append an input audio chunk to the active realtime session (experimental); returns `{}`.
|
||||
- `thread/realtime/appendText` — append text input to the active realtime session with a required `role` of `user`, `developer`, or `assistant` (experimental); returns `{}`. Older clients that omit `role` default to `user`.
|
||||
- `thread/realtime/appendSpeech` — append text that the realtime model should speak to the user (experimental); returns `{}`.
|
||||
@@ -1023,6 +1023,10 @@ Requests are limited to 128 items, 8,192 estimated text tokens per item, and
|
||||
Omitting `initialItems`, or passing an empty list, preserves the previous
|
||||
session payload and startup behavior. V1 and V2 reject non-empty
|
||||
`initialItems`.
|
||||
For V3, pass `delegationAckFiller: false` to suppress the Realtime API's
|
||||
delegation acknowledgement filler during WebRTC session creation, or pass `true`
|
||||
to restore the legacy acknowledgement. Omitting `delegationAckFiller` preserves
|
||||
the Realtime API's default. V1 and V2 ignore this setting.
|
||||
Pass `clientManagedHandoffs: true` to suppress automatic Codex response handoffs
|
||||
and items. The client can then choose which updates to deliver with
|
||||
`thread/realtime/appendText` or `thread/realtime/appendSpeech`.
|
||||
|
||||
@@ -1075,6 +1075,7 @@ impl TurnRequestProcessor {
|
||||
thread.as_ref(),
|
||||
Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: params.client_managed_handoffs.unwrap_or(false),
|
||||
delegation_ack_filler: params.delegation_ack_filler,
|
||||
flush_transcript_tail_on_session_end: params
|
||||
.flush_transcript_tail_on_session_end
|
||||
.unwrap_or(false),
|
||||
|
||||
@@ -90,6 +90,7 @@ async fn realtime_conversation_start_requires_experimental_api_capability() -> R
|
||||
let request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -223,6 +224,7 @@ async fn realtime_webrtc_start_requires_experimental_api_capability() -> Result<
|
||||
let request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -329,6 +329,7 @@ impl RealtimeE2eHarness {
|
||||
/*client_managed_handoffs*/ None,
|
||||
/*codex_responses_as_items*/ None,
|
||||
/*codex_response_handoff_mode*/ None,
|
||||
/*delegation_ack_filler*/ None,
|
||||
RealtimeConversationVersion::V1,
|
||||
)
|
||||
.await
|
||||
@@ -343,6 +344,7 @@ impl RealtimeE2eHarness {
|
||||
/*client_managed_handoffs*/ None,
|
||||
/*codex_responses_as_items*/ Some(true),
|
||||
/*codex_response_handoff_mode*/ None,
|
||||
/*delegation_ack_filler*/ None,
|
||||
RealtimeConversationVersion::V1,
|
||||
)
|
||||
.await
|
||||
@@ -354,6 +356,7 @@ impl RealtimeE2eHarness {
|
||||
client_managed_handoffs: Option<bool>,
|
||||
codex_responses_as_items: Option<bool>,
|
||||
codex_response_handoff_mode: Option<CodexResponseHandoffMode>,
|
||||
delegation_ack_filler: Option<bool>,
|
||||
version: RealtimeConversationVersion,
|
||||
) -> Result<StartedWebrtcRealtime> {
|
||||
// Starts realtime through the public JSON-RPC method, then waits for the same client-visible
|
||||
@@ -362,6 +365,7 @@ impl RealtimeE2eHarness {
|
||||
.mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs,
|
||||
delegation_ack_filler,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
thread_id: self.thread_id.clone(),
|
||||
codex_response_item_prefix: codex_responses_as_items
|
||||
@@ -423,6 +427,7 @@ impl RealtimeE2eHarness {
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
thread_id: self.thread_id.clone(),
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_response_item_prefix: codex_responses_as_items
|
||||
.unwrap_or(false)
|
||||
@@ -461,6 +466,7 @@ impl RealtimeE2eHarness {
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
thread_id: self.thread_id.clone(),
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_response_item_prefix: None,
|
||||
codex_response_handoff_mode,
|
||||
@@ -730,6 +736,7 @@ async fn realtime_conversation_streams_v2_notifications() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1011,6 +1018,7 @@ async fn realtime_start_can_skip_startup_context() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1108,6 +1116,7 @@ async fn realtime_text_output_modality_requests_text_output_and_final_transcript
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1286,6 +1295,7 @@ async fn realtime_conversation_stop_emits_closed_notification() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1359,6 +1369,7 @@ async fn realtime_mode_uses_client_instructions_on_entry_and_exit() -> Result<()
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
thread_id: harness.thread_id.clone(),
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1493,6 +1504,7 @@ async fn realtime_webrtc_start_emits_sdp_notification() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1687,6 +1699,7 @@ async fn webrtc_v3_start_posts_live_session_and_joins_without_session_update() -
|
||||
/*client_managed_handoffs*/ None,
|
||||
/*codex_responses_as_items*/ None,
|
||||
/*codex_response_handoff_mode*/ None,
|
||||
/*delegation_ack_filler*/ Some(false),
|
||||
RealtimeConversationVersion::V3,
|
||||
)
|
||||
.await?;
|
||||
@@ -1708,7 +1721,7 @@ async fn webrtc_v3_start_posts_live_session_and_joins_without_session_update() -
|
||||
assert_call_create_multipart(
|
||||
harness.call_capture.single_request(),
|
||||
"v=offer\r\n",
|
||||
r#"{"audio":{"output":{"voice":"cove"}},"delegation":{"type":"client"},"instructions":"backend prompt\n\nstartup context","model":"gpt-live-1-boulder-alpha"}"#,
|
||||
r#"{"audio":{"output":{"voice":"cove"}},"delegation":{"ack_filler":false,"type":"client"},"instructions":"backend prompt\n\nstartup context","model":"gpt-live-1-boulder-alpha"}"#,
|
||||
"/v1/live",
|
||||
)?;
|
||||
assert!(
|
||||
@@ -1812,6 +1825,7 @@ async fn webrtc_v1_client_managed_handoffs_disable_automatic_output() -> Result<
|
||||
/*client_managed_handoffs*/ Some(true),
|
||||
/*codex_responses_as_items*/ None,
|
||||
/*codex_response_handoff_mode*/ None,
|
||||
/*delegation_ack_filler*/ None,
|
||||
RealtimeConversationVersion::V1,
|
||||
)
|
||||
.await?;
|
||||
@@ -1902,6 +1916,7 @@ async fn webrtc_v1_ignores_codex_response_handoff_mode() -> Result<()> {
|
||||
/*client_managed_handoffs*/ None,
|
||||
/*codex_responses_as_items*/ None,
|
||||
/*codex_response_handoff_mode*/ Some(CodexResponseHandoffMode::BemTags),
|
||||
/*delegation_ack_filler*/ None,
|
||||
RealtimeConversationVersion::V1,
|
||||
)
|
||||
.await?;
|
||||
@@ -3130,6 +3145,7 @@ async fn realtime_webrtc_start_surfaces_backend_error() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3195,6 +3211,7 @@ async fn realtime_conversation_requires_feature_flag() -> Result<()> {
|
||||
let start_request_id = mcp
|
||||
.send_thread_realtime_start_request(ThreadRealtimeStartParams {
|
||||
client_managed_handoffs: None,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: None,
|
||||
codex_responses_as_items: None,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -389,6 +389,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "hi".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("gpt-realtime".to_string()),
|
||||
session_id: Some(session_id.to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
|
||||
@@ -384,6 +384,7 @@ impl RealtimeWebsocketWriter {
|
||||
session_mode: RealtimeSessionMode,
|
||||
output_modality: RealtimeOutputModality,
|
||||
voice: RealtimeVoice,
|
||||
delegation_ack_filler: Option<bool>,
|
||||
) -> Result<(), ApiError> {
|
||||
let session_mode = normalized_session_mode(self.event_parser, session_mode);
|
||||
let message = session_update_message(
|
||||
@@ -393,6 +394,7 @@ impl RealtimeWebsocketWriter {
|
||||
session_mode,
|
||||
output_modality,
|
||||
voice,
|
||||
delegation_ack_filler,
|
||||
);
|
||||
self.send_json(&message).await
|
||||
}
|
||||
@@ -868,6 +870,7 @@ impl RealtimeWebsocketClient {
|
||||
config.session_mode,
|
||||
config.output_modality,
|
||||
config.voice,
|
||||
config.delegation_ack_filler,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -2019,6 +2022,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_1".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -2344,6 +2348,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_1".to_string()),
|
||||
event_parser: RealtimeEventParser::RealtimeV2,
|
||||
@@ -2470,6 +2475,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_1".to_string()),
|
||||
event_parser: RealtimeEventParser::RealtimeV2,
|
||||
@@ -2575,6 +2581,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_1".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -2666,6 +2673,7 @@ mod tests {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_1".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
|
||||
@@ -118,15 +118,19 @@ pub(super) fn session_update_message(
|
||||
session_mode: RealtimeSessionMode,
|
||||
output_modality: RealtimeOutputModality,
|
||||
voice: RealtimeVoice,
|
||||
delegation_ack_filler: Option<bool>,
|
||||
) -> RealtimeOutboundMessage {
|
||||
let session_mode = normalized_session_mode(wire_adapter, session_mode);
|
||||
match wire_adapter {
|
||||
RealtimeWireAdapter::V1 => RealtimeOutboundMessage::SessionUpdate {
|
||||
session: v1_session_update_session(instructions, voice),
|
||||
},
|
||||
RealtimeWireAdapter::FramelessBidi => {
|
||||
frameless_session_update_message(instructions, initial_items, voice)
|
||||
}
|
||||
RealtimeWireAdapter::FramelessBidi => frameless_session_update_message(
|
||||
instructions,
|
||||
initial_items,
|
||||
voice,
|
||||
delegation_ack_filler,
|
||||
),
|
||||
RealtimeWireAdapter::RealtimeV2 => RealtimeOutboundMessage::SessionUpdate {
|
||||
session: v2_session_update_session(instructions, session_mode, output_modality, voice),
|
||||
},
|
||||
@@ -157,6 +161,7 @@ pub fn session_update_session_json(config: RealtimeSessionConfig) -> JsonResult<
|
||||
config.instructions,
|
||||
config.initial_items,
|
||||
config.voice,
|
||||
config.delegation_ack_filler,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,51 @@
|
||||
use super::conversation_function_call_output_message;
|
||||
use super::conversation_handoff_append_message;
|
||||
use super::session_update_message;
|
||||
use super::standalone_handoff_message;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeContextAppendChannel;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeOutputModality;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeSessionMode;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeVoice;
|
||||
use crate::endpoint::realtime_websocket::protocol::RealtimeWireAdapter;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use serde_json::json;
|
||||
use serde_json::to_value;
|
||||
|
||||
#[test]
|
||||
fn frameless_session_update_encodes_explicit_delegation_ack_filler() {
|
||||
for delegation_ack_filler in [false, true] {
|
||||
let message = session_update_message(
|
||||
RealtimeWireAdapter::FramelessBidi,
|
||||
"instructions".to_string(),
|
||||
Vec::new(),
|
||||
RealtimeSessionMode::Conversational,
|
||||
RealtimeOutputModality::Audio,
|
||||
RealtimeVoice::Marin,
|
||||
Some(delegation_ack_filler),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
to_value(message).expect("frameless session update should serialize"),
|
||||
json!({
|
||||
"type": "session.update",
|
||||
"session": {
|
||||
"instructions": "instructions",
|
||||
"audio": {
|
||||
"output": {
|
||||
"voice": "marin",
|
||||
},
|
||||
},
|
||||
"delegation": {
|
||||
"type": "client",
|
||||
"ack_filler": delegation_ack_filler,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_append_channel_only_encodes_for_frameless_handoff_output() {
|
||||
let legacy = conversation_handoff_append_message(
|
||||
|
||||
@@ -36,9 +36,16 @@ pub(super) fn session_update_message(
|
||||
instructions: String,
|
||||
initial_items: Vec<ConversationTextParams>,
|
||||
voice: RealtimeVoice,
|
||||
delegation_ack_filler: Option<bool>,
|
||||
) -> RealtimeOutboundMessage {
|
||||
RealtimeOutboundMessage::FramelessSessionUpdate {
|
||||
session: session_json(/*model*/ None, instructions, initial_items, voice),
|
||||
session: session_json(
|
||||
/*model*/ None,
|
||||
instructions,
|
||||
initial_items,
|
||||
voice,
|
||||
delegation_ack_filler,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +54,7 @@ pub(super) fn session_json(
|
||||
instructions: String,
|
||||
initial_items: Vec<ConversationTextParams>,
|
||||
voice: RealtimeVoice,
|
||||
delegation_ack_filler: Option<bool>,
|
||||
) -> Value {
|
||||
let mut session = json!({
|
||||
"instructions": instructions,
|
||||
@@ -62,6 +70,9 @@ pub(super) fn session_json(
|
||||
if let Some(model) = model {
|
||||
session["model"] = Value::String(model);
|
||||
}
|
||||
if let Some(delegation_ack_filler) = delegation_ack_filler {
|
||||
session["delegation"]["ack_filler"] = Value::Bool(delegation_ack_filler);
|
||||
}
|
||||
if !initial_items.is_empty() {
|
||||
session["initial_items"] = Value::Array(
|
||||
initial_items
|
||||
|
||||
@@ -27,6 +27,7 @@ fn session_json_omits_initial_items_when_empty() {
|
||||
"instructions".to_string(),
|
||||
Vec::new(),
|
||||
RealtimeVoice::Marin,
|
||||
/*delegation_ack_filler*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -66,6 +67,7 @@ fn session_json_encodes_role_bearing_initial_items() {
|
||||
},
|
||||
],
|
||||
RealtimeVoice::Marin,
|
||||
/*delegation_ack_filler*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -38,6 +38,7 @@ pub enum RealtimeContextAppendChannel {
|
||||
pub struct RealtimeSessionConfig {
|
||||
pub instructions: String,
|
||||
pub initial_items: Vec<ConversationTextParams>,
|
||||
pub delegation_ack_filler: Option<bool>,
|
||||
pub model: Option<String>,
|
||||
pub session_id: Option<String>,
|
||||
pub event_parser: RealtimeEventParser,
|
||||
|
||||
@@ -145,6 +145,7 @@ async fn realtime_ws_e2e_session_create_and_event_flow() {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -251,6 +252,7 @@ async fn realtime_ws_connect_webrtc_sideband_retries_join_until_server_is_availa
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::RealtimeV2,
|
||||
@@ -324,6 +326,7 @@ async fn realtime_ws_e2e_send_while_next_event_waits() {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -393,6 +396,7 @@ async fn realtime_ws_e2e_disconnected_emitted_once() {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -458,6 +462,7 @@ async fn realtime_ws_e2e_ignores_unknown_text_events() {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::V1,
|
||||
@@ -566,6 +571,7 @@ async fn realtime_ws_e2e_realtime_v2_parser_emits_handoff_requested() {
|
||||
RealtimeSessionConfig {
|
||||
instructions: "backend prompt".to_string(),
|
||||
initial_items: Vec::new(),
|
||||
delegation_ack_filler: None,
|
||||
model: Some("realtime-test-model".to_string()),
|
||||
session_id: Some("conv_123".to_string()),
|
||||
event_parser: RealtimeEventParser::RealtimeV2,
|
||||
|
||||
@@ -1357,6 +1357,7 @@ pub(crate) async fn build_realtime_session_config(
|
||||
Ok(RealtimeSessionConfig {
|
||||
instructions: prompt,
|
||||
initial_items: params.initial_items.clone(),
|
||||
delegation_ack_filler: params.delegation_ack_filler,
|
||||
model,
|
||||
session_id: Some(
|
||||
params
|
||||
|
||||
@@ -243,6 +243,7 @@ async fn start_realtime_conversation(codex: &codex_core::CodexThread) -> Result<
|
||||
codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -286,6 +286,7 @@ async fn conversation_start_audio_text_close_round_trip() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -438,6 +439,7 @@ async fn conversation_start_defaults_to_v2_and_gpt_realtime_1_5() -> Result<()>
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -531,6 +533,7 @@ async fn conversation_webrtc_frameless_chatgpt_sends_codex_headers_to_backend()
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -657,6 +660,7 @@ async fn conversation_webrtc_start_posts_generated_session() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -850,6 +854,7 @@ async fn conversation_webrtc_start_uses_avas_query() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -954,6 +959,7 @@ async fn conversation_webrtc_default_v1_ignores_configured_v2_voice() -> Result<
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1020,6 +1026,7 @@ async fn conversation_webrtc_default_v1_rejects_explicit_v2_voice() -> Result<()
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1096,6 +1103,7 @@ async fn conversation_webrtc_start_uses_configured_call_base_url_for_avas() -> R
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1209,6 +1217,7 @@ async fn conversation_webrtc_close_while_sideband_connecting_drops_pending_join(
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1315,6 +1324,7 @@ async fn conversation_webrtc_sideband_connect_failure_closes_with_error() -> Res
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1414,6 +1424,7 @@ async fn conversation_start_uses_openai_env_key_fallback_with_chatgpt_auth() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1511,6 +1522,7 @@ async fn assert_transport_close_tail_flush(
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1633,6 +1645,7 @@ async fn conversation_start_preflight_failure_emits_realtime_error_only() -> Res
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1690,6 +1703,7 @@ async fn conversation_start_connect_failure_emits_realtime_error_only() -> Resul
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1795,6 +1809,7 @@ async fn conversation_second_start_replaces_runtime() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1831,6 +1846,7 @@ async fn conversation_second_start_replaces_runtime() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -1938,6 +1954,7 @@ async fn conversation_uses_experimental_realtime_ws_base_url_override() -> Resul
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2013,6 +2030,7 @@ async fn conversation_uses_default_realtime_backend_prompt() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2096,6 +2114,7 @@ async fn conversation_uses_empty_instructions_for_null_or_empty_prompt() -> Resu
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2172,6 +2191,7 @@ async fn conversation_uses_explicit_start_voice() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2240,6 +2260,7 @@ async fn conversation_uses_configured_realtime_voice() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2296,6 +2317,7 @@ async fn conversation_rejects_voice_for_wrong_realtime_version() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2353,6 +2375,7 @@ async fn conversation_uses_experimental_realtime_ws_backend_prompt_override() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2436,6 +2459,7 @@ async fn conversation_uses_experimental_realtime_ws_startup_context_override() -
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2513,6 +2537,7 @@ async fn conversation_disables_realtime_startup_context_with_empty_override() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2583,6 +2608,7 @@ async fn conversation_start_injects_startup_context_from_thread_history() -> Res
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2708,6 +2734,7 @@ async fn conversation_startup_context_current_thread_selects_many_turns_by_budge
|
||||
codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2826,6 +2853,7 @@ async fn conversation_startup_context_falls_back_to_workspace_map() -> Result<()
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2896,6 +2924,7 @@ async fn conversation_startup_context_is_truncated_and_sent_once_per_start() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -2987,6 +3016,7 @@ async fn conversation_user_text_turn_is_not_sent_to_realtime() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3094,6 +3124,7 @@ async fn realtime_v2_noop_tool_call_returns_empty_function_output_without_respon
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3203,6 +3234,7 @@ async fn conversation_mirrors_assistant_message_text_to_realtime_handoff() -> Re
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3373,6 +3405,7 @@ async fn conversation_flushes_assistant_deltas_every_200ms_for_v3_handoff() -> R
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3551,6 +3584,7 @@ async fn conversation_handoff_persists_across_item_done_until_turn_complete() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3713,6 +3747,7 @@ async fn inbound_handoff_request_starts_turn() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3833,6 +3868,7 @@ async fn inbound_handoff_request_uses_active_transcript() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -3946,6 +3982,7 @@ async fn inbound_handoff_request_sends_transcript_delta_after_each_handoff() ->
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4078,6 +4115,7 @@ async fn conversation_close_routes_only_remaining_transcript_tail_once() -> Resu
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: true,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4171,6 +4209,7 @@ async fn inbound_conversation_item_does_not_start_turn_and_still_forwards_audio(
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4304,6 +4343,7 @@ async fn delegated_turn_user_role_echo_does_not_redelegate_and_still_forwards_au
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4467,6 +4507,7 @@ async fn inbound_handoff_request_does_not_block_realtime_event_forwarding() -> R
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4619,6 +4660,7 @@ async fn inbound_handoff_request_steers_active_turn() -> Result<()> {
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
@@ -4782,6 +4824,7 @@ async fn inbound_handoff_request_starts_turn_and_does_not_block_realtime_audio()
|
||||
test.codex
|
||||
.submit(Op::RealtimeConversationStart(ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -199,6 +199,7 @@ async fn assert_start_error(params: ConversationStartParams, expected_error: &st
|
||||
fn start_params(version: RealtimeConversationVersion) -> ConversationStartParams {
|
||||
ConversationStartParams {
|
||||
client_managed_handoffs: false,
|
||||
delegation_ack_filler: None,
|
||||
flush_transcript_tail_on_session_end: false,
|
||||
codex_responses_as_items: false,
|
||||
codex_response_item_prefix: None,
|
||||
|
||||
@@ -200,6 +200,9 @@ pub struct W3cTraceContext {
|
||||
pub struct ConversationStartParams {
|
||||
/// Whether Codex response handoffs are managed through explicit client append calls.
|
||||
pub client_managed_handoffs: bool,
|
||||
/// Whether a realtime V3 delegation produces an acknowledgement filler.
|
||||
/// `None` preserves the Realtime API's default behavior.
|
||||
pub delegation_ack_filler: Option<bool>,
|
||||
/// Whether to route any remaining transcript tail through Codex when the session ends.
|
||||
/// TODO: Remove this rollout knob once transcript-tail flushing is always enabled.
|
||||
pub flush_transcript_tail_on_session_end: bool,
|
||||
|
||||
Reference in New Issue
Block a user