rpi_agent/agent_tool.rs
1//! Mirrors the tool-definition portion of `packages/agent/src/types.ts`
2//! (`AgentTool<TParameters, TDetails>`).
3//!
4//! TS `AgentTool` extends `Tool` with `label`, optional `prepareArguments`,
5//! `execute`, and per-tool `executionMode`. The Rust port keeps the same shape:
6//! a struct-less `#[async_trait]` `AgentTool` returning the public `Tool`
7//! schema from `rpi_ai`. Tools are held behind `Arc<dyn AgentTool>` so the agent
8//! can store + dispatch them without generics.
9//!
10//! The `execute` signature carries an `on_update` callback (`&dyn Fn`) for
11//! streaming partial results. The loop wraps it with an
12//! `accepting_updates: Arc<AtomicBool>` gate so calls made after `execute`
13//! resolves are no-ops — the late-update suppression invariant (plan §5.3).
14
15use async_trait::async_trait;
16use rpi_ai::types::Tool;
17use tokio_util::sync::CancellationToken;
18
19use crate::error::AgentError;
20use crate::types::{AgentToolResult, ToolExecutionMode, ToolResultPartial};
21use std::sync::Arc;
22
23/// A tool the agent can call. Mirrors TS `AgentTool<TParameters, TDetails>`.
24///
25/// Implementations are `Send + Sync` and conventionally held as
26/// `Arc<dyn AgentTool>`. The loop looks up tools by `schema().name` matching a
27/// `ToolCall::name`.
28#[async_trait]
29pub trait AgentTool: Send + Sync {
30 /// The provider-facing tool definition (name, description, parameters schema).
31 fn schema(&self) -> &Tool;
32
33 /// Human-readable label for UI display.
34 fn label(&self) -> &str;
35
36 /// Per-tool execution-mode override. `Sequential` forces one-at-a-time for
37 /// the whole batch when any tool in it returns `Sequential`; `Parallel`
38 /// (default) allows concurrency.
39 fn execution_mode(&self) -> ToolExecutionMode {
40 ToolExecutionMode::Parallel
41 }
42
43 /// Optional compatibility shim for raw tool-call arguments before schema
44 /// validation. Default is identity. Mirrors TS `prepareArguments`.
45 fn prepare_arguments(&self, args: serde_json::Value) -> Result<serde_json::Value, AgentError> {
46 Ok(args)
47 }
48
49 /// Execute the tool call. Throw via `Err(AgentError)` on failure — the loop
50 /// encodes the error message into an error `ToolResultMessage`.
51 ///
52 /// `on_update` streams partial results. It is passed as an `Arc<dyn Fn>`
53 /// (not a borrow) so a tool may clone it into a background task that emits
54 /// progress after `execute` has returned its main result — e.g. a bash tool
55 /// whose throttled output flusher outlives the `await` point. Calls made
56 /// after the loop has flipped its `accepting_updates` gate to false are
57 /// silently dropped by the loop (late-update suppression, invariant §5.3);
58 /// the tool never needs to track settlement itself.
59 async fn execute(
60 &self,
61 tool_call_id: &str,
62 params: serde_json::Value,
63 signal: CancellationToken,
64 on_update: Arc<dyn Fn(ToolResultPartial) + Send + Sync>,
65 ) -> Result<AgentToolResult, AgentError>;
66}