mirror of
https://github.com/openai/codex.git
synced 2026-09-16 12:13:30 +00:00
fflag for web_search with images
This commit is contained in:
@@ -461,6 +461,9 @@
|
||||
"web_search_cached": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"web_search_image_support": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"web_search_request": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@@ -1852,6 +1855,9 @@
|
||||
"web_search_cached": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"web_search_image_support": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"web_search_request": {
|
||||
"type": "boolean"
|
||||
}
|
||||
|
||||
@@ -176,6 +176,8 @@ pub(crate) mod tools {
|
||||
WebSearch {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
external_web_access: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
search_content_types: Option<Vec<String>>,
|
||||
},
|
||||
#[serde(rename = "custom")]
|
||||
Freeform(FreeformTool),
|
||||
|
||||
@@ -163,6 +163,9 @@ pub enum Feature {
|
||||
ResponsesWebsockets,
|
||||
/// Enable Responses API websocket v2 mode.
|
||||
ResponsesWebsocketsV2,
|
||||
/// Allow Codex web_search to request image results and send the temporary
|
||||
/// Responses feature-override header needed to enable that server-side path.
|
||||
WebSearchImageSupport,
|
||||
}
|
||||
|
||||
impl Feature {
|
||||
@@ -753,6 +756,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::WebSearchImageSupport,
|
||||
key: "web_search_image_support",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Push a warning event if any under-development features are enabled.
|
||||
|
||||
@@ -36,6 +36,8 @@ use std::collections::HashMap;
|
||||
|
||||
const SEARCH_TOOL_BM25_DESCRIPTION_TEMPLATE: &str =
|
||||
include_str!("../../templates/search_tool/tool_description.md");
|
||||
const WEB_SEARCH_CONTENT_TYPES: [&str; 2] = ["text", "image"];
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum ShellCommandBackendConfig {
|
||||
Classic,
|
||||
@@ -49,6 +51,7 @@ pub(crate) struct ToolsConfig {
|
||||
pub allow_login_shell: bool,
|
||||
pub apply_patch_tool_type: Option<ApplyPatchToolType>,
|
||||
pub web_search_mode: Option<WebSearchMode>,
|
||||
pub web_search_image_support: bool,
|
||||
pub image_gen_tool: bool,
|
||||
pub agent_roles: BTreeMap<String, AgentRoleConfig>,
|
||||
pub search_tool: bool,
|
||||
@@ -140,6 +143,7 @@ impl ToolsConfig {
|
||||
allow_login_shell: true,
|
||||
apply_patch_tool_type,
|
||||
web_search_mode: *web_search_mode,
|
||||
web_search_image_support: features.enabled(Feature::WebSearchImageSupport),
|
||||
image_gen_tool: include_image_gen_tool,
|
||||
agent_roles: BTreeMap::new(),
|
||||
search_tool: include_search_tool,
|
||||
@@ -167,6 +171,15 @@ impl ToolsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn web_search_content_types(web_search_image_support: bool) -> Option<Vec<String>> {
|
||||
web_search_image_support.then(|| {
|
||||
WEB_SEARCH_CONTENT_TYPES
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_image_generation(model_info: &ModelInfo) -> bool {
|
||||
model_info.input_modalities.contains(&InputModality::Image)
|
||||
}
|
||||
@@ -1881,11 +1894,13 @@ pub(crate) fn build_specs(
|
||||
Some(WebSearchMode::Cached) => {
|
||||
builder.push_spec(ToolSpec::WebSearch {
|
||||
external_web_access: Some(false),
|
||||
search_content_types: web_search_content_types(config.web_search_image_support),
|
||||
});
|
||||
}
|
||||
Some(WebSearchMode::Live) => {
|
||||
builder.push_spec(ToolSpec::WebSearch {
|
||||
external_web_access: Some(true),
|
||||
search_content_types: web_search_content_types(config.web_search_image_support),
|
||||
});
|
||||
}
|
||||
Some(WebSearchMode::Disabled) | None => {}
|
||||
@@ -2172,6 +2187,7 @@ mod tests {
|
||||
create_apply_patch_freeform_tool(),
|
||||
ToolSpec::WebSearch {
|
||||
external_web_access: Some(true),
|
||||
search_content_types: None,
|
||||
},
|
||||
create_view_image_tool(),
|
||||
] {
|
||||
@@ -2486,6 +2502,7 @@ mod tests {
|
||||
tool.spec,
|
||||
ToolSpec::WebSearch {
|
||||
external_web_access: Some(false),
|
||||
search_content_types: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2510,6 +2527,38 @@ mod tests {
|
||||
tool.spec,
|
||||
ToolSpec::WebSearch {
|
||||
external_web_access: Some(true),
|
||||
search_content_types: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn web_search_image_support_sets_search_content_types_when_web_search_enabled() {
|
||||
let config = test_config();
|
||||
let model_info =
|
||||
ModelsManager::construct_model_info_offline_for_tests("gpt-5-codex", &config);
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::WebSearchImageSupport);
|
||||
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
session_source: SessionSource::Cli,
|
||||
});
|
||||
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
|
||||
|
||||
let tool = find_tool(&tools, "web_search");
|
||||
assert_eq!(
|
||||
tool.spec,
|
||||
ToolSpec::WebSearch {
|
||||
external_web_access: Some(false),
|
||||
search_content_types: Some(
|
||||
WEB_SEARCH_CONTENT_TYPES
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -223,3 +223,47 @@ async fn web_search_mode_updates_between_turns_with_sandbox_policy() {
|
||||
"danger-full-access policy should default web_search to live"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn web_search_image_support_feature_sets_content_types() {
|
||||
skip_if_no_network!();
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let sse = responses::sse(vec![
|
||||
responses::ev_response_created("resp-1"),
|
||||
responses::ev_completed("resp-1"),
|
||||
]);
|
||||
let resp_mock = responses::mount_sse_once(&server, sse).await;
|
||||
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5-codex")
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::WebSearchImageSupport);
|
||||
config
|
||||
.web_search_mode
|
||||
.set(WebSearchMode::Cached)
|
||||
.expect("test web_search_mode should satisfy constraints");
|
||||
});
|
||||
let test = builder
|
||||
.build(&server)
|
||||
.await
|
||||
.expect("create test Codex conversation");
|
||||
|
||||
test.submit_turn_with_policy(
|
||||
"hello image web search",
|
||||
SandboxPolicy::new_read_only_policy(),
|
||||
)
|
||||
.await
|
||||
.expect("submit turn");
|
||||
|
||||
let body = resp_mock.single_request().body_json();
|
||||
let tool = find_web_search_tool(&body);
|
||||
assert_eq!(
|
||||
tool.get("search_content_types").and_then(Value::as_array),
|
||||
Some(&vec![
|
||||
Value::String("text".to_string()),
|
||||
Value::String("image".to_string()),
|
||||
]),
|
||||
"web_search image support should request both text and image results"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user