adk_tool/toolset/
compose.rs1use adk_core::{ReadonlyContext, Result, Tool, ToolContext, ToolPredicate, Toolset};
2use async_trait::async_trait;
3use serde_json::Value;
4use std::sync::Arc;
5
6pub struct FilteredToolset {
24 inner: Arc<dyn Toolset>,
25 predicate: ToolPredicate,
26 name: String,
27}
28
29impl FilteredToolset {
30 pub fn new(inner: Arc<dyn Toolset>, predicate: ToolPredicate) -> Self {
32 let name = format!("{}_filtered", inner.name());
33 Self { inner, predicate, name }
34 }
35
36 pub fn with_name(
38 inner: Arc<dyn Toolset>,
39 predicate: ToolPredicate,
40 name: impl Into<String>,
41 ) -> Self {
42 Self { inner, predicate, name: name.into() }
43 }
44}
45
46#[async_trait]
47impl Toolset for FilteredToolset {
48 fn name(&self) -> &str {
49 &self.name
50 }
51
52 async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
53 let all = self.inner.tools(ctx).await?;
54 Ok(all.into_iter().filter(|t| (self.predicate)(t.as_ref())).collect())
55 }
56}
57
58pub struct MergedToolset {
75 name: String,
76 inner: Vec<Arc<dyn Toolset>>,
77}
78
79impl MergedToolset {
80 pub fn new(name: impl Into<String>, toolsets: Vec<Arc<dyn Toolset>>) -> Self {
82 Self { name: name.into(), inner: toolsets }
83 }
84
85 pub fn with_toolset(mut self, toolset: Arc<dyn Toolset>) -> Self {
87 self.inner.push(toolset);
88 self
89 }
90}
91
92#[async_trait]
93impl Toolset for MergedToolset {
94 fn name(&self) -> &str {
95 &self.name
96 }
97
98 async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
99 let mut seen = std::collections::HashSet::new();
100 let mut merged = Vec::new();
101
102 for toolset in &self.inner {
103 let tools = toolset.tools(ctx.clone()).await?;
104 for tool in tools {
105 let tool_name = tool.name().to_string();
106 if seen.contains(&tool_name) {
107 tracing::warn!(
108 tool.name = %tool_name,
109 toolset.name = %toolset.name(),
110 merged_toolset.name = %self.name,
111 "duplicate tool name in MergedToolset, skipping"
112 );
113 continue;
114 }
115 seen.insert(tool_name);
116 merged.push(tool);
117 }
118 }
119
120 Ok(merged)
121 }
122}
123
124pub struct PrefixedToolset {
139 inner: Arc<dyn Toolset>,
140 prefix: String,
141 name: String,
142}
143
144impl PrefixedToolset {
145 pub fn new(inner: Arc<dyn Toolset>, prefix: impl Into<String>) -> Self {
147 let prefix = prefix.into();
148 let name = format!("{}_{}", prefix, inner.name());
149 Self { inner, prefix, name }
150 }
151}
152
153#[async_trait]
154impl Toolset for PrefixedToolset {
155 fn name(&self) -> &str {
156 &self.name
157 }
158
159 async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
160 let tools = self.inner.tools(ctx).await?;
161 Ok(tools
162 .into_iter()
163 .map(|t| -> Arc<dyn Tool> { Arc::new(PrefixedTool::new(t, &self.prefix)) })
164 .collect())
165 }
166}
167
168struct PrefixedTool {
170 inner: Arc<dyn Tool>,
171 prefixed_name: String,
172 prefixed_description: String,
173}
174
175impl PrefixedTool {
176 fn new(inner: Arc<dyn Tool>, prefix: &str) -> Self {
177 let prefixed_name = format!("{prefix}_{}", inner.name());
178 let prefixed_description = inner.description().to_string();
179 Self { inner, prefixed_name, prefixed_description }
180 }
181}
182
183#[async_trait]
184impl Tool for PrefixedTool {
185 fn name(&self) -> &str {
186 &self.prefixed_name
187 }
188
189 fn description(&self) -> &str {
190 &self.prefixed_description
191 }
192
193 fn enhanced_description(&self) -> String {
194 self.inner.enhanced_description()
195 }
196
197 fn is_long_running(&self) -> bool {
198 self.inner.is_long_running()
199 }
200
201 fn parameters_schema(&self) -> Option<Value> {
202 self.inner.parameters_schema()
203 }
204
205 fn response_schema(&self) -> Option<Value> {
206 self.inner.response_schema()
207 }
208
209 fn required_scopes(&self) -> &[&str] {
210 self.inner.required_scopes()
211 }
212
213 async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
214 self.inner.execute(ctx, args).await
215 }
216}