Support configurable realtime BEM channel prefixes (#34816)

## What changed

- Route realtime V3 `bemTags` handoffs using `[ANALYSIS]`, `[COMMENTARY]`, and `[FINAL]` prefixes by default.
- Add `codexResponseHandoffChannelPrefixes` to realtime start requests so clients can replace the accepted prefixes for individual channels. Each channel may accept multiple prefixes, and omitted channels retain their defaults.
- Apply the configured prefixes to both complete and streamed handoff messages, without treating empty prefixes as matches.

## Testing

- Cover request deserialization, default and custom routing, partial streamed prefixes, empty-prefix handling, and end-to-end V3 handoffs.

GitOrigin-RevId: d0aef50aa71362a5beef2c6f6d28b8633da4e529
This commit is contained in:
jiayuhuang-openai authored and copyberry committed 2026-07-22 19:43:35 +00:00
1 parent 08ae0fc0ce
commit 4ebd976312
15 files changed
+325 -73

No files matched your search

@@ -3482,6 +3482,14 @@ mod tests {
codex_responses_as_items: None,
codex_response_item_prefix: None,
codex_response_handoff_mode: Some(CodexResponseHandoffMode::BemTags),
codex_response_handoff_channel_prefixes: Some(std::collections::BTreeMap::from([
("analysis".to_string(), vec!["[THINKING]".to_string()]),
(
"commentary".to_string(),
vec!["[PROGRESS]".to_string(), "[UPDATE]".to_string()],
),
("final".to_string(), vec!["[DONE]".to_string()]),
])),
thread_id: "thr_123".to_string(),
model: Some("realtime-treatment-model".to_string()),
output_modality: RealtimeOutputModality::Audio,
@@ -3514,6 +3522,11 @@ mod tests {
"codexResponsesAsItems": null,
"codexResponseItemPrefix": null,
"codexResponseHandoffMode": "bemTags",
"codexResponseHandoffChannelPrefixes": {
"analysis": ["[THINKING]"],
"commentary": ["[PROGRESS]", "[UPDATE]"],
"final": ["[DONE]"]
},
"model": "realtime-treatment-model",
"outputModality": "audio",
"includeStartupContext": false,
@@ -3549,6 +3562,7 @@ mod tests {
codex_responses_as_items: None,
codex_response_item_prefix: None,
codex_response_handoff_mode: None,
codex_response_handoff_channel_prefixes: None,
thread_id: "thr_123".to_string(),
model: None,
output_modality: RealtimeOutputModality::Audio,
@@ -3572,6 +3586,7 @@ mod tests {
"codexResponsesAsItems": null,
"codexResponseItemPrefix": null,
"codexResponseHandoffMode": null,
"codexResponseHandoffChannelPrefixes": null,
"model": null,
"outputModality": "audio",
"includeStartupContext": null,
@@ -3593,6 +3608,7 @@ mod tests {
codex_responses_as_items: None,
codex_response_item_prefix: None,
codex_response_handoff_mode: None,
codex_response_handoff_channel_prefixes: None,
thread_id: "thr_123".to_string(),
model: None,
output_modality: RealtimeOutputModality::Audio,
@@ -3616,6 +3632,7 @@ mod tests {
"codexResponsesAsItems": null,
"codexResponseItemPrefix": null,
"codexResponseHandoffMode": null,
"codexResponseHandoffChannelPrefixes": null,
"model": null,
"outputModality": "audio",
"includeStartupContext": null,
@@ -3837,6 +3854,7 @@ mod tests {
codex_responses_as_items: None,
codex_response_item_prefix: None,
codex_response_handoff_mode: None,
codex_response_handoff_channel_prefixes: None,
thread_id: "thr_123".to_string(),
model: None,
output_modality: RealtimeOutputModality::Audio,