feat(search_tool): gate search_tool on model supports_search_tool field (#14502)
This commit is contained in:
parent
a2546d5dff
commit
651717323c
14 changed files with 50 additions and 17 deletions
|
|
@ -47,6 +47,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ async fn models_client_hits_models_endpoint() {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
}],
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ pub(crate) fn model_info_from_slug(slug: &str) -> ModelInfo {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: true, // this is the fallback model metadata
|
||||
supports_search_tool: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ impl ToolsConfig {
|
|||
let include_request_user_input = !matches!(session_source, SessionSource::SubAgent(_));
|
||||
let include_default_mode_request_user_input =
|
||||
include_request_user_input && features.enabled(Feature::DefaultModeRequestUserInput);
|
||||
let include_search_tool = features.enabled(Feature::Apps);
|
||||
let include_search_tool = model_info.supports_search_tool;
|
||||
let include_tool_suggest = include_search_tool && features.enabled(Feature::ToolSuggest);
|
||||
let include_original_image_detail = can_request_original_image_detail(features, model_info);
|
||||
let include_artifact_tools =
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ fn discoverable_connector(id: &str, name: &str, description: &str) -> Discoverab
|
|||
}))
|
||||
}
|
||||
|
||||
fn search_capable_model_info() -> ModelInfo {
|
||||
let config = test_config();
|
||||
let mut model_info =
|
||||
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
model_info.supports_search_tool = true;
|
||||
model_info
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_tool_to_openai_tool_inserts_empty_properties() {
|
||||
let mut schema = rmcp::model::JsonObject::new();
|
||||
|
|
@ -1582,8 +1590,7 @@ fn test_build_specs_mcp_tools_sorted_by_name() {
|
|||
|
||||
#[test]
|
||||
fn search_tool_description_includes_only_codex_apps_connector_names() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
let available_models = Vec::new();
|
||||
|
|
@ -1659,9 +1666,8 @@ fn search_tool_description_includes_only_codex_apps_connector_names() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn search_tool_requires_apps_feature_flag_only() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
fn search_tool_requires_model_capability_only() {
|
||||
let model_info = search_capable_model_info();
|
||||
let app_tools = Some(HashMap::from([(
|
||||
"mcp__codex_apps__calendar_create_event".to_string(),
|
||||
ToolInfo {
|
||||
|
|
@ -1683,7 +1689,10 @@ fn search_tool_requires_apps_feature_flag_only() {
|
|||
let features = Features::with_defaults();
|
||||
let available_models = Vec::new();
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
model_info: &ModelInfo {
|
||||
supports_search_tool: false,
|
||||
..model_info.clone()
|
||||
},
|
||||
available_models: &available_models,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
|
|
@ -1693,8 +1702,6 @@ fn search_tool_requires_apps_feature_flag_only() {
|
|||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, app_tools.clone(), &[]).build();
|
||||
assert_lacks_tool_name(&tools, TOOL_SEARCH_TOOL_NAME);
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
let available_models = Vec::new();
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
|
|
@ -1711,8 +1718,7 @@ fn search_tool_requires_apps_feature_flag_only() {
|
|||
|
||||
#[test]
|
||||
fn tool_suggest_is_not_registered_without_feature_flag() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
let available_models = Vec::new();
|
||||
|
|
@ -1747,8 +1753,7 @@ fn tool_suggest_is_not_registered_without_feature_flag() {
|
|||
|
||||
#[test]
|
||||
fn search_tool_description_handles_no_enabled_apps() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
let available_models = Vec::new();
|
||||
|
|
@ -1774,8 +1779,7 @@ fn search_tool_description_handles_no_enabled_apps() {
|
|||
|
||||
#[test]
|
||||
fn search_tool_registers_namespaced_app_tool_aliases() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
let available_models = Vec::new();
|
||||
|
|
@ -1840,8 +1844,7 @@ fn search_tool_registers_namespaced_app_tool_aliases() {
|
|||
|
||||
#[test]
|
||||
fn tool_suggest_description_lists_discoverable_tools() {
|
||||
let config = test_config();
|
||||
let model_info = ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let model_info = search_capable_model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Apps);
|
||||
features.enable(Feature::ToolSuggest);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ fn test_model_info(
|
|||
input_modalities,
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
@ -675,6 +676,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result<
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
|
|||
|
|
@ -353,5 +353,6 @@ fn test_remote_model(slug: &str, priority: i32) -> ModelInfo {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -658,6 +658,7 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
};
|
||||
|
||||
let _models_mock = mount_models_once(
|
||||
|
|
@ -773,6 +774,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
};
|
||||
|
||||
let _models_mock = mount_models_once(
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
@ -533,6 +534,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: remote_base.to_string(),
|
||||
|
|
@ -999,6 +1001,7 @@ fn test_remote_model_with_policy(
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
|
|||
|
|
@ -421,6 +421,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
|||
input_modalities: vec![InputModality::Text],
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
}],
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use anyhow::Result;
|
|||
use codex_core::CodexAuth;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::features::Feature;
|
||||
use codex_protocol::openai_models::ModelsResponse;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::McpInvocation;
|
||||
|
|
@ -93,6 +94,17 @@ fn configure_apps(config: &mut Config, apps_base_url: &str) {
|
|||
.disable(Feature::AppsMcpGateway)
|
||||
.expect("test config should allow feature update");
|
||||
config.chatgpt_base_url = apps_base_url.to_string();
|
||||
config.model = Some("gpt-5-codex".to_string());
|
||||
|
||||
let mut model_catalog: ModelsResponse =
|
||||
serde_json::from_str(include_str!("../../models.json")).expect("valid models.json");
|
||||
let model = model_catalog
|
||||
.models
|
||||
.iter_mut()
|
||||
.find(|model| model.slug == "gpt-5-codex")
|
||||
.expect("gpt-5-codex exists in bundled models.json");
|
||||
model.supports_search_tool = true;
|
||||
config.model_catalog = Some(model_catalog);
|
||||
}
|
||||
|
||||
fn configured_builder(apps_base_url: String) -> TestCodexBuilder {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ fn test_model_info(
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
|
|||
|
|
@ -1272,6 +1272,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an
|
|||
input_modalities: vec![InputModality::Text],
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
|
|
|
|||
|
|
@ -282,6 +282,8 @@ pub struct ModelInfo {
|
|||
#[schemars(skip)]
|
||||
#[ts(skip)]
|
||||
pub used_fallback_model_metadata: bool,
|
||||
#[serde(default)]
|
||||
pub supports_search_tool: bool,
|
||||
}
|
||||
|
||||
impl ModelInfo {
|
||||
|
|
@ -538,6 +540,7 @@ mod tests {
|
|||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
supports_search_tool: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -732,6 +735,7 @@ mod tests {
|
|||
assert_eq!(model.availability_nux, None);
|
||||
assert!(!model.supports_image_detail_original);
|
||||
assert_eq!(model.web_search_tool_type, WebSearchToolType::Text);
|
||||
assert!(!model.supports_search_tool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue