Compare commits

...

1 Commits

Author SHA1 Message Date
Dylan Hurd
ff4f892e8c fix(config) tools.view_image patch 2026-05-12 21:21:01 -07:00
7 changed files with 84 additions and 1 deletions

View File

@@ -404,6 +404,26 @@ web_search = false
);
}
#[tokio::test]
async fn tools_view_image_false_disables_runtime_tool_config() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
r#"
[tools]
view_image = false
"#,
)?;
let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.build()
.await?;
assert!(!config.view_image_tool_enabled);
Ok(())
}
#[test]
fn rejects_provider_auth_with_env_key() {
let err = toml::from_str::<ConfigToml>(
@@ -7363,6 +7383,7 @@ async fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
include_apply_patch_tool: true,
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
web_search_config: None,
view_image_tool_enabled: true,
use_experimental_unified_exec_tool: !cfg!(windows),
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
ghost_snapshot: GhostSnapshotConfig::default(),
@@ -7810,6 +7831,7 @@ async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
include_apply_patch_tool: true,
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
web_search_config: None,
view_image_tool_enabled: true,
use_experimental_unified_exec_tool: !cfg!(windows),
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
ghost_snapshot: GhostSnapshotConfig::default(),
@@ -7971,6 +7993,7 @@ async fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
include_apply_patch_tool: true,
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
web_search_config: None,
view_image_tool_enabled: true,
use_experimental_unified_exec_tool: !cfg!(windows),
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
ghost_snapshot: GhostSnapshotConfig::default(),
@@ -8117,6 +8140,7 @@ async fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
include_apply_patch_tool: true,
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
web_search_config: None,
view_image_tool_enabled: true,
use_experimental_unified_exec_tool: !cfg!(windows),
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
ghost_snapshot: GhostSnapshotConfig::default(),

View File

@@ -773,6 +773,9 @@ pub struct Config {
/// Additional parameters for the web search tool when it is enabled.
pub web_search_config: Option<WebSearchConfig>,
/// Whether the `view_image` tool should be registered for interactive turns.
pub view_image_tool_enabled: bool,
/// If set to `true`, used only the experimental unified exec tool.
pub use_experimental_unified_exec_tool: bool,
@@ -1957,6 +1960,24 @@ fn resolve_web_search_config(
}
}
fn resolve_view_image_tool_enabled(
config_toml: &ConfigToml,
config_profile: &ConfigProfile,
) -> bool {
config_profile
.tools
.as_ref()
.and_then(|tools| tools.view_image)
.or(config_profile.tools_view_image)
.or_else(|| {
config_toml
.tools
.as_ref()
.and_then(|tools| tools.view_image)
})
.unwrap_or(true)
}
fn resolve_multi_agent_v2_config(
config_toml: &ConfigToml,
config_profile: &ConfigProfile,
@@ -2592,6 +2613,7 @@ impl Config {
let web_search_mode = resolve_web_search_mode(&cfg, &config_profile, &features)
.unwrap_or(WebSearchMode::Cached);
let web_search_config = resolve_web_search_config(&cfg, &config_profile);
let view_image_tool_enabled = resolve_view_image_tool_enabled(&cfg, &config_profile);
let multi_agent_v2 = resolve_multi_agent_v2_config(&cfg, &config_profile);
let apps_mcp_path_override = if features.enabled(Feature::AppsMcpPathOverride) {
let base = apps_mcp_path_override_toml_config(cfg.features.as_ref());
@@ -3161,6 +3183,7 @@ impl Config {
include_apply_patch_tool: include_apply_patch_tool_flag,
web_search_mode: constrained_web_search_mode.value,
web_search_config,
view_image_tool_enabled,
use_experimental_unified_exec_tool,
background_terminal_max_timeout,
ghost_snapshot,

View File

@@ -44,6 +44,7 @@ pub(super) async fn spawn_review_thread(
})
.with_namespace_tools_capability(provider_capabilities.namespace_tools)
.with_image_generation_capability(provider_capabilities.image_generation)
.with_view_image_tool_enabled(false)
.with_web_search_capability(provider_capabilities.web_search)
.with_unified_exec_shell_mode_for_session(
crate::tools::spec::tool_user_shell_type(sess.services.user_shell.as_ref()),

View File

@@ -209,6 +209,7 @@ impl TurnContext {
})
.with_namespace_tools_capability(provider_capabilities.namespace_tools)
.with_image_generation_capability(provider_capabilities.image_generation)
.with_view_image_tool_enabled(self.tools_config.view_image_tool_enabled)
.with_web_search_capability(provider_capabilities.web_search)
.with_unified_exec_shell_mode(self.tools_config.unified_exec_shell_mode.clone())
.with_web_search_config(self.tools_config.web_search_config.clone())
@@ -484,6 +485,7 @@ impl Session {
})
.with_namespace_tools_capability(provider_capabilities.namespace_tools)
.with_image_generation_capability(provider_capabilities.image_generation)
.with_view_image_tool_enabled(per_turn_config.view_image_tool_enabled)
.with_web_search_capability(provider_capabilities.web_search)
.with_unified_exec_shell_mode_for_session(
crate::tools::spec::tool_user_shell_type(user_shell),

View File

@@ -363,7 +363,7 @@ fn collect_handler_tools(
handlers.push(Arc::new(TestSyncHandler));
}
if config.environment_mode.has_environment() {
if config.environment_mode.has_environment() && config.view_image_tool_enabled {
let include_environment_id =
matches!(config.environment_mode, ToolEnvironmentMode::Multiple);
handlers.push(Arc::new(ViewImageHandler::new(ViewImageToolOptions {

View File

@@ -703,6 +703,32 @@ fn view_image_tool_includes_detail_with_original_detail_support() {
assert!(description.contains("omit this field for default resized behavior"));
}
#[test]
fn view_image_tool_is_omitted_when_disabled() {
let model_info = model_info();
let features = Features::with_defaults();
let available_models = Vec::new();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
available_models: &available_models,
features: &features,
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
})
.with_view_image_tool_enabled(false);
let (tools, _) = build_specs(
&tools_config,
/*mcp_tools*/ None,
/*deferred_mcp_tools*/ None,
&[],
);
assert_lacks_tool_name(&tools, VIEW_IMAGE_TOOL_NAME);
}
#[test]
fn disabled_environment_omits_environment_backed_tools() {
let model_info = model_info();

View File

@@ -106,6 +106,7 @@ pub struct ToolsConfig {
pub web_search_config: Option<WebSearchConfig>,
pub web_search_tool_type: WebSearchToolType,
pub image_gen_tool: bool,
pub view_image_tool_enabled: bool,
pub search_tool: bool,
pub namespace_tools: bool,
pub tool_suggest: bool,
@@ -243,6 +244,7 @@ impl ToolsConfig {
web_search_config: None,
web_search_tool_type: model_info.web_search_tool_type,
image_gen_tool: include_image_gen_tool,
view_image_tool_enabled: true,
search_tool: include_search_tool,
namespace_tools: true,
tool_suggest: include_tool_suggest,
@@ -286,6 +288,11 @@ impl ToolsConfig {
self
}
pub fn with_view_image_tool_enabled(mut self, enabled: bool) -> Self {
self.view_image_tool_enabled = enabled;
self
}
pub fn with_web_search_capability(mut self, web_search: bool) -> Self {
if !web_search {
self.web_search_mode = None;