mod apply_code_action_tool; mod context_server_registry; mod copy_path_tool; mod create_directory_tool; mod delete_path_tool; mod diagnostics_tool; mod edit_file_tool; mod edit_session; #[cfg(all(test, feature = "unit-eval"))] mod evals; mod fetch_tool; mod find_path_tool; mod find_references_tool; mod get_code_actions_tool; mod go_to_definition_tool; mod grep_tool; mod list_directory_tool; mod move_path_tool; mod now_tool; mod open_tool; mod read_file_tool; mod rename_tool; mod spawn_agent_tool; mod symbol_locator; mod terminal_tool; mod tool_permissions; mod update_plan_tool; mod web_search_tool; mod write_file_tool; use crate::AgentTool; use language_model::{LanguageModelRequestTool, LanguageModelToolSchemaFormat}; use serde::{ Deserialize, Deserializer, de::{DeserializeOwned, Error as _}, }; /// Deserialize a value that may have been provided as a JSON-encoded string /// instead of the structured value. Some models occasionally stringify nested /// arguments, so we accept either form. pub(crate) fn deserialize_maybe_stringified<'de, T, D>(deserializer: D) -> Result where T: DeserializeOwned, D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum ValueOrJsonString { Value(T), String(String), } match ValueOrJsonString::::deserialize(deserializer)? { ValueOrJsonString::Value(value) => Ok(value), ValueOrJsonString::String(string) => serde_json::from_str::(&string).map_err(|error| { D::Error::custom(format!("failed to parse stringified value: {error}")) }), } } pub use apply_code_action_tool::*; pub use context_server_registry::*; pub use copy_path_tool::*; pub use create_directory_tool::*; pub use delete_path_tool::*; pub use diagnostics_tool::*; pub use edit_file_tool::*; pub use fetch_tool::*; pub use find_path_tool::*; pub use find_references_tool::*; pub use get_code_actions_tool::*; pub use go_to_definition_tool::*; pub use grep_tool::*; pub use list_directory_tool::*; pub use move_path_tool::*; pub use now_tool::*; pub use open_tool::*; pub use read_file_tool::*; pub use rename_tool::*; pub use spawn_agent_tool::*; pub use symbol_locator::*; pub use terminal_tool::*; pub use tool_permissions::*; pub use update_plan_tool::*; pub use web_search_tool::*; pub use write_file_tool::*; macro_rules! tools { ($($tool:ty),* $(,)?) => { /// Every built-in tool name, determined at compile time. pub const ALL_TOOL_NAMES: &[&str] = &[ $(<$tool>::NAME,)* ]; const _: () = { const fn str_eq(a: &str, b: &str) -> bool { let a = a.as_bytes(); let b = b.as_bytes(); if a.len() != b.len() { return false; } let mut i = 0; while i < a.len() { if a[i] != b[i] { return false; } i += 1; } true } const NAMES: &[&str] = ALL_TOOL_NAMES; let mut i = 0; while i < NAMES.len() { let mut j = i + 1; while j < NAMES.len() { if str_eq(NAMES[i], NAMES[j]) { panic!("Duplicate tool name in tools! macro"); } j += 1; } i += 1; } }; /// Returns whether the tool with the given name supports the given provider. pub fn tool_supports_provider(name: &str, provider: &language_model::LanguageModelProviderId) -> bool { $( if name == <$tool>::NAME { return <$tool>::supports_provider(provider); } )* false } /// A list of all built-in tools pub fn built_in_tools() -> impl Iterator { fn language_model_tool() -> LanguageModelRequestTool { LanguageModelRequestTool { name: T::NAME.to_string(), description: T::description().to_string(), input_schema: T::input_schema(LanguageModelToolSchemaFormat::JsonSchema).to_value(), use_input_streaming: T::supports_input_streaming(), } } [ $( language_model_tool::<$tool>(), )* ] .into_iter() } }; } tools! { ApplyCodeActionTool, CopyPathTool, CreateDirectoryTool, DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, FindReferencesTool, GetCodeActionsTool, GoToDefinitionTool, GrepTool, ListDirectoryTool, MovePathTool, NowTool, OpenTool, ReadFileTool, RenameTool, SpawnAgentTool, TerminalTool, UpdatePlanTool, WebSearchTool, WriteFileTool, }