Tool

Struct Tool 

Source
pub struct Tool { /* private fields */ }
Expand description

Tool definition for OpenAI-compatible function calling.

A Tool encapsulates everything needed for an LLM to understand and execute a function: its identity, purpose, expected inputs, and implementation.

§Design Philosophy

Tools are immutable by design. Once created, their metadata and handler cannot be changed. This ensures:

  • Thread safety through simple cloning (all fields are cheaply cloned)
  • Predictable behavior - a tool’s signature never changes mid-execution
  • Safe concurrent access without locks

§Cloning Behavior

The Clone implementation is efficient:

  • name and description: String clones (heap allocation)
  • input_schema: JSON Value clone (reference counted internally in some cases)
  • handler: Arc clone (only increments atomic counter, shares same handler)

This means cloning a tool is relatively cheap and won’t duplicate the actual handler implementation.

§Thread Safety

Tools are fully thread-safe:

  • All fields are Send + Sync
  • Handler is wrapped in Arc for shared ownership
  • Can be stored in agent registries accessed by multiple threads
  • Can be cloned and sent across thread boundaries

§Examples

use open_agent::Tool;
use serde_json::json;

// Create a tool using the constructor
let calculator = Tool::new(
    "multiply",
    "Multiply two numbers together",
    json!({
        "a": "number",
        "b": "number"
    }),
    |args| Box::pin(async move {
        let a = args["a"].as_f64().unwrap_or(1.0);
        let b = args["b"].as_f64().unwrap_or(1.0);
        Ok(json!({"result": a * b}))
    })
);

// Access tool metadata
println!("Tool: {}", calculator.name());
println!("Description: {}", calculator.description());
println!("Schema: {}", calculator.input_schema());

Implementations§

Source§

impl Tool

Source

pub fn new<F, Fut>( name: impl Into<String>, description: impl Into<String>, input_schema: Value, handler: F, ) -> Self
where F: Fn(Value) -> Fut + Send + Sync + 'static, Fut: Future<Output = Result<Value>> + Send + 'static,

Create a new tool with flexible schema definition.

This constructor handles schema conversion automatically, accepting multiple formats:

§Schema Formats
§1. Simple Type Notation
{
  "location": "string",
  "temperature": "number"
}

All parameters are marked as required by default.

§2. Extended Property Schema
{
  "query": {
    "type": "string",
    "description": "Search query"
  },
  "limit": {
    "type": "integer",
    "optional": true
  }
}

Use "optional": true or "required": false to mark parameters as optional.

§3. Full JSON Schema
{
  "type": "object",
  "properties": {
    "name": {"type": "string"}
  },
  "required": ["name"]
}

Already valid JSON Schema - passed through as-is.

§Handler Requirements

The handler must satisfy several trait bounds:

  • Fn(Value) -> Fut: Takes JSON arguments, returns a future
  • Send + Sync: Can be shared across threads safely
  • 'static: No non-static references (must own all data)
  • Fut: Future<Output = Result<Value>> + Send: Future is sendable and produces Result

The constructor automatically wraps the handler in Arc<...> and boxes the futures, so you don’t need to do this manually.

§Generic Parameters
  • F: The handler function type
  • Fut: The future type returned by the handler

These are inferred automatically from the handler you provide.

§Examples
§Simple Calculator Tool
use open_agent::Tool;
use serde_json::json;

let add_tool = Tool::new(
    "add",
    "Add two numbers together",
    json!({
        "a": "number",
        "b": "number"
    }),
    |args| {
        Box::pin(async move {
            let a = args.get("a")
                .and_then(|v| v.as_f64())
                .ok_or_else(|| open_agent::Error::invalid_input("Parameter 'a' must be a number"))?;
            let b = args.get("b")
                .and_then(|v| v.as_f64())
                .ok_or_else(|| open_agent::Error::invalid_input("Parameter 'b' must be a number"))?;
            Ok(json!({"result": a + b}))
        })
    }
);
§Tool with Optional Parameters
use open_agent::Tool;
use serde_json::json;

let search_tool = Tool::new(
    "search",
    "Search for information",
    json!({
        "query": {
            "type": "string",
            "description": "What to search for"
        },
        "max_results": {
            "type": "integer",
            "description": "Maximum results to return",
            "optional": true,
            "default": 10
        }
    }),
    |args| Box::pin(async move {
        let query = args["query"].as_str().unwrap_or("");
        let max = args.get("max_results")
            .and_then(|v| v.as_i64())
            .unwrap_or(10);

        // Perform search...
        Ok(json!({"results": [], "query": query, "limit": max}))
    })
);
§Tool with External State
use open_agent::Tool;
use serde_json::json;
use std::sync::Arc;

// State that needs to be shared
let api_key = Arc::new("secret-key".to_string());

let tool = Tool::new(
    "api_call",
    "Make an API call",
    json!({"endpoint": "string"}),
    move |args| {
        // Clone Arc to move into async block
        let api_key = api_key.clone();
        Box::pin(async move {
            let endpoint = args["endpoint"].as_str().unwrap_or("");
            // Use api_key in async operation
            println!("Calling {} with key {}", endpoint, api_key);
            Ok(json!({"status": "success"}))
        })
    }
);
Source

pub async fn execute(&self, arguments: Value) -> Result<Value>

Execute the tool with the provided arguments.

This method invokes the tool’s handler asynchronously, passing the arguments and awaiting the result. It’s the primary way to run a tool’s logic.

§Execution Flow
  1. Call the handler function (stored in Arc) with arguments
  2. The handler returns a Pin<Box<dyn Future>>
  3. Await the future to get the Result<Value>
  4. Return the result (success value or error)
§Arguments

Arguments should be a JSON object matching the tool’s input_schema:

{
  "param1": "value1",
  "param2": 42
}

The handler is responsible for extracting and validating these arguments.

§Error Handling

If the handler returns an error, it’s propagated directly. The agent calling this method should handle errors appropriately (e.g., retry logic, error reporting to the LLM).

§Examples
let calculator = Tool::new(
    "add",
    "Add numbers",
    json!({"a": "number", "b": "number"}),
    |args| Box::pin(async move {
        let sum = args["a"].as_f64().unwrap() + args["b"].as_f64().unwrap();
        Ok(json!({"result": sum}))
    })
);

// Execute the tool
let result = calculator.execute(json!({"a": 5.0, "b": 3.0})).await?;
assert_eq!(result["result"], 8.0);
Source

pub fn to_openai_format(&self) -> Value

Convert the tool definition to OpenAI’s function calling format.

This method generates the JSON structure expected by OpenAI’s Chat Completion API when using function calling. The format is also compatible with other LLM providers that follow OpenAI’s conventions.

§Output Format

Returns a JSON structure like:

{
  "type": "function",
  "function": {
    "name": "tool_name",
    "description": "Tool description",
    "parameters": {
      "type": "object",
      "properties": { ... },
      "required": [ ... ]
    }
  }
}
§Usage in API Calls

This format is typically used when constructing the tools array for API requests:

{
  "model": "gpt-4",
  "messages": [...],
  "tools": [
    // Output of to_openai_format() for each tool
  ]
}
§Examples
let my_tool = tool("search", "Search for information")
    .param("query", "string")
    .build(|_| async { Ok(json!({})) });

let openai_format = my_tool.to_openai_format();

// Verify the structure
assert_eq!(openai_format["type"], "function");
assert_eq!(openai_format["function"]["name"], "search");
assert_eq!(openai_format["function"]["description"], "Search for information");
assert!(openai_format["function"]["parameters"].is_object());
Source

pub fn name(&self) -> &str

Returns the tool’s name.

Source

pub fn description(&self) -> &str

Returns the tool’s description.

Source

pub fn input_schema(&self) -> &Value

Returns a reference to the tool’s input schema.

Trait Implementations§

Source§

impl Clone for Tool

Source§

fn clone(&self) -> Tool

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Tool

Custom Debug implementation for Tool.

The handler field is omitted from debug output because:

  • Function pointers/closures don’t have meaningful debug representations
  • The Arc<dyn Fn...> type is complex and not useful to display
  • Showing the handler would just print something like “Arc { … }”

Only the metadata fields (name, description, input_schema) are shown, which are the most useful for debugging tool definitions.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Tool

§

impl !RefUnwindSafe for Tool

§

impl Send for Tool

§

impl Sync for Tool

§

impl Unpin for Tool

§

impl !UnwindSafe for Tool

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more