tools_rs/lib.rs
1//! # Tools-rs: A Tool Collection and Execution Framework
2//!
3//! Tools-rs provides a framework for building, registering, and executing tools
4//! with automatic JSON schema generation for LLM integration.
5//!
6//! ## Quick Start
7//!
8//! ```rust
9//! use tools_rs::{tool, collect_tools, call_tool_with_args};
10//!
11//! #[tool]
12//! /// Adds two numbers together
13//! async fn add(a: i32, b: i32) -> i32 {
14//! a + b
15//! }
16//!
17//! #[tokio::main]
18//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
19//! let tools = collect_tools();
20//! let result = call_tool_with_args(&tools, "add", &[1, 2]).await?;
21//! println!("Result: {}", result);
22//! Ok(())
23//! }
24//! ```
25//!
26//! ## Features
27//!
28//! - **Automatic Registration**: Use `#[tool]` to automatically register functions
29//! - **JSON Schema Generation**: Automatic schema generation for LLM integration
30//! - **Type Safety**: Full type safety with JSON serialization at boundaries
31//! - **Async Support**: Built for async/await from the ground up
32//! - **Error Handling**: Comprehensive error types with context
33//!
34//! ## Manual Registration
35//!
36//! ```rust
37//! use tools_rs::ToolCollection;
38//!
39//! # fn example() -> Result<(), tools_rs::ToolError> {
40//! let mut tools: ToolCollection = ToolCollection::new();
41//! tools.register(
42//! "greet",
43//! "Greets a person",
44//! |name: String| async move { format!("Hello, {}!", name) },
45//! (),
46//! )?;
47//! # Ok(())
48//! # }
49//! ```
50
51// Re-export core functionality
52pub use tools_core::{
53 CallId, CollectionBuilder, DeserializationError, FunctionCall, FunctionDecl, FunctionResponse,
54 Language, RawToolDef, ToolCollection, ToolError, ToolMetadata, ToolRegistration, ToolsBuilder,
55 TypeSignature,
56};
57
58// Re-export schema functionality (trait from tools_core)
59pub use tools_core::ToolSchema;
60
61// Re-export macros (both tool attribute and ToolSchema derive)
62pub use tools_macros::{ToolSchema, tool};
63
64/// Convenient imports for common usage patterns.
65///
66/// Import everything you typically need with:
67/// ```rust
68/// use tools_rs::prelude::*;
69/// ```
70pub mod prelude;
71
72/// Collect all tools registered via the `#[tool]` macro.
73///
74/// This function discovers all tools that were registered at compile time
75/// using the `#[tool]` attribute macro.
76///
77/// # Example
78///
79/// ```rust
80/// use tools_rs::{collect_tools, list_tool_names};
81///
82/// let tools = collect_tools();
83/// println!("Available tools: {:?}", list_tool_names(&tools));
84/// ```
85#[inline]
86pub fn collect_tools() -> ToolCollection {
87 // `NoMeta` deserializes from any JSON object, so this never fails in
88 // practice — a panic here means the `#[tool]` macro emitted malformed
89 // JSON, which is a bug in tools-rs itself. Typed collections that can
90 // realistically fail use `ToolCollection::<M>::collect_tools()?`.
91 ToolCollection::collect_tools()
92 .expect("tools-rs internal error: macro emitted malformed meta_json")
93}
94
95/// Generate function declarations in JSON format for LLM consumption.
96///
97/// This is equivalent to `collect_tools().json()` but provides a more
98/// convenient API for the common use case of generating LLM-compatible
99/// function declarations.
100///
101/// # Example
102///
103/// ```rust
104/// use tools_rs::function_declarations;
105///
106/// let declarations = function_declarations()?;
107/// // Send to LLM for function calling
108/// # Ok::<(), tools_rs::ToolError>(())
109/// ```
110#[inline]
111pub fn function_declarations() -> Result<serde_json::Value, ToolError> {
112 collect_tools().json()
113}
114
115/// Call a tool by name with JSON arguments.
116///
117/// This is a convenience function that combines tool collection and execution
118/// in a single call. Useful for simple scenarios where you don't need to
119/// manage the tool collection yourself.
120///
121/// # Arguments
122///
123/// * `name` - The name of the tool to call
124/// * `arguments` - JSON value containing the arguments
125///
126/// # Example
127///
128/// ```rust
129/// use tools_rs::call_tool;
130/// use serde_json::json;
131///
132/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
133/// let result = call_tool("add", json!({"a": 1, "b": 2})).await?;
134/// # Ok(())
135/// # }
136/// ```
137pub async fn call_tool(
138 name: &str,
139 arguments: serde_json::Value,
140) -> Result<FunctionResponse, ToolError> {
141 let tools = collect_tools();
142 let call = FunctionCall::new(name.to_string(), arguments);
143 tools.call(call).await
144}
145
146/// Call a tool by name with typed arguments.
147///
148/// This function provides a more ergonomic API for calling tools when you
149/// have typed arguments that can be serialized to JSON.
150///
151/// # Arguments
152///
153/// * `name` - The name of the tool to call
154/// * `args` - Arguments that implement `serde::Serialize`
155///
156/// # Example
157///
158/// ```rust
159/// use tools_rs::call_tool_with;
160/// use serde::Serialize;
161///
162/// #[derive(Serialize)]
163/// struct AddArgs { a: i32, b: i32 }
164///
165/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
166/// let result = call_tool_with("add", &AddArgs { a: 1, b: 2 }).await?;
167/// # Ok(())
168/// # }
169/// ```
170pub async fn call_tool_with<T: serde::Serialize>(
171 name: &str,
172 args: &T,
173) -> Result<FunctionResponse, ToolError> {
174 let arguments = serde_json::to_value(args)
175 .map_err(|e| ToolError::Runtime(format!("Failed to serialize arguments: {}", e)))?;
176 call_tool(name, arguments).await
177}
178
179/// Call a tool by name with JSON arguments on a given collection.
180///
181/// # Example
182///
183/// ```rust
184/// use tools_rs::{collect_tools, call_tool_by_name};
185/// use serde_json::json;
186///
187/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
188/// let tools = collect_tools();
189/// let result = call_tool_by_name(&tools, "add", json!([1, 2])).await?;
190/// # Ok(())
191/// # }
192/// ```
193pub async fn call_tool_by_name(
194 collection: &ToolCollection,
195 name: &str,
196 arguments: serde_json::Value,
197) -> Result<FunctionResponse, ToolError> {
198 let call = FunctionCall::new(name.to_string(), arguments);
199 collection.call(call).await
200}
201
202/// Call a tool by name with typed arguments on a given collection.
203///
204/// # Example
205///
206/// ```rust
207/// use tools_rs::{collect_tools, call_tool_with_args};
208///
209/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
210/// let tools = collect_tools();
211/// let result = call_tool_with_args(&tools, "add", &[1, 2]).await?;
212/// # Ok(())
213/// # }
214/// ```
215pub async fn call_tool_with_args<T: serde::Serialize>(
216 collection: &ToolCollection,
217 name: &str,
218 args: &T,
219) -> Result<FunctionResponse, ToolError> {
220 let arguments = serde_json::to_value(args)
221 .map_err(|e| ToolError::Runtime(format!("Failed to serialize arguments: {}", e)))?;
222 call_tool_by_name(collection, name, arguments).await
223}
224
225/// List all available tool names in a collection.
226///
227/// # Example
228///
229/// ```rust
230/// use tools_rs::{collect_tools, list_tool_names};
231///
232/// let tools = collect_tools();
233/// let names = list_tool_names(&tools);
234/// println!("Available tools: {:?}", names);
235/// ```
236pub fn list_tool_names(collection: &ToolCollection) -> Vec<&'static str> {
237 collection.descriptions().map(|(name, _)| name).collect()
238}
239
240#[cfg(test)]
241mod tests {
242 #[test]
243 fn test_prelude_exports() {
244 // Ensure prelude exports don't cause compilation errors
245 use crate::prelude::*;
246 let _tools = collect_tools();
247 }
248}