mirror of
https://github.com/openai/codex.git
synced 2026-05-03 04:42:20 +03:00
Thread tool origin into code mode rendering
This commit is contained in:
@@ -49,6 +49,14 @@ pub enum CodeModeToolKind {
|
||||
Freeform,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ToolOrigin {
|
||||
Native,
|
||||
Mcp,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ToolDefinition {
|
||||
pub name: String,
|
||||
@@ -56,6 +64,7 @@ pub struct ToolDefinition {
|
||||
pub kind: CodeModeToolKind,
|
||||
pub input_schema: Option<JsonValue>,
|
||||
pub output_schema: Option<JsonValue>,
|
||||
pub origin: ToolOrigin,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -185,7 +194,9 @@ pub fn build_exec_tool_description(
|
||||
if !enabled_tools.is_empty() {
|
||||
let mut current_namespace: Option<&str> = None;
|
||||
let mut nested_tool_sections = Vec::with_capacity(enabled_tools.len());
|
||||
let include_shared_mcp_types = enabled_tools.iter().any(is_mcp_tool);
|
||||
let include_shared_mcp_types = enabled_tools
|
||||
.iter()
|
||||
.any(|tool| tool.origin == ToolOrigin::Mcp);
|
||||
|
||||
if include_shared_mcp_types {
|
||||
sections.push(format!(
|
||||
@@ -196,7 +207,7 @@ pub fn build_exec_tool_description(
|
||||
|
||||
for tool in enabled_tools {
|
||||
let name = tool.name.as_str();
|
||||
let nested_description = append_code_mode_sample_for_definition(tool);
|
||||
let nested_description = render_code_mode_sample_for_definition(tool);
|
||||
let next_namespace = namespace_descriptions
|
||||
.get(name)
|
||||
.map(|namespace_description| namespace_description.name.as_str());
|
||||
@@ -216,10 +227,11 @@ pub fn build_exec_tool_description(
|
||||
let global_name = normalize_code_mode_identifier(name);
|
||||
let nested_description = nested_description.trim();
|
||||
if nested_description.is_empty() {
|
||||
nested_tool_sections.push(format!("### `{global_name}` (`{name}`)"));
|
||||
nested_tool_sections.push(render_tool_heading(&global_name, name));
|
||||
} else {
|
||||
nested_tool_sections.push(format!(
|
||||
"### `{global_name}` (`{name}`)\n{nested_description}"
|
||||
"{}\n{nested_description}",
|
||||
render_tool_heading(&global_name, name)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -261,7 +273,7 @@ pub fn normalize_code_mode_identifier(tool_key: &str) -> String {
|
||||
|
||||
pub fn augment_tool_definition(mut definition: ToolDefinition) -> ToolDefinition {
|
||||
if definition.name != PUBLIC_TOOL_NAME {
|
||||
definition.description = append_code_mode_sample_for_definition(&definition);
|
||||
definition.description = render_code_mode_sample_for_definition(&definition);
|
||||
}
|
||||
definition
|
||||
}
|
||||
@@ -283,7 +295,7 @@ pub struct EnabledToolMetadata {
|
||||
pub kind: CodeModeToolKind,
|
||||
}
|
||||
|
||||
pub fn append_code_mode_sample(
|
||||
pub fn render_code_mode_sample(
|
||||
description: &str,
|
||||
tool_name: &str,
|
||||
input_name: &str,
|
||||
@@ -297,7 +309,7 @@ pub fn append_code_mode_sample(
|
||||
format!("{description}\n\nexec tool declaration:\n```ts\n{declaration}\n```")
|
||||
}
|
||||
|
||||
fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String {
|
||||
fn render_code_mode_sample_for_definition(definition: &ToolDefinition) -> String {
|
||||
let input_name = match definition.kind {
|
||||
CodeModeToolKind::Function => "args",
|
||||
CodeModeToolKind::Freeform => "input",
|
||||
@@ -310,7 +322,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
CodeModeToolKind::Freeform => "string".to_string(),
|
||||
};
|
||||
if is_mcp_tool(definition) {
|
||||
if definition.origin == ToolOrigin::Mcp {
|
||||
let structured_content_type = definition
|
||||
.output_schema
|
||||
.as_ref()
|
||||
@@ -323,7 +335,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
|
||||
format!("mcp_result<{structured_content_type}>")
|
||||
};
|
||||
|
||||
return append_code_mode_sample(
|
||||
return render_code_mode_sample(
|
||||
&definition.description,
|
||||
&definition.name,
|
||||
input_name,
|
||||
@@ -337,7 +349,7 @@ fn append_code_mode_sample_for_definition(definition: &ToolDefinition) -> String
|
||||
.as_ref()
|
||||
.map(render_json_schema_to_typescript)
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
append_code_mode_sample(
|
||||
render_code_mode_sample(
|
||||
&definition.description,
|
||||
&definition.name,
|
||||
input_name,
|
||||
@@ -356,6 +368,14 @@ fn render_code_mode_tool_declaration(
|
||||
format!("{tool_name}({input_name}: {input_type}): Promise<{output_type}>;")
|
||||
}
|
||||
|
||||
fn render_tool_heading(global_name: &str, raw_name: &str) -> String {
|
||||
if global_name == raw_name {
|
||||
format!("### `{global_name}`")
|
||||
} else {
|
||||
format!("### `{global_name}` (`{raw_name}`)")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_json_schema_to_typescript(schema: &JsonValue) -> String {
|
||||
render_json_schema_to_typescript_inner(schema)
|
||||
}
|
||||
@@ -369,13 +389,6 @@ fn extract_mcp_structured_content_schema(output_schema: &JsonValue) -> Option<&J
|
||||
)
|
||||
}
|
||||
|
||||
fn is_mcp_tool(definition: &ToolDefinition) -> bool {
|
||||
// MCP tools are qualified in `mcp_connection_manager::qualify_tools` as
|
||||
// `mcp__<server>__<tool>`. Code mode nested tool specs do not include the
|
||||
// separate `codex_apps` app-tool registration path.
|
||||
definition.name.starts_with("mcp__")
|
||||
}
|
||||
|
||||
fn render_mcp_typescript_preamble() -> &'static str {
|
||||
concat!(
|
||||
"type mcp_output = { [key: string]: unknown; };\n",
|
||||
@@ -614,6 +627,7 @@ mod tests {
|
||||
use super::ParsedExecSource;
|
||||
use super::ToolDefinition;
|
||||
use super::ToolNamespaceDescription;
|
||||
use super::ToolOrigin;
|
||||
use super::augment_tool_definition;
|
||||
use super::build_exec_tool_description;
|
||||
use super::normalize_code_mode_identifier;
|
||||
@@ -675,6 +689,7 @@ mod tests {
|
||||
"properties": { "ok": { "type": "boolean" } },
|
||||
"required": ["ok"]
|
||||
})),
|
||||
origin: ToolOrigin::Native,
|
||||
};
|
||||
|
||||
let description = augment_tool_definition(definition).description;
|
||||
@@ -719,6 +734,7 @@ mod tests {
|
||||
},
|
||||
"required": ["forecast"]
|
||||
})),
|
||||
origin: ToolOrigin::Native,
|
||||
};
|
||||
|
||||
let description = augment_tool_definition(definition).description;
|
||||
@@ -742,11 +758,13 @@ mod tests {
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
output_schema: None,
|
||||
origin: ToolOrigin::Native,
|
||||
}],
|
||||
&BTreeMap::new(),
|
||||
/*code_mode_only*/ true,
|
||||
);
|
||||
assert!(description.contains("### `foo` (`foo`)"));
|
||||
assert!(description.contains("### `foo`
|
||||
bar"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -781,15 +799,33 @@ mod tests {
|
||||
name: "mcp__sample__alpha".to_string(),
|
||||
description: "First tool".to_string(),
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
output_schema: None,
|
||||
input_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
output_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
origin: ToolOrigin::Mcp,
|
||||
},
|
||||
ToolDefinition {
|
||||
name: "mcp__sample__beta".to_string(),
|
||||
description: "Second tool".to_string(),
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
output_schema: None,
|
||||
input_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
output_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
origin: ToolOrigin::Mcp,
|
||||
},
|
||||
],
|
||||
&namespace_descriptions,
|
||||
@@ -800,11 +836,21 @@ mod tests {
|
||||
r#"## mcp__sample
|
||||
Shared namespace guidance.
|
||||
|
||||
### `mcp__sample__alpha` (`mcp__sample__alpha`)
|
||||
### `mcp__sample__alpha`
|
||||
First tool
|
||||
|
||||
### `mcp__sample__beta` (`mcp__sample__beta`)
|
||||
Second tool"#
|
||||
exec tool declaration:
|
||||
```ts
|
||||
declare const tools: { mcp__sample__alpha(args: {}): Promise<mcp_result>; };
|
||||
```
|
||||
|
||||
### `mcp__sample__beta`
|
||||
Second tool
|
||||
|
||||
exec tool declaration:
|
||||
```ts
|
||||
declare const tools: { mcp__sample__beta(args: {}): Promise<mcp_result>; };
|
||||
```"#
|
||||
));
|
||||
}
|
||||
|
||||
@@ -822,15 +868,24 @@ Second tool"#
|
||||
name: "mcp__sample__alpha".to_string(),
|
||||
description: "First tool".to_string(),
|
||||
kind: CodeModeToolKind::Function,
|
||||
input_schema: None,
|
||||
output_schema: None,
|
||||
input_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
output_schema: Some(json!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"additionalProperties": false
|
||||
})),
|
||||
origin: ToolOrigin::Mcp,
|
||||
}],
|
||||
&namespace_descriptions,
|
||||
/*code_mode_only*/ true,
|
||||
);
|
||||
|
||||
assert!(!description.contains("## mcp__sample"));
|
||||
assert!(description.contains("### `mcp__sample__alpha` (`mcp__sample__alpha`)"));
|
||||
assert!(description.contains("### `mcp__sample__alpha`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -867,6 +922,7 @@ Second tool"#
|
||||
"required": ["content"],
|
||||
"additionalProperties": false
|
||||
})),
|
||||
origin: ToolOrigin::Mcp,
|
||||
});
|
||||
let second_tool = augment_tool_definition(ToolDefinition {
|
||||
name: "mcp__sample__beta".to_string(),
|
||||
@@ -900,6 +956,7 @@ Second tool"#
|
||||
"required": ["content"],
|
||||
"additionalProperties": false
|
||||
})),
|
||||
origin: ToolOrigin::Mcp,
|
||||
});
|
||||
|
||||
let description = build_exec_tool_description(
|
||||
@@ -910,6 +967,7 @@ Second tool"#
|
||||
kind: first_tool.kind,
|
||||
input_schema: first_tool.input_schema,
|
||||
output_schema: first_tool.output_schema,
|
||||
origin: first_tool.origin,
|
||||
},
|
||||
ToolDefinition {
|
||||
name: second_tool.name,
|
||||
@@ -917,6 +975,7 @@ Second tool"#
|
||||
kind: second_tool.kind,
|
||||
input_schema: second_tool.input_schema,
|
||||
output_schema: second_tool.output_schema,
|
||||
origin: second_tool.origin,
|
||||
},
|
||||
],
|
||||
&BTreeMap::new(),
|
||||
|
||||
@@ -7,13 +7,14 @@ pub use description::CODE_MODE_PRAGMA_PREFIX;
|
||||
pub use description::CodeModeToolKind;
|
||||
pub use description::ToolDefinition;
|
||||
pub use description::ToolNamespaceDescription;
|
||||
pub use description::append_code_mode_sample;
|
||||
pub use description::ToolOrigin;
|
||||
pub use description::augment_tool_definition;
|
||||
pub use description::build_exec_tool_description;
|
||||
pub use description::build_wait_tool_description;
|
||||
pub use description::is_code_mode_nested_tool;
|
||||
pub use description::normalize_code_mode_identifier;
|
||||
pub use description::parse_exec_source;
|
||||
pub use description::render_code_mode_sample;
|
||||
pub use description::render_json_schema_to_typescript;
|
||||
pub use response::FunctionCallOutputContentItem;
|
||||
pub use response::ImageDetail;
|
||||
|
||||
Reference in New Issue
Block a user