Support end_turn in response.completed (#19610)

Some providers of Responses API forward a model-defined `end_turn`
boolean indicating explicitly the model's indication of whether it would
like to end the turn or to be inferenced again. In this PR, we update
the sampling loop to use this field correctly if it's set. If the field
is not set by the provider, we fall back to the existing sampling logic.
This commit is contained in:
Andrey Mishchenko
2026-04-25 21:57:42 -07:00
committed by GitHub
parent 5591912f0b
commit 355c40ad7e
7 changed files with 44 additions and 7 deletions

View File

@@ -81,6 +81,9 @@ pub enum ResponseEvent {
Completed {
response_id: String,
token_usage: Option<TokenUsage>,
/// Did the model affirmatively end its turn? Some providers do not set this,
/// so we rely on fallback logic when this is `None`.
end_turn: Option<bool>,
},
OutputTextDelta(String),
ToolCallInputDelta {

View File

@@ -123,6 +123,8 @@ struct ResponseCompleted {
id: String,
#[serde(default)]
usage: Option<ResponseCompletedUsage>,
#[serde(default)]
end_turn: Option<bool>,
}
#[derive(Debug, Deserialize)]
@@ -382,6 +384,7 @@ pub fn process_responses_event(
return Ok(Some(ResponseEvent::Completed {
response_id: resp.id,
token_usage: resp.usage.map(Into::into),
end_turn: resp.end_turn,
}));
}
Err(err) => {
@@ -704,9 +707,11 @@ mod tests {
Ok(ResponseEvent::Completed {
response_id,
token_usage,
end_turn,
}) => {
assert_eq!(response_id, "resp1");
assert!(token_usage.is_none());
assert!(end_turn.is_none());
}
other => panic!("unexpected third event: {other:?}"),
}
@@ -843,9 +848,11 @@ mod tests {
Ok(ResponseEvent::Completed {
response_id,
token_usage,
end_turn,
}) => {
assert_eq!(response_id, "resp1");
assert!(token_usage.is_none());
assert!(end_turn.is_none());
}
other => panic!("unexpected event: {other:?}"),
}
@@ -1148,7 +1155,8 @@ mod tests {
&events[1],
ResponseEvent::Completed {
response_id,
token_usage: None
token_usage: None,
end_turn: None,
} if response_id == "resp-1"
);
}
@@ -1184,7 +1192,8 @@ mod tests {
&events[2],
ResponseEvent::Completed {
response_id,
token_usage: None
token_usage: None,
end_turn: None,
} if response_id == "resp-1"
);
}
@@ -1218,7 +1227,8 @@ mod tests {
&events[1],
ResponseEvent::Completed {
response_id,
token_usage: None
token_usage: None,
end_turn: None,
} if response_id == "resp-1"
);
}