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 = 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}