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
2026-07-22 20:47:57 +00:00
committed by copyberry
parent bbfc3f0152
commit 12c115d558
12 changed files with 145 additions and 42 deletions

View File

@@ -26,7 +26,7 @@ rmcp = { workspace = true, default-features = false, features = [
"server",
] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_json = { workspace = true, features = ["raw_value"] }
thiserror = { workspace = true }
tracing = { workspace = true }
urlencoding = { workspace = true }

View File

@@ -105,3 +105,4 @@ pub use tool_spec::ResponsesApiWebSearchFilters;
pub use tool_spec::ResponsesApiWebSearchUserLocation;
pub use tool_spec::ToolSpec;
pub use tool_spec::create_tools_json_for_responses_api;
pub use tool_spec::create_tools_raw_json_for_responses_api;

View File

@@ -9,6 +9,8 @@ use codex_protocol::config_types::WebSearchUserLocation as ConfigWebSearchUserLo
use codex_protocol::config_types::WebSearchUserLocationType;
use serde::Serialize;
use serde_json::Value;
use serde_json::value::RawValue;
use std::sync::Arc;
/// When serialized as JSON, this produces a valid "Tool" in the OpenAI
/// Responses API.
@@ -87,6 +89,13 @@ pub fn create_tools_json_for_responses_api(
Ok(tools_json)
}
/// Returns raw JSON that can be embedded directly in a Responses API request.
pub fn create_tools_raw_json_for_responses_api(
tools: &[ToolSpec],
) -> Result<Arc<RawValue>, serde_json::Error> {
serde_json::value::to_raw_value(tools).map(Arc::from)
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct ResponsesApiWebSearchFilters {
#[serde(skip_serializing_if = "Option::is_none")]

View File

@@ -9,6 +9,7 @@ use crate::JsonSchema;
use crate::ResponsesApiNamespaceTool;
use crate::ResponsesApiTool;
use crate::create_tools_json_for_responses_api;
use crate::create_tools_raw_json_for_responses_api;
use codex_protocol::config_types::WebSearchContextSize;
use codex_protocol::config_types::WebSearchFilters as ConfigWebSearchFilters;
use codex_protocol::config_types::WebSearchUserLocation as ConfigWebSearchUserLocation;
@@ -143,6 +144,29 @@ fn create_tools_json_for_responses_api_includes_top_level_name() {
);
}
#[test]
fn raw_tool_json_matches_value_encoding() {
let specs = vec![ToolSpec::Function(ResponsesApiTool {
name: "demo".to_string(),
description: "A demo tool".to_string(),
strict: false,
defer_loading: None,
parameters: JsonSchema::object(
BTreeMap::new(),
/*required*/ None,
/*additional_properties*/ None,
),
output_schema: None,
})];
let expected = create_tools_json_for_responses_api(&specs).expect("serialize tools");
let raw = create_tools_raw_json_for_responses_api(&specs).expect("serialize raw tools");
assert_eq!(
serde_json::from_str::<Vec<serde_json::Value>>(raw.get()).expect("parse raw tools"),
expected,
);
}
#[test]
fn namespace_tool_spec_serializes_expected_wire_shape() {
assert_eq!(