adk_tool/
function_tool.rs1use adk_core::{Result, Tool, ToolContext};
2use async_trait::async_trait;
3use schemars::{JsonSchema, schema::RootSchema};
4use serde::Serialize;
5use serde_json::Value;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10type AsyncHandler = Box<
11 dyn Fn(Arc<dyn ToolContext>, Value) -> Pin<Box<dyn Future<Output = Result<Value>> + Send>>
12 + Send
13 + Sync,
14>;
15
16pub struct FunctionTool {
17 name: String,
18 description: String,
19 handler: AsyncHandler,
20 long_running: bool,
21 read_only: bool,
22 concurrency_safe: bool,
23 parameters_schema: Option<Value>,
24 response_schema: Option<Value>,
25 scopes: Vec<&'static str>,
26}
27
28impl FunctionTool {
29 pub fn new<F, Fut>(name: impl Into<String>, description: impl Into<String>, handler: F) -> Self
30 where
31 F: Fn(Arc<dyn ToolContext>, Value) -> Fut + Send + Sync + 'static,
32 Fut: Future<Output = Result<Value>> + Send + 'static,
33 {
34 Self {
35 name: name.into(),
36 description: description.into(),
37 handler: Box::new(move |ctx, args| Box::pin(handler(ctx, args))),
38 long_running: false,
39 read_only: false,
40 concurrency_safe: false,
41 parameters_schema: None,
42 response_schema: None,
43 scopes: Vec::new(),
44 }
45 }
46
47 pub fn with_long_running(mut self, long_running: bool) -> Self {
48 self.long_running = long_running;
49 self
50 }
51
52 pub fn with_read_only(mut self, read_only: bool) -> Self {
53 self.read_only = read_only;
54 self
55 }
56
57 pub fn with_concurrency_safe(mut self, concurrency_safe: bool) -> Self {
58 self.concurrency_safe = concurrency_safe;
59 self
60 }
61
62 pub fn with_parameters_schema<T>(mut self) -> Self
63 where
64 T: JsonSchema + Serialize,
65 {
66 self.parameters_schema = Some(generate_schema::<T>());
67 self
68 }
69
70 pub fn with_response_schema<T>(mut self) -> Self
71 where
72 T: JsonSchema + Serialize,
73 {
74 self.response_schema = Some(generate_schema::<T>());
75 self
76 }
77
78 pub fn with_scopes(mut self, scopes: &[&'static str]) -> Self {
90 self.scopes = scopes.to_vec();
91 self
92 }
93
94 pub fn parameters_schema(&self) -> Option<&Value> {
95 self.parameters_schema.as_ref()
96 }
97
98 pub fn response_schema(&self) -> Option<&Value> {
99 self.response_schema.as_ref()
100 }
101}
102
103const LONG_RUNNING_NOTE: &str = "NOTE: This is a long-running operation. Do not call this tool again if it has already returned some intermediate or pending status.";
105
106#[async_trait]
107impl Tool for FunctionTool {
108 fn name(&self) -> &str {
109 &self.name
110 }
111
112 fn description(&self) -> &str {
113 &self.description
114 }
115
116 fn enhanced_description(&self) -> String {
119 if self.long_running {
120 if self.description.is_empty() {
121 LONG_RUNNING_NOTE.to_string()
122 } else {
123 format!("{}\n\n{}", self.description, LONG_RUNNING_NOTE)
124 }
125 } else {
126 self.description.clone()
127 }
128 }
129
130 fn is_long_running(&self) -> bool {
131 self.long_running
132 }
133
134 fn is_read_only(&self) -> bool {
135 self.read_only
136 }
137
138 fn is_concurrency_safe(&self) -> bool {
139 self.concurrency_safe
140 }
141
142 fn parameters_schema(&self) -> Option<Value> {
143 self.parameters_schema.clone()
144 }
145
146 fn response_schema(&self) -> Option<Value> {
147 self.response_schema.clone()
148 }
149
150 fn required_scopes(&self) -> &[&str] {
151 &self.scopes
152 }
153
154 #[adk_telemetry::instrument(
155 skip(self, ctx, args),
156 fields(
157 tool.name = %self.name,
158 tool.description = %self.description,
159 tool.long_running = %self.long_running,
160 function_call.id = %ctx.function_call_id()
161 )
162 )]
163 async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
164 adk_telemetry::debug!("Executing tool");
165 (self.handler)(ctx, args).await
166 }
167}
168
169fn generate_schema<T>() -> Value
170where
171 T: JsonSchema + Serialize,
172{
173 let settings = schemars::r#gen::SchemaSettings::openapi3().with(|s| {
174 s.inline_subschemas = true;
175 s.meta_schema = None;
176 });
177 let generator = schemars::r#gen::SchemaGenerator::new(settings);
178 let mut schema: RootSchema = generator.into_root_schema_for::<T>();
179 schema.schema.metadata().title = None;
180 serde_json::to_value(schema).unwrap()
181}