use std::collections::HashMap;
use std::sync::Arc;
use crate::registry::ToolRegistry;
use crate::tool::SharedTool;
pub trait ToolSource: Send + Sync {
fn name(&self) -> &str;
fn tools(&self) -> Vec<SharedTool>;
}
#[derive(Clone)]
pub struct StaticToolSource {
name: String,
tools: Vec<SharedTool>,
}
impl std::fmt::Debug for StaticToolSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StaticToolSource")
.field("name", &self.name)
.field("tools", &self.tools.len())
.finish()
}
}
impl StaticToolSource {
#[must_use]
pub fn new(name: impl Into<String>, tools: Vec<SharedTool>) -> Self {
Self {
name: name.into(),
tools,
}
}
}
impl ToolSource for StaticToolSource {
fn name(&self) -> &str {
&self.name
}
fn tools(&self) -> Vec<SharedTool> {
self.tools.clone()
}
}
#[must_use]
pub fn merge_tool_sources<'a>(
sources: impl IntoIterator<Item = &'a dyn ToolSource>,
) -> ToolRegistry {
let mut map: HashMap<String, SharedTool> = HashMap::new();
for source in sources {
for tool in source.tools() {
map.insert(tool.name().to_owned(), tool);
}
}
ToolRegistry::from_tools(map.into_values().collect())
}
#[must_use]
pub fn merge_arc_sources(sources: &[Arc<dyn ToolSource>]) -> ToolRegistry {
let refs: Vec<&dyn ToolSource> = sources.iter().map(AsRef::as_ref).collect();
merge_tool_sources(refs)
}
#[cfg(test)]
mod tests {
use async_trait::async_trait;
use serde_json::{Value, json};
use super::*;
use crate::calc::CalcTool;
use crate::tool::DynTool;
struct NamedTool {
n: &'static str,
}
#[async_trait]
impl DynTool for NamedTool {
fn name(&self) -> &str {
self.n
}
fn description(&self) -> &str {
"t"
}
fn parameters(&self) -> Value {
json!({"type":"object","properties":{}})
}
async fn call(
&self,
_ctx: crate::context::ToolCallContext,
_arguments: Value,
) -> Result<crate::tool::ToolResult, crate::error::ToolError> {
Ok(crate::tool::ToolResult::text(self.n))
}
}
#[test]
fn last_source_wins_on_name() {
let a = StaticToolSource::new("a", vec![Arc::new(NamedTool { n: "dup" })]);
let b = StaticToolSource::new(
"b",
vec![Arc::new(NamedTool { n: "dup" }), Arc::new(CalcTool)],
);
let sources: [&dyn ToolSource; 2] = [&a, &b];
let reg = merge_tool_sources(sources);
assert_eq!(reg.len(), 2);
assert_eq!(reg.get("dup").expect("dup").name(), "dup");
assert!(reg.get(CalcTool.name()).is_some());
assert_eq!(reg.names().len(), 2);
}
#[test]
fn empty_merge() {
let reg = merge_tool_sources([]);
assert!(reg.is_empty());
}
}