add WebSearchMode enum (#9216)

### What
Add `WebSearchMode` enum (disabled, cached live, defaults to cached) to
config + V2 protocol. This enum takes precedence over legacy flags:
`web_search_cached`, `web_search_request`, and `tools.web_search`.

Keep `--search` as live.

### Tests
Added tests
This commit is contained in:
sayan-oai
2026-01-14 12:51:42 -08:00
committed by GitHub
parent 27da8a68d3
commit 5e426ac270
22 changed files with 302 additions and 72 deletions

View File

@@ -8,6 +8,7 @@ use crate::tools::handlers::apply_patch::create_apply_patch_json_tool;
use crate::tools::handlers::collab::DEFAULT_WAIT_TIMEOUT_MS;
use crate::tools::handlers::collab::MAX_WAIT_TIMEOUT_MS;
use crate::tools::registry::ToolRegistryBuilder;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::models::VIEW_IMAGE_TOOL_NAME;
use codex_protocol::openai_models::ApplyPatchToolType;
use codex_protocol::openai_models::ConfigShellToolType;
@@ -23,8 +24,7 @@ use std::collections::HashMap;
pub(crate) struct ToolsConfig {
pub shell_type: ConfigShellToolType,
pub apply_patch_tool_type: Option<ApplyPatchToolType>,
pub web_search_request: bool,
pub web_search_cached: bool,
pub web_search_mode: WebSearchMode,
pub collab_tools: bool,
pub experimental_supported_tools: Vec<String>,
}
@@ -32,6 +32,7 @@ pub(crate) struct ToolsConfig {
pub(crate) struct ToolsConfigParams<'a> {
pub(crate) model_info: &'a ModelInfo,
pub(crate) features: &'a Features,
pub(crate) web_search_mode: WebSearchMode,
}
impl ToolsConfig {
@@ -39,10 +40,9 @@ impl ToolsConfig {
let ToolsConfigParams {
model_info,
features,
web_search_mode,
} = params;
let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform);
let include_web_search_request = features.enabled(Feature::WebSearchRequest);
let include_web_search_cached = features.enabled(Feature::WebSearchCached);
let include_collab_tools = features.enabled(Feature::Collab);
let shell_type = if !features.enabled(Feature::ShellTool) {
@@ -73,8 +73,7 @@ impl ToolsConfig {
Self {
shell_type,
apply_patch_tool_type,
web_search_request: include_web_search_request,
web_search_cached: include_web_search_cached,
web_search_mode: *web_search_mode,
collab_tools: include_collab_tools,
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
}
@@ -1225,15 +1224,18 @@ pub(crate) fn build_specs(
builder.register_handler("test_sync_tool", test_sync_handler);
}
// Prefer web_search_cached flag over web_search_request
if config.web_search_cached {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(false),
});
} else if config.web_search_request {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(true),
});
match config.web_search_mode {
WebSearchMode::Disabled => {}
WebSearchMode::Cached => {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(false),
});
}
WebSearchMode::Live => {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(true),
});
}
}
builder.push_spec_with_parallel_support(create_view_image_tool(), true);
@@ -1374,10 +1376,10 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
});
let (tools, _) = build_specs(&config, None).build();
@@ -1439,6 +1441,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(&tools_config, None).build();
assert_contains_tool_names(
@@ -1447,12 +1450,18 @@ mod tests {
);
}
fn assert_model_tools(model_slug: &str, features: &Features, expected_tools: &[&str]) {
fn assert_model_tools(
model_slug: &str,
features: &Features,
web_search_mode: WebSearchMode,
expected_tools: &[&str],
) {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline(model_slug, &config);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features,
web_search_mode,
});
let (tools, _) = build_specs(&tools_config, Some(HashMap::new())).build();
let tool_names = tools.iter().map(|t| t.spec.name()).collect::<Vec<_>>();
@@ -1460,15 +1469,15 @@ mod tests {
}
#[test]
fn web_search_cached_sets_external_web_access_false() {
fn web_search_mode_cached_sets_external_web_access_false() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::WebSearchCached);
let features = Features::with_defaults();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(&tools_config, None).build();
@@ -1482,16 +1491,15 @@ mod tests {
}
#[test]
fn web_search_cached_takes_precedence_over_web_search_request() {
fn web_search_mode_live_sets_external_web_access_true() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::WebSearchCached);
features.enable(Feature::WebSearchRequest);
let features = Features::with_defaults();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
});
let (tools, _) = build_specs(&tools_config, None).build();
@@ -1499,7 +1507,7 @@ mod tests {
assert_eq!(
tool.spec,
ToolSpec::WebSearch {
external_web_access: Some(false),
external_web_access: Some(true),
}
);
}
@@ -1509,6 +1517,7 @@ mod tests {
assert_model_tools(
"gpt-5-codex",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"shell_command",
"list_mcp_resources",
@@ -1516,6 +1525,7 @@ mod tests {
"read_mcp_resource",
"update_plan",
"apply_patch",
"web_search",
"view_image",
],
);
@@ -1526,6 +1536,7 @@ mod tests {
assert_model_tools(
"gpt-5.1-codex",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"shell_command",
"list_mcp_resources",
@@ -1533,6 +1544,7 @@ mod tests {
"read_mcp_resource",
"update_plan",
"apply_patch",
"web_search",
"view_image",
],
);
@@ -1542,9 +1554,8 @@ mod tests {
fn test_build_specs_gpt5_codex_unified_exec_web_search() {
assert_model_tools(
"gpt-5-codex",
Features::with_defaults()
.enable(Feature::UnifiedExec)
.enable(Feature::WebSearchRequest),
Features::with_defaults().enable(Feature::UnifiedExec),
WebSearchMode::Live,
&[
"exec_command",
"write_stdin",
@@ -1563,9 +1574,8 @@ mod tests {
fn test_build_specs_gpt51_codex_unified_exec_web_search() {
assert_model_tools(
"gpt-5.1-codex",
Features::with_defaults()
.enable(Feature::UnifiedExec)
.enable(Feature::WebSearchRequest),
Features::with_defaults().enable(Feature::UnifiedExec),
WebSearchMode::Live,
&[
"exec_command",
"write_stdin",
@@ -1585,12 +1595,14 @@ mod tests {
assert_model_tools(
"codex-mini-latest",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"local_shell",
"list_mcp_resources",
"list_mcp_resource_templates",
"read_mcp_resource",
"update_plan",
"web_search",
"view_image",
],
);
@@ -1601,6 +1613,7 @@ mod tests {
assert_model_tools(
"gpt-5.1-codex-mini",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"shell_command",
"list_mcp_resources",
@@ -1608,6 +1621,7 @@ mod tests {
"read_mcp_resource",
"update_plan",
"apply_patch",
"web_search",
"view_image",
],
);
@@ -1618,12 +1632,14 @@ mod tests {
assert_model_tools(
"gpt-5",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"shell",
"list_mcp_resources",
"list_mcp_resource_templates",
"read_mcp_resource",
"update_plan",
"web_search",
"view_image",
],
);
@@ -1634,6 +1650,7 @@ mod tests {
assert_model_tools(
"gpt-5.1",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"shell_command",
"list_mcp_resources",
@@ -1641,6 +1658,7 @@ mod tests {
"read_mcp_resource",
"update_plan",
"apply_patch",
"web_search",
"view_image",
],
);
@@ -1651,6 +1669,7 @@ mod tests {
assert_model_tools(
"exp-5.1",
&Features::with_defaults(),
WebSearchMode::Cached,
&[
"exec_command",
"write_stdin",
@@ -1659,6 +1678,7 @@ mod tests {
"read_mcp_resource",
"update_plan",
"apply_patch",
"web_search",
"view_image",
],
);
@@ -1668,9 +1688,8 @@ mod tests {
fn test_codex_mini_unified_exec_web_search() {
assert_model_tools(
"codex-mini-latest",
Features::with_defaults()
.enable(Feature::UnifiedExec)
.enable(Feature::WebSearchRequest),
Features::with_defaults().enable(Feature::UnifiedExec),
WebSearchMode::Live,
&[
"exec_command",
"write_stdin",
@@ -1689,11 +1708,11 @@ mod tests {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline("o3", &config);
let mut features = Features::with_defaults();
features.enable(Feature::WebSearchRequest);
features.enable(Feature::UnifiedExec);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
});
let (tools, _) = build_specs(&tools_config, Some(HashMap::new())).build();
@@ -1715,6 +1734,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(&tools_config, None).build();
@@ -1733,6 +1753,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(&tools_config, None).build();
@@ -1760,10 +1781,10 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("o3", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
});
let (tools, _) = build_specs(
&tools_config,
@@ -1858,6 +1879,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
// Intentionally construct a map with keys that would sort alphabetically.
@@ -1931,10 +1953,10 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(
@@ -1988,10 +2010,10 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(
@@ -2041,11 +2063,11 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
features.enable(Feature::ApplyPatchFreeform);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(
@@ -2098,10 +2120,10 @@ mod tests {
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(
@@ -2210,10 +2232,10 @@ Examples of valid command strings:
let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::WebSearchRequest);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
});
let (tools, _) = build_specs(
&tools_config,