Skip to main content

zeroclaw_api/
tool.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3
4/// Boilerplate-collapsing macro: pair a concrete `Tool` impl with a
5/// matching `Attributable` impl that surfaces the supplied `ToolKind`
6/// and uses the tool's `name()` as its alias.
7///
8/// Invoke once per `Tool` struct, in the same module as the struct:
9///
10/// ```ignore
11/// crate::tool_attribution!(ShellTool, ::zeroclaw_api::attribution::ToolKind::Shell);
12/// ```
13#[macro_export]
14macro_rules! tool_attribution {
15    ($ty:ty, $kind:expr) => {
16        impl $crate::attribution::Attributable for $ty {
17            fn role(&self) -> $crate::attribution::Role {
18                $crate::attribution::Role::Tool($kind)
19            }
20            fn alias(&self) -> &str {
21                <Self as $crate::tool::Tool>::name(self)
22            }
23        }
24    };
25}
26
27/// Bulk-impl `Attributable` for one or more `Tool` mock types in a
28/// test module. Every type gets `Role::Tool(ToolKind::Plugin)` and uses
29/// the mock's own `name()` as the alias — sufficient for test
30/// scaffolding where individual kinds don't matter.
31///
32/// ```ignore
33/// zeroclaw_api::mock_tool_attribution!(CountingTool, FailingTool);
34/// ```
35#[macro_export]
36macro_rules! mock_tool_attribution {
37    ($($ty:ty),+ $(,)?) => {
38        $(
39            $crate::tool_attribution!($ty, $crate::attribution::ToolKind::Plugin);
40        )+
41    };
42}
43
44/// Result of a tool execution
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ToolResult {
47    pub success: bool,
48    pub output: String,
49    pub error: Option<String>,
50}
51
52/// Description of a tool for the LLM
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ToolSpec {
55    pub name: String,
56    pub description: String,
57    pub parameters: serde_json::Value,
58}
59
60/// Core tool trait — implement for any capability.
61///
62/// Every `Tool` is `Attributable`: log emissions and audit traces from
63/// a tool call carry the same `<kind>.<alias>` composite the rest of
64/// the runtime uses for channels, providers, and memory. The supertrait
65/// bound makes `&dyn Tool` coerce to `&dyn Attributable` automatically,
66/// so dispatch-site logging can attribute without knowing the concrete
67/// tool type.
68#[async_trait]
69pub trait Tool: Send + Sync + crate::attribution::Attributable {
70    /// Tool name (used in LLM function calling)
71    fn name(&self) -> &str;
72
73    /// Human-readable description
74    fn description(&self) -> &str;
75
76    /// JSON schema for parameters
77    fn parameters_schema(&self) -> serde_json::Value;
78
79    /// Execute the tool with given arguments
80    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult>;
81
82    /// Get the full spec for LLM registration
83    fn spec(&self) -> ToolSpec {
84        ToolSpec {
85            name: self.name().to_string(),
86            description: self.description().to_string(),
87            parameters: self.parameters_schema(),
88        }
89    }
90}