Skip to main content

agentic_core/tool/
handler.rs

1use std::future::Future;
2use std::pin::Pin;
3
4use serde_json::Value;
5
6use crate::types::io::FunctionTool;
7
8#[derive(Debug, Clone)]
9pub struct ToolOutput {
10    pub call_id: String,
11    pub output: String,
12}
13
14#[derive(Debug, thiserror::Error)]
15pub enum ToolError {
16    #[error("execution failed: {0}")]
17    Execution(String),
18    #[error("invalid tool config: {0}")]
19    Config(String),
20}
21
22/// Trait implemented by every tool type — client-owned and gateway-owned alike.
23///
24/// Covers validation and normalization: the steps that apply to all tools
25/// regardless of who executes them.
26///
27/// Implementations must be `Send + Sync` so they can be stored behind `Arc<dyn
28/// ToolHandler>` and used across async task boundaries.
29pub trait ToolHandler: Send + Sync {
30    #[must_use]
31    fn tool_type(&self) -> super::registry::ToolType;
32
33    /// Validate the tool param JSON.
34    ///
35    /// # Errors
36    ///
37    /// Returns [`ToolError::Config`] for obviously invalid configurations.
38    fn validate(&self, param: &Value) -> Result<(), ToolError>;
39
40    /// Normalise this tool declaration into vLLM-compatible `FunctionTool` entries.
41    #[must_use]
42    fn normalize(&self, param: &Value) -> Vec<FunctionTool>;
43}
44
45/// Extension of [`ToolHandler`] for tool types that are executed by the gateway.
46///
47/// Only gateway-owned tools (`Mcp`, `WebSearch`, `FileSearch`, `CodeInterpreter`)
48/// implement this trait. Client-owned tools (`Function`) do not — the type system
49/// makes it impossible to call `execute()` on them.
50///
51/// ## Note on `async fn` in traits
52///
53/// Native `async fn` in traits (Rust 1.75+) is not yet `dyn`-compatible. Since
54/// PR B will store handlers as `Arc<dyn GatewayExecutor>`, we use explicit
55/// `Pin<Box<dyn Future>>` return types.
56pub trait GatewayExecutor: ToolHandler + 'static {
57    /// Execute a tool call and return the result.
58    ///
59    /// ## `config` parameter
60    ///
61    /// `config` is the serialised **server-level** tool param (i.e. the `*ToolParam`
62    /// struct stored in [`super::registry::ToolEntry::config`]). It is **not** the
63    /// per-tool parameter schema.
64    ///
65    /// # Errors
66    ///
67    /// Returns [`ToolError::Execution`] if the tool call fails.
68    fn execute(
69        &self,
70        call_id: &str,
71        tool_name: &str,
72        arguments: &str,
73        config: &Value,
74    ) -> Pin<Box<dyn Future<Output = Result<ToolOutput, ToolError>> + Send + '_>>;
75}
76
77#[cfg(test)]
78mod tests {
79    use std::sync::Arc;
80
81    use super::*;
82
83    // Compile-time check: Arc<dyn GatewayExecutor> must be constructable.
84    // This fails to compile if GatewayExecutor ever becomes dyn-incompatible.
85    fn _assert_gateway_executor_dyn_compatible(_: Arc<dyn GatewayExecutor>) {}
86}