Reduce cloning when building Responses requests (#34825)

## Why

Preparing Responses API requests rebuilt tool definitions as a generic JSON
tree, and incremental WebSocket requests cloned their full item prefix for
comparison.

## What changed

- Serialize tool definitions into shared raw JSON that can be embedded directly
  in HTTP and WebSocket requests.
- Compare incremental request prefixes in place while still ignoring internal
  message metadata.

## Testing

- Verify raw tool JSON matches the existing value encoding.
- Preserve the serialized WebSocket request payload.

GitOrigin-RevId: 66e2921792c332e8904954dafa597f6d39abcf29
This commit is contained in:
jif authored and copyberry committed 2026-07-22 21:00:01 +00:00
1 parent bbfc3f0152
commit 12c115d558
12 files changed
+145 -42

No files matched your search

+35 -20
View File
@@ -89,6 +89,7 @@ use codex_rollout_trace::CompactionTraceContext;
use codex_rollout_trace::InferenceTraceAttempt;
use codex_rollout_trace::InferenceTraceContext;
use codex_tools::create_tools_json_for_responses_api;
use codex_tools::create_tools_raw_json_for_responses_api;
use eventsource_stream::Event;
use eventsource_stream::EventStreamError;
use futures::StreamExt;
@@ -358,6 +359,21 @@ fn responses_request_properties_match(
&& previous_text == current_text
}
fn response_items_equal_ignoring_internal_metadata(
previous: &ResponseItem,
current: &ResponseItem,
) -> bool {
if previous == current {
return true;
}
let mut previous = previous.clone();
previous.clear_internal_chat_message_metadata_passthrough();
let mut current = current.clone();
current.clear_internal_chat_message_metadata_passthrough();
previous == current
}
impl WebsocketSession {
fn set_connection_reused(&self, connection_reused: bool) {
*self
@@ -836,8 +852,8 @@ impl ModelClient {
.iter_mut()
.for_each(ResponseItem::clear_internal_chat_message_metadata_passthrough);
}
let tools = create_tools_json_for_responses_api(&prompt.tools)?;
let (instructions, tools) = if model_info.use_responses_lite {
let tools = create_tools_json_for_responses_api(&prompt.tools)?;
let mut prefix = vec![ResponseItem::AdditionalTools {
id: None,
role: "developer".to_string(),
@@ -857,7 +873,10 @@ impl ModelClient {
input.splice(0..0, prefix);
(String::new(), None)
} else {
(prompt.base_instructions.text.clone(), Some(tools))
(
prompt.base_instructions.text.clone(),
Some(create_tools_raw_json_for_responses_api(&prompt.tools)?.into()),
)
};
let reasoning = Self::build_reasoning(model_info, effort, summary);
let stream_options = (self.state.concurrent_reasoning_summaries_enabled
@@ -1167,29 +1186,25 @@ impl ModelClientSession {
return None;
}
// To compare the inputs, we concatenate the previous request items with the response items,
// then compare that against the equivalent slice of request items, ignoring metadata. If
// they match, we can consider the remaining items the incremental request.
let mut previous_items = previous_request.input.clone();
if let Some(response) = last_response {
previous_items.extend_from_slice(&response.items_added);
}
previous_items
.iter_mut()
.for_each(ResponseItem::clear_internal_chat_message_metadata_passthrough);
let response_items =
last_response.map_or(&[][..], |response| response.items_added.as_slice());
let previous_items_len = previous_request
.input
.len()
.checked_add(response_items.len())?;
let Some((request_items_to_compare, incremental_items)) =
request.input.split_at_checked(previous_items.len())
request.input.split_at_checked(previous_items_len)
else {
trace!("incremental request failed, incompatible request length");
return None;
};
let mut request_prefix = request_items_to_compare.to_vec();
request_prefix
.iter_mut()
.for_each(ResponseItem::clear_internal_chat_message_metadata_passthrough);
if previous_items != request_prefix {
let previous_items = previous_request.input.iter().chain(response_items);
if !previous_items
.zip(request_items_to_compare)
.all(|(previous, current)| {
response_items_equal_ignoring_internal_metadata(previous, current)
})
{
trace!("incremental request failed, items didn't match");
return None;
}