mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[App-server] add new v2 events:item/reasoning/delta, item/agentMessage/delta & item/reasoning/summaryPartAdded (#6559)
core event to app server event mapping: 1. `codex/event/reasoning_content_delta` -> `item/reasoning/summaryTextDelta`. 2. `codex/event/reasoning_raw_content_delta` -> `item/reasoning/textDelta` 3. `codex/event/agent_message_content_delta` → `item/agentMessage/delta`. 4. `codex/event/agent_reasoning_section_break` -> `item/reasoning/summaryPartAdded`. Also added a change in core to pass down content index, summary index and item id from events. Tested with the `git checkout owen/app_server_test_client && cargo run -p codex-app-server-test-client -- send-message-v2 "hello"` and verified that new events are emitted correctly.
This commit is contained in:
@@ -477,10 +477,14 @@ async fn append_reasoning_text(
|
||||
..
|
||||
}) = reasoning_item
|
||||
{
|
||||
let content_index = content.len() as i64;
|
||||
content.push(ReasoningItemContent::ReasoningText { text: text.clone() });
|
||||
|
||||
let _ = tx_event
|
||||
.send(Ok(ResponseEvent::ReasoningContentDelta(text.clone())))
|
||||
.send(Ok(ResponseEvent::ReasoningContentDelta {
|
||||
delta: text.clone(),
|
||||
content_index,
|
||||
}))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
@@ -898,20 +902,26 @@ where
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta(delta)))) => {
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta {
|
||||
delta,
|
||||
content_index,
|
||||
}))) => {
|
||||
// Always accumulate reasoning deltas so we can emit a final Reasoning item at Completed.
|
||||
this.cumulative_reasoning.push_str(&delta);
|
||||
if matches!(this.mode, AggregateMode::Streaming) {
|
||||
// In streaming mode, also forward the delta immediately.
|
||||
return Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta(delta))));
|
||||
return Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta {
|
||||
delta,
|
||||
content_index,
|
||||
})));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryDelta(_)))) => {
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryDelta { .. }))) => {
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryPartAdded))) => {
|
||||
Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryPartAdded { .. }))) => {
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(Some(Ok(ResponseEvent::OutputItemAdded(item)))) => {
|
||||
|
||||
@@ -560,6 +560,8 @@ struct SseEvent {
|
||||
response: Option<Value>,
|
||||
item: Option<Value>,
|
||||
delta: Option<String>,
|
||||
summary_index: Option<i64>,
|
||||
content_index: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -819,16 +821,22 @@ async fn process_sse<S>(
|
||||
}
|
||||
}
|
||||
"response.reasoning_summary_text.delta" => {
|
||||
if let Some(delta) = event.delta {
|
||||
let event = ResponseEvent::ReasoningSummaryDelta(delta);
|
||||
if let (Some(delta), Some(summary_index)) = (event.delta, event.summary_index) {
|
||||
let event = ResponseEvent::ReasoningSummaryDelta {
|
||||
delta,
|
||||
summary_index,
|
||||
};
|
||||
if tx_event.send(Ok(event)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
"response.reasoning_text.delta" => {
|
||||
if let Some(delta) = event.delta {
|
||||
let event = ResponseEvent::ReasoningContentDelta(delta);
|
||||
if let (Some(delta), Some(content_index)) = (event.delta, event.content_index) {
|
||||
let event = ResponseEvent::ReasoningContentDelta {
|
||||
delta,
|
||||
content_index,
|
||||
};
|
||||
if tx_event.send(Ok(event)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
@@ -905,10 +913,12 @@ async fn process_sse<S>(
|
||||
}
|
||||
}
|
||||
"response.reasoning_summary_part.added" => {
|
||||
// Boundary between reasoning summary sections (e.g., titles).
|
||||
let event = ResponseEvent::ReasoningSummaryPartAdded;
|
||||
if tx_event.send(Ok(event)).await.is_err() {
|
||||
return;
|
||||
if let Some(summary_index) = event.summary_index {
|
||||
// Boundary between reasoning summary sections (e.g., titles).
|
||||
let event = ResponseEvent::ReasoningSummaryPartAdded { summary_index };
|
||||
if tx_event.send(Ok(event)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
"response.reasoning_summary_text.done" => {}
|
||||
|
||||
@@ -203,9 +203,17 @@ pub enum ResponseEvent {
|
||||
token_usage: Option<TokenUsage>,
|
||||
},
|
||||
OutputTextDelta(String),
|
||||
ReasoningSummaryDelta(String),
|
||||
ReasoningContentDelta(String),
|
||||
ReasoningSummaryPartAdded,
|
||||
ReasoningSummaryDelta {
|
||||
delta: String,
|
||||
summary_index: i64,
|
||||
},
|
||||
ReasoningContentDelta {
|
||||
delta: String,
|
||||
content_index: i64,
|
||||
},
|
||||
ReasoningSummaryPartAdded {
|
||||
summary_index: i64,
|
||||
},
|
||||
RateLimits(RateLimitSnapshot),
|
||||
}
|
||||
|
||||
|
||||
@@ -2232,13 +2232,17 @@ async fn try_run_turn(
|
||||
error_or_panic("ReasoningSummaryDelta without active item".to_string());
|
||||
}
|
||||
}
|
||||
ResponseEvent::ReasoningSummaryDelta(delta) => {
|
||||
ResponseEvent::ReasoningSummaryDelta {
|
||||
delta,
|
||||
summary_index,
|
||||
} => {
|
||||
if let Some(active) = active_item.as_ref() {
|
||||
let event = ReasoningContentDeltaEvent {
|
||||
thread_id: sess.conversation_id.to_string(),
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
item_id: active.id(),
|
||||
delta: delta.clone(),
|
||||
delta,
|
||||
summary_index,
|
||||
};
|
||||
sess.send_event(&turn_context, EventMsg::ReasoningContentDelta(event))
|
||||
.await;
|
||||
@@ -2246,18 +2250,29 @@ async fn try_run_turn(
|
||||
error_or_panic("ReasoningSummaryDelta without active item".to_string());
|
||||
}
|
||||
}
|
||||
ResponseEvent::ReasoningSummaryPartAdded => {
|
||||
let event =
|
||||
EventMsg::AgentReasoningSectionBreak(AgentReasoningSectionBreakEvent {});
|
||||
sess.send_event(&turn_context, event).await;
|
||||
ResponseEvent::ReasoningSummaryPartAdded { summary_index } => {
|
||||
if let Some(active) = active_item.as_ref() {
|
||||
let event =
|
||||
EventMsg::AgentReasoningSectionBreak(AgentReasoningSectionBreakEvent {
|
||||
item_id: active.id(),
|
||||
summary_index,
|
||||
});
|
||||
sess.send_event(&turn_context, event).await;
|
||||
} else {
|
||||
error_or_panic("ReasoningSummaryPartAdded without active item".to_string());
|
||||
}
|
||||
}
|
||||
ResponseEvent::ReasoningContentDelta(delta) => {
|
||||
ResponseEvent::ReasoningContentDelta {
|
||||
delta,
|
||||
content_index,
|
||||
} => {
|
||||
if let Some(active) = active_item.as_ref() {
|
||||
let event = ReasoningRawContentDeltaEvent {
|
||||
thread_id: sess.conversation_id.to_string(),
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
item_id: active.id(),
|
||||
delta: delta.clone(),
|
||||
delta,
|
||||
content_index,
|
||||
};
|
||||
sess.send_event(&turn_context, EventMsg::ReasoningRawContentDelta(event))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user