1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! The `Tool` trait — the Hand contract (invariant 4).
//!
//! `Tool<D>` is intentionally minimal: a single [`ToolMetadata`]
//! accessor and a single `execute(input, ctx) → output` method. No
//! `prepare`, no `cleanup`, no back-channels. Anything else lives
//! in [`crate::AgentContext`] (request-scope carrier with infra
//! context + typed operator-side deps `D`) or in adapter types.
//!
//! `D` defaults to `()` so deps-less tools stay `Tool` without an
//! annotation. Tools that need typed operator-side handles (DB
//! pool, HTTP client, tenant config) declare `Tool<MyDeps>` and
//! reach for them via `ctx.deps()`. .
//!
//! Per, `Tool` does NOT extend `Runnable`. The
//! `entelix-runnable::adapter::ToolToRunnableAdapter` provides the
//! bridge when composition (`.pipe()`) is needed.
use async_trait;
use crateAgentContext;
use crateResult;
use crateToolMetadata;
/// A capability the agent can dispatch.
///
/// Implementors hold a [`ToolMetadata`] (typically constructed once
/// in `new()`) and return a borrow from [`Tool::metadata`]. The
/// runtime treats that struct as authoritative — codecs render it
/// into the on-the-wire `ToolSpec`, OTel layers stamp
/// `gen_ai.tool.*` attributes, and `Approver` defaults route off
/// `metadata.effect`.
///
/// # Implementing a tool
///
/// ```ignore
/// use async_trait::async_trait;
/// use entelix_core::tools::{Tool, ToolMetadata};
/// use entelix_core::{AgentContext, Result};
///
/// pub struct EchoTool {
/// metadata: ToolMetadata,
/// }
///
/// impl EchoTool {
/// pub fn new() -> Self {
/// Self {
/// metadata: ToolMetadata::function(
/// "echo",
/// "Echoes its input verbatim.",
/// serde_json::json!({ "type": "object" }),
/// ),
/// }
/// }
/// }
///
/// #[async_trait]
/// impl Tool for EchoTool {
/// fn metadata(&self) -> &ToolMetadata {
/// &self.metadata
/// }
///
/// async fn execute(
/// &self,
/// input: serde_json::Value,
/// _ctx: &AgentContext,
/// ) -> Result<serde_json::Value> {
/// Ok(input)
/// }
/// }
/// ```