Skip to main content

ToolHandler

Trait ToolHandler 

Source
pub trait ToolHandler:
    ToolMeta
    + Send
    + Sync {
    type Input: DeserializeOwned + JsonSchema + Send;
    type Output: Serialize;

    // Required method
    fn call<'life0, 'life1, 'async_trait>(
        &'life0 self,
        ctx: &'life1 ToolCtx,
        input: Self::Input,
    ) -> Pin<Box<dyn Future<Output = Result<ToolOutcome<Self::Output>, HandlerError>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;

    // Provided method
    fn input_schema() -> Value
       where Self: Sized { ... }
}
Expand description

The behavior half of the tool contract: typed input, typed output, and the async call that turns one into the other.

A type implements this by hand (the macro only generates its ToolMeta supertrait; see the derive seam). The associated types carry the bounds the rest of the system needs:

  • Input: DeserializeOwned so the type-erased layer can turn the model’s JSON into it, and Input: JsonSchema so a schema can be generated to hand the model in the first place.
  • Output: Serialize so the type-erased layer can turn the handler’s result back into JSON for the event log.

The Send/Sync bounds and the Send bound on Input are what let a handler be wrapped as a DynTool and dispatched from an async runtime that may move the work across threads.

call returns a ToolOutcome, not a bare Output: a human-in-the-loop tool may return ToolOutcome::Suspend to park the run. Its error type is HandlerError, the tool’s own failure; a schema mismatch is impossible here because call only ever receives an already-deserialized Input.

Required Associated Types§

Source

type Input: DeserializeOwned + JsonSchema + Send

The typed input the model must supply. DeserializeOwned drives erased dispatch; JsonSchema drives the schema handed to the model.

Source

type Output: Serialize

The typed output the tool produces on success.

Required Methods§

Source

fn call<'life0, 'life1, 'async_trait>( &'life0 self, ctx: &'life1 ToolCtx, input: Self::Input, ) -> Pin<Box<dyn Future<Output = Result<ToolOutcome<Self::Output>, HandlerError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Runs the tool for one attempt.

ctx carries the idempotency key for this attempt (see ToolCtx). The input is already validated and typed. Return ToolOutcome::Output on success, ToolOutcome::Suspend to park the run for a human, or Err(HandlerError) on a genuine failure.

Provided Methods§

Source

fn input_schema() -> Value
where Self: Sized,

The JSON Schema for Input, generated by schemars.

This is a provided method: it lives on the handler trait because it needs the Input type, and no tool should override it. The runtime hands this schema to the model so the model knows how to shape its tool call. It is surfaced without erasure here and, after wrapping, through DynTool::input_schema.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§