use std::sync::Arc;
use async_trait::async_trait;
use atomr_agents_callable::CallableHandle;
use atomr_agents_core::{AgentContext, Result, TokenBudget};
use atomr_agents_strategy::{ToolRef, ToolStrategy};
use crate::r#trait::{DynTool, ToolCallable};
fn dyn_tool_to_callable(t: DynTool) -> CallableHandle {
Arc::new(DynToolCallable { inner: t })
}
struct DynToolCallable {
inner: DynTool,
}
#[async_trait::async_trait]
impl atomr_agents_callable::Callable for DynToolCallable {
async fn call(
&self,
input: atomr_agents_core::Value,
ctx: atomr_agents_core::CallCtx,
) -> atomr_agents_core::Result<atomr_agents_core::Value> {
let invoke_ctx = atomr_agents_core::InvokeCtx {
call: ctx,
tool_call_id: String::new(),
raw_args: input.clone(),
};
self.inner.invoke(input, &invoke_ctx).await
}
fn label(&self) -> &str {
&self.inner.descriptor().name
}
}
pub struct StaticToolStrategy {
tools: Vec<DynTool>,
}
impl StaticToolStrategy {
pub fn new(tools: Vec<DynTool>) -> Self {
Self { tools }
}
}
#[async_trait]
impl ToolStrategy for StaticToolStrategy {
async fn select(&self, _ctx: &AgentContext, _budget: &mut TokenBudget) -> Result<Vec<ToolRef>> {
Ok(self
.tools
.iter()
.map(|t| {
let d = t.descriptor();
ToolRef {
id: d.id.clone(),
name: d.name.clone(),
handle: dyn_tool_to_callable(t.clone()),
}
})
.collect())
}
}
pub struct KeywordToolStrategy {
tools: Vec<DynTool>,
max_tools: usize,
}
impl KeywordToolStrategy {
pub fn new(tools: Vec<DynTool>, max_tools: usize) -> Self {
Self { tools, max_tools }
}
}
#[async_trait]
impl ToolStrategy for KeywordToolStrategy {
async fn select(&self, ctx: &AgentContext, _budget: &mut TokenBudget) -> Result<Vec<ToolRef>> {
let needle = ctx.turn.user.to_lowercase();
let words: Vec<&str> = needle
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
.collect();
let mut scored: Vec<(usize, &DynTool)> = self
.tools
.iter()
.map(|t| {
let d = t.descriptor();
let hay = format!("{} {}", d.name.to_lowercase(), d.description.to_lowercase());
let score = words.iter().filter(|w| hay.contains(*w)).count();
(score, t)
})
.filter(|(s, _)| *s > 0)
.collect();
scored.sort_by_key(|(score, _)| std::cmp::Reverse(*score));
scored.truncate(self.max_tools);
Ok(scored
.into_iter()
.map(|(_, t)| {
let d = t.descriptor();
ToolRef {
id: d.id.clone(),
name: d.name.clone(),
handle: dyn_tool_to_callable(t.clone()),
}
})
.collect())
}
}
#[allow(dead_code)]
fn _keep_tool_callable_alive<T: crate::r#trait::Tool>(t: T) -> ToolCallable<T> {
ToolCallable::new(t)
}