pulse_system_types/tool.rs
1//! Tool trait — the shared contract for LLM-callable tools.
2//!
3//! Plugins contribute tools via [`Plugin::tools()`](crate::plugin::Plugin::tools).
4//! The host registers them and exposes them to the LLM.
5
6use std::fmt;
7use std::future::Future;
8use std::pin::Pin;
9
10/// Async result type for tool execution.
11pub type ToolResult<'a> = Pin<Box<dyn Future<Output = Result<String, ToolError>> + Send + 'a>>;
12
13/// Errors from tool execution.
14#[derive(Debug)]
15pub enum ToolError {
16 /// The requested resource was not found.
17 NotFound(String),
18 /// Tool execution failed.
19 ExecutionFailed(String),
20 /// Access denied.
21 PermissionDenied(String),
22}
23
24impl fmt::Display for ToolError {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 ToolError::NotFound(msg) => write!(f, "not found: {msg}"),
28 ToolError::ExecutionFailed(msg) => write!(f, "execution failed: {msg}"),
29 ToolError::PermissionDenied(msg) => write!(f, "permission denied: {msg}"),
30 }
31 }
32}
33
34impl std::error::Error for ToolError {}
35
36/// A tool that can be invoked by an LLM.
37///
38/// Tools are contributed by plugins via [`Plugin::tools()`](crate::plugin::Plugin::tools)
39/// and registered with the host's tool registry. The LLM calls tools by name.
40///
41/// # Object Safety
42///
43/// This trait is object-safe and designed for use as `Box<dyn Tool>`.
44pub trait Tool: Send + Sync {
45 /// Tool name — must match what the LLM calls.
46 fn name(&self) -> &str;
47
48 /// Human-readable description for the LLM.
49 fn description(&self) -> &str;
50
51 /// JSON Schema for the tool's input parameters.
52 fn input_schema(&self) -> serde_json::Value;
53
54 /// Execute the tool with the given input.
55 fn execute(&self, input: serde_json::Value) -> ToolResult<'_>;
56}