114 lines
3.7 KiB
Rust
114 lines
3.7 KiB
Rust
//! Examples demonstrating the `#[tool]` procedural macro.
|
|
//!
|
|
//! The macro eliminates boilerplate for defining tools:
|
|
//! Instead of manually building `ToolDefinition` + `ToolSchema` + serde structures,
|
|
//! you write a typed Rust function and derive everything automatically.
|
|
//!
|
|
//! # Manual way (without macro)
|
|
//!
|
|
//! ```
|
|
//! use agent::{ToolDefinition, ToolParam, ToolRegistry, ToolSchema};
|
|
//! use serde_json::json;
|
|
//!
|
|
//! fn register_manual(registry: &mut ToolRegistry) {
|
|
//! registry.register_fn("search_issues", |_ctx, args| {
|
|
//! async move {
|
|
//! let args: serde_json::Value = args;
|
|
//! let title = args["title"].as_str().unwrap_or("");
|
|
//! Ok(json!([{ "title": title, "status": "open" }]))
|
|
//! }.boxed()
|
|
//! });
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! # With `#[tool]` macro (recommended)
|
|
//!
|
|
//! ```
|
|
//! use agent_tool_derive::tool;
|
|
//! use agent::{ToolDefinition, ToolRegistry, ToolError};
|
|
//!
|
|
//! #[tool(description = "Search issues by title", params(
|
|
//! title = "Issue title to search for",
|
|
//! status = "Filter by status (open/closed/all)"
|
|
//! ))]
|
|
//! async fn search_issues(
|
|
//! title: String,
|
|
//! status: Option<String>,
|
|
//! ) -> Result<Vec<serde_json::Value>, String> {
|
|
//! Ok(vec![serde_json::json!({
|
|
//! "title": title,
|
|
//! "status": status.unwrap_or_else(|| "open".to_string())
|
|
//! })])
|
|
//! }
|
|
//!
|
|
//! fn register_with_macro(registry: &mut ToolRegistry) {
|
|
//! register_search_issues(registry); // Generated by #[tool]
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! The macro generates:
|
|
//! - `SearchIssuesParameters` struct (serde Deserialize)
|
|
//! - `SEARCH_ISSUES_DEFINITION: ToolDefinition` constant
|
|
//! - `register_search_issues(registry: &mut ToolRegistry)` helper
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::{ToolDefinition, ToolError, ToolRegistry};
|
|
|
|
// Example: using the manual approach (without macro)
|
|
// This demonstrates the baseline — how it looks without #[tool]
|
|
#[test]
|
|
fn manual_tool_registration_shows_boilerplate() {
|
|
use futures::FutureExt;
|
|
|
|
let mut registry = ToolRegistry::new();
|
|
registry.register_fn("echo", |_ctx, args| {
|
|
async move {
|
|
let text: serde_json::Value = serde_json::from_value(args)
|
|
.map_err(|e| ToolError::ParseError(e.to_string()))?;
|
|
Ok(text)
|
|
}
|
|
.boxed()
|
|
});
|
|
assert_eq!(registry.len(), 1);
|
|
assert!(registry.get("echo").is_some());
|
|
}
|
|
|
|
// NOTE: To use #[tool], the `agent-tool-derive` crate must be a dependency.
|
|
// Since proc-macro crates cannot be conditionally compiled via cfg_attr,
|
|
// the macro usage example is documented above in the module doc comment.
|
|
//
|
|
// Full working example (requires agent-tool-derive dependency):
|
|
//
|
|
// ```ignore
|
|
// use agent_tool_derive::tool;
|
|
//
|
|
// #[tool(description = "Echo back the input text", params(
|
|
// text = "The text to echo back"
|
|
// ))]
|
|
// async fn echo(text: String) -> Result<String, String> {
|
|
// Ok(text)
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn test_macro_generates_definition() {
|
|
// let def = ECHO_DEFINITION;
|
|
// assert_eq!(def.name, "echo");
|
|
// assert!(def.description.is_some());
|
|
// assert!(def.parameters.is_some());
|
|
//
|
|
// let schema = def.parameters.unwrap();
|
|
// assert_eq!(schema.schema_type, "object");
|
|
// let props = schema.properties.unwrap();
|
|
// assert!(props.contains_key("text"));
|
|
// }
|
|
//
|
|
// #[test]
|
|
// fn test_macro_registers_tool() {
|
|
// let mut registry = ToolRegistry::new();
|
|
// register_echo(&mut registry);
|
|
// assert!(registry.get("echo").is_some());
|
|
// }
|
|
// ```
|
|
}
|