mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
[core] Support interleaved response items (#30876)
## Summary - preserve reasoning item IDs on summary part and text delta events - track streamed response items by ID so reasoning summaries can continue after later items begin - keep TUI output complete and deduplicated when reasoning and final-answer events interleave ## Stack - **1/2: this PR — interleaved item support** - 2/2: #30752 — wire reasoning summary delivery configuration through the CLI and app-server ## Validation - just test -p codex-api preserves_reasoning_summary_item_ids - just test -p codex-core interleaved_reasoning_summary_events_keep_reasoning_item_metadata - just test -p codex-tui live_reasoning_summary_is_not_rendered_twice_when_item_completes - just fix -p codex-api -p codex-core -p codex-tui - just fmt
This commit is contained in:
@@ -101,6 +101,7 @@ pub enum ResponseEvent {
|
||||
delta: String,
|
||||
},
|
||||
ReasoningSummaryDelta {
|
||||
item_id: String,
|
||||
delta: String,
|
||||
summary_index: i64,
|
||||
},
|
||||
@@ -109,6 +110,7 @@ pub enum ResponseEvent {
|
||||
content_index: i64,
|
||||
},
|
||||
ReasoningSummaryPartAdded {
|
||||
item_id: String,
|
||||
summary_index: i64,
|
||||
},
|
||||
RateLimits(RateLimitSnapshot),
|
||||
|
||||
@@ -351,8 +351,11 @@ pub fn process_responses_event(
|
||||
}
|
||||
}
|
||||
"response.reasoning_summary_text.delta" => {
|
||||
if let (Some(delta), Some(summary_index)) = (event.delta, event.summary_index) {
|
||||
if let (Some(item_id), Some(delta), Some(summary_index)) =
|
||||
(event.item_id, event.delta, event.summary_index)
|
||||
{
|
||||
return Ok(Some(ResponseEvent::ReasoningSummaryDelta {
|
||||
item_id,
|
||||
delta,
|
||||
summary_index,
|
||||
}));
|
||||
@@ -444,8 +447,9 @@ pub fn process_responses_event(
|
||||
}
|
||||
}
|
||||
"response.reasoning_summary_part.added" => {
|
||||
if let Some(summary_index) = event.summary_index {
|
||||
if let (Some(item_id), Some(summary_index)) = (event.item_id, event.summary_index) {
|
||||
return Ok(Some(ResponseEvent::ReasoningSummaryPartAdded {
|
||||
item_id,
|
||||
summary_index,
|
||||
}));
|
||||
}
|
||||
@@ -796,6 +800,44 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preserves_reasoning_summary_item_ids() {
|
||||
let events = run_sse(vec![
|
||||
json!({
|
||||
"type": "response.reasoning_summary_part.added",
|
||||
"item_id": "reasoning-1",
|
||||
"summary_index": 0
|
||||
}),
|
||||
json!({
|
||||
"type": "response.reasoning_summary_text.delta",
|
||||
"item_id": "reasoning-1",
|
||||
"summary_index": 0,
|
||||
"delta": "Checking"
|
||||
}),
|
||||
json!({
|
||||
"type": "response.completed",
|
||||
"response": { "id": "resp1" }
|
||||
}),
|
||||
])
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
&events[0],
|
||||
ResponseEvent::ReasoningSummaryPartAdded {
|
||||
item_id,
|
||||
summary_index: 0,
|
||||
} if item_id == "reasoning-1"
|
||||
);
|
||||
assert_matches!(
|
||||
&events[1],
|
||||
ResponseEvent::ReasoningSummaryDelta {
|
||||
item_id,
|
||||
delta,
|
||||
summary_index: 0,
|
||||
} if item_id == "reasoning-1" && delta == "Checking"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn error_when_missing_completed() {
|
||||
let item1 = json!({
|
||||
|
||||
Reference in New Issue
Block a user