Seed realtime V3 sessions with initial text items (#34067)

## What changed

- Add an optional `initialItems` field to `thread/realtime/start` for seeding
  realtime V3 session history with `user`, `developer`, and `assistant` text.
- Encode the items in Frameless Bidi session bootstrap payloads for websocket
  and WebRTC transports while preserving existing payloads when the list is
  omitted or empty.
- Reject non-empty initial items for V1 and V2, more than 128 items, any item
  over 8,192 estimated tokens, or a combined total over 8,192 estimated tokens.

## Testing

- Cover protocol serialization, app-server forwarding, websocket and WebRTC
  payloads, empty-list compatibility, version checks, and size limits.

GitOrigin-RevId: 09af367f9a07543e3f8d59b7fa5387f932dc5a6a
This commit is contained in:
jiayuhuang-openai authored and copyberry committed 2026-07-18 20:59:57 +00:00
1 parent 35eaf3ffb0
commit 312caf176a
25 files changed
+657 -9

No files matched your search

@@ -96,6 +96,11 @@ pub struct ThreadRealtimeStartParams {
/// Set to false to start without Codex's startup context. Omitted or null includes it.
#[ts(optional = nullable)]
pub include_startup_context: Option<bool>,
/// Adds complete role-bearing text items to the initial Frameless Bidi session history.
/// This is only supported by realtime V3 and is sent during session startup. Requests are
/// limited to 128 items and 8,192 estimated text tokens in total.
#[ts(optional = nullable)]
pub initial_items: Option<Vec<ThreadRealtimeInitialItem>>,
#[serde(
default,
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
@@ -115,6 +120,15 @@ pub struct ThreadRealtimeStartParams {
pub voice: Option<RealtimeVoice>,
}
/// EXPERIMENTAL - role-bearing text item included when a realtime V3 session starts.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadRealtimeInitialItem {
pub role: ConversationTextRole,
pub text: String,
}
/// EXPERIMENTAL - transport used by thread realtime.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
@@ -4375,3 +4375,14 @@ fn realtime_append_text_defaults_role_to_user() {
}
);
}
#[test]
fn realtime_start_omitted_initial_items_remain_none() {
let params = serde_json::from_value::<ThreadRealtimeStartParams>(json!({
"threadId": "thread_123",
"outputModality": "audio",
}))
.expect("params should deserialize");
assert_eq!(params.initial_items, None);
}