Skip to main content

llm_tool

Attribute Macro llm_tool 

Source
#[llm_tool]
Expand description

Re-export the #[llm_tool] proc macro for defining tools from plain functions.

§Usage

use llm_tool::{RustTool, ToolContext, ToolRegistry, llm_tool};

/// Adds two numbers together (with a twist).
#[llm_tool]
fn wonky_add(
    /// First number.
    a: i64,
    /// Second number.
    b: i64,
) -> Result<String, String> {
    Ok(format!("{}", a + b + 1))
}

let mut registry = ToolRegistry::new();
registry.register(WonkyAdd);
assert_eq!(registry.definitions().len(), 1);

Transforms a function into a RustTool implementation.

The macro generates:

  • A {FnName}Params struct deriving Deserialize and JsonSchema
  • A {FnName} unit struct (PascalCase) implementing RustTool

The tool name is the function name (snake_case). The tool description comes from one of the sources below. Parameter names and types come from the function signature. Doc comments on parameters become schema descriptions.

§Description sources (in priority order)

SyntaxCostFeature
#[llm_tool] + doc commentZero (static &str)
#[llm_tool(prompt = "inline text")]Zero (static &str)
#[llm_tool(response_file = "...")]Runtime renderprompt-templates
#[llm_tool(prompt_file = "tools/x.tmpl.md")]Zero (compiled)prompt-templates
#[llm_tool(prompt_file = "...", params(k = "v"))]Zero (compiled)prompt-templates
#[llm_tool(prompt_file = "...", context = fn)]Runtime Cow::Ownedprompt-templates

§Inline description

Override or replace the doc comment with an inline string:

#[llm_tool(prompt = "Get the current weather for a city.")]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }

§Template descriptions (feature: prompt-templates)

Load the description from a .tmpl.md file:

#[llm_tool(prompt_file = "tools/weather.tmpl.md")]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }

For templates with variables, provide compile-time key-value pairs:

#[llm_tool(prompt_file = "tools/weather.tmpl.md", params(api = "v3", env = "prod"))]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }

The macro reads the template, validates all declared variables are provided, renders the description, and embeds the result as a static string — zero runtime cost.

For runtime context (e.g. values from config), provide a context function:

#[llm_tool(prompt_file = "tools/weather.tmpl.md", context = build_ctx)]
fn get_weather(/* … */) -> Result<String, ToolError> { /* … */ }

The context function signature is fn(&ToolStruct) -> Context. Templates are parsed once at startup via LazyLock.

§Typed parameters

Parameters may use &str — the generated params struct stores an owned String and the macro auto-borrows it before passing to your function body.

§Response templates

When response_file = "path/to/response.tmpl.md" is provided, the tool’s return value (T: Serialize) is used to build a template context via Context::from_serialize, rendered through the template, and returned as ToolOutput. The struct is also attached as metadata.

§Return types

The return type can be Result<T, E> or just T (infallible):

  • T: String (wrapped as-is), ToolOutput (passed through), any T: Serialize (auto-serialized to JSON), or any T: Into<ToolOutput>
  • E: any E: Into<ToolError> — built-in for String, ToolError, std::io::Error, serde_json::Error