use super::ConfiguredToolSpec; use super::ResponsesApiWebSearchFilters; use super::ResponsesApiWebSearchUserLocation; use super::ToolSpec; use crate::AdditionalProperties; use crate::FreeformTool; use crate::FreeformToolFormat; use crate::JsonSchema; use crate::ResponsesApiTool; use crate::create_tools_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; use codex_protocol::config_types::WebSearchUserLocationType; use pretty_assertions::assert_eq; use serde_json::json; use std::collections::BTreeMap; #[test] fn tool_spec_name_covers_all_variants() { assert_eq!( ToolSpec::Function(ResponsesApiTool { name: "lookup_order".to_string(), description: "Look up an order".to_string(), strict: false, defer_loading: None, parameters: JsonSchema::Object { properties: BTreeMap::new(), required: None, additional_properties: None, }, output_schema: None, }) .name(), "lookup_order" ); assert_eq!( ToolSpec::ToolSearch { execution: "sync".to_string(), description: "Search for tools".to_string(), parameters: JsonSchema::Object { properties: BTreeMap::new(), required: None, additional_properties: None, }, } .name(), "tool_search" ); assert_eq!(ToolSpec::LocalShell {}.name(), "local_shell"); assert_eq!( ToolSpec::ImageGeneration { output_format: "png".to_string(), } .name(), "image_generation" ); assert_eq!( ToolSpec::WebSearch { external_web_access: Some(true), filters: None, user_location: None, search_context_size: None, search_content_types: None, } .name(), "web_search" ); assert_eq!( ToolSpec::Freeform(FreeformTool { name: "exec".to_string(), description: "Run a command".to_string(), format: FreeformToolFormat { r#type: "grammar".to_string(), syntax: "lark".to_string(), definition: "start: \"exec\"".to_string(), }, }) .name(), "exec" ); } #[test] fn configured_tool_spec_name_delegates_to_tool_spec() { assert_eq!( ConfiguredToolSpec::new( ToolSpec::Function(ResponsesApiTool { name: "lookup_order".to_string(), description: "Look up an order".to_string(), strict: false, defer_loading: None, parameters: JsonSchema::Object { properties: BTreeMap::new(), required: None, additional_properties: None, }, output_schema: None, }), /*supports_parallel_tool_calls*/ true, ) .name(), "lookup_order" ); } #[test] fn web_search_config_converts_to_responses_api_types() { assert_eq!( ResponsesApiWebSearchFilters::from(ConfigWebSearchFilters { allowed_domains: Some(vec!["example.com".to_string()]), }), ResponsesApiWebSearchFilters { allowed_domains: Some(vec!["example.com".to_string()]), } ); assert_eq!( ResponsesApiWebSearchUserLocation::from(ConfigWebSearchUserLocation { r#type: WebSearchUserLocationType::Approximate, country: Some("US".to_string()), region: Some("California".to_string()), city: Some("San Francisco".to_string()), timezone: Some("America/Los_Angeles".to_string()), }), ResponsesApiWebSearchUserLocation { r#type: WebSearchUserLocationType::Approximate, country: Some("US".to_string()), region: Some("California".to_string()), city: Some("San Francisco".to_string()), timezone: Some("America/Los_Angeles".to_string()), } ); } #[test] fn create_tools_json_for_responses_api_includes_top_level_name() { assert_eq!( create_tools_json_for_responses_api(&[ToolSpec::Function(ResponsesApiTool { name: "demo".to_string(), description: "A demo tool".to_string(), strict: false, defer_loading: None, parameters: JsonSchema::Object { properties: BTreeMap::from([( "foo".to_string(), JsonSchema::String { description: None }, )]), required: None, additional_properties: None, }, output_schema: None, })]) .expect("serialize tools"), vec![json!({ "type": "function", "name": "demo", "description": "A demo tool", "strict": false, "parameters": { "type": "object", "properties": { "foo": { "type": "string" } }, }, })] ); } #[test] fn web_search_tool_spec_serializes_expected_wire_shape() { assert_eq!( serde_json::to_value(ToolSpec::WebSearch { external_web_access: Some(true), filters: Some(ResponsesApiWebSearchFilters { allowed_domains: Some(vec!["example.com".to_string()]), }), user_location: Some(ResponsesApiWebSearchUserLocation { r#type: WebSearchUserLocationType::Approximate, country: Some("US".to_string()), region: Some("California".to_string()), city: Some("San Francisco".to_string()), timezone: Some("America/Los_Angeles".to_string()), }), search_context_size: Some(WebSearchContextSize::High), search_content_types: Some(vec!["text".to_string(), "image".to_string()]), }) .expect("serialize web_search"), json!({ "type": "web_search", "external_web_access": true, "filters": { "allowed_domains": ["example.com"], }, "user_location": { "type": "approximate", "country": "US", "region": "California", "city": "San Francisco", "timezone": "America/Los_Angeles", }, "search_context_size": "high", "search_content_types": ["text", "image"], }) ); } #[test] fn tool_search_tool_spec_serializes_expected_wire_shape() { assert_eq!( serde_json::to_value(ToolSpec::ToolSearch { execution: "sync".to_string(), description: "Search app tools".to_string(), parameters: JsonSchema::Object { properties: BTreeMap::from([( "query".to_string(), JsonSchema::String { description: Some("Tool search query".to_string()), }, )]), required: Some(vec!["query".to_string()]), additional_properties: Some(AdditionalProperties::Boolean(false)), }, }) .expect("serialize tool_search"), json!({ "type": "tool_search", "execution": "sync", "description": "Search app tools", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Tool search query", } }, "required": ["query"], "additionalProperties": false, }, }) ); }