atomr_agents_tool/
strategies.rs1use std::sync::Arc;
2
3use async_trait::async_trait;
4use atomr_agents_callable::CallableHandle;
5use atomr_agents_core::{AgentContext, Result, TokenBudget};
6use atomr_agents_strategy::{ToolRef, ToolStrategy};
7
8use crate::r#trait::{DynTool, ToolCallable};
9
10fn dyn_tool_to_callable(t: DynTool) -> CallableHandle {
11 Arc::new(DynToolCallable { inner: t })
15}
16
17struct DynToolCallable {
18 inner: DynTool,
19}
20
21#[async_trait::async_trait]
22impl atomr_agents_callable::Callable for DynToolCallable {
23 async fn call(
24 &self,
25 input: atomr_agents_core::Value,
26 ctx: atomr_agents_core::CallCtx,
27 ) -> atomr_agents_core::Result<atomr_agents_core::Value> {
28 let invoke_ctx = atomr_agents_core::InvokeCtx {
29 call: ctx,
30 tool_call_id: String::new(),
31 raw_args: input.clone(),
32 };
33 self.inner.invoke(input, &invoke_ctx).await
34 }
35
36 fn label(&self) -> &str {
37 &self.inner.descriptor().name
38 }
39}
40
41pub struct StaticToolStrategy {
43 tools: Vec<DynTool>,
44}
45
46impl StaticToolStrategy {
47 pub fn new(tools: Vec<DynTool>) -> Self {
48 Self { tools }
49 }
50}
51
52#[async_trait]
53impl ToolStrategy for StaticToolStrategy {
54 async fn select(&self, _ctx: &AgentContext, _budget: &mut TokenBudget) -> Result<Vec<ToolRef>> {
55 Ok(self
56 .tools
57 .iter()
58 .map(|t| {
59 let d = t.descriptor();
60 ToolRef {
61 id: d.id.clone(),
62 name: d.name.clone(),
63 handle: dyn_tool_to_callable(t.clone()),
64 }
65 })
66 .collect())
67 }
68}
69
70pub struct KeywordToolStrategy {
74 tools: Vec<DynTool>,
75 max_tools: usize,
77}
78
79impl KeywordToolStrategy {
80 pub fn new(tools: Vec<DynTool>, max_tools: usize) -> Self {
81 Self { tools, max_tools }
82 }
83}
84
85#[async_trait]
86impl ToolStrategy for KeywordToolStrategy {
87 async fn select(&self, ctx: &AgentContext, _budget: &mut TokenBudget) -> Result<Vec<ToolRef>> {
88 let needle = ctx.turn.user.to_lowercase();
89 let words: Vec<&str> = needle
90 .split(|c: char| !c.is_alphanumeric())
91 .filter(|w| !w.is_empty())
92 .collect();
93 let mut scored: Vec<(usize, &DynTool)> = self
94 .tools
95 .iter()
96 .map(|t| {
97 let d = t.descriptor();
98 let hay = format!("{} {}", d.name.to_lowercase(), d.description.to_lowercase());
99 let score = words.iter().filter(|w| hay.contains(*w)).count();
100 (score, t)
101 })
102 .filter(|(s, _)| *s > 0)
103 .collect();
104 scored.sort_by_key(|(score, _)| std::cmp::Reverse(*score));
105 scored.truncate(self.max_tools);
106 Ok(scored
107 .into_iter()
108 .map(|(_, t)| {
109 let d = t.descriptor();
110 ToolRef {
111 id: d.id.clone(),
112 name: d.name.clone(),
113 handle: dyn_tool_to_callable(t.clone()),
114 }
115 })
116 .collect())
117 }
118}
119
120#[allow(dead_code)]
123fn _keep_tool_callable_alive<T: crate::r#trait::Tool>(t: T) -> ToolCallable<T> {
124 ToolCallable::new(t)
125}