mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add indexed web search mode (#28489)
## Summary - Add `web_search = "indexed"` alongside `disabled`, `cached`, and `live`. - Use that same resolved mode for both hosted and standalone web search. - For hosted search, send `index_gated_web_access: true` with external web access enabled only when `indexed` is selected. - For standalone search, preserve the existing boolean wire values for existing modes (`cached` maps to `false` and `live` to `true`) and send `"indexed"` only for `indexed`; `disabled` keeps the tool unavailable. - Carry the mode through managed configuration requirements and generated schemas. ## Why Indexed search provides a middle ground between cached-only search and unrestricted live page fetching. Search queries can remain live while direct page fetches are limited to URLs admitted by the server. The existing `web_search` setting remains the single source of truth, so hosted and standalone executors cannot drift into different access modes. Without an explicit `indexed` selection, the existing model-visible tool and request shapes are unchanged. ```toml web_search = "indexed" [features] standalone_web_search = true ``` ## Validation - `just fmt` - `just test -p codex-api` (`126 passed`) - `just test -p codex-web-search-extension` (`7 passed`) - `just test -p codex-core code_mode_can_call_indexed_standalone_web_search` (`1 passed`) - Focused configuration, hosted request, standalone request, and managed-requirement coverage is included in the PR; remaining suites run in CI. The full workspace test suite was not run locally.
This commit is contained in:
@@ -5186,6 +5186,14 @@ fn web_search_mode_disabled_overrides_legacy_request() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_for_turn_preserves_indexed_for_disabled_permissions() {
|
||||
let web_search_mode = Constrained::allow_any(WebSearchMode::Indexed);
|
||||
let mode = resolve_web_search_mode_for_turn(&web_search_mode, &PermissionProfile::Disabled);
|
||||
|
||||
assert_eq!(mode, WebSearchMode::Indexed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_for_turn_uses_preference_for_read_only() {
|
||||
let web_search_mode = Constrained::allow_any(WebSearchMode::Cached);
|
||||
@@ -5232,6 +5240,31 @@ fn web_search_mode_for_turn_falls_back_when_live_is_disallowed() -> anyhow::Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_mode_for_turn_does_not_implicitly_select_indexed() -> anyhow::Result<()> {
|
||||
let allowed = [
|
||||
WebSearchMode::Disabled,
|
||||
WebSearchMode::Cached,
|
||||
WebSearchMode::Indexed,
|
||||
];
|
||||
let web_search_mode = Constrained::new(WebSearchMode::Cached, move |candidate| {
|
||||
if allowed.contains(candidate) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ConstraintError::InvalidValue {
|
||||
field_name: "web_search_mode",
|
||||
candidate: format!("{candidate:?}"),
|
||||
allowed: format!("{allowed:?}"),
|
||||
requirement_source: RequirementSource::Unknown,
|
||||
})
|
||||
}
|
||||
})?;
|
||||
let mode = resolve_web_search_mode_for_turn(&web_search_mode, &PermissionProfile::Disabled);
|
||||
|
||||
assert_eq!(mode, WebSearchMode::Cached);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn project_profiles_are_ignored() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -2667,7 +2667,7 @@ pub(crate) fn resolve_web_search_mode_for_turn(
|
||||
let preferred = web_search_mode.value();
|
||||
|
||||
if matches!(permission_profile, PermissionProfile::Disabled)
|
||||
&& preferred != WebSearchMode::Disabled
|
||||
&& !matches!(preferred, WebSearchMode::Disabled | WebSearchMode::Indexed)
|
||||
{
|
||||
for mode in [
|
||||
WebSearchMode::Live,
|
||||
|
||||
@@ -18,11 +18,12 @@ pub fn create_image_generation_tool(output_format: &str) -> ToolSpec {
|
||||
}
|
||||
|
||||
pub fn create_web_search_tool(options: WebSearchToolOptions<'_>) -> Option<ToolSpec> {
|
||||
let external_web_access = match options.web_search_mode {
|
||||
Some(WebSearchMode::Cached) => Some(false),
|
||||
Some(WebSearchMode::Live) => Some(true),
|
||||
Some(WebSearchMode::Disabled) | None => None,
|
||||
}?;
|
||||
let (external_web_access, index_gated_web_access) = match options.web_search_mode {
|
||||
Some(WebSearchMode::Cached) => (false, None),
|
||||
Some(WebSearchMode::Indexed) => (true, Some(true)),
|
||||
Some(WebSearchMode::Live) => (true, None),
|
||||
Some(WebSearchMode::Disabled) | None => return None,
|
||||
};
|
||||
|
||||
let search_content_types = match options.web_search_tool_type {
|
||||
WebSearchToolType::Text => None,
|
||||
@@ -36,6 +37,7 @@ pub fn create_web_search_tool(options: WebSearchToolOptions<'_>) -> Option<ToolS
|
||||
|
||||
Some(ToolSpec::WebSearch {
|
||||
external_web_access: Some(external_web_access),
|
||||
index_gated_web_access,
|
||||
filters: options
|
||||
.web_search_config
|
||||
.and_then(|config| config.filters.clone().map(Into::into)),
|
||||
|
||||
@@ -39,6 +39,7 @@ fn web_search_tool_preserves_configured_options() {
|
||||
}),
|
||||
Some(ToolSpec::WebSearch {
|
||||
external_web_access: Some(true),
|
||||
index_gated_web_access: None,
|
||||
filters: Some(ResponsesApiWebSearchFilters {
|
||||
allowed_domains: Some(vec!["example.com".to_string()]),
|
||||
}),
|
||||
|
||||
@@ -1441,6 +1441,7 @@ async fn hosted_tools_follow_provider_auth_model_and_config_gates() {
|
||||
live_web_search.visible_spec("web_search"),
|
||||
&ToolSpec::WebSearch {
|
||||
external_web_access: Some(true),
|
||||
index_gated_web_access: None,
|
||||
filters: None,
|
||||
user_location: None,
|
||||
search_context_size: None,
|
||||
|
||||
Reference in New Issue
Block a user