From 1fe55ad1dd364249cd0a82bc09f934cb2bf32d11 Mon Sep 17 00:00:00 2001 From: Collin Brittain Date: Mon, 7 Apr 2025 11:20:59 -0500 Subject: [PATCH] Add examples --- Cargo.lock | 1 + rig-macros/Cargo.toml | 1 + rig-macros/examples/README.md | 56 +++++++++++++++++++ rig-macros/examples/async_tool.rs | 59 ++++++++++++++++++++ rig-macros/examples/full.rs | 65 ++++++++++++++++++++++ rig-macros/examples/simple.rs | 72 +++++++++++++++++++++++++ rig-macros/examples/with_description.rs | 61 +++++++++++++++++++++ 7 files changed, 315 insertions(+) create mode 100644 rig-macros/examples/README.md create mode 100644 rig-macros/examples/async_tool.rs create mode 100644 rig-macros/examples/full.rs create mode 100644 rig-macros/examples/simple.rs create mode 100644 rig-macros/examples/with_description.rs diff --git a/Cargo.lock b/Cargo.lock index 3cff8a1..768b697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8685,6 +8685,7 @@ dependencies = [ "serde_json", "syn 2.0.100", "tokio", + "tracing-subscriber", ] [[package]] diff --git a/rig-macros/Cargo.toml b/rig-macros/Cargo.toml index 7f7201d..b4f5dc6 100644 --- a/rig-macros/Cargo.toml +++ b/rig-macros/Cargo.toml @@ -17,3 +17,4 @@ schemars = "0.8.22" rig-core = { path = "../rig-core" } serde = "1.0" tokio = { version = "1.44.0", features = ["full"] } +tracing-subscriber = "0.3.0" diff --git a/rig-macros/examples/README.md b/rig-macros/examples/README.md new file mode 100644 index 0000000..d6ef64b --- /dev/null +++ b/rig-macros/examples/README.md @@ -0,0 +1,56 @@ +# Rig Tool Macro Examples + +This directory contains examples demonstrating different ways to use the `rig_tool` macro with a rig Agent. + +## Examples + +### 1. Simple Example (`simple.rs`) +Demonstrates the most basic usage of the macro without any attributes. Shows how to create a simple tool that adds two numbers and use it with a rig Agent. + +### 2. With Description (`with_description.rs`) +Shows how to add a description to your tool using the `description` attribute. Implements a calculator that can perform basic arithmetic operations and uses it with a rig Agent. + +### 3. Full Attributes (`full.rs`) +Demonstrates using all available attributes including parameter descriptions. Implements a string processor that can perform various string operations and uses it with a rig Agent. + +### 4. Error Handling (`error_handling.rs`) +Shows how to handle errors in your tools, including: +- Domain-specific errors (e.g., square root of negative numbers) +- Parameter validation errors +- Missing parameter errors +- Type conversion errors + +### 5. Async Tool (`async_tool.rs`) +Demonstrates how to create and use async tools with a rig Agent, including: +- Basic async operation +- Error handling in async context + +## Running the Examples + +To run any example, use: + +```bash +cargo run --example +``` + +For example: +```bash +cargo run --example simple +cargo run --example with_description +cargo run --example full +cargo run --example error_handling +cargo run --example async_tool +``` + +## Features Demonstrated + +- Basic tool creation +- Optional attributes +- Parameter descriptions +- Error handling +- Async support +- Static tool instances +- Parameter validation +- Integration with rig Agent +- Natural language interaction with tools +- Tool definitions and schemas diff --git a/rig-macros/examples/async_tool.rs b/rig-macros/examples/async_tool.rs new file mode 100644 index 0000000..2585f26 --- /dev/null +++ b/rig-macros/examples/async_tool.rs @@ -0,0 +1,59 @@ +use rig::completion::Prompt; +use rig::providers; +use rig::tool::Tool; +use rig_macros::rig_tool; +use std::time::Duration; +use tracing_subscriber; + +// Example demonstrating async tool usage +#[rig_tool( + description = "A tool that simulates an async operation", + params( + input = "Input value to process", + delay_ms = "Delay in milliseconds before returning result" + ) +)] +async fn async_operation(input: String, delay_ms: u64) -> Result { + // Simulate some async work + tokio::time::sleep(Duration::from_millis(delay_ms)).await; + + // Process the input + Ok(format!( + "Processed after {}ms: {}", + delay_ms, + input.to_uppercase() + )) +} + +#[tokio::main] +async fn main() { + // Initialize tracing + tracing_subscriber::fmt().pretty().init(); + + // Create an agent with the ASYNCOPERATION tool + let async_agent = providers::openai::Client::from_env() + .agent(providers::openai::GPT_4O) + .preamble("You are an agent with tools access, always use the tools") + .max_tokens(1024) + .tool(AsyncOperation) + .build(); + + // Print out the tool definition to verify + println!("Tool definition:"); + println!( + "ASYNCOPERATION: {}", + serde_json::to_string_pretty(&AsyncOperation.definition(String::default()).await).unwrap() + ); + + // Test prompts + for prompt in [ + "What tools do you have?", + "Process the text 'hello world' with a delay of 1000ms", + "Process the text 'async operation' with a delay of 500ms", + "Process the text 'concurrent calls' with a delay of 200ms", + "Process the text 'error handling' with a delay of 'not a number'", + ] { + println!("User: {}", prompt); + println!("Agent: {}", async_agent.prompt(prompt).await.unwrap()); + } +} diff --git a/rig-macros/examples/full.rs b/rig-macros/examples/full.rs new file mode 100644 index 0000000..74df3a2 --- /dev/null +++ b/rig-macros/examples/full.rs @@ -0,0 +1,65 @@ +use rig::completion::Prompt; +use rig::providers; +use rig::tool::Tool; +use rig_macros::rig_tool; +use tracing_subscriber; + +// Example with full attributes including parameter descriptions +#[rig_tool( + description = "A tool that performs string operations", + params( + text = "The input text to process", + operation = "The operation to perform (uppercase, lowercase, reverse)", + ) +)] +fn string_processor( + text: String, + operation: String, +) -> Result { + let result = match operation.as_str() { + "uppercase" => text.to_uppercase(), + "lowercase" => text.to_lowercase(), + "reverse" => text.chars().rev().collect(), + _ => { + return Err(rig::tool::ToolError::ToolCallError( + format!("Unknown operation: {}", operation).into(), + )) + } + }; + + Ok(result) +} + +#[tokio::main] +async fn main() { + // Initialize tracing + tracing_subscriber::fmt().pretty().init(); + + // Create an agent with the STRINGPROCESSOR tool + let string_agent = providers::openai::Client::from_env() + .agent(providers::openai::GPT_4O) + .preamble("You are an agent with tools access, always use the tools") + .max_tokens(1024) + .tool(StringProcessor) + .build(); + + // Print out the tool definition to verify + println!("Tool definition:"); + println!( + "STRINGPROCESSOR: {}", + serde_json::to_string_pretty(&StringProcessor.definition(String::default()).await).unwrap() + ); + + // Test prompts + for prompt in [ + "What tools do you have?", + "Convert 'hello world' to uppercase", + "Convert 'HELLO WORLD' to lowercase", + "Reverse the string 'hello world'", + "Convert 'hello world' to uppercase and repeat it 3 times", + "Perform an invalid operation on 'hello world'", + ] { + println!("User: {}", prompt); + println!("Agent: {}", string_agent.prompt(prompt).await.unwrap()); + } +} diff --git a/rig-macros/examples/simple.rs b/rig-macros/examples/simple.rs new file mode 100644 index 0000000..4c54644 --- /dev/null +++ b/rig-macros/examples/simple.rs @@ -0,0 +1,72 @@ +use rig_macros::rig_tool; +use rig::completion::Prompt; +use rig::providers; +use tracing_subscriber; + +// Simple example with no attributes +#[rig_tool] +fn add(a: i32, b: i32) -> Result { + Ok(a + b) +} + +#[rig_tool] +fn subtract(a: i32, b: i32) -> Result { + Ok(a - b) +} + +#[rig_tool] +fn multiply(a: i32, b: i32) -> Result { + Ok(a * b) +} + +#[rig_tool] +fn divide(a: i32, b: i32) -> Result { + if b == 0 { + Err(rig::tool::ToolError::ToolCallError("Division by zero".into())) + } else { + Ok(a / b) + } +} + +#[rig_tool] +fn answer_secret_question() -> Result<(bool, bool, bool, bool, bool), rig::tool::ToolError> { + Ok((false, false, true, false, false)) +} + +#[rig_tool] +fn how_many_rs(s: String) -> Result { + Ok(s.chars() + .filter(|c| *c == 'r' || *c == 'R') + .collect::>() + .len()) +} + +#[rig_tool] +fn sum_numbers(numbers: Vec) -> Result { + Ok(numbers.iter().sum()) +} + +#[tokio::main] +async fn main() { + // Initialize tracing + tracing_subscriber::fmt().pretty().init(); + + // Create an agent with the ADD tool + let calculator_agent = providers::openai::Client::from_env() + .agent(providers::openai::GPT_4O) + .preamble("You are an agent with tools access, always use the tools") + .max_tokens(1024) + .tool(Add) + .build(); + + // Test prompts + for prompt in [ + "What tools do you have?", + "Calculate 5 + 3", + "What is 10 + 20?", + "Add 100 and 200", + ] { + println!("User: {}", prompt); + println!("Agent: {}", calculator_agent.prompt(prompt).await.unwrap()); + } +} diff --git a/rig-macros/examples/with_description.rs b/rig-macros/examples/with_description.rs new file mode 100644 index 0000000..d38664e --- /dev/null +++ b/rig-macros/examples/with_description.rs @@ -0,0 +1,61 @@ +use rig::completion::Prompt; +use rig::providers; +use rig::tool::Tool; +use rig_macros::rig_tool; +use tracing_subscriber; + +// Example with description attribute +#[rig_tool(description = "Perform basic arithmetic operations")] +fn calculator(x: i32, y: i32, operation: String) -> Result { + match operation.as_str() { + "add" => Ok(x + y), + "subtract" => Ok(x - y), + "multiply" => Ok(x * y), + "divide" => { + if y == 0 { + Err(rig::tool::ToolError::ToolCallError( + "Division by zero".into(), + )) + } else { + Ok(x / y) + } + } + _ => Err(rig::tool::ToolError::ToolCallError( + format!("Unknown operation: {}", operation).into(), + )), + } +} + +#[tokio::main] +async fn main() { + // Initialize tracing + tracing_subscriber::fmt().pretty().init(); + + // Create an agent with the CALCULATOR tool + let calculator_agent = providers::openai::Client::from_env() + .agent(providers::openai::GPT_4O) + .preamble("You are an agent with tools access, always use the tools") + .max_tokens(1024) + .tool(Calculator) + .build(); + + // Print out the tool definition to verify + println!("Tool definition:"); + println!( + "CALCULATOR: {}", + serde_json::to_string_pretty(&CALCULATOR.definition(String::default()).await).unwrap() + ); + + // Test prompts + for prompt in [ + "What tools do you have?", + "Calculate 5 + 3", + "What is 10 - 4?", + "Multiply 6 and 7", + "Divide 20 by 5", + "What is 10 / 0?", + ] { + println!("User: {}", prompt); + println!("Agent: {}", calculator_agent.prompt(prompt).await.unwrap()); + } +}