Render namespace description for tools (#16879)

This commit is contained in:
Vivian Fang
2026-04-08 02:39:40 -07:00
committed by GitHub
parent 9091999c83
commit d47b755aa2
19 changed files with 323 additions and 54 deletions

View File

@@ -96,6 +96,7 @@ pub fn create_wait_tool() -> ToolSpec {
pub fn create_code_mode_tool(
enabled_tools: &[(String, String)],
namespace_descriptions: &BTreeMap<String, codex_code_mode::ToolNamespaceDescription>,
code_mode_only_enabled: bool,
) -> ToolSpec {
const CODE_MODE_FREEFORM_GRAMMAR: &str = r#"
@@ -112,6 +113,7 @@ SOURCE: /[\s\S]+/
name: codex_code_mode::PUBLIC_TOOL_NAME.to_string(),
description: codex_code_mode::build_exec_tool_description(
enabled_tools,
namespace_descriptions,
code_mode_only_enabled,
),
format: FreeformToolFormat {

View File

@@ -20,10 +20,14 @@ fn augment_tool_spec_for_code_mode_augments_function_tools() {
description: "Look up an order".to_string(),
strict: false,
defer_loading: Some(true),
parameters: JsonSchema::object(BTreeMap::from([(
parameters: JsonSchema::object(
BTreeMap::from([(
"order_id".to_string(),
JsonSchema::string(/*description*/ None),
)]), Some(vec!["order_id".to_string()]), Some(AdditionalProperties::Boolean(false))),
)]),
Some(vec!["order_id".to_string()]),
Some(AdditionalProperties::Boolean(false))
),
output_schema: Some(json!({
"type": "object",
"properties": {
@@ -34,13 +38,23 @@ fn augment_tool_spec_for_code_mode_augments_function_tools() {
})),
ToolSpec::Function(ResponsesApiTool {
name: "lookup_order".to_string(),
description: "Look up an order\n\nexec tool declaration:\n```ts\ndeclare const tools: { lookup_order(args: { order_id: string; }): Promise<{ ok: boolean; }>; };\n```".to_string(),
description: r#"Look up an order
exec tool declaration:
```ts
declare const tools: { lookup_order(args: { order_id: string; }): Promise<{ ok: boolean; }>; };
```"#
.to_string(),
strict: false,
defer_loading: Some(true),
parameters: JsonSchema::object(BTreeMap::from([(
parameters: JsonSchema::object(
BTreeMap::from([(
"order_id".to_string(),
JsonSchema::string(/*description*/ None),
)]), Some(vec!["order_id".to_string()]), Some(AdditionalProperties::Boolean(false))),
)]),
Some(vec!["order_id".to_string()]),
Some(AdditionalProperties::Boolean(false))
),
output_schema: Some(json!({
"type": "object",
"properties": {
@@ -92,7 +106,13 @@ fn tool_spec_to_code_mode_tool_definition_returns_augmented_nested_tools() {
tool_spec_to_code_mode_tool_definition(&spec),
Some(codex_code_mode::ToolDefinition {
name: "apply_patch".to_string(),
description: "Apply a patch\n\nexec tool declaration:\n```ts\ndeclare const tools: { apply_patch(input: string): Promise<unknown>; };\n```".to_string(),
description: r#"Apply a patch
exec tool declaration:
```ts
declare const tools: { apply_patch(input: string): Promise<unknown>; };
```"#
.to_string(),
kind: codex_code_mode::CodeModeToolKind::Freeform,
input_schema: None,
output_schema: None,
@@ -165,11 +185,16 @@ fn create_code_mode_tool_matches_expected_spec() {
let enabled_tools = vec![("update_plan".to_string(), "Update the plan".to_string())];
assert_eq!(
create_code_mode_tool(&enabled_tools, /*code_mode_only_enabled*/ true),
create_code_mode_tool(
&enabled_tools,
&BTreeMap::new(),
/*code_mode_only_enabled*/ true,
),
ToolSpec::Freeform(FreeformTool {
name: codex_code_mode::PUBLIC_TOOL_NAME.to_string(),
description: codex_code_mode::build_exec_tool_description(
&enabled_tools,
&BTreeMap::new(),
/*code_mode_only*/ true
),
format: FreeformToolFormat {

View File

@@ -114,6 +114,7 @@ pub use tool_discovery::filter_tool_suggest_discoverable_tools_for_client;
pub use tool_registry_plan::build_tool_registry_plan;
pub use tool_registry_plan_types::ToolHandlerKind;
pub use tool_registry_plan_types::ToolHandlerSpec;
pub use tool_registry_plan_types::ToolNamespace;
pub use tool_registry_plan_types::ToolRegistryPlan;
pub use tool_registry_plan_types::ToolRegistryPlanAppTool;
pub use tool_registry_plan_types::ToolRegistryPlanParams;

View File

@@ -61,6 +61,7 @@ use crate::tool_registry_plan_types::agent_type_description;
use codex_protocol::openai_models::ApplyPatchToolType;
use codex_protocol::openai_models::ConfigShellToolType;
use rmcp::model::Tool as McpTool;
use std::collections::BTreeMap;
pub fn build_tool_registry_plan(
config: &ToolsConfig,
@@ -70,6 +71,20 @@ pub fn build_tool_registry_plan(
let exec_permission_approvals_enabled = config.exec_permission_approvals_enabled;
if config.code_mode_enabled {
let namespace_descriptions = params
.tool_namespaces
.into_iter()
.flatten()
.map(|(name, detail)| {
(
name.clone(),
codex_code_mode::ToolNamespaceDescription {
name: detail.name.clone(),
description: detail.description.clone().unwrap_or_default(),
},
)
})
.collect::<BTreeMap<_, _>>();
let nested_config = config.for_code_mode_nested_tools();
let nested_plan = build_tool_registry_plan(
&nested_config,
@@ -78,7 +93,7 @@ pub fn build_tool_registry_plan(
..params
},
);
let enabled_tools = collect_code_mode_tool_definitions(
let mut enabled_tools = collect_code_mode_tool_definitions(
nested_plan
.specs
.iter()
@@ -87,8 +102,15 @@ pub fn build_tool_registry_plan(
.into_iter()
.map(|tool| (tool.name, tool.description))
.collect::<Vec<_>>();
enabled_tools.sort_by(|(left_name, _), (right_name, _)| {
compare_code_mode_tool_names(left_name, right_name, &namespace_descriptions)
});
plan.push_spec(
create_code_mode_tool(&enabled_tools, config.code_mode_only_enabled),
create_code_mode_tool(
&enabled_tools,
&namespace_descriptions,
config.code_mode_only_enabled,
),
/*supports_parallel_tool_calls*/ false,
config.code_mode_enabled,
);
@@ -494,6 +516,41 @@ pub fn build_tool_registry_plan(
plan
}
fn compare_code_mode_tool_names(
left_name: &str,
right_name: &str,
namespace_descriptions: &BTreeMap<String, codex_code_mode::ToolNamespaceDescription>,
) -> std::cmp::Ordering {
let left_namespace = code_mode_namespace_name(left_name, namespace_descriptions);
let right_namespace = code_mode_namespace_name(right_name, namespace_descriptions);
left_namespace
.cmp(&right_namespace)
.then_with(|| {
code_mode_function_name(left_name, left_namespace)
.cmp(code_mode_function_name(right_name, right_namespace))
})
.then_with(|| left_name.cmp(right_name))
}
fn code_mode_namespace_name<'a>(
name: &str,
namespace_descriptions: &'a BTreeMap<String, codex_code_mode::ToolNamespaceDescription>,
) -> Option<&'a str> {
namespace_descriptions
.get(name)
.map(|namespace_description| namespace_description.name.as_str())
}
fn code_mode_function_name<'a>(name: &'a str, namespace: Option<&str>) -> &'a str {
namespace
.and_then(|namespace| {
name.strip_prefix(namespace)
.and_then(|suffix| suffix.strip_prefix("__"))
})
.unwrap_or(name)
}
#[cfg(test)]
#[path = "tool_registry_plan_tests.rs"]
mod tests;

View File

@@ -11,6 +11,7 @@ use crate::ResponsesApiTool;
use crate::ResponsesApiWebSearchFilters;
use crate::ResponsesApiWebSearchUserLocation;
use crate::ToolHandlerSpec;
use crate::ToolNamespace;
use crate::ToolRegistryPlanAppTool;
use crate::ToolsConfigParams;
use crate::WaitAgentTimeoutOptions;
@@ -1332,6 +1333,7 @@ fn tool_suggest_is_not_registered_without_feature_flag() {
&tools_config,
/*mcp_tools*/ None,
/*app_tools*/ None,
/*tool_namespaces*/ None,
Some(vec![discoverable_connector(
"connector_2128aebfecb84f64a069897515042a44",
"Google Calendar",
@@ -1371,6 +1373,7 @@ fn tool_suggest_can_be_registered_without_search_tool() {
&tools_config,
/*mcp_tools*/ None,
/*app_tools*/ None,
/*tool_namespaces*/ None,
Some(vec![discoverable_connector(
"connector_2128aebfecb84f64a069897515042a44",
"Google Calendar",
@@ -1438,6 +1441,7 @@ fn tool_suggest_description_lists_discoverable_tools() {
&tools_config,
/*mcp_tools*/ None,
/*app_tools*/ None,
/*tool_namespaces*/ None,
Some(discoverable_tools),
&[],
);
@@ -1501,6 +1505,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() {
let model_info = model_info();
let mut features = Features::with_defaults();
features.enable(Feature::CodeMode);
features.enable(Feature::CodeModeOnly);
features.enable(Feature::UnifiedExec);
let available_models = Vec::new();
let tools_config = ToolsConfig::new(&ToolsConfigParams {
@@ -1542,7 +1547,12 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() {
assert_eq!(
description,
"Echo text\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__sample__echo(args: { message: string; }): Promise<{ _meta?: unknown; content: Array<unknown>; isError?: boolean; structuredContent?: unknown; }>; };\n```"
r#"Echo text
exec tool declaration:
```ts
declare const tools: { mcp__sample__echo(args: { message: string; }): Promise<{ _meta?: unknown; content: Array<unknown>; isError?: boolean; structuredContent?: unknown; }>; };
```"#
);
}
@@ -1789,6 +1799,7 @@ fn build_specs<'a>(
config,
mcp_tools,
app_tools,
/*tool_namespaces*/ None,
/*discoverable_tools*/ None,
dynamic_tools,
)
@@ -1798,6 +1809,25 @@ fn build_specs_with_discoverable_tools<'a>(
config: &ToolsConfig,
mcp_tools: Option<HashMap<String, rmcp::model::Tool>>,
app_tools: Option<Vec<ToolRegistryPlanAppTool<'a>>>,
tool_namespaces: Option<HashMap<String, ToolNamespace>>,
discoverable_tools: Option<Vec<DiscoverableTool>>,
dynamic_tools: &[DynamicToolSpec],
) -> (Vec<ConfiguredToolSpec>, Vec<ToolHandlerSpec>) {
build_specs_with_optional_tool_namespaces(
config,
mcp_tools,
tool_namespaces,
app_tools,
discoverable_tools,
dynamic_tools,
)
}
fn build_specs_with_optional_tool_namespaces<'a>(
config: &ToolsConfig,
mcp_tools: Option<HashMap<String, rmcp::model::Tool>>,
tool_namespaces: Option<HashMap<String, ToolNamespace>>,
app_tools: Option<Vec<ToolRegistryPlanAppTool<'a>>>,
discoverable_tools: Option<Vec<DiscoverableTool>>,
dynamic_tools: &[DynamicToolSpec],
) -> (Vec<ConfiguredToolSpec>, Vec<ToolHandlerSpec>) {
@@ -1805,6 +1835,7 @@ fn build_specs_with_discoverable_tools<'a>(
config,
ToolRegistryPlanParams {
mcp_tools: mcp_tools.as_ref(),
tool_namespaces: tool_namespaces.as_ref(),
app_tools: app_tools.as_deref(),
discoverable_tools: discoverable_tools.as_deref(),
dynamic_tools,

View File

@@ -58,6 +58,7 @@ pub struct ToolRegistryPlan {
#[derive(Debug, Clone, Copy)]
pub struct ToolRegistryPlanParams<'a> {
pub mcp_tools: Option<&'a HashMap<String, McpTool>>,
pub tool_namespaces: Option<&'a HashMap<String, ToolNamespace>>,
pub app_tools: Option<&'a [ToolRegistryPlanAppTool<'a>]>,
pub discoverable_tools: Option<&'a [DiscoverableTool]>,
pub dynamic_tools: &'a [DynamicToolSpec],
@@ -66,6 +67,12 @@ pub struct ToolRegistryPlanParams<'a> {
pub codex_apps_mcp_server_name: &'a str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolNamespace {
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy)]
pub struct ToolRegistryPlanAppTool<'a> {
pub tool_name: &'a str,