1use agents_core::agent::{ToolHandle, ToolResponse};
8use agents_core::messaging::{AgentMessage, MessageContent, MessageRole, ToolInvocation};
9use async_trait::async_trait;
10use serde_json::Value;
11use std::sync::Arc;
12
13pub fn create_tool<F, Fut>(
14 name: &'static str,
15 _description: &'static str,
16 handler: F,
17) -> Arc<dyn ToolHandle>
18where
19 F: Fn(Value) -> Fut + Send + Sync + 'static,
20 Fut: std::future::Future<Output = anyhow::Result<String>> + Send + 'static,
21{
22 Arc::new(FunctionTool {
23 name,
24 handler: Box::new(move |args| Box::pin(handler(args))),
26 })
27}
28
29pub fn create_sync_tool<F>(
31 name: &'static str,
32 handler: F,
34) -> Arc<dyn ToolHandle>
35where
36 F: Fn(Value) -> anyhow::Result<String> + Send + Sync + 'static,
37{
38 Arc::new(SyncFunctionTool {
39 name,
40 handler: Box::new(handler),
42 })
43}
44
45type AsyncHandler = Box<
47 dyn Fn(
48 Value,
49 )
50 -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<String>> + Send>>
51 + Send
52 + Sync,
53>;
54
55struct FunctionTool {
57 name: &'static str,
58 handler: AsyncHandler,
60}
61
62#[async_trait]
63impl ToolHandle for FunctionTool {
64 fn name(&self) -> &str {
65 self.name
66 }
67
68 async fn invoke(&self, invocation: ToolInvocation) -> anyhow::Result<ToolResponse> {
69 let result = (self.handler)(invocation.args).await?;
70
71 Ok(ToolResponse::Message(AgentMessage {
72 role: MessageRole::Tool,
73 content: MessageContent::Text(result),
74 metadata: invocation
75 .tool_call_id
76 .map(|id| agents_core::messaging::MessageMetadata {
77 tool_call_id: Some(id),
78 cache_control: None,
79 }),
80 }))
81 }
82}
83
84struct SyncFunctionTool {
86 name: &'static str,
87 handler: Box<dyn Fn(Value) -> anyhow::Result<String> + Send + Sync>,
89}
90
91#[async_trait]
92impl ToolHandle for SyncFunctionTool {
93 fn name(&self) -> &str {
94 self.name
95 }
96
97 async fn invoke(&self, invocation: ToolInvocation) -> anyhow::Result<ToolResponse> {
98 let result = (self.handler)(invocation.args)?;
99
100 Ok(ToolResponse::Message(AgentMessage {
101 role: MessageRole::Tool,
102 content: MessageContent::Text(result),
103 metadata: invocation
104 .tool_call_id
105 .map(|id| agents_core::messaging::MessageMetadata {
106 tool_call_id: Some(id),
107 cache_control: None,
108 }),
109 }))
110 }
111}
112
113#[macro_export]
115macro_rules! tool_fn {
116 (
117 name: $name:expr,
118 description: $desc:expr,
119 |$($param:ident: $param_type:ty),*| $body:expr
120 ) => {
121 $crate::create_tool($name, $desc, move |args: serde_json::Value| async move {
122 $(
124 let $param: $param_type = args.get(stringify!($param))
125 .ok_or_else(|| anyhow::anyhow!("Missing required parameter: {}", stringify!($param)))?
126 .clone()
127 .try_into()
128 .map_err(|_| anyhow::anyhow!("Invalid type for parameter: {}", stringify!($param)))?;
129 )*
130
131 $body.await
133 })
134 };
135}