Skip to main content

atomr_agents_tool/
trait.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use atomr_agents_callable::Callable;
5use atomr_agents_core::{CallCtx, InvokeCtx, Result, Value};
6
7use crate::descriptor::ToolDescriptor;
8
9/// A tool the agent may call. Tools are also `Callable`, so they
10/// can stand in as workflow steps.
11#[async_trait]
12pub trait Tool: Send + Sync + 'static {
13    fn descriptor(&self) -> &ToolDescriptor;
14    async fn invoke(&self, args: Value, ctx: &InvokeCtx) -> Result<Value>;
15}
16
17/// `Arc<dyn Tool>` — what the registry stores.
18pub type DynTool = Arc<dyn Tool>;
19
20/// Adapter so any `Tool` exposes itself as a `Callable`. The adapter
21/// fabricates an `InvokeCtx` by promoting the `CallCtx` and using a
22/// synthetic tool-call-id.
23pub struct ToolCallable<T: Tool> {
24    inner: Arc<T>,
25}
26
27impl<T: Tool> ToolCallable<T> {
28    pub fn new(inner: T) -> Self {
29        Self {
30            inner: Arc::new(inner),
31        }
32    }
33
34    #[allow(dead_code)]
35    pub fn from_arc(inner: Arc<T>) -> Self {
36        Self { inner }
37    }
38}
39
40#[async_trait]
41impl<T: Tool> Callable for ToolCallable<T> {
42    async fn call(&self, input: Value, ctx: CallCtx) -> Result<Value> {
43        let invoke_ctx = InvokeCtx {
44            call: ctx,
45            tool_call_id: format!("synth-{}", uuid_like()),
46            raw_args: input.clone(),
47        };
48        self.inner.invoke(input, &invoke_ctx).await
49    }
50
51    fn label(&self) -> &str {
52        &self.inner.descriptor().name
53    }
54}
55
56fn uuid_like() -> String {
57    // Tiny non-crypto id; uuid is in atomr-agents-core but not
58    // re-exported here to keep the dep graph small.
59    use std::sync::atomic::{AtomicU64, Ordering};
60    static N: AtomicU64 = AtomicU64::new(0);
61    format!("{:016x}", N.fetch_add(1, Ordering::Relaxed))
62}