mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
27da8a68d3
commit
5e426ac270
@@ -8,6 +8,7 @@ use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SandboxMode as CoreSandboxMode;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::items::AgentMessageContent as CoreAgentMessageContent;
|
||||
use codex_protocol::items::TurnItem as CoreTurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -327,6 +328,7 @@ pub struct ProfileV2 {
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
pub model_verbosity: Option<Verbosity>,
|
||||
pub web_search: Option<WebSearchMode>,
|
||||
pub chatgpt_base_url: Option<String>,
|
||||
#[serde(default, flatten)]
|
||||
pub additional: HashMap<String, JsonValue>,
|
||||
@@ -355,6 +357,7 @@ pub struct Config {
|
||||
pub sandbox_workspace_write: Option<SandboxWorkspaceWrite>,
|
||||
pub forced_chatgpt_workspace_id: Option<String>,
|
||||
pub forced_login_method: Option<ForcedLoginMethod>,
|
||||
pub web_search: Option<WebSearchMode>,
|
||||
pub tools: Option<ToolsV2>,
|
||||
pub profile: Option<String>,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -691,11 +691,11 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
// Honor `--search` via the new feature toggle.
|
||||
// Honor `--search` via the canonical web_search mode.
|
||||
if interactive.web_search {
|
||||
cli_kv_overrides.push((
|
||||
"features.web_search_request".to_string(),
|
||||
toml::Value::Boolean(true),
|
||||
"web_search".to_string(),
|
||||
toml::Value::String("live".to_string()),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -395,6 +395,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"web_search": {
|
||||
"description": "Controls the web search tool mode: disabled, cached, or live.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WebSearchMode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"windows_wsl_setup_acknowledged": {
|
||||
"description": "Tracks whether the Windows onboarding screen has been acknowledged.",
|
||||
"type": "boolean"
|
||||
@@ -634,6 +642,9 @@
|
||||
},
|
||||
"tools_web_search": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"web_search": {
|
||||
"$ref": "#/definitions/WebSearchMode"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1420,6 +1431,14 @@
|
||||
"high"
|
||||
]
|
||||
},
|
||||
"WebSearchMode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled",
|
||||
"cached",
|
||||
"live"
|
||||
]
|
||||
},
|
||||
"WireApi": {
|
||||
"description": "Wire protocol that the provider speaks. Most third-party services only implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI itself (and a handful of others) additionally expose the more modern *Responses* API. The two protocols use different request/response shapes and *cannot* be auto-detected at runtime, therefore each provider entry must declare which one it expects.",
|
||||
"oneOf": [
|
||||
|
||||
@@ -34,6 +34,7 @@ use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
@@ -537,6 +538,7 @@ impl Session {
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
features: &per_turn_config.features,
|
||||
web_search_mode: per_turn_config.web_search_mode,
|
||||
});
|
||||
|
||||
let base_instructions = if per_turn_config.features.enabled(Feature::Collab) {
|
||||
@@ -2409,9 +2411,11 @@ async fn spawn_review_thread(
|
||||
review_features
|
||||
.disable(crate::features::Feature::WebSearchRequest)
|
||||
.disable(crate::features::Feature::WebSearchCached);
|
||||
let review_web_search_mode = WebSearchMode::Disabled;
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &review_model_info,
|
||||
features: &review_features,
|
||||
web_search_mode: review_web_search_mode,
|
||||
});
|
||||
|
||||
let base_instructions = REVIEW_PROMPT.to_string();
|
||||
@@ -2424,6 +2428,7 @@ async fn spawn_review_thread(
|
||||
let mut per_turn_config = (*config).clone();
|
||||
per_turn_config.model = Some(model.clone());
|
||||
per_turn_config.features = review_features.clone();
|
||||
per_turn_config.web_search_mode = review_web_search_mode;
|
||||
|
||||
let otel_manager = parent_turn_context
|
||||
.client
|
||||
|
||||
@@ -42,6 +42,7 @@ use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_rmcp_client::OAuthCredentialsStoreMode;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -336,7 +337,7 @@ pub struct Config {
|
||||
/// model info's default preference.
|
||||
pub include_apply_patch_tool: bool,
|
||||
|
||||
pub tools_web_search_request: bool,
|
||||
pub web_search_mode: WebSearchMode,
|
||||
|
||||
/// If set to `true`, used only the experimental unified exec tool.
|
||||
pub use_experimental_unified_exec_tool: bool,
|
||||
@@ -894,6 +895,9 @@ pub struct ConfigToml {
|
||||
|
||||
pub projects: Option<HashMap<String, ProjectConfig>>,
|
||||
|
||||
/// Controls the web search tool mode: disabled, cached, or live.
|
||||
pub web_search: Option<WebSearchMode>,
|
||||
|
||||
/// Nested tools section for feature toggles
|
||||
pub tools: Option<ToolsToml>,
|
||||
|
||||
@@ -1178,6 +1182,26 @@ pub fn resolve_oss_provider(
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the web search mode from the config, profile, and features.
|
||||
fn resolve_web_search_mode(
|
||||
config_toml: &ConfigToml,
|
||||
config_profile: &ConfigProfile,
|
||||
features: &Features,
|
||||
) -> WebSearchMode {
|
||||
// Enum gets precedence over features flags
|
||||
if let Some(mode) = config_profile.web_search.or(config_toml.web_search) {
|
||||
return mode;
|
||||
}
|
||||
if features.enabled(Feature::WebSearchCached) {
|
||||
return WebSearchMode::Cached;
|
||||
}
|
||||
if features.enabled(Feature::WebSearchRequest) {
|
||||
return WebSearchMode::Live;
|
||||
}
|
||||
// Fall back to default
|
||||
WebSearchMode::default()
|
||||
}
|
||||
|
||||
impl Config {
|
||||
#[cfg(test)]
|
||||
fn load_from_base_config_with_overrides(
|
||||
@@ -1242,6 +1266,7 @@ impl Config {
|
||||
};
|
||||
|
||||
let features = Features::from_config(&cfg, &config_profile, feature_overrides);
|
||||
let web_search_mode = resolve_web_search_mode(&cfg, &config_profile, &features);
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Base flag controls sandbox on/off; elevated only applies when base is enabled.
|
||||
@@ -1361,7 +1386,6 @@ impl Config {
|
||||
};
|
||||
|
||||
let include_apply_patch_tool_flag = features.enabled(Feature::ApplyPatchFreeform);
|
||||
let tools_web_search_request = features.enabled(Feature::WebSearchRequest);
|
||||
let use_experimental_unified_exec_tool = features.enabled(Feature::UnifiedExec);
|
||||
|
||||
let forced_chatgpt_workspace_id =
|
||||
@@ -1502,7 +1526,7 @@ impl Config {
|
||||
forced_chatgpt_workspace_id,
|
||||
forced_login_method,
|
||||
include_apply_patch_tool: include_apply_patch_tool_flag,
|
||||
tools_web_search_request,
|
||||
web_search_mode,
|
||||
use_experimental_unified_exec_tool,
|
||||
ghost_snapshot,
|
||||
features,
|
||||
@@ -2177,6 +2201,50 @@ trust_level = "trusted"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_uses_default_if_unset() {
|
||||
let cfg = ConfigToml::default();
|
||||
let profile = ConfigProfile::default();
|
||||
let features = Features::with_defaults();
|
||||
|
||||
assert_eq!(
|
||||
resolve_web_search_mode(&cfg, &profile, &features),
|
||||
WebSearchMode::default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_prefers_profile_over_legacy_flags() {
|
||||
let cfg = ConfigToml::default();
|
||||
let profile = ConfigProfile {
|
||||
web_search: Some(WebSearchMode::Live),
|
||||
..Default::default()
|
||||
};
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::WebSearchCached);
|
||||
|
||||
assert_eq!(
|
||||
resolve_web_search_mode(&cfg, &profile, &features),
|
||||
WebSearchMode::Live
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_disabled_overrides_legacy_request() {
|
||||
let cfg = ConfigToml {
|
||||
web_search: Some(WebSearchMode::Disabled),
|
||||
..Default::default()
|
||||
};
|
||||
let profile = ConfigProfile::default();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::WebSearchRequest);
|
||||
|
||||
assert_eq!(
|
||||
resolve_web_search_mode(&cfg, &profile, &features),
|
||||
WebSearchMode::Disabled
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn profile_legacy_toggles_override_base() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -3513,7 +3581,7 @@ model_verbosity = "high"
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
include_apply_patch_tool: false,
|
||||
tools_web_search_request: false,
|
||||
web_search_mode: WebSearchMode::default(),
|
||||
use_experimental_unified_exec_tool: false,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
features: Features::with_defaults(),
|
||||
@@ -3600,7 +3668,7 @@ model_verbosity = "high"
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
include_apply_patch_tool: false,
|
||||
tools_web_search_request: false,
|
||||
web_search_mode: WebSearchMode::default(),
|
||||
use_experimental_unified_exec_tool: false,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
features: Features::with_defaults(),
|
||||
@@ -3702,7 +3770,7 @@ model_verbosity = "high"
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
include_apply_patch_tool: false,
|
||||
tools_web_search_request: false,
|
||||
web_search_mode: WebSearchMode::default(),
|
||||
use_experimental_unified_exec_tool: false,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
features: Features::with_defaults(),
|
||||
@@ -3790,7 +3858,7 @@ model_verbosity = "high"
|
||||
forced_chatgpt_workspace_id: None,
|
||||
forced_login_method: None,
|
||||
include_apply_patch_tool: false,
|
||||
tools_web_search_request: false,
|
||||
web_search_mode: WebSearchMode::default(),
|
||||
use_experimental_unified_exec_tool: false,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
features: Features::with_defaults(),
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::protocol::AskForApproval;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
|
||||
/// Collection of common configuration options that a user can define as a unit
|
||||
@@ -31,6 +32,7 @@ pub struct ConfigProfile {
|
||||
pub experimental_use_freeform_apply_patch: Option<bool>,
|
||||
pub tools_web_search: Option<bool>,
|
||||
pub tools_view_image: Option<bool>,
|
||||
pub web_search: Option<WebSearchMode>,
|
||||
pub analytics: Option<crate::config::types::AnalyticsConfigToml>,
|
||||
/// Optional feature toggles scoped to this profile.
|
||||
#[serde(default)]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// definitions that do not contain business logic.
|
||||
|
||||
pub use codex_protocol::config_types::AltScreenMode;
|
||||
pub use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -85,9 +86,7 @@ async fn start_review_conversation(
|
||||
let mut sub_agent_config = config.as_ref().clone();
|
||||
// Carry over review-only feature restrictions so the delegate cannot
|
||||
// re-enable blocked tools (web search, view image).
|
||||
sub_agent_config
|
||||
.features
|
||||
.disable(crate::features::Feature::WebSearchRequest);
|
||||
sub_agent_config.web_search_mode = WebSearchMode::Disabled;
|
||||
|
||||
// Set explicit review rubric for the sub-agent
|
||||
sub_agent_config.base_instructions = Some(crate::REVIEW_PROMPT.to_string());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use core_test_support::load_sse_fixture_with_id;
|
||||
use core_test_support::responses;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
@@ -32,7 +33,10 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
|
||||
let sse = sse_completed(model);
|
||||
let resp_mock = responses::mount_sse_once(&server, sse).await;
|
||||
|
||||
let mut builder = test_codex().with_model(model);
|
||||
let mut builder = test_codex()
|
||||
.with_model(model)
|
||||
// Keep tool expectations stable when the default web_search mode changes.
|
||||
.with_config(|config| config.web_search_mode = WebSearchMode::Cached);
|
||||
let test = builder
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -58,6 +62,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"codex-mini-latest should expose the local shell tool",
|
||||
@@ -73,6 +78,7 @@ async fn model_selects_expected_tools() {
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"gpt-5-codex should expose the apply_patch tool",
|
||||
@@ -88,6 +94,7 @@ async fn model_selects_expected_tools() {
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"gpt-5.1-codex should expose the apply_patch tool",
|
||||
@@ -102,6 +109,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"gpt-5 should expose the apply_patch tool",
|
||||
@@ -117,6 +125,7 @@ async fn model_selects_expected_tools() {
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"gpt-5.1 should expose the apply_patch tool",
|
||||
@@ -132,6 +141,7 @@ async fn model_selects_expected_tools() {
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
],
|
||||
"exp-5.1 should expose the apply_patch tool",
|
||||
|
||||
@@ -11,6 +11,7 @@ use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol_config_types::ReasoningSummary;
|
||||
use codex_core::shell::Shell;
|
||||
use codex_core::shell::default_user_shell;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -52,7 +53,13 @@ fn assert_tool_names(body: &serde_json::Value, expected_names: &[&str]) {
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|t| t["name"].as_str().unwrap().to_string())
|
||||
.map(|t| {
|
||||
t.get("name")
|
||||
.and_then(|value| value.as_str())
|
||||
.or_else(|| t.get("type").and_then(|value| value.as_str()))
|
||||
.unwrap()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
expected_names
|
||||
);
|
||||
@@ -80,6 +87,8 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
||||
.with_config(|config| {
|
||||
config.user_instructions = Some("be consistent and helpful".to_string());
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
// Keep tool expectations stable when the default web_search mode changes.
|
||||
config.web_search_mode = WebSearchMode::Cached;
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
@@ -122,6 +131,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
"view_image",
|
||||
];
|
||||
let body0 = req1.single_request().body_json();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use codex_core::features::Feature;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use core_test_support::load_sse_fixture_with_id;
|
||||
use core_test_support::responses;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
@@ -24,7 +25,7 @@ fn find_web_search_tool(body: &Value) -> &Value {
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn web_search_cached_sets_external_web_access_false_in_request_body() {
|
||||
async fn web_search_mode_cached_sets_external_web_access_false_in_request_body() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
@@ -34,7 +35,7 @@ async fn web_search_cached_sets_external_web_access_false_in_request_body() {
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5-codex")
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::WebSearchCached);
|
||||
config.web_search_mode = WebSearchMode::Cached;
|
||||
});
|
||||
let test = builder
|
||||
.build(&server)
|
||||
@@ -50,12 +51,12 @@ async fn web_search_cached_sets_external_web_access_false_in_request_body() {
|
||||
assert_eq!(
|
||||
tool.get("external_web_access").and_then(Value::as_bool),
|
||||
Some(false),
|
||||
"web_search_cached should force external_web_access=false"
|
||||
"web_search cached mode should force external_web_access=false"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn web_search_cached_takes_precedence_over_web_search_request_in_request_body() {
|
||||
async fn web_search_mode_takes_precedence_over_legacy_flags_in_request_body() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
@@ -66,7 +67,7 @@ async fn web_search_cached_takes_precedence_over_web_search_request_in_request_b
|
||||
.with_model("gpt-5-codex")
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::WebSearchRequest);
|
||||
config.features.enable(Feature::WebSearchCached);
|
||||
config.web_search_mode = WebSearchMode::Cached;
|
||||
});
|
||||
let test = builder
|
||||
.build(&server)
|
||||
@@ -82,6 +83,6 @@ async fn web_search_cached_takes_precedence_over_web_search_request_in_request_b
|
||||
assert_eq!(
|
||||
tool.get("external_web_access").and_then(Value::as_bool),
|
||||
Some(false),
|
||||
"web_search_cached should win over web_search_request"
|
||||
"web_search mode should win over legacy web_search_request"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,18 @@ pub enum SandboxMode {
|
||||
DangerFullAccess,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS, Default,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum WebSearchMode {
|
||||
#[default]
|
||||
Disabled,
|
||||
Cached,
|
||||
Live,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
|
||||
@@ -94,7 +94,7 @@ pub struct Cli {
|
||||
#[clap(long = "cd", short = 'C', value_name = "DIR")]
|
||||
pub cwd: Option<PathBuf>,
|
||||
|
||||
/// Enable web search (off by default). When enabled, the native Responses `web_search` tool is available to the model (no per‑call approval).
|
||||
/// Enable live web search. When enabled, the native Responses `web_search` tool is available to the model (no per‑call approval).
|
||||
#[arg(long = "search", default_value_t = false)]
|
||||
pub web_search: bool,
|
||||
|
||||
|
||||
@@ -123,11 +123,11 @@ pub async fn run_main(
|
||||
)
|
||||
};
|
||||
|
||||
// Map the legacy --search flag to the new feature toggle.
|
||||
// Map the legacy --search flag to the canonical web_search mode.
|
||||
if cli.web_search {
|
||||
cli.config_overrides
|
||||
.raw_overrides
|
||||
.push("features.web_search_request=true".to_string());
|
||||
.push("web_search=\"live\"".to_string());
|
||||
}
|
||||
|
||||
// When using `--oss`, let the bootstrapper pick the model (defaulting to
|
||||
|
||||
@@ -94,7 +94,7 @@ pub struct Cli {
|
||||
#[clap(long = "cd", short = 'C', value_name = "DIR")]
|
||||
pub cwd: Option<PathBuf>,
|
||||
|
||||
/// Enable web search (off by default). When enabled, the native Responses `web_search` tool is available to the model (no per‑call approval).
|
||||
/// Enable live web search. When enabled, the native Responses `web_search` tool is available to the model (no per‑call approval).
|
||||
#[arg(long = "search", default_value_t = false)]
|
||||
pub web_search: bool,
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ pub async fn run_main(
|
||||
if cli.web_search {
|
||||
cli.config_overrides
|
||||
.raw_overrides
|
||||
.push("features.web_search_request=true".to_string());
|
||||
.push("web_search=\"live\"".to_string());
|
||||
}
|
||||
|
||||
// When using `--oss`, let the bootstrapper pick the model (defaulting to
|
||||
|
||||
@@ -3,7 +3,12 @@ import path from "node:path";
|
||||
import readline from "node:readline";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { SandboxMode, ModelReasoningEffort, ApprovalMode } from "./threadOptions";
|
||||
import {
|
||||
SandboxMode,
|
||||
ModelReasoningEffort,
|
||||
ApprovalMode,
|
||||
WebSearchMode,
|
||||
} from "./threadOptions";
|
||||
|
||||
export type CodexExecArgs = {
|
||||
input: string;
|
||||
@@ -30,7 +35,9 @@ export type CodexExecArgs = {
|
||||
signal?: AbortSignal;
|
||||
// --config sandbox_workspace_write.network_access
|
||||
networkAccessEnabled?: boolean;
|
||||
// --config features.web_search_request
|
||||
// --config web_search
|
||||
webSearchMode?: WebSearchMode;
|
||||
// legacy --config features.web_search_request
|
||||
webSearchEnabled?: boolean;
|
||||
// --config approval_policy
|
||||
approvalPolicy?: ApprovalMode;
|
||||
@@ -88,8 +95,12 @@ export class CodexExec {
|
||||
);
|
||||
}
|
||||
|
||||
if (args.webSearchEnabled !== undefined) {
|
||||
commandArgs.push("--config", `features.web_search_request=${args.webSearchEnabled}`);
|
||||
if (args.webSearchMode) {
|
||||
commandArgs.push("--config", `web_search="${args.webSearchMode}"`);
|
||||
} else if (args.webSearchEnabled === true) {
|
||||
commandArgs.push("--config", `web_search="live"`);
|
||||
} else if (args.webSearchEnabled === false) {
|
||||
commandArgs.push("--config", `web_search="disabled"`);
|
||||
}
|
||||
|
||||
if (args.approvalPolicy) {
|
||||
|
||||
@@ -35,5 +35,6 @@ export type {
|
||||
ApprovalMode,
|
||||
SandboxMode,
|
||||
ModelReasoningEffort,
|
||||
WebSearchMode,
|
||||
} from "./threadOptions";
|
||||
export type { TurnOptions } from "./turnOptions";
|
||||
|
||||
@@ -88,6 +88,7 @@ export class Thread {
|
||||
modelReasoningEffort: options?.modelReasoningEffort,
|
||||
signal: turnOptions.signal,
|
||||
networkAccessEnabled: options?.networkAccessEnabled,
|
||||
webSearchMode: options?.webSearchMode,
|
||||
webSearchEnabled: options?.webSearchEnabled,
|
||||
approvalPolicy: options?.approvalPolicy,
|
||||
additionalDirectories: options?.additionalDirectories,
|
||||
|
||||
@@ -4,6 +4,8 @@ export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access"
|
||||
|
||||
export type ModelReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
|
||||
export type WebSearchMode = "disabled" | "cached" | "live";
|
||||
|
||||
export type ThreadOptions = {
|
||||
model?: string;
|
||||
sandboxMode?: SandboxMode;
|
||||
@@ -11,6 +13,7 @@ export type ThreadOptions = {
|
||||
skipGitRepoCheck?: boolean;
|
||||
modelReasoningEffort?: ModelReasoningEffort;
|
||||
networkAccessEnabled?: boolean;
|
||||
webSearchMode?: WebSearchMode;
|
||||
webSearchEnabled?: boolean;
|
||||
approvalPolicy?: ApprovalMode;
|
||||
additionalDirectories?: string[];
|
||||
|
||||
@@ -310,7 +310,69 @@ describe("Codex", () => {
|
||||
|
||||
const commandArgs = spawnArgs[0];
|
||||
expect(commandArgs).toBeDefined();
|
||||
expectPair(commandArgs, ["--config", "features.web_search_request=true"]);
|
||||
expectPair(commandArgs, ["--config", 'web_search="live"']);
|
||||
} finally {
|
||||
restore();
|
||||
await close();
|
||||
}
|
||||
});
|
||||
|
||||
it("passes webSearchMode to exec", async () => {
|
||||
const { url, close } = await startResponsesTestProxy({
|
||||
statusCode: 200,
|
||||
responseBodies: [
|
||||
sse(
|
||||
responseStarted("response_1"),
|
||||
assistantMessage("Web search cached", "item_1"),
|
||||
responseCompleted("response_1"),
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
const { args: spawnArgs, restore } = codexExecSpy();
|
||||
|
||||
try {
|
||||
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||
|
||||
const thread = client.startThread({
|
||||
webSearchMode: "cached",
|
||||
});
|
||||
await thread.run("test web search mode");
|
||||
|
||||
const commandArgs = spawnArgs[0];
|
||||
expect(commandArgs).toBeDefined();
|
||||
expectPair(commandArgs, ["--config", 'web_search="cached"']);
|
||||
} finally {
|
||||
restore();
|
||||
await close();
|
||||
}
|
||||
});
|
||||
|
||||
it("passes webSearchEnabled false to exec", async () => {
|
||||
const { url, close } = await startResponsesTestProxy({
|
||||
statusCode: 200,
|
||||
responseBodies: [
|
||||
sse(
|
||||
responseStarted("response_1"),
|
||||
assistantMessage("Web search disabled", "item_1"),
|
||||
responseCompleted("response_1"),
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
const { args: spawnArgs, restore } = codexExecSpy();
|
||||
|
||||
try {
|
||||
const client = new Codex({ codexPathOverride: codexExecPath, baseUrl: url, apiKey: "test" });
|
||||
|
||||
const thread = client.startThread({
|
||||
webSearchEnabled: false,
|
||||
});
|
||||
await thread.run("test web search disabled");
|
||||
|
||||
const commandArgs = spawnArgs[0];
|
||||
expect(commandArgs).toBeDefined();
|
||||
expectPair(commandArgs, ["--config", 'web_search="disabled"']);
|
||||
} finally {
|
||||
restore();
|
||||
await close();
|
||||
|
||||
Reference in New Issue
Block a user