use adk_core::{ReadonlyContext, Result, Tool, ToolContext, ToolPredicate, Toolset};
use async_trait::async_trait;
use serde_json::Value;
use std::sync::Arc;
pub struct FilteredToolset {
inner: Arc<dyn Toolset>,
predicate: ToolPredicate,
name: String,
}
impl FilteredToolset {
pub fn new(inner: Arc<dyn Toolset>, predicate: ToolPredicate) -> Self {
let name = format!("{}_filtered", inner.name());
Self { inner, predicate, name }
}
pub fn with_name(
inner: Arc<dyn Toolset>,
predicate: ToolPredicate,
name: impl Into<String>,
) -> Self {
Self { inner, predicate, name: name.into() }
}
}
#[async_trait]
impl Toolset for FilteredToolset {
fn name(&self) -> &str {
&self.name
}
async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
let all = self.inner.tools(ctx).await?;
Ok(all.into_iter().filter(|t| (self.predicate)(t.as_ref())).collect())
}
}
pub struct MergedToolset {
name: String,
inner: Vec<Arc<dyn Toolset>>,
}
impl MergedToolset {
pub fn new(name: impl Into<String>, toolsets: Vec<Arc<dyn Toolset>>) -> Self {
Self { name: name.into(), inner: toolsets }
}
pub fn with_toolset(mut self, toolset: Arc<dyn Toolset>) -> Self {
self.inner.push(toolset);
self
}
}
#[async_trait]
impl Toolset for MergedToolset {
fn name(&self) -> &str {
&self.name
}
async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
let mut seen = std::collections::HashSet::new();
let mut merged = Vec::new();
for toolset in &self.inner {
let tools = toolset.tools(ctx.clone()).await?;
for tool in tools {
let tool_name = tool.name().to_string();
if seen.contains(&tool_name) {
tracing::warn!(
tool.name = %tool_name,
toolset.name = %toolset.name(),
merged_toolset.name = %self.name,
"duplicate tool name in MergedToolset, skipping"
);
continue;
}
seen.insert(tool_name);
merged.push(tool);
}
}
Ok(merged)
}
}
pub struct PrefixedToolset {
inner: Arc<dyn Toolset>,
prefix: String,
name: String,
}
impl PrefixedToolset {
pub fn new(inner: Arc<dyn Toolset>, prefix: impl Into<String>) -> Self {
let prefix = prefix.into();
let name = format!("{}_{}", prefix, inner.name());
Self { inner, prefix, name }
}
}
#[async_trait]
impl Toolset for PrefixedToolset {
fn name(&self) -> &str {
&self.name
}
async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
let tools = self.inner.tools(ctx).await?;
Ok(tools
.into_iter()
.map(|t| -> Arc<dyn Tool> { Arc::new(PrefixedTool::new(t, &self.prefix)) })
.collect())
}
}
struct PrefixedTool {
inner: Arc<dyn Tool>,
prefixed_name: String,
prefixed_description: String,
}
impl PrefixedTool {
fn new(inner: Arc<dyn Tool>, prefix: &str) -> Self {
let prefixed_name = format!("{prefix}_{}", inner.name());
let prefixed_description = inner.description().to_string();
Self { inner, prefixed_name, prefixed_description }
}
}
#[async_trait]
impl Tool for PrefixedTool {
fn name(&self) -> &str {
&self.prefixed_name
}
fn description(&self) -> &str {
&self.prefixed_description
}
fn enhanced_description(&self) -> String {
self.inner.enhanced_description()
}
fn is_long_running(&self) -> bool {
self.inner.is_long_running()
}
fn parameters_schema(&self) -> Option<Value> {
self.inner.parameters_schema()
}
fn response_schema(&self) -> Option<Value> {
self.inner.response_schema()
}
fn required_scopes(&self) -> &[&str] {
self.inner.required_scopes()
}
async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
self.inner.execute(ctx, args).await
}
}