diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index 1fb3b7b47..135166919 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -155,12 +155,11 @@ fn strip_total_output_header(output: &str) -> Option<(&str, u32)> { pub(crate) mod tools { use crate::tools::spec::JsonSchema; use codex_protocol::config_types::WebSearchContextSize; - use codex_protocol::config_types::WebSearchFilters; - use codex_protocol::config_types::WebSearchUserLocation; + use codex_protocol::config_types::WebSearchFilters as ConfigWebSearchFilters; + use codex_protocol::config_types::WebSearchUserLocation as ConfigWebSearchUserLocation; use codex_protocol::config_types::WebSearchUserLocationType; use serde::Deserialize; use serde::Serialize; - use serde::Serializer; /// When serialized as JSON, this produces a valid "Tool" in the OpenAI /// Responses API. @@ -181,16 +180,10 @@ pub(crate) mod tools { WebSearch { #[serde(skip_serializing_if = "Option::is_none")] external_web_access: Option, - #[serde( - skip_serializing_if = "Option::is_none", - serialize_with = "serialize_web_search_filters" - )] - filters: Option, - #[serde( - skip_serializing_if = "Option::is_none", - serialize_with = "serialize_web_search_user_location" - )] - user_location: Option, + #[serde(skip_serializing_if = "Option::is_none")] + filters: Option, + #[serde(skip_serializing_if = "Option::is_none")] + user_location: Option, #[serde(skip_serializing_if = "Option::is_none")] search_context_size: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -212,63 +205,43 @@ pub(crate) mod tools { } } - fn serialize_web_search_filters( - filters: &Option, - serializer: S, - ) -> Result - where - S: Serializer, - { - match filters { - Some(filters) => { - #[derive(Serialize)] - struct SerializableWebSearchFilters<'a> { - #[serde(skip_serializing_if = "Option::is_none")] - allowed_domains: Option<&'a Vec>, - } + #[derive(Debug, Clone, Serialize, PartialEq)] + pub(crate) struct ResponsesApiWebSearchFilters { + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) allowed_domains: Option>, + } - SerializableWebSearchFilters { - allowed_domains: filters.allowed_domains.as_ref(), - } - .serialize(serializer) + impl From for ResponsesApiWebSearchFilters { + fn from(filters: ConfigWebSearchFilters) -> Self { + Self { + allowed_domains: filters.allowed_domains, } - None => serializer.serialize_none(), } } - fn serialize_web_search_user_location( - user_location: &Option, - serializer: S, - ) -> Result - where - S: Serializer, - { - match user_location { - Some(user_location) => { - #[derive(Serialize)] - struct SerializableWebSearchUserLocation<'a> { - #[serde(rename = "type")] - r#type: WebSearchUserLocationType, - #[serde(skip_serializing_if = "Option::is_none")] - country: Option<&'a String>, - #[serde(skip_serializing_if = "Option::is_none")] - region: Option<&'a String>, - #[serde(skip_serializing_if = "Option::is_none")] - city: Option<&'a String>, - #[serde(skip_serializing_if = "Option::is_none")] - timezone: Option<&'a String>, - } + #[derive(Debug, Clone, Serialize, PartialEq)] + pub(crate) struct ResponsesApiWebSearchUserLocation { + #[serde(rename = "type")] + pub(crate) r#type: WebSearchUserLocationType, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) country: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) region: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) city: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) timezone: Option, + } - SerializableWebSearchUserLocation { - r#type: user_location.r#type, - country: user_location.country.as_ref(), - region: user_location.region.as_ref(), - city: user_location.city.as_ref(), - timezone: user_location.timezone.as_ref(), - } - .serialize(serializer) + impl From for ResponsesApiWebSearchUserLocation { + fn from(user_location: ConfigWebSearchUserLocation) -> Self { + Self { + r#type: user_location.r#type, + country: user_location.country, + region: user_location.region, + city: user_location.city, + timezone: user_location.timezone, } - None => serializer.serialize_none(), } } diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index f3cec19a1..219484c44 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -2017,11 +2017,11 @@ pub(crate) fn build_specs( filters: config .web_search_config .as_ref() - .and_then(|cfg| cfg.filters.clone()), + .and_then(|cfg| cfg.filters.clone().map(Into::into)), user_location: config .web_search_config .as_ref() - .and_then(|cfg| cfg.user_location.clone()), + .and_then(|cfg| cfg.user_location.clone().map(Into::into)), search_context_size: config .web_search_config .as_ref() @@ -2766,8 +2766,12 @@ mod tests { tool.spec, ToolSpec::WebSearch { external_web_access: Some(true), - filters: web_search_config.filters, - user_location: web_search_config.user_location, + filters: web_search_config + .filters + .map(crate::client_common::tools::ResponsesApiWebSearchFilters::from), + user_location: web_search_config + .user_location + .map(crate::client_common::tools::ResponsesApiWebSearchUserLocation::from), search_context_size: web_search_config.search_context_size, search_content_types: None, }