Skip to main content

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}