reductool-proc-macro

Crates.ioreductool-proc-macro
lib.rsreductool-proc-macro
version0.1.2
created_at2025-08-16 21:57:29.421509+00
updated_at2025-08-17 21:07:32.844111+00
descriptionInternal proc-macro crate for `reductool`. End users should depend on `reductool`.
homepage
repositoryhttps://github.com/hadydotai/reductool
max_upload_size
id1798937
size36,967
Hady (hadydotai)

documentation

README

Reductool

GitHub Actions Workflow Status

This will let you turn Rust functions into LLM tools through an attribute macro. Here's a quick example

use reductool::aitool;

#[aitool]
/// Add allows you to add two numbers
fn add(a: i32, b: i32) -> i32 {
    a + b
}

Later when you need to get your functions as a tool schema, you'll call reductool::tools_to_schema(). When the LLM responds back with a tool call, you'll pass the name of the target tool and the arguments to reductool::dispatch_tool(name, args).

Here's a full example and more present in the examples directory

#[reductool::aitool]
/// Greet a person by name; defaults to "Guest" when not provided.
fn greet(name: Option<String>) -> String {
    format!("Hello, {}!", name.unwrap_or_else(|| "Guest".to_string()))
}

fn main() {
    let result = futures::executor::block_on(async {
        reductool::dispatch_tool("greet", serde_json::json!({"name": "World"}))
            .await
            .expect("failed result")
    });
    println!("result -> {}", result);
}

The output from calling reductool::tools_to_schema() with the greet function defined up there looks like this

  [{
    "description": " Greet a person by name; defaults to \"Guest\" when not provided.",
    "name": "greet",
    "parameters": {
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": [],
      "type": "object"
    }
  }]

Supported parameter types

The #[aitool] macro inspects function parameter types and emits a JSON Schema for each one. The following types are recognized specially; everything else falls back to "type": "string" in the schema and must implement serde::Deserialize to compile.

Rust type JSON Schema emitted Required? Notes
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize { "type": "integer" } Yes, unless wrapped in Option
f32, f64 { "type": "number" } Yes, unless wrapped in Option
bool { "type": "boolean" } Yes, unless wrapped in Option
String { "type": "string" } Yes, unless wrapped in Option Prefer String over &str for deserialization.
[T; N] { "type": "array", "items": schema(T) } Yes, unless wrapped in Option At runtime, JSON must be an array of length exactly N; the schema does not encode this length constraint.
(T1, T2, ..., Tn) { "type": "array", "items": [schema(T1), …, schema(Tn)], "minItems": n, "maxItems": n } Yes, unless wrapped in Option Fixed-length, heterogeneous tuple.
Vec { "type": "array", "items": schema(T) } Yes, unless wrapped in Option If T is unrecognized, items default to { "type": "string" }.
Option schema(T) No (omitted from "required") Treated as optional; the schema does not add "null" type. Omit the field to pass None.
serde_json::Value (or a type named Value) {} Yes, unless wrapped in Option Accepts any JSON value. The detection also matches a bare Value ident.
Other path types (custom structs/enums, etc.) { "type": "string" } Yes, unless wrapped in Option Must implement serde::Deserialize to compile; schema may not reflect true shape.
&T (references) schema(T) The schema normalizes &T to T, but the generated args struct uses the exact type; borrowed fields like &str generally cannot derive Deserialize here. Use owned types (e.g., String) instead.

Notes and constraints:

  • Parameters must be simple identifiers like arg: T. Patterns such as (_: T), (a, b): (T, U), or destructuring are rejected at compile time.
  • Methods with a receiver (e.g., self, &self) are not supported; annotate free functions only.
  • Required vs optional is determined solely by whether the type is Option<T>. All non-Option params are marked required.
  • The example in crates/reductool/examples/basic.rs demonstrates both a required-arg function (add(i32, i32)) and an optional-arg function (greet(Option<String>)).

License

Reductool Copyright (C) 2025 hadydotai

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Commit count: 0

cargo fmt