use std::sync::Arc;
use super::ids::{ToolId, validate_identifier};
use super::output::{ToolError, ToolOutput};
#[derive(Clone, Default)]
pub(crate) struct SharedTools {
tools: Arc<[Arc<dyn Tool>]>,
}
impl std::fmt::Debug for SharedTools {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("SharedTools")
.field(
"ids",
&self.tools.iter().map(|tool| tool.id()).collect::<Vec<_>>(),
)
.finish()
}
}
impl SharedTools {
pub(crate) fn new(tools: &[Arc<dyn Tool>]) -> Result<Self, ToolRegistryError> {
ToolRegistry::new(tools.iter().map(AsRef::as_ref))?;
Ok(Self {
tools: Arc::from(tools),
})
}
#[must_use]
pub(crate) fn registry(&self) -> ToolRegistry<'_> {
ToolRegistry::from_unique(self.tools.iter().map(AsRef::as_ref))
}
}
#[derive(Debug)]
#[non_exhaustive]
pub(crate) struct NearDuplicateDiagnostic {
pub(crate) first_alias: String,
pub(crate) first_id: ToolId,
pub(crate) second_alias: String,
pub(crate) second_id: ToolId,
pub(crate) similarity: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ToolRegistryErrorKind {
DuplicateId,
InvalidWireName,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ToolRegistryError {
#[error("duplicate tool identity {id:?} in registry")]
#[non_exhaustive]
DuplicateId {
id: ToolId,
},
#[error("invalid tool wire name {wire_name:?}: {reason}")]
#[non_exhaustive]
InvalidWireName {
wire_name: String,
reason: &'static str,
},
}
impl ToolRegistryError {
#[must_use]
pub fn kind(&self) -> ToolRegistryErrorKind {
match self {
ToolRegistryError::DuplicateId { .. } => ToolRegistryErrorKind::DuplicateId,
ToolRegistryError::InvalidWireName { .. } => ToolRegistryErrorKind::InvalidWireName,
}
}
#[must_use]
pub fn duplicate_id(&self) -> Option<&ToolId> {
match self {
ToolRegistryError::DuplicateId { id } => Some(id),
ToolRegistryError::InvalidWireName { .. } => None,
}
}
}
impl From<ToolRegistryError> for crate::error::Error {
fn from(error: ToolRegistryError) -> Self {
match error {
ToolRegistryError::DuplicateId { id } => {
crate::error::Error::DuplicateLiveToolId { id }
}
invalid @ ToolRegistryError::InvalidWireName { .. } => {
crate::error::Error::InvalidToolWireName {
source: Box::new(invalid),
}
}
}
}
}
#[async_trait::async_trait]
pub trait Tool: Send + Sync {
fn id(&self) -> ToolId;
fn wire_name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> serde_json::Value;
async fn call(&self, args: serde_json::Value) -> Result<ToolOutput, ToolError>;
}
#[non_exhaustive]
pub struct ToolRegistry<'a> {
tools: Vec<&'a dyn Tool>,
ids: Vec<ToolId>,
}
impl std::fmt::Debug for ToolRegistry<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ToolRegistry")
.field("ids", &self.ids)
.finish()
}
}
impl<'a> ToolRegistry<'a> {
pub fn new(
tools: impl IntoIterator<Item = &'a dyn Tool>,
) -> Result<ToolRegistry<'a>, ToolRegistryError> {
let tools: Vec<&'a dyn Tool> = tools.into_iter().collect();
let mut ids = Vec::with_capacity(tools.len());
let mut seen = std::collections::BTreeSet::new();
for tool in &tools {
if let Err(error) = validate_identifier("wire name", tool.wire_name()) {
return Err(ToolRegistryError::InvalidWireName {
wire_name: tool.wire_name().to_owned(),
reason: error.reason(),
});
}
let id = tool.id();
if !seen.insert(id.clone()) {
return Err(ToolRegistryError::DuplicateId { id });
}
ids.push(id);
}
Ok(Self { tools, ids })
}
pub(crate) fn from_unique(tools: impl IntoIterator<Item = &'a dyn Tool>) -> ToolRegistry<'a> {
let tools: Vec<&'a dyn Tool> = tools.into_iter().collect();
let ids = tools.iter().map(|tool| tool.id()).collect();
Self { tools, ids }
}
#[must_use]
pub fn len(&self) -> usize {
self.tools.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tools.is_empty()
}
#[must_use]
pub fn tools(&self) -> &[&'a dyn Tool] {
&self.tools
}
#[must_use]
pub fn get(&self, id: &ToolId) -> Option<&'a dyn Tool> {
self.ids
.iter()
.position(|entry| entry == id)
.map(|index| self.tools[index])
}
}