mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Move tool specs into core handlers (#21416)
## Why This is the first mechanical slice of moving tool spec ownership toward the handlers. `codex-tools` should keep shared primitives and conversion helpers, while builtin tool specs and registration planning live in `codex-core` with the handlers that own those tools. Keeping this PR to relocation and import updates isolates the copy/move review from the later logic change that wires specs through registered handlers. ## What changed - Moved builtin tool spec constructors from `codex-rs/tools/src` into `codex-rs/core/src/tools/handlers/*_spec.rs` or nearby core tool modules. - Moved the registry planning code into `codex-rs/core/src/tools/spec_plan.rs` and its associated types/tests into core. - Kept shared primitives in `codex-tools`, including `ToolSpec`, schema/types, discovery/config primitives, dynamic/MCP conversion helpers, and code-mode collection helpers. - Updated handlers that referenced moved argument types or tool-name constants to use the core spec modules. - Moved spec tests next to the moved spec modules. ## Verification - `cargo check -p codex-tools` - `cargo check -p codex-core` - `cargo test -p codex-tools` - `cargo test -p codex-core _spec::tests` - `cargo test -p codex-core tools::spec_plan::tests` - `just fix -p codex-tools` - `just fix -p codex-core` Note: I also tried the broader `cargo test -p codex-core tools::`; it reached the moved spec-plan/spec tests successfully, then aborted with a stack overflow in `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`, which is outside this spec relocation.
This commit is contained in:
@@ -1,146 +1,7 @@
|
||||
use super::*;
|
||||
use crate::JsonSchema;
|
||||
use codex_app_server_protocol::AppInfo;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn create_tool_search_tool_deduplicates_and_renders_enabled_sources() {
|
||||
assert_eq!(
|
||||
create_tool_search_tool(
|
||||
&[
|
||||
ToolSearchSourceInfo {
|
||||
name: "Google Drive".to_string(),
|
||||
description: Some(
|
||||
"Use Google Drive as the single entrypoint for Drive, Docs, Sheets, and Slides work."
|
||||
.to_string(),
|
||||
),
|
||||
},
|
||||
ToolSearchSourceInfo {
|
||||
name: "Google Drive".to_string(),
|
||||
description: None,
|
||||
},
|
||||
ToolSearchSourceInfo {
|
||||
name: "docs".to_string(),
|
||||
description: None,
|
||||
},
|
||||
],
|
||||
/*default_limit*/ 8,
|
||||
),
|
||||
ToolSpec::ToolSearch {
|
||||
execution: "client".to_string(),
|
||||
description: "# Tool discovery\n\nSearches over deferred tool metadata with BM25 and exposes matching tools for the next model call.\n\nYou have access to tools from the following sources:\n- Google Drive: Use Google Drive as the single entrypoint for Drive, Docs, Sheets, and Slides work.\n- docs\nSome of the tools may not have been provided to you upfront, and you should use this tool (`tool_search`) to search for the required tools. For MCP tool discovery, always use `tool_search` instead of `list_mcp_resources` or `list_mcp_resource_templates`.".to_string(),
|
||||
parameters: JsonSchema::object(BTreeMap::from([
|
||||
(
|
||||
"limit".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tools to return (defaults to 8)."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"query".to_string(),
|
||||
JsonSchema::string(Some("Search query for deferred tools.".to_string()),),
|
||||
),
|
||||
]), Some(vec!["query".to_string()]), Some(false.into())),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_request_plugin_install_tool_uses_plugin_summary_fallback() {
|
||||
let expected_description = concat!(
|
||||
"# Request plugin/connector install\n\n",
|
||||
"Use this tool only to ask the user to install one known plugin or connector from the list below. The list contains known candidates that are not currently installed.\n\n",
|
||||
"Use this ONLY when all of the following are true:\n",
|
||||
"- The user explicitly asks to use a specific plugin or connector that is not already available in the current context or active `tools` list.\n",
|
||||
"- `tool_search` is not available, or it has already been called and did not find or make the requested tool callable.\n",
|
||||
"- The plugin or connector is one of the known installable plugins or connectors listed below. Only ask to install plugins or connectors from this list.\n\n",
|
||||
"Do not use this tool for adjacent capabilities, broad recommendations, or tools that merely seem useful. Only use when the user explicitly asks to use that exact listed plugin or connector.\n\n",
|
||||
"Known plugins/connectors available to install:\n",
|
||||
"- GitHub (id: `github`, type: plugin, action: install): skills; MCP servers: github-mcp; app connectors: github-app\n",
|
||||
"- Slack (id: `slack@openai-curated`, type: connector, action: install): No description provided.\n\n",
|
||||
"Workflow:\n\n",
|
||||
"1. Check the current context and active `tools` list first. If current active tools aren't relevant and `tool_search` is available, only call this tool after `tool_search` has already been tried and found no relevant tool.\n",
|
||||
"2. Match the user's explicit request against the known plugin/connector list above. Only proceed when one listed plugin or connector exactly fits.\n",
|
||||
"3. If we found both connectors and plugins to install, use plugins first, only use connectors if the corresponding plugin is installed but the connector is not.\n",
|
||||
"4. If one plugin or connector clearly fits, call `request_plugin_install` with:\n",
|
||||
" - `tool_type`: `connector` or `plugin`\n",
|
||||
" - `action_type`: `install`\n",
|
||||
" - `tool_id`: exact id from the known plugin/connector list above\n",
|
||||
" - `suggest_reason`: concise one-line user-facing reason this plugin or connector can help with the current request\n",
|
||||
"5. After the request flow completes:\n",
|
||||
" - if the user finished the install flow, continue by searching again or using the newly available plugin or connector\n",
|
||||
" - if the user did not finish, continue without that plugin or connector, and don't request it again unless the user explicitly asks for it.\n\n",
|
||||
"IMPORTANT: DO NOT call this tool in parallel with other tools.",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
create_request_plugin_install_tool(&[
|
||||
RequestPluginInstallEntry {
|
||||
id: "slack@openai-curated".to_string(),
|
||||
name: "Slack".to_string(),
|
||||
description: None,
|
||||
tool_type: DiscoverableToolType::Connector,
|
||||
has_skills: false,
|
||||
mcp_server_names: Vec::new(),
|
||||
app_connector_ids: Vec::new(),
|
||||
},
|
||||
RequestPluginInstallEntry {
|
||||
id: "github".to_string(),
|
||||
name: "GitHub".to_string(),
|
||||
description: None,
|
||||
tool_type: DiscoverableToolType::Plugin,
|
||||
has_skills: true,
|
||||
mcp_server_names: vec!["github-mcp".to_string()],
|
||||
app_connector_ids: vec!["github-app".to_string()],
|
||||
},
|
||||
]),
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: "request_plugin_install".to_string(),
|
||||
description: expected_description.to_string(),
|
||||
strict: false,
|
||||
defer_loading: None,
|
||||
parameters: JsonSchema::object(BTreeMap::from([
|
||||
(
|
||||
"action_type".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Suggested action for the tool. Use \"install\"."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"suggest_reason".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Concise one-line user-facing reason why this plugin or connector can help with the current request."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"tool_id".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Connector or plugin id to suggest."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"tool_type".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Type of discoverable tool to suggest. Use \"connector\" or \"plugin\"."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
]), Some(vec![
|
||||
"tool_type".to_string(),
|
||||
"action_type".to_string(),
|
||||
"tool_id".to_string(),
|
||||
"suggest_reason".to_string(),
|
||||
]), Some(false.into())),
|
||||
output_schema: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discoverable_tool_enums_use_expected_wire_names() {
|
||||
|
||||
Reference in New Issue
Block a user