Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion codex-rs/core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use codex_otel::OtelManager;

use codex_protocol::ThreadId;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
Expand Down Expand Up @@ -64,6 +65,8 @@ use crate::model_provider_info::WireApi;
use crate::tools::spec::create_tools_json_for_chat_completions_api;
use crate::tools::spec::create_tools_json_for_responses_api;

pub const WEB_SEARCH_ELIGIBLE_HEADER: &str = "x-oai-web-search-eligible";

#[derive(Debug)]
struct ModelClientState {
config: Arc<Config>,
Expand Down Expand Up @@ -319,7 +322,7 @@ impl ModelClientSession {
store_override: None,
conversation_id: Some(conversation_id),
session_source: Some(self.state.session_source.clone()),
extra_headers: beta_feature_headers(&self.state.config),
extra_headers: build_responses_headers(&self.state.config),
compression,
}
}
Expand Down Expand Up @@ -635,6 +638,21 @@ fn beta_feature_headers(config: &Config) -> ApiHeaderMap {
headers
}

fn build_responses_headers(config: &Config) -> ApiHeaderMap {
let mut headers = beta_feature_headers(config);
headers.insert(
WEB_SEARCH_ELIGIBLE_HEADER,
HeaderValue::from_static(
if matches!(config.web_search_mode, Some(WebSearchMode::Disabled)) {
"false"
} else {
"true"
},
),
);
headers
}

fn map_response_stream<S>(api_stream: S, otel_manager: OtelManager) -> ResponseStream
where
S: futures::Stream<Item = std::result::Result<ResponseEvent, ApiError>>
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2416,7 +2416,7 @@ async fn spawn_review_thread(
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &review_model_info,
features: &review_features,
web_search_mode: review_web_search_mode,
web_search_mode: Some(review_web_search_mode),
});

let base_instructions = REVIEW_PROMPT.to_string();
Expand All @@ -2429,7 +2429,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;
per_turn_config.web_search_mode = Some(review_web_search_mode);

let otel_manager = parent_turn_context
.client
Expand Down
36 changes: 16 additions & 20 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ pub struct Config {
/// model info's default preference.
pub include_apply_patch_tool: bool,

pub web_search_mode: WebSearchMode,
/// Explicit or feature-derived web search mode.
pub web_search_mode: Option<WebSearchMode>,

/// If set to `true`, used only the experimental unified exec tool.
pub use_experimental_unified_exec_tool: bool,
Expand Down Expand Up @@ -1182,24 +1183,22 @@ pub fn resolve_oss_provider(
}
}

/// Resolve the web search mode from the config, profile, and features.
/// Resolve the web search mode from explicit config and feature flags.
fn resolve_web_search_mode(
config_toml: &ConfigToml,
config_profile: &ConfigProfile,
features: &Features,
) -> WebSearchMode {
// Enum gets precedence over features flags
) -> Option<WebSearchMode> {
if let Some(mode) = config_profile.web_search.or(config_toml.web_search) {
return mode;
return Some(mode);
}
if features.enabled(Feature::WebSearchCached) {
return WebSearchMode::Cached;
return Some(WebSearchMode::Cached);
}
if features.enabled(Feature::WebSearchRequest) {
return WebSearchMode::Live;
return Some(WebSearchMode::Live);
}
// Fall back to default
WebSearchMode::default()
None
}

impl Config {
Expand Down Expand Up @@ -2202,15 +2201,12 @@ trust_level = "trusted"
}

#[test]
fn web_search_mode_uses_default_if_unset() {
fn web_search_mode_uses_none_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()
);
assert_eq!(resolve_web_search_mode(&cfg, &profile, &features), None);
}

#[test]
Expand All @@ -2225,7 +2221,7 @@ trust_level = "trusted"

assert_eq!(
resolve_web_search_mode(&cfg, &profile, &features),
WebSearchMode::Live
Some(WebSearchMode::Live)
);
}

Expand All @@ -2241,7 +2237,7 @@ trust_level = "trusted"

assert_eq!(
resolve_web_search_mode(&cfg, &profile, &features),
WebSearchMode::Disabled
Some(WebSearchMode::Disabled)
);
}

Expand Down Expand Up @@ -3581,7 +3577,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::default(),
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
Expand Down Expand Up @@ -3668,7 +3664,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::default(),
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
Expand Down Expand Up @@ -3770,7 +3766,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::default(),
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
Expand Down Expand Up @@ -3858,7 +3854,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::default(),
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ mod user_shell_command;
pub mod util;

pub use apply_patch::CODEX_APPLY_PATCH_ARG1;
pub use client::WEB_SEARCH_ELIGIBLE_HEADER;
pub use command_safety::is_dangerous_command;
pub use command_safety::is_safe_command;
pub use exec_policy::ExecPolicyError;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/src/tasks/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +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.web_search_mode = WebSearchMode::Disabled;
sub_agent_config.web_search_mode = Some(WebSearchMode::Disabled);

// Set explicit review rubric for the sub-agent
sub_agent_config.base_instructions = Some(crate::REVIEW_PROMPT.to_string());
Expand Down
Loading
Loading