Stream apply_patch changes (#17862)

Adds new events for streaming apply_patch changes from responses api.
This is to enable clients to show progress during file writes.

Caveat: This does not work with apply_patch in function call mode, since
that required adding streaming json parsing.
This commit is contained in:
Akshay Nathan
2026-04-16 18:12:19 -07:00
committed by GitHub
parent 9effa0509f
commit 7995c66032
20 changed files with 729 additions and 29 deletions

View File

@@ -80,6 +80,11 @@ pub enum ResponseEvent {
token_usage: Option<TokenUsage>,
},
OutputTextDelta(String),
ToolCallInputDelta {
item_id: String,
call_id: Option<String>,
delta: String,
},
ReasoningSummaryDelta {
delta: String,
summary_index: i64,

View File

@@ -167,6 +167,8 @@ pub struct ResponsesStreamEvent {
headers: Option<Value>,
response: Option<Value>,
item: Option<Value>,
item_id: Option<String>,
call_id: Option<String>,
delta: Option<String>,
summary_index: Option<i64>,
content_index: Option<i64>,
@@ -250,6 +252,17 @@ pub fn process_responses_event(
return Ok(Some(ResponseEvent::OutputTextDelta(delta)));
}
}
"response.custom_tool_call_input.delta" => {
if let (Some(delta), Some(item_id)) =
(event.delta, event.item_id.clone().or(event.call_id.clone()))
{
return Ok(Some(ResponseEvent::ToolCallInputDelta {
item_id,
call_id: event.call_id,
delta,
}));
}
}
"response.reasoning_summary_text.delta" => {
if let (Some(delta), Some(summary_index)) = (event.delta, event.summary_index) {
return Ok(Some(ResponseEvent::ReasoningSummaryDelta {
@@ -692,6 +705,38 @@ mod tests {
);
}
#[tokio::test]
async fn parses_tool_call_input_deltas() {
let events = run_sse(vec![
json!({
"type": "response.custom_tool_call_input.delta",
"item_id": "ctc_1",
"call_id": "call_1",
"delta": "*** Begin",
}),
json!({
"type": "response.function_call_arguments.delta",
"item_id": "fc_1",
"delta": "{\"input\":\"",
}),
json!({
"type": "response.completed",
"response": { "id": "resp1" }
}),
])
.await;
assert_matches!(
&events[0],
ResponseEvent::ToolCallInputDelta {
item_id,
call_id: Some(call_id),
delta,
} if item_id == "ctc_1" && call_id == "call_1" && delta == "*** Begin"
);
assert_matches!(&events[1], ResponseEvent::Completed { .. });
}
#[tokio::test]
async fn emits_completed_without_stream_end() {
let completed = json!({