mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
Add a partial answer message phase (#51241)
## Why Assistant answer text may be followed by more output or tools. A distinct `partial_answer` phase lets callers represent this separately from commentary and a terminal `final_answer`. ## What changed - Add `MessagePhase::PartialAnswer` and expose `partial_answer` in the app-server schemas and generated TypeScript and Python types. - Keep the TUI working indicator visible after partial answers and exclude them from the thread history's final-answer summary field. - Route partial answers to the speakable channel in realtime V3 BEM-tag mode. ## Testing Add coverage for phase preservation through serialization, input-message conversion, and SSE item start/completion events. Extend TUI snapshot coverage to verify that partial answers keep the working indicator visible while final answers hide it. GitOrigin-RevId: 1ae3ad9264b6e36ae1cbe6b93df3d345ab4460e6
This commit is contained in:
@@ -734,6 +734,46 @@ mod tests {
|
||||
Duration::from_millis(1000)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn partial_answer_phase_survives_item_start_and_completion() {
|
||||
let item = json!({
|
||||
"id": "msg_partial",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [],
|
||||
"phase": "partial_answer"
|
||||
});
|
||||
let added = json!({"type": "response.output_item.added", "item": item});
|
||||
let delta = json!({"type": "response.output_text.delta", "delta": "First result."});
|
||||
let mut done_item = item;
|
||||
done_item["content"] = json!([{"type": "output_text", "text": "First result."}]);
|
||||
let done = json!({"type": "response.output_item.done", "item": done_item});
|
||||
let completed = json!({
|
||||
"type": "response.completed",
|
||||
"response": {"id": "resp1", "end_turn": false}
|
||||
});
|
||||
let stream = [added, delta, done, completed]
|
||||
.into_iter()
|
||||
.map(|event| format!("data: {event}\n\n"))
|
||||
.collect::<String>();
|
||||
|
||||
let events = collect_events(&[stream.as_bytes()]).await;
|
||||
assert_matches!(events.as_slice(), [
|
||||
Ok(ResponseEvent::OutputItemAdded(ResponseItem::Message {
|
||||
phase: Some(MessagePhase::PartialAnswer), content: start_content, ..
|
||||
})),
|
||||
Ok(ResponseEvent::OutputTextDelta(text)),
|
||||
Ok(ResponseEvent::OutputItemDone(ResponseItem::Message {
|
||||
phase: Some(MessagePhase::PartialAnswer), content: done_content, ..
|
||||
})),
|
||||
Ok(ResponseEvent::Completed { end_turn: Some(false), .. }),
|
||||
] if start_content.is_empty()
|
||||
&& text == "First result."
|
||||
&& done_content == &vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "First result.".to_string()
|
||||
}]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn parses_items_and_completed() {
|
||||
let item1 = json!({
|
||||
|
||||
Reference in New Issue
Block a user