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