Skip to main content

tool_macro

Attribute Macro tool_macro 

Source
#[tool_macro]
Available on crate feature derive only.
Expand description

A procedural macro that transforms a function into a portable rig_core::tool::PortableTool, or into the classic contextual rig::tool::Tool when the function accepts classic runtime context.

§Examples

Basic usage:

use rig_derive::rig_tool;

#[rig_tool]
fn add(a: i32, b: i32) -> Result<i32, rig::tool::ToolExecutionError> {
    Ok(a + b)
}

With description:

use rig_derive::rig_tool;

#[rig_tool(description = "Perform basic arithmetic operations")]
fn calculator(x: i32, y: i32, operation: String) -> Result<i32, rig::tool::ToolExecutionError> {
    match operation.as_str() {
        "add" => Ok(x + y),
        "subtract" => Ok(x - y),
        "multiply" => Ok(x * y),
        "divide" => Ok(x / y),
        _ => Err(rig::tool::ToolExecutionError::other("Unknown operation")),
    }
}

With a custom tool name:

use rig_derive::rig_tool;

// Explicit names must be string literals that start with an ASCII letter
// or `_`, may contain ASCII letters, digits, `_`, or `-`, and be at most
// 64 characters long.
#[rig_tool(name = "search-docs", description = "Search the documentation")]
fn search_docs_impl(query: String) -> Result<String, rig::tool::ToolExecutionError> {
    Ok(format!("Searching docs for {query}"))
}

With parameter descriptions:

use rig_derive::rig_tool;

#[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<String, rig::tool::ToolExecutionError> {
    match operation.as_str() {
        "uppercase" => Ok(text.to_uppercase()),
        "lowercase" => Ok(text.to_lowercase()),
        "reverse" => Ok(text.chars().rev().collect()),
        _ => Err(rig::tool::ToolExecutionError::other("Unknown operation")),
    }
}

§Required parameters

Required-ness is derived from the parameter types: every non-Option parameter is required, and Option<T> parameters are optional (absent fields deserialize to None). An explicit required(...) list overrides this. A parameter omitted from an explicit list is deserialized with #[serde(default)], so its type must be Option<T> or implement Default — the advertised schema and the deserializer always agree. Listing an Option<T> parameter is a compile error (schemars and serde would both silently ignore the directive). Names in params(...) and required(...) must match actual parameters.

use rig_derive::rig_tool;

// `b` is advertised as optional and defaults to `0` when omitted.
#[rig_tool(required(a))]
fn add(a: i64, b: i64) -> Result<i64, rig::tool::ToolExecutionError> {
    Ok(a + b)
}

§Execution context

use rig::tool::ToolContext;
use rig_derive::rig_tool;

#[rig_tool]
fn current_user(
    // The marker is required for imported names and type aliases. A fully
    // qualified `&mut rig::tool::ToolContext` — including under a renamed
    // dependency — is recognized directly.
    #[rig(context)] context: &mut ToolContext,
    greeting: String,
) -> Result<String, rig::tool::ToolExecutionError> {
    let user = context
        .get::<String>()
        .map(String::as_str)
        .unwrap_or("guest");
    Ok(format!("{greeting}, {user}!"))
}