mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Keep Code Mode tool discovery guidance stable across catalog changes (#50562)
## Why Tool availability can change between calls. Conditioning discovery guidance on whether deferred tools are currently present changes the `exec` description when the catalog changes. ## What changed Always include the configured discovery guidance in the Code Mode `exec` description, even when no deferred tools are present. The default guidance explains how to find tools in `ALL_TOOLS` and notes that availability can change between calls. Custom guidance and explicit empty overrides remain supported. ## Testing Add regression coverage for stable guidance with empty and populated deferred tool catalogs in both Code Mode configurations, including custom and empty overrides. Update tool-description assertions and request snapshots. GitOrigin-RevId: 086ea19a48e3c51ec8c429a619676a936d658721
This commit is contained in:
@@ -14,7 +14,9 @@ use crate::json_schema_types::render_json_schema_to_typescript_with_budget;
|
||||
|
||||
const MAX_JS_SAFE_INTEGER: u64 = (1_u64 << 53) - 1;
|
||||
const DEFERRED_NESTED_TOOLS_GUIDANCE: &str = r#"Some deferred nested tools may be omitted from this description. They are still available on the global `tools` object and listed in `ALL_TOOLS`.
|
||||
To find one, filter `ALL_TOOLS` by `name` and `description`."#;
|
||||
To find one, filter `ALL_TOOLS` by `name` and `description`.
|
||||
|
||||
Tool availability can change between calls."#;
|
||||
const LEGACY_IMAGE_HELPER_DESCRIPTION: &str = r#"`image(imageUrlOrItem: string | { image_url: string; detail?: "auto" | "low" | "high" | "original" | null } | ImageContent, detail?: "auto" | "low" | "high" | "original" | null)`: Appends an image item. `image_url` should be a base64-encoded `data:` URL. To forward an MCP tool image, pass an individual `ImageContent` block from `result.content`, for example `image(result.content[0])`. MCP image blocks may request detail with `_meta: { "codex/imageDetail": "original" }`. When provided, the second `detail` argument overrides any detail embedded in the first argument."#;
|
||||
const UNIFIED_IMAGE_HELPER_DESCRIPTION: &str = r#"`image(imageUrlOrItem: string | { image_url: string } | ImageContent)`: Appends an image item. `image_url` should be a base64-encoded `data:` URL. To forward an MCP tool image, pass an individual `ImageContent` block from `result.content`, for example `image(result.content[0])`."#;
|
||||
const EXEC_DESCRIPTION_TEMPLATE: &str = r#"Run JavaScript code to orchestrate/compose tool calls
|
||||
@@ -268,7 +270,7 @@ pub enum ImageDetailVisibility {
|
||||
|
||||
pub fn build_exec_tool_description(
|
||||
enabled_tools: &[ToolDefinition],
|
||||
deferred_tools: &[ToolDefinition],
|
||||
_deferred_tools: &[ToolDefinition],
|
||||
namespace_descriptions: &BTreeMap<String, ToolNamespaceDescription>,
|
||||
default_exec_yield_time_ms: u64,
|
||||
code_mode_only: bool,
|
||||
@@ -292,13 +294,11 @@ pub fn build_exec_tool_description(
|
||||
if !description.is_empty() {
|
||||
sections.push(description);
|
||||
}
|
||||
if !deferred_tools.is_empty() {
|
||||
let guidance = messages
|
||||
.and_then(|messages| messages.deferred_nested_tools_guidance.as_deref())
|
||||
.unwrap_or(DEFERRED_NESTED_TOOLS_GUIDANCE);
|
||||
if !guidance.is_empty() {
|
||||
sections.push(guidance.to_string());
|
||||
}
|
||||
let guidance = messages
|
||||
.and_then(|messages| messages.deferred_nested_tools_guidance.as_deref())
|
||||
.unwrap_or(DEFERRED_NESTED_TOOLS_GUIDANCE);
|
||||
if !guidance.is_empty() {
|
||||
sections.push(guidance.to_string());
|
||||
}
|
||||
if !code_mode_only {
|
||||
return sections.join("\n\n");
|
||||
|
||||
@@ -46,7 +46,7 @@ fn exec_override_renders_only_known_literal_placeholders() {
|
||||
assert_eq!(
|
||||
description,
|
||||
format!(
|
||||
" \nDefaults to 10000 ms. 4567 ms.\n{image_helper}\n{{{{ unknown }}}} {{{{default_exec_yield_time_ms}}}}\t "
|
||||
" \nDefaults to 10000 ms. 4567 ms.\n{image_helper}\n{{{{ unknown }}}} {{{{default_exec_yield_time_ms}}}}\t \n\n{DEFERRED_NESTED_TOOLS_GUIDANCE}"
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -77,6 +77,7 @@ fn exec_override_preserves_empty_and_whitespace_only_text() {
|
||||
),
|
||||
[
|
||||
description_override,
|
||||
"No deferred tools; omit this.",
|
||||
"Shared MCP Types:\n```ts\nNo MCP tools; omit this.\n```"
|
||||
]
|
||||
.into_iter()
|
||||
@@ -208,3 +209,47 @@ fn mcp_types_stay_stable_when_a_deferred_tool_changes_its_output_schema() {
|
||||
assert_eq!(before, render(&tool, preamble));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_guidance_survives_catalog_changes_and_respects_overrides() {
|
||||
let deferred_tools = [ToolDefinition {
|
||||
name: "deferred_echo".to_string(),
|
||||
tool_name: ToolName::plain("deferred_echo"),
|
||||
description: "Echo a value.".to_string(),
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
input_schema_max_bytes: None,
|
||||
output_schema: None,
|
||||
}];
|
||||
let render = |tools: &[ToolDefinition], code_mode_only, guidance: Option<&str>| {
|
||||
build_exec_tool_description(
|
||||
&[],
|
||||
tools,
|
||||
&BTreeMap::new(),
|
||||
crate::DEFAULT_EXEC_YIELD_TIME_MS,
|
||||
code_mode_only,
|
||||
ImageDetailVisibility::Visible,
|
||||
Some(&CodeModeToolMessages {
|
||||
exec: Some(ToolMessage {
|
||||
description: Some(String::new()),
|
||||
..Default::default()
|
||||
}),
|
||||
deferred_nested_tools_guidance: guidance.map(str::to_string),
|
||||
mcp_typescript_preamble: Some(String::new()),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
};
|
||||
for code_mode_only in [false, true] {
|
||||
for (guidance, expected) in [
|
||||
(None, DEFERRED_NESTED_TOOLS_GUIDANCE),
|
||||
(Some("Catalog discovery."), "Catalog discovery."),
|
||||
(Some(""), ""),
|
||||
] {
|
||||
let prompt = render(&[], code_mode_only, guidance);
|
||||
assert_eq!(prompt, expected);
|
||||
assert_eq!(prompt, render(&deferred_tools, code_mode_only, guidance));
|
||||
}
|
||||
}
|
||||
assert!(DEFERRED_NESTED_TOOLS_GUIDANCE.contains("Tool availability can change between calls"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user