gitdataai/libs/agent/tool/examples.rs
2026-04-14 19:02:01 +08:00

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());
// }
// ```
}