mirror of
https://github.com/openai/codex.git
synced 2026-09-09 15:58:47 +00:00
add tools.description_overrides
This commit is contained in:
@@ -482,6 +482,11 @@ pub struct ToolsToml {
|
||||
/// Enable the `view_image` tool that lets the agent attach local images.
|
||||
#[serde(default)]
|
||||
pub view_image: Option<bool>,
|
||||
|
||||
/// Tool descriptions keyed by tool name. Overrides only apply to tools
|
||||
/// that are enabled for the current turn.
|
||||
#[serde(default)]
|
||||
pub description_overrides: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -1699,6 +1699,14 @@
|
||||
"ToolsToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description_overrides": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {},
|
||||
"description": "Tool descriptions keyed by tool name. Overrides only apply to tools that are enabled for the current turn.",
|
||||
"type": "object"
|
||||
},
|
||||
"view_image": {
|
||||
"default": null,
|
||||
"description": "Enable the `view_image` tool that lets the agent attach local images.",
|
||||
|
||||
@@ -959,6 +959,7 @@ impl TurnContext {
|
||||
})
|
||||
.with_unified_exec_shell_mode(self.tools_config.unified_exec_shell_mode.clone())
|
||||
.with_web_search_config(self.tools_config.web_search_config.clone())
|
||||
.with_tool_description_overrides(self.tools_config.tool_description_overrides.clone())
|
||||
.with_allow_login_shell(self.tools_config.allow_login_shell)
|
||||
.with_has_environment(self.tools_config.has_environment)
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
@@ -1441,6 +1442,7 @@ impl Session {
|
||||
main_execve_wrapper_exe,
|
||||
)
|
||||
.with_web_search_config(per_turn_config.web_search_config.clone())
|
||||
.with_tool_description_overrides(per_turn_config.tool_description_overrides.clone())
|
||||
.with_allow_login_shell(per_turn_config.permissions.allow_login_shell)
|
||||
.with_has_environment(environment.is_some())
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
@@ -5619,6 +5621,7 @@ async fn spawn_review_thread(
|
||||
sess.services.main_execve_wrapper_exe.as_ref(),
|
||||
)
|
||||
.with_web_search_config(/*web_search_config*/ None)
|
||||
.with_tool_description_overrides(config.tool_description_overrides.clone())
|
||||
.with_allow_login_shell(config.permissions.allow_login_shell)
|
||||
.with_has_environment(parent_turn_context.environment.is_some())
|
||||
.with_agent_type_description(crate::agent::role::spawn_tool_spec::build(
|
||||
|
||||
@@ -247,6 +247,7 @@ web_search = true
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
view_image: None,
|
||||
description_overrides: BTreeMap::new(),
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -266,10 +267,97 @@ web_search = false
|
||||
Some(ToolsToml {
|
||||
web_search: None,
|
||||
view_image: None,
|
||||
description_overrides: BTreeMap::new(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tools_description_overrides_deserialize() {
|
||||
let cfg: ConfigToml = toml::from_str(
|
||||
r#"
|
||||
[tools.description_overrides]
|
||||
spawn_agent = "Use custom spawn guidance."
|
||||
update_plan = "Use custom plan guidance."
|
||||
"#,
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
assert_eq!(
|
||||
cfg.tools.expect("tools config").description_overrides,
|
||||
BTreeMap::from([
|
||||
(
|
||||
"spawn_agent".to_string(),
|
||||
"Use custom spawn guidance.".to_string()
|
||||
),
|
||||
(
|
||||
"update_plan".to_string(),
|
||||
"Use custom plan guidance.".to_string()
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_description_overrides_profile_overlays_base() -> std::io::Result<()> {
|
||||
let mut cfg = ConfigToml {
|
||||
tools: Some(ToolsToml {
|
||||
description_overrides: BTreeMap::from([
|
||||
(
|
||||
"spawn_agent".to_string(),
|
||||
"Base spawn guidance.".to_string(),
|
||||
),
|
||||
("update_plan".to_string(), "Base plan guidance.".to_string()),
|
||||
]),
|
||||
..Default::default()
|
||||
}),
|
||||
profile: Some("custom".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
cfg.profiles.insert(
|
||||
"custom".to_string(),
|
||||
ConfigProfile {
|
||||
tools: Some(ToolsToml {
|
||||
description_overrides: BTreeMap::from([
|
||||
(
|
||||
"spawn_agent".to_string(),
|
||||
"Profile spawn guidance.".to_string(),
|
||||
),
|
||||
(
|
||||
"wait_agent".to_string(),
|
||||
"Profile wait guidance.".to_string(),
|
||||
),
|
||||
]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let codex_home = tempdir()?;
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.path().to_path_buf(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
config.tool_description_overrides,
|
||||
BTreeMap::from([
|
||||
(
|
||||
"spawn_agent".to_string(),
|
||||
"Profile spawn guidance.".to_string(),
|
||||
),
|
||||
("update_plan".to_string(), "Base plan guidance.".to_string()),
|
||||
(
|
||||
"wait_agent".to_string(),
|
||||
"Profile wait guidance.".to_string()
|
||||
),
|
||||
])
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_provider_auth_with_env_key() {
|
||||
let err = toml::from_str::<ConfigToml>(
|
||||
@@ -4517,6 +4605,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
include_apply_patch_tool: false,
|
||||
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
|
||||
web_search_config: None,
|
||||
tool_description_overrides: BTreeMap::new(),
|
||||
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
@@ -4662,6 +4751,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
include_apply_patch_tool: false,
|
||||
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
|
||||
web_search_config: None,
|
||||
tool_description_overrides: BTreeMap::new(),
|
||||
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
@@ -4805,6 +4895,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
include_apply_patch_tool: false,
|
||||
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
|
||||
web_search_config: None,
|
||||
tool_description_overrides: BTreeMap::new(),
|
||||
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
@@ -4934,6 +5025,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
include_apply_patch_tool: false,
|
||||
web_search_mode: Constrained::allow_any(WebSearchMode::Cached),
|
||||
web_search_config: None,
|
||||
tool_description_overrides: BTreeMap::new(),
|
||||
use_experimental_unified_exec_tool: !cfg!(windows),
|
||||
background_terminal_max_timeout: DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS,
|
||||
ghost_snapshot: GhostSnapshotConfig::default(),
|
||||
|
||||
@@ -510,6 +510,9 @@ pub struct Config {
|
||||
/// Additional parameters for the web search tool when it is enabled.
|
||||
pub web_search_config: Option<WebSearchConfig>,
|
||||
|
||||
/// Tool descriptions keyed by tool name. Overrides only apply to enabled tools.
|
||||
pub tool_description_overrides: BTreeMap<String, String>,
|
||||
|
||||
/// If set to `true`, used only the experimental unified exec tool.
|
||||
pub use_experimental_unified_exec_tool: bool,
|
||||
|
||||
@@ -1279,6 +1282,20 @@ fn resolve_web_search_config(
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_tool_description_overrides(
|
||||
config_toml: &ConfigToml,
|
||||
config_profile: &ConfigProfile,
|
||||
) -> BTreeMap<String, String> {
|
||||
let mut overrides = BTreeMap::new();
|
||||
if let Some(tools) = &config_toml.tools {
|
||||
overrides.extend(tools.description_overrides.clone());
|
||||
}
|
||||
if let Some(tools) = &config_profile.tools {
|
||||
overrides.extend(tools.description_overrides.clone());
|
||||
}
|
||||
overrides
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_web_search_mode_for_turn(
|
||||
web_search_mode: &Constrained<WebSearchMode>,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
@@ -1607,6 +1624,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 tool_description_overrides = resolve_tool_description_overrides(&cfg, &config_profile);
|
||||
|
||||
let agent_roles =
|
||||
agent_roles::load_agent_roles(&cfg, &config_layer_stack, &mut startup_warnings)?;
|
||||
@@ -2037,6 +2055,7 @@ impl Config {
|
||||
include_apply_patch_tool: include_apply_patch_tool_flag,
|
||||
web_search_mode: constrained_web_search_mode.value,
|
||||
web_search_config,
|
||||
tool_description_overrides,
|
||||
use_experimental_unified_exec_tool,
|
||||
background_terminal_max_timeout,
|
||||
ghost_snapshot,
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
@@ -110,6 +111,7 @@ pub struct ToolsConfig {
|
||||
pub agent_jobs_tools: bool,
|
||||
pub agent_jobs_worker_tools: bool,
|
||||
pub agent_type_description: String,
|
||||
pub tool_description_overrides: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
pub struct ToolsConfigParams<'a> {
|
||||
@@ -225,6 +227,7 @@ impl ToolsConfig {
|
||||
agent_jobs_tools: include_agent_jobs,
|
||||
agent_jobs_worker_tools,
|
||||
agent_type_description: String::new(),
|
||||
tool_description_overrides: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +236,14 @@ impl ToolsConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_tool_description_overrides(
|
||||
mut self,
|
||||
tool_description_overrides: BTreeMap<String, String>,
|
||||
) -> Self {
|
||||
self.tool_description_overrides = tool_description_overrides;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_allow_login_shell(mut self, allow_login_shell: bool) -> Self {
|
||||
self.allow_login_shell = allow_login_shell;
|
||||
self
|
||||
|
||||
@@ -491,6 +491,22 @@ pub fn build_tool_registry_plan(
|
||||
}
|
||||
}
|
||||
|
||||
if !config.tool_description_overrides.is_empty() {
|
||||
for configured_tool in &mut plan.specs {
|
||||
let Some(override_description) = config
|
||||
.tool_description_overrides
|
||||
.get(configured_tool.name())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(description) = configured_tool.spec.description_mut() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
*description = override_description.clone();
|
||||
}
|
||||
}
|
||||
|
||||
plan
|
||||
}
|
||||
|
||||
|
||||
@@ -179,6 +179,74 @@ fn test_build_specs_collab_tools_enabled() {
|
||||
assert!(!properties.contains_key("fork_turns"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_description_override_replaces_enabled_tool_description() {
|
||||
let model_info = model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::Collab);
|
||||
let available_models = Vec::new();
|
||||
let mut overrides = BTreeMap::new();
|
||||
overrides.insert(
|
||||
"spawn_agent".to_string(),
|
||||
"Use this custom spawn guidance.".to_string(),
|
||||
);
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
available_models: &available_models,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
session_source: SessionSource::Cli,
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
})
|
||||
.with_tool_description_overrides(overrides);
|
||||
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
);
|
||||
let ToolSpec::Function(ResponsesApiTool { description, .. }) =
|
||||
&find_tool(&tools, "spawn_agent").spec
|
||||
else {
|
||||
panic!("expected spawn_agent function tool");
|
||||
};
|
||||
|
||||
assert_eq!(description, "Use this custom spawn guidance.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_description_override_for_disabled_tool_is_ignored() {
|
||||
let model_info = model_info();
|
||||
let features = Features::with_defaults();
|
||||
let available_models = Vec::new();
|
||||
let mut overrides = BTreeMap::new();
|
||||
overrides.insert(
|
||||
"spawn_agents_on_csv".to_string(),
|
||||
"Use this custom batch guidance.".to_string(),
|
||||
);
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
available_models: &available_models,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
session_source: SessionSource::Cli,
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
})
|
||||
.with_tool_description_overrides(overrides);
|
||||
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
);
|
||||
|
||||
assert_lacks_tool_name(&tools, "spawn_agents_on_csv");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_specs_multi_agent_v2_uses_task_names_and_hides_resume() {
|
||||
let model_info = model_info();
|
||||
@@ -1660,6 +1728,43 @@ fn code_mode_only_exec_description_includes_full_nested_tool_details() {
|
||||
assert!(description.contains("### `view_image` (`view_image`)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_only_exec_description_uses_nested_tool_description_overrides() {
|
||||
let model_info = model_info();
|
||||
let mut features = Features::with_defaults();
|
||||
features.enable(Feature::CodeMode);
|
||||
features.enable(Feature::CodeModeOnly);
|
||||
let available_models = Vec::new();
|
||||
let mut overrides = BTreeMap::new();
|
||||
overrides.insert(
|
||||
"update_plan".to_string(),
|
||||
"Use this custom plan guidance.".to_string(),
|
||||
);
|
||||
let tools_config = ToolsConfig::new(&ToolsConfigParams {
|
||||
model_info: &model_info,
|
||||
available_models: &available_models,
|
||||
features: &features,
|
||||
web_search_mode: Some(WebSearchMode::Cached),
|
||||
session_source: SessionSource::Cli,
|
||||
sandbox_policy: &SandboxPolicy::DangerFullAccess,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
})
|
||||
.with_tool_description_overrides(overrides);
|
||||
|
||||
let (tools, _) = build_specs(
|
||||
&tools_config,
|
||||
/*mcp_tools*/ None,
|
||||
/*app_tools*/ None,
|
||||
&[],
|
||||
);
|
||||
let ToolSpec::Freeform(FreeformTool { description, .. }) = &find_tool(&tools, "exec").spec
|
||||
else {
|
||||
panic!("expected freeform tool");
|
||||
};
|
||||
|
||||
assert!(description.contains("Use this custom plan guidance."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_exec_description_omits_nested_tool_details_when_not_code_mode_only() {
|
||||
let model_info = model_info();
|
||||
|
||||
@@ -64,6 +64,17 @@ impl ToolSpec {
|
||||
ToolSpec::Freeform(tool) => tool.name.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description_mut(&mut self) -> Option<&mut String> {
|
||||
match self {
|
||||
ToolSpec::Function(tool) => Some(&mut tool.description),
|
||||
ToolSpec::ToolSearch { description, .. } => Some(description),
|
||||
ToolSpec::Freeform(tool) => Some(&mut tool.description),
|
||||
ToolSpec::LocalShell {}
|
||||
| ToolSpec::ImageGeneration { .. }
|
||||
| ToolSpec::WebSearch { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_local_shell_tool() -> ToolSpec {
|
||||
|
||||
@@ -40,6 +40,18 @@ When Codex knows which client started the turn, the legacy notify JSON payload a
|
||||
|
||||
The generated JSON Schema for `config.toml` lives at `codex-rs/core/config.schema.json`.
|
||||
|
||||
## Tool Description Overrides
|
||||
|
||||
Tool descriptions can be overridden by tool name under `[tools.description_overrides]`.
|
||||
Overrides only apply when that tool is enabled for the current turn; entries for
|
||||
disabled tools are ignored.
|
||||
|
||||
```toml
|
||||
[tools.description_overrides]
|
||||
spawn_agent = "Use these custom spawn instructions."
|
||||
update_plan = "Use these custom planning instructions."
|
||||
```
|
||||
|
||||
## SQLite State DB
|
||||
|
||||
Codex stores the SQLite-backed state DB under `sqlite_home` (config key) or the
|
||||
|
||||
Reference in New Issue
Block a user