Derive Macros

Synwire provides two procedural macros for ergonomic definitions: #[tool] and #[derive(State)].

#[tool] attribute macro

Transforms an annotated async function into a StructuredTool factory. The original function is preserved, and a companion {name}_tool() function is generated.

Usage

use synwire_derive::tool;
use synwire_core::error::SynwireError;

/// Searches the web for information.
#[tool]
async fn search(query: String) -> Result<String, SynwireError> {
    Ok(format!("Results for: {query}"))
}

// Generated: search_tool() -> Result<StructuredTool, SynwireError>

Parameter type mapping

Function parameters are automatically mapped to JSON Schema types:

Rust typeJSON Schema type
String, &str"string"
i32, u64, etc."integer"
f32, f64"number"
bool"boolean"
Vec<T>"array"

Documentation

Doc comments on the function become the tool's description:

/// Calculates the sum of two numbers.
#[tool]
async fn add(a: i64, b: i64) -> Result<String, SynwireError> {
    Ok(format!("{}", a + b))
}
// Tool description: "Calculates the sum of two numbers."

#[derive(State)] derive macro

Generates channel configuration from struct field annotations for use with StateGraph.

Usage

use synwire_derive::State;

#[derive(State)]
struct AgentState {
    /// Messages accumulate via a Topic channel
    #[reducer(topic)]
    messages: Vec<String>,

    /// Current step uses LastValue (default)
    current_step: String,
}

// Generated: AgentState::channels() -> Vec<(String, Box<dyn BaseChannel>)>

Channel types

AnnotationChannel typeBehaviour
(none)LastValueOverwrites with the latest value
#[reducer(topic)]TopicAppends values (accumulator)

Using with StateGraph

use synwire_orchestrator::graph::StateGraph;

let channels = AgentState::channels();
// Use channels when configuring the graph

Combining both macros

A typical agent combines #[tool] for tool definitions and #[derive(State)] for graph state:

use synwire_derive::{tool, State};
use synwire_core::error::SynwireError;

#[derive(State)]
struct MyAgentState {
    #[reducer(topic)]
    messages: Vec<String>,
    result: String,
}

/// Looks up information in a database.
#[tool]
async fn lookup(key: String) -> Result<String, SynwireError> {
    Ok(format!("Value for {key}"))
}

Next steps