Skip to main content

tokitai_macros/
lib.rs

1//! # Tokitai Macros
2//!
3//! **Procedural macros for Tokitai - Zero-dependency macro for AI tool integration**
4//!
5//! This crate provides the `#[tool]` procedural macro that enables compile-time tool definitions
6//! for AI/LLM tool calling systems. It generates all the boilerplate code needed to expose
7//! your Rust functions as AI-callable tools.
8//!
9//! ## 🎯 Key Features
10//!
11//! - **Zero Runtime Dependencies** - The macro itself has no runtime overhead
12//! - **Compile-time Generation** - Tool definitions are generated at compile time
13//! - **Type Safety** - Parameter validation happens at compile time
14//! - **Automatic Discovery** - Mark an `impl` block and all `pub` methods become tools
15//! - **Customizable** - Override tool names and descriptions via attributes
16//! - **Vendor Neutral** - Works with any AI/LLM provider (Ollama, OpenAI, Anthropic, etc.)
17//!
18//! ## Quick Start
19//!
20//! Add to your `Cargo.toml`:
21//!
22//! ```toml
23//! [dependencies]
24//! tokitai = "0.3.3"
25//! ```
26//!
27//! Then use the `#[tool]` macro:
28//!
29//! ```rust,ignore
30//! use tokitai::tool;
31//!
32//! pub struct Calculator;
33//!
34//! #[tool]
35//! impl Calculator {
36//!     /// Add two numbers together
37//!     pub async fn add(&self, a: i32, b: i32) -> i32 {
38//!         a + b
39//!     }
40//!
41//!     /// Multiply two numbers
42//!     pub async fn multiply(&self, a: i32, b: i32) -> i32 {
43//!         a * b
44//!     }
45//! }
46//!
47//! // Usage
48//! let calc = Calculator;
49//!
50//! // Get tool definitions (compile-time generated)
51//! let tools = Calculator::tool_definitions();
52//! println!("Number of tools: {}", tools.len());
53//!
54//! // Call a tool
55//! let result = calc.call_tool("add", &serde_json::json!({"a": 10, "b": 20})).unwrap();
56//! println!("Result: {}", result);  // 30
57//! ```
58//!
59//! ## How It Works
60//!
61//! The `#[tool]` macro automatically:
62//!
63//! 1. Extracts doc comments as tool descriptions
64//! 2. Generates JSON Schema for parameters from Rust types
65//! 3. Creates `TOOL_DEFINITIONS` constant with all tool metadata
66//! 4. Implements `call_tool` dispatcher for runtime invocation
67//! 5. Generates parameter parsing and validation code
68//!
69//! ## Customization
70//!
71//! You can customize tool names and descriptions using the attribute syntax:
72//!
73//! ```rust,ignore
74//! #[tool]
75//! impl MyTools {
76//!     #[tool(name = "fetch_url", desc = "Fetch content from a URL")]
77//!     pub async fn fetch(&self, url: String) -> String {
78//!         // implementation
79//!     }
80//! }
81//! ```
82//!
83//! ## Generated Code
84//!
85//! For each `#[tool]` impl block, the macro generates:
86//!
87//! ```rust,ignore
88//! // 1. Tool definitions constant
89//! impl Calculator {
90//!     pub const TOOL_DEFINITIONS: &'static [ToolDefinition] = &[
91//!         ToolDefinition {
92//!             name: "add",
93//!             description: "Add two numbers together",
94//!             input_schema: "{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"integer\"},\"b\":{\"type\":\"integer\"}},\"required\":[\"a\",\"b\"]}",
95//!         },
96//!         // ... more tools
97//!     ];
98//! }
99//!
100//! // 2. ToolProvider trait implementation
101//! impl ToolProvider for Calculator {
102//!     fn tool_definitions() -> &'static [ToolDefinition] {
103//!         Self::tool_definitions()
104//!     }
105//! }
106//!
107//! // 3. call_tool dispatcher
108//! impl Calculator {
109//!     pub fn call_tool(&self, name: &str, args: &Value) -> Result<Value, ToolError> {
110//!         match name {
111//!             "add" => self.__call_add(args).await,
112//!             "multiply" => self.__call_multiply(args).await,
113//!             _ => Err(ToolError::not_found(format!("Unknown tool: {}", name))),
114//!         }
115//!     }
116//! }
117//! ```
118//!
119//! ## Type Mapping
120//!
121//! Rust types are automatically mapped to JSON Schema types with full recursive support:
122//!
123//! ### Basic Types
124//!
125//! | Rust Type | JSON Schema Type |
126//! |-----------|------------------|
127//! | `String`, `str` | `string` |
128//! | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `isize` | `integer` |
129//! | `f32`, `f64` | `number` |
130//! | `bool` | `boolean` |
131//!
132//! ### Compound Types
133//!
134//! | Rust Type | JSON Schema Type |
135//! |-----------|------------------|
136//! | `Vec<T>` | `array` with `items` schema |
137//! | `[T; N]` | `array` with `items` schema |
138//! | `&[T]` | `array` with `items` schema |
139//! | `HashMap<K, V>` | `object` with `additionalProperties` |
140//! | `Option<T>` | `anyOf` with inner type and `null` |
141//! | `(T, U, ...)` | `array` (tuple representation) |
142//!
143//! ### Third-party Types
144//!
145//! | Rust Type | JSON Schema Type |
146//! |-----------|------------------|
147//! | `chrono::DateTime<Utc>` | `string` with `format: date-time` |
148//! | `chrono::NaiveDateTime` | `string` with `format: date-time` |
149//! | `uuid::Uuid` | `string` with `format: uuid` |
150//! | `url::Url` | `string` with `format: uri` |
151//! | `PathBuf`, `Path` | `string` with `format: file-path` |
152//!
153//! ### Custom Types
154//!
155//! Custom structs are represented as `object` types. For full field-level schema generation,
156//! ensure your struct derives `serde::Deserialize` and the macro will handle it at runtime.
157//!
158//! ## Requirements
159//!
160//! - **Rust Version**: 1.70+
161//! - **Edition**: 2021
162//!
163//! ## License
164//!
165//! Licensed under either of:
166//!
167//! - Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/silverenternal/tokitai/blob/main/LICENSE))
168//! - MIT License ([LICENSE-MIT](https://github.com/silverenternal/tokitai/blob/main/LICENSE))
169//!
170//! at your option.
171//!
172//! ## Contributing
173//!
174//! Unless you explicitly state otherwise, any contribution intentionally submitted
175//! for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be
176//! dual licensed as above, without any additional terms or conditions.
177//!
178//! ## See Also
179//!
180//! - [`tokitai`](https://crates.io/crates/tokitai) - Main crate with runtime support
181//! - [`tokitai-core`](https://crates.io/crates/tokitai-core) - Core types and traits
182
183mod tool;
184
185use proc_macro::TokenStream;
186
187/// # `#[tool]` Attribute Macro
188///
189/// Marks an `impl` block or individual methods as AI-callable tools.
190///
191/// ## Usage
192///
193/// ### 1. Mark an impl block (Recommended)
194///
195/// When placed on an `impl` block, all `pub` methods are automatically registered as tools:
196///
197/// ```rust,ignore
198/// pub struct Calculator;
199///
200/// #[tool]
201/// impl Calculator {
202///     /// Add two numbers together
203///     pub async fn add(&self, a: i32, b: i32) -> i32 {
204///         a + b
205///     }
206/// }
207/// ```
208///
209/// ### 2. Mark individual methods
210///
211/// Use `#[tool(...)]` on specific methods to customize tool properties:
212///
213/// ```rust,ignore
214/// #[tool]
215/// impl Calculator {
216///     #[tool(name = "add_numbers", desc = "Add two numbers together")]
217///     pub async fn add(&self, a: i32, b: i32) -> i32 {
218///         a + b
219///     }
220///
221///     /// This method won't be registered as a tool
222///     fn helper(&self) {}
223/// }
224/// ```
225///
226/// ### 3. Mark individual parameters
227///
228/// Use `#[tool_attr(...)]` on specific parameters to customize parameter properties:
229///
230/// ```rust,ignore
231/// #[tool]
232/// impl Calculator {
233///     pub async fn add(
234///         &self,
235///         #[tool_attr(required)] a: Option<i32>,  // Option but required
236///         #[tool_attr(default = "0")] b: Option<i32>,  // Has default value
237///     ) -> i32 {
238///         a.unwrap_or(0) + b.unwrap_or(0)
239///     }
240/// }
241/// ```
242///
243/// ## Generated Code
244///
245/// The macro generates:
246///
247/// 1. `const TOOL_DEFINITIONS: &'static [ToolDefinition]` - Compile-time tool definitions
248/// 2. `fn call_tool(&self, name: &str, args: &Value) -> Result<Value, ToolError>` - Tool dispatcher
249/// 3. Wrapper functions for each tool with JSON parameter parsing
250///
251/// ## Features
252///
253/// - βœ… Zero runtime dependencies (macro itself)
254/// - βœ… Compile-time tool definition generation
255/// - βœ… Automatic description extraction from doc comments
256/// - βœ… Custom tool names and descriptions support
257/// - βœ… Type-safe parameter parsing
258/// - βœ… Recursive type resolution for complex types
259/// - βœ… JSON Schema generation with proper formatting
260#[proc_macro_attribute]
261pub fn tool(attr: TokenStream, item: TokenStream) -> TokenStream {
262    tool::tool(attr, item)
263}
264
265/// # `#[tool_type]` Attribute Macro
266///
267/// Registers a custom type with a manually defined JSON schema.
268///
269/// Use this macro when you have custom struct types that the `#[tool]` macro cannot automatically parse.
270/// This allows you to provide explicit schema information for AI tool calling.
271///
272/// ## Usage
273///
274/// ```rust,ignore
275/// use tokitai::tool_type;
276///
277/// #[tool_type(
278///     name = "Location",
279///     properties = "latitude: number, longitude: number",
280///     required = "latitude, longitude"
281/// )]
282/// pub struct Location {
283///     pub latitude: f64,
284///     pub longitude: f64,
285/// }
286/// ```
287///
288/// ## Attributes
289///
290/// - `name`: The type name (required)
291/// - `properties`: Comma-separated list of `field_name: type` pairs
292///   - Supported types: `string`, `integer`, `number`, `boolean`, `array`, `object`
293/// - `required`: Comma-separated list of required field names
294///
295/// ## Generated Schema
296///
297/// The above example generates:
298///
299/// ```json
300/// {
301///   "type": "object",
302///   "properties": {
303///     "latitude": { "type": "number" },
304///     "longitude": { "type": "number" }
305///   },
306///   "required": ["latitude", "longitude"]
307/// }
308/// ```
309#[proc_macro_attribute]
310pub fn tool_type(attr: TokenStream, item: TokenStream) -> TokenStream {
311    tool::tool_type(attr, item)
312}
313
314/// Parameter validation attribute (used internally by #[tool] macro)
315///
316/// This attribute is automatically processed by the `#[tool]` macro.
317/// It should not be used directly by users - instead use the `#[tool(...)]` syntax on parameters.
318///
319/// ## Example (internal usage)
320///
321/// ```text
322/// #[tool]
323/// impl MyTools {
324///     pub fn create_user(
325///         &self,
326///         #[tool_validate = "!value.is_empty()")]
327///         name: String,
328///     ) -> Result<String, Error> {
329///         // ...
330///     }
331/// }
332/// ```
333#[proc_macro_attribute]
334pub fn tool_validate(_attr: TokenStream, item: TokenStream) -> TokenStream {
335    // This is a no-op attribute, processed by #[tool] macro
336    item
337}
338
339/// Parameter transformation attribute (used internally by #[tool] macro)
340///
341/// This attribute is automatically processed by the `#[tool]` macro.
342/// It should not be used directly by users - instead use the `#[tool(...)]` syntax on parameters.
343///
344/// ## Example (internal usage)
345///
346/// ```text
347/// #[tool]
348/// impl MyTools {
349///     pub fn create_user(
350///         &self,
351///         #[tool_transform = "value.to_lowercase()")]
352///         email: String,
353///     ) -> Result<String, Error> {
354///         // ...
355///     }
356/// }
357/// ```
358#[proc_macro_attribute]
359pub fn tool_transform(_attr: TokenStream, item: TokenStream) -> TokenStream {
360    // This is a no-op attribute, processed by #[tool] macro
361    item
362}
363
364/// Parameter description attribute (used internally by #[tool] macro)
365#[proc_macro_attribute]
366pub fn tool_desc(_attr: TokenStream, item: TokenStream) -> TokenStream {
367    item
368}
369
370/// Parameter example attribute (used internally by #[tool] macro)
371#[proc_macro_attribute]
372pub fn tool_example(_attr: TokenStream, item: TokenStream) -> TokenStream {
373    item
374}
375
376/// Parameter default attribute (used internally by #[tool] macro)
377#[proc_macro_attribute]
378pub fn tool_default(_attr: TokenStream, item: TokenStream) -> TokenStream {
379    item
380}
381
382/// Parameter required attribute (used internally by #[tool] macro)
383#[proc_macro_attribute]
384pub fn tool_required(_attr: TokenStream, item: TokenStream) -> TokenStream {
385    item
386}
387
388/// Parameter min attribute (used internally by #[tool] macro)
389#[proc_macro_attribute]
390pub fn tool_min(_attr: TokenStream, item: TokenStream) -> TokenStream {
391    item
392}
393
394/// Parameter max attribute (used internally by #[tool] macro)
395#[proc_macro_attribute]
396pub fn tool_max(_attr: TokenStream, item: TokenStream) -> TokenStream {
397    item
398}
399
400/// Parameter min_length attribute (used internally by #[tool] macro)
401#[proc_macro_attribute]
402pub fn tool_min_length(_attr: TokenStream, item: TokenStream) -> TokenStream {
403    item
404}
405
406/// Parameter max_length attribute (used internally by #[tool] macro)
407#[proc_macro_attribute]
408pub fn tool_max_length(_attr: TokenStream, item: TokenStream) -> TokenStream {
409    item
410}
411
412/// Parameter pattern attribute (used internally by #[tool] macro)
413#[proc_macro_attribute]
414pub fn tool_pattern(_attr: TokenStream, item: TokenStream) -> TokenStream {
415    item
416}
417
418/// Parameter min_items attribute (used internally by #[tool] macro)
419#[proc_macro_attribute]
420pub fn tool_min_items(_attr: TokenStream, item: TokenStream) -> TokenStream {
421    item
422}
423
424/// Parameter max_items attribute (used internally by #[tool] macro)
425#[proc_macro_attribute]
426pub fn tool_max_items(_attr: TokenStream, item: TokenStream) -> TokenStream {
427    item
428}
429
430/// Parameter multiple_of attribute (used internally by #[tool] macro)
431#[proc_macro_attribute]
432pub fn tool_multiple_of(_attr: TokenStream, item: TokenStream) -> TokenStream {
433    item
434}
435
436/// Parameter-level tool attributes (helper macro for #[tool])
437///
438/// This attribute is used to add validation, transformation, and other metadata
439/// to individual parameters. It is automatically processed by the `#[tool]` macro.
440///
441/// ## Usage
442///
443/// ```rust,ignore
444/// #[tool]
445/// impl MyTools {
446///     pub fn create_user(
447///         &self,
448///         #[param_tool(validate = "!value.is_empty()", desc = "Name cannot be empty")]
449///         name: String,
450///         #[param_tool(transform = "value.to_lowercase()")]
451///         email: String,
452///         #[param_tool(default = 10)]
453///         count: i32,
454///     ) -> Result<String, Error> {
455///         // ...
456///     }
457/// }
458/// ```
459///
460/// ## Supported Attributes
461///
462/// - `validate = "expression"` - Validation expression (use `value` to refer to the parameter)
463/// - `transform = "expression"` - Transformation expression (use `value` to refer to the parameter)
464/// - `desc = "description"` - Parameter description
465/// - `default = value` - Default value (supports literals: integers, floats, booleans, strings, arrays, objects)
466/// - `example = value` - Example value
467/// - `required` - Mark parameter as required (even if Option type)
468#[proc_macro_attribute]
469pub fn param_tool(_attr: TokenStream, item: TokenStream) -> TokenStream {
470    // This is a no-op attribute, processed by #[tool] macro
471    item
472}
473
474/// # `tokitai!` Configuration Macro
475///
476/// Used to centrally configure tool properties without modifying original code.
477///
478/// ## Usage
479///
480/// ```rust,ignore
481/// tokitai::config! {
482///     MyService {
483///         get_user: {
484///             desc: "θŽ·ε–η”¨ζˆ·δΏ‘ζ―",
485///             tags: ["user", "read"],
486///             params: {
487///                 id: {
488///                     desc: "η”¨ζˆ·ε”―δΈ€ζ ‡θ―†",
489///                     example: "1001"
490///                 }
491///             }
492///         }
493///     }
494/// }
495/// ```
496///
497/// ## Features
498///
499/// - Override method descriptions
500/// - Add tags to methods
501/// - Configure parameter-level descriptions and examples
502/// - Works with existing `#[tool]` annotated code
503///
504/// ## Priority
505///
506/// 1. `#[tool(desc = "...")]` > doc comments
507/// 2. `tokitai!` config > `#[tool]` attributes
508/// 3. Parameter-level: `#[param_tool]` > default inference
509#[proc_macro]
510pub fn config(item: TokenStream) -> TokenStream {
511    tool::config(item)
512}