mirror of
https://github.com/openai/codex.git
synced 2026-05-28 15:00:16 +00:00
Refactor ResponseItem to typed variant payload structs
This commit is contained in:
@@ -36,12 +36,15 @@ impl Stream for AggregatedStream {
|
||||
Poll::Ready(Some(Ok(ResponseEvent::OutputItemDone(item)))) => {
|
||||
let is_assistant_message = matches!(
|
||||
&item,
|
||||
ResponseItem::Message { role, .. } if role == "assistant"
|
||||
ResponseItem::Message(codex_protocol::models::Message { role, .. }) if role == "assistant"
|
||||
);
|
||||
|
||||
if is_assistant_message {
|
||||
if this.cumulative.is_empty()
|
||||
&& let ResponseItem::Message { content, .. } = &item
|
||||
&& let ResponseItem::Message(codex_protocol::models::Message {
|
||||
content,
|
||||
..
|
||||
}) = &item
|
||||
&& let Some(text) = content.iter().find_map(|c| match c {
|
||||
ContentItem::OutputText { text } => Some(text),
|
||||
_ => None,
|
||||
@@ -70,29 +73,31 @@ impl Stream for AggregatedStream {
|
||||
let mut emitted_any = false;
|
||||
|
||||
if !this.cumulative_reasoning.is_empty() {
|
||||
let aggregated_reasoning = ResponseItem::Reasoning {
|
||||
id: String::new(),
|
||||
summary: Vec::new(),
|
||||
content: Some(vec![ReasoningItemContent::ReasoningText {
|
||||
text: std::mem::take(&mut this.cumulative_reasoning),
|
||||
}]),
|
||||
encrypted_content: None,
|
||||
};
|
||||
let aggregated_reasoning =
|
||||
ResponseItem::Reasoning(codex_protocol::models::Reasoning {
|
||||
id: String::new(),
|
||||
summary: Vec::new(),
|
||||
content: Some(vec![ReasoningItemContent::ReasoningText {
|
||||
text: std::mem::take(&mut this.cumulative_reasoning),
|
||||
}]),
|
||||
encrypted_content: None,
|
||||
});
|
||||
this.pending
|
||||
.push_back(ResponseEvent::OutputItemDone(aggregated_reasoning));
|
||||
emitted_any = true;
|
||||
}
|
||||
|
||||
if !this.cumulative.is_empty() {
|
||||
let aggregated_message = ResponseItem::Message {
|
||||
id: None,
|
||||
role: "assistant".to_string(),
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: std::mem::take(&mut this.cumulative),
|
||||
}],
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
let aggregated_message =
|
||||
ResponseItem::Message(codex_protocol::models::Message {
|
||||
id: None,
|
||||
role: "assistant".to_string(),
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: std::mem::take(&mut this.cumulative),
|
||||
}],
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
this.pending
|
||||
.push_back(ResponseEvent::OutputItemDone(aggregated_message));
|
||||
emitted_any = true;
|
||||
|
||||
@@ -169,12 +169,24 @@ fn attach_item_ids(payload_json: &mut Value, original_items: &[ResponseItem]) {
|
||||
};
|
||||
|
||||
for (value, item) in items.iter_mut().zip(original_items.iter()) {
|
||||
if let ResponseItem::Reasoning { id, .. }
|
||||
| ResponseItem::Message { id: Some(id), .. }
|
||||
| ResponseItem::WebSearchCall { id: Some(id), .. }
|
||||
| ResponseItem::FunctionCall { id: Some(id), .. }
|
||||
| ResponseItem::LocalShellCall { id: Some(id), .. }
|
||||
| ResponseItem::CustomToolCall { id: Some(id), .. } = item
|
||||
if let ResponseItem::Reasoning(codex_protocol::models::Reasoning { id, .. })
|
||||
| ResponseItem::Message(codex_protocol::models::Message { id: Some(id), .. })
|
||||
| ResponseItem::WebSearchCall(codex_protocol::models::WebSearchCall {
|
||||
id: Some(id),
|
||||
..
|
||||
})
|
||||
| ResponseItem::FunctionCall(codex_protocol::models::FunctionCall {
|
||||
id: Some(id),
|
||||
..
|
||||
})
|
||||
| ResponseItem::LocalShellCall(codex_protocol::models::LocalShellCall {
|
||||
id: Some(id),
|
||||
..
|
||||
})
|
||||
| ResponseItem::CustomToolCall(codex_protocol::models::CustomToolCall {
|
||||
id: Some(id),
|
||||
..
|
||||
}) = item
|
||||
{
|
||||
if id.is_empty() {
|
||||
continue;
|
||||
@@ -217,20 +229,20 @@ mod tests {
|
||||
fn azure_default_store_attaches_ids_and_headers() {
|
||||
let provider = provider("azure", "https://example.openai.azure.com/v1");
|
||||
let input = vec![
|
||||
ResponseItem::Message {
|
||||
ResponseItem::Message(codex_protocol::models::Message {
|
||||
id: Some("m1".into()),
|
||||
role: "assistant".into(),
|
||||
content: Vec::new(),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
ResponseItem::Message {
|
||||
}),
|
||||
ResponseItem::Message(codex_protocol::models::Message {
|
||||
id: None,
|
||||
role: "assistant".into(),
|
||||
content: Vec::new(),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
let request = ResponsesRequestBuilder::new("gpt-test", "inst", &input)
|
||||
|
||||
@@ -531,16 +531,16 @@ mod tests {
|
||||
|
||||
assert_matches!(
|
||||
&events[0],
|
||||
Ok(ResponseEvent::OutputItemDone(ResponseItem::Message {
|
||||
Ok(ResponseEvent::OutputItemDone(ResponseItem::Message(codex_protocol::models::Message {
|
||||
role,
|
||||
phase: Some(MessagePhase::Commentary),
|
||||
..
|
||||
})) if role == "assistant"
|
||||
}))) if role == "assistant"
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
&events[1],
|
||||
Ok(ResponseEvent::OutputItemDone(ResponseItem::Message { role, .. }))
|
||||
Ok(ResponseEvent::OutputItemDone(ResponseItem::Message(codex_protocol::models::Message { role, .. })))
|
||||
if role == "assistant"
|
||||
);
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ async fn streaming_client_retries_on_transport_error() -> Result<()> {
|
||||
|
||||
let prompt = codex_api::Prompt {
|
||||
instructions: "Say hi".to_string(),
|
||||
input: vec![ResponseItem::Message {
|
||||
input: vec![ResponseItem::Message(codex_protocol::models::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
@@ -263,7 +263,7 @@ async fn streaming_client_retries_on_transport_error() -> Result<()> {
|
||||
}],
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
})],
|
||||
tools: Vec::<Value>::new(),
|
||||
parallel_tool_calls: false,
|
||||
output_schema: None,
|
||||
|
||||
@@ -144,14 +144,20 @@ async fn responses_stream_parses_items_and_completed_end_to_end() -> Result<()>
|
||||
assert_eq!(events.len(), 3);
|
||||
|
||||
match &events[0] {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message { role, .. }) => {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message(codex_protocol::models::Message {
|
||||
role,
|
||||
..
|
||||
})) => {
|
||||
assert_eq!(role, "assistant");
|
||||
}
|
||||
other => panic!("unexpected first event: {other:?}"),
|
||||
}
|
||||
|
||||
match &events[1] {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message { role, .. }) => {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message(codex_protocol::models::Message {
|
||||
role,
|
||||
..
|
||||
})) => {
|
||||
assert_eq!(role, "assistant");
|
||||
}
|
||||
other => panic!("unexpected second event: {other:?}"),
|
||||
@@ -215,7 +221,10 @@ async fn responses_stream_aggregates_output_text_deltas() -> Result<()> {
|
||||
assert_eq!(events.len(), 2);
|
||||
|
||||
match &events[0] {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message { content, .. }) => {
|
||||
ResponseEvent::OutputItemDone(ResponseItem::Message(codex_protocol::models::Message {
|
||||
content,
|
||||
..
|
||||
})) => {
|
||||
let mut aggregated = String::new();
|
||||
for item in content {
|
||||
if let ContentItem::OutputText { text } = item {
|
||||
|
||||
Reference in New Issue
Block a user