Skip to main content

ToolRoundExecutor

Trait ToolRoundExecutor 

Source
pub trait ToolRoundExecutor: Send + Sync {
    // Required method
    fn execute_round<'a, 'async_trait>(
        &'a mut self,
        ctx: ToolRoundCtx<'a>,
        calls: Vec<ToolCall>,
    ) -> Pin<Box<dyn Future<Output = BoxStream<'a, ToolCallOutcome>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'a: 'async_trait;
}
Expand description

Round-level tool-call policy: decides how one round’s tool calls are executed — order, concurrency, approval gates. Inject it with with_tool_round_executor; the default SerialToolRoundExecutor runs calls one by one in request order, the classic serial loop with identical behavior.

The executor owns how calls run; the framework owns that they run correctly: every call goes through ToolRoundCtx::run, which carries the standard mechanics (tool span, ToolStarted / ToolCompleted events, registry dispatch, protected-result declaration) — a custom executor can reorder, parallelize, or gate calls without reimplementing any of that.

§Contract

  • Atomicity: tool rounds are executed atomically and never interrupted — tools are user code with no cancellation interface, and a partial execution risks side effects; cancellation takes effect after the round ends, before the next round’s conversation. An executor that interrupts mid-round accepts that risk itself.
  • Outcomes stream back as they complete: the agent records (and, on the streaming path, dispatches) each outcome as the stream yields it. Dropping the stream early aborts the rest of the round — already-produced outcomes stay recorded, and the model adapts to the partial results.
  • Decisions feed back as text: to deny a call (approval gate, rate limit, …), yield a synthetic ToolCallOutcome carrying the decision text — it reaches the model through the normal ToolResult channel without the tool ever running.

§Examples

An approval gate: calls to “dangerous” are denied without running, everything else executes as usual.

use molo::agent::{
    Agent, ReActAgent, ToolCallOutcome, ToolRoundCtx, ToolRoundExecutor,
};
use molo::message::ToolCall;
use molo::provider::{FakeProvider, FakeReply};
use molo::tool::ToolRegistry;
use futures::stream::BoxStream;

#[derive(Default)]
struct ApprovalGateToolRoundExecutor;

#[molo::async_trait]
impl ToolRoundExecutor for ApprovalGateToolRoundExecutor {
    async fn execute_round<'a>(
        &'a mut self,
        ctx: ToolRoundCtx<'a>,
        calls: Vec<ToolCall>,
    ) -> BoxStream<'a, ToolCallOutcome> {
        Box::pin(async_stream::stream! {
            for call in calls {
                if call.name == "dangerous" {
                    yield ToolCallOutcome::text(call, "denied by approval gate");
                } else {
                    yield ctx.run(call).await;
                }
            }
        })
    }
}

let mut agent = ReActAgent::new(
    FakeProvider::new([FakeReply::Text("Hello".into())]),
    ToolRegistry::new(),
    "",
)
.with_tool_round_executor(ApprovalGateToolRoundExecutor);

assert_eq!(agent.run("Are you there").await?, "Hello");

Required Methods§

Source

fn execute_round<'a, 'async_trait>( &'a mut self, ctx: ToolRoundCtx<'a>, calls: Vec<ToolCall>, ) -> Pin<Box<dyn Future<Output = BoxStream<'a, ToolCallOutcome>> + Send + 'async_trait>>
where Self: 'async_trait, 'a: 'async_trait,

Execute one tool round: decide the policy over calls — which to run, in what order, with what concurrency — and yield each ToolCallOutcome as it completes.

Use ToolRoundCtx::run to execute a call; its futures borrow ctx, so when the policy runs calls concurrently, the futures must materialize inside the returned stream (which owns ctx) rather than across this method’s return — see the parallel example on with_tool_round_executor.

§Errors

None at the round level: a tool’s failure is not an error here — it rides along in the outcome’s text, which the agent records and feeds back to the model.

Dyn Compatibility§

This trait is dyn compatible.

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

Implementors§