Skip to main content

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}