mirror of
https://github.com/openai/codex.git
synced 2026-05-05 22:01:37 +03:00
Render namespace description for tools (#16879)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user