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::new();
41//! tools.register("greet", "Greets a person", |name: String| async move {
42//! format!("Hello, {}!", name)
43//! })?;
44//! # Ok(())
45//! # }
46//! ```
47
48// Re-export core functionality
49pub use tools_core::{
50 DeserializationError, FunctionCall, FunctionDecl, FunctionResponse, ToolCollection, ToolError,
51 ToolMetadata, ToolRegistration, TypeSignature,
52 CallId
53};
54
55// Re-export schema functionality (trait from tools_core)
56pub use tools_core::ToolSchema;
57
58// Re-export macros (both tool attribute and ToolSchema derive)
59pub use tools_macros::{ToolSchema, tool};
60
61/// Convenient imports for common usage patterns.
62///
63/// Import everything you typically need with:
64/// ```rust
65/// use tools_rs::prelude::*;
66/// ```
67pub mod prelude;
68
69/// Collect all tools registered via the `#[tool]` macro.
70///
71/// This function discovers all tools that were registered at compile time
72/// using the `#[tool]` attribute macro.
73///
74/// # Example
75///
76/// ```rust
77/// use tools_rs::{collect_tools, list_tool_names};
78///
79/// let tools = collect_tools();
80/// println!("Available tools: {:?}", list_tool_names(&tools));
81/// ```
82#[inline]
83pub fn collect_tools() -> ToolCollection {
84 ToolCollection::collect_tools()
85}
86
87/// Generate function declarations in JSON format for LLM consumption.
88///
89/// This is equivalent to `collect_tools().json()` but provides a more
90/// convenient API for the common use case of generating LLM-compatible
91/// function declarations.
92///
93/// # Example
94///
95/// ```rust
96/// use tools_rs::function_declarations;
97///
98/// let declarations = function_declarations()?;
99/// // Send to LLM for function calling
100/// # Ok::<(), tools_rs::ToolError>(())
101/// ```
102#[inline]
103pub fn function_declarations() -> Result<serde_json::Value, ToolError> {
104 collect_tools().json()
105}
106
107/// Call a tool by name with JSON arguments.
108///
109/// This is a convenience function that combines tool collection and execution
110/// in a single call. Useful for simple scenarios where you don't need to
111/// manage the tool collection yourself.
112///
113/// # Arguments
114///
115/// * `name` - The name of the tool to call
116/// * `arguments` - JSON value containing the arguments
117///
118/// # Example
119///
120/// ```rust
121/// use tools_rs::call_tool;
122/// use serde_json::json;
123///
124/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
125/// let result = call_tool("add", json!({"a": 1, "b": 2})).await?;
126/// # Ok(())
127/// # }
128/// ```
129pub async fn call_tool(
130 name: &str,
131 arguments: serde_json::Value,
132) -> Result<FunctionResponse, ToolError> {
133 let tools = collect_tools();
134 let call = FunctionCall::new(name.to_string(), arguments);
135 tools.call(call).await
136}
137
138/// Call a tool by name with typed arguments.
139///
140/// This function provides a more ergonomic API for calling tools when you
141/// have typed arguments that can be serialized to JSON.
142///
143/// # Arguments
144///
145/// * `name` - The name of the tool to call
146/// * `args` - Arguments that implement `serde::Serialize`
147///
148/// # Example
149///
150/// ```rust
151/// use tools_rs::call_tool_with;
152/// use serde::Serialize;
153///
154/// #[derive(Serialize)]
155/// struct AddArgs { a: i32, b: i32 }
156///
157/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
158/// let result = call_tool_with("add", &AddArgs { a: 1, b: 2 }).await?;
159/// # Ok(())
160/// # }
161/// ```
162pub async fn call_tool_with<T: serde::Serialize>(
163 name: &str,
164 args: &T,
165) -> Result<FunctionResponse, ToolError> {
166 let arguments = serde_json::to_value(args)
167 .map_err(|e| ToolError::Runtime(format!("Failed to serialize arguments: {}", e)))?;
168 call_tool(name, arguments).await
169}
170
171/// Call a tool by name with JSON arguments on a given collection.
172///
173/// # Example
174///
175/// ```rust
176/// use tools_rs::{collect_tools, call_tool_by_name};
177/// use serde_json::json;
178///
179/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
180/// let tools = collect_tools();
181/// let result = call_tool_by_name(&tools, "add", json!([1, 2])).await?;
182/// # Ok(())
183/// # }
184/// ```
185pub async fn call_tool_by_name(
186 collection: &ToolCollection,
187 name: &str,
188 arguments: serde_json::Value,
189) -> Result<FunctionResponse, ToolError> {
190 let call = FunctionCall::new(name.to_string(), arguments);
191 collection.call(call).await
192}
193
194/// Call a tool by name with typed arguments on a given collection.
195///
196/// # Example
197///
198/// ```rust
199/// use tools_rs::{collect_tools, call_tool_with_args};
200///
201/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
202/// let tools = collect_tools();
203/// let result = call_tool_with_args(&tools, "add", &[1, 2]).await?;
204/// # Ok(())
205/// # }
206/// ```
207pub async fn call_tool_with_args<T: serde::Serialize>(
208 collection: &ToolCollection,
209 name: &str,
210 args: &T,
211) -> Result<FunctionResponse, ToolError> {
212 let arguments = serde_json::to_value(args)
213 .map_err(|e| ToolError::Runtime(format!("Failed to serialize arguments: {}", e)))?;
214 call_tool_by_name(collection, name, arguments).await
215}
216
217/// List all available tool names in a collection.
218///
219/// # Example
220///
221/// ```rust
222/// use tools_rs::{collect_tools, list_tool_names};
223///
224/// let tools = collect_tools();
225/// let names = list_tool_names(&tools);
226/// println!("Available tools: {:?}", names);
227/// ```
228pub fn list_tool_names(collection: &ToolCollection) -> Vec<&'static str> {
229 collection.descriptions().map(|(name, _)| name).collect()
230}
231
232#[cfg(test)]
233mod tests {
234 #[test]
235 fn test_prelude_exports() {
236 // Ensure prelude exports don't cause compilation errors
237 use crate::prelude::*;
238 let _tools = collect_tools();
239 }
240}