mod types;
use std::sync::Arc;
use crate::error::{Result, TinyAgentsError};
use crate::harness::model::{ChatModel, ModelRegistry};
use crate::harness::tool::{Tool, ToolRegistry};
use crate::language::Blueprint;
use crate::language::compiler::CapabilityResolver;
use crate::registry::component::{ComponentKind, ComponentMetadata};
pub use types::*;
impl<State: Send + Sync> CapabilityRegistry<State> {
pub fn new() -> Self {
Self {
models: std::collections::HashMap::new(),
tools: std::collections::HashMap::new(),
graphs: std::collections::HashMap::new(),
meta: std::collections::HashMap::new(),
aliases: std::collections::HashMap::new(),
}
}
fn ensure_absent(&self, kind: ComponentKind, name: &str) -> Result<()> {
if self.meta.contains_key(&(kind, name.to_owned())) {
return Err(TinyAgentsError::DuplicateComponent(format!(
"{kind} `{name}` is already registered"
)));
}
Ok(())
}
fn record_meta(&mut self, kind: ComponentKind, name: &str) {
self.meta
.entry((kind, name.to_owned()))
.or_insert_with(|| ComponentMetadata::new(name, kind));
}
pub fn register_model(
&mut self,
name: impl Into<String>,
model: Arc<dyn ChatModel<State>>,
) -> Result<&mut Self> {
let name = name.into();
self.ensure_absent(ComponentKind::Model, &name)?;
self.record_meta(ComponentKind::Model, &name);
self.models.insert(name, model);
Ok(self)
}
pub fn replace_model(
&mut self,
name: impl Into<String>,
model: Arc<dyn ChatModel<State>>,
) -> &mut Self {
let name = name.into();
self.record_meta(ComponentKind::Model, &name);
self.models.insert(name, model);
self
}
pub fn register_tool(&mut self, tool: Arc<dyn Tool<State>>) -> Result<&mut Self> {
let name = tool.name().to_owned();
self.ensure_absent(ComponentKind::Tool, &name)?;
self.record_meta(ComponentKind::Tool, &name);
self.tools.insert(name, tool);
Ok(self)
}
pub fn replace_tool(&mut self, tool: Arc<dyn Tool<State>>) -> &mut Self {
let name = tool.name().to_owned();
self.record_meta(ComponentKind::Tool, &name);
self.tools.insert(name, tool);
self
}
pub fn register_graph_blueprint(
&mut self,
name: impl Into<String>,
blueprint: Blueprint,
) -> Result<&mut Self> {
let name = name.into();
self.ensure_absent(ComponentKind::Graph, &name)?;
self.record_meta(ComponentKind::Graph, &name);
self.graphs.insert(name, blueprint);
Ok(self)
}
pub fn replace_graph_blueprint(
&mut self,
name: impl Into<String>,
blueprint: Blueprint,
) -> &mut Self {
let name = name.into();
self.record_meta(ComponentKind::Graph, &name);
self.graphs.insert(name, blueprint);
self
}
pub fn register_router(&mut self, name: impl Into<String>) -> Result<&mut Self> {
self.register_descriptor(ComponentKind::Router, name.into())
}
pub fn register_reducer(&mut self, name: impl Into<String>) -> Result<&mut Self> {
self.register_descriptor(ComponentKind::Reducer, name.into())
}
pub fn register_descriptor(
&mut self,
kind: ComponentKind,
name: impl Into<String>,
) -> Result<&mut Self> {
let name = name.into();
self.ensure_absent(kind, &name)?;
self.record_meta(kind, &name);
Ok(self)
}
pub fn alias(
&mut self,
kind: ComponentKind,
alias: impl Into<String>,
target: impl Into<String>,
) -> Result<&mut Self> {
let alias = alias.into();
let target = target.into();
if !self.meta.contains_key(&(kind, target.clone())) {
return Err(TinyAgentsError::Capability(format!(
"cannot alias {kind} `{alias}` -> `{target}`: target is not registered"
)));
}
if self.meta.contains_key(&(kind, alias.clone())) {
return Err(TinyAgentsError::DuplicateComponent(format!(
"{kind} `{alias}` is already a registered component"
)));
}
if self.aliases.contains_key(&(kind, alias.clone())) {
return Err(TinyAgentsError::DuplicateComponent(format!(
"{kind} alias `{alias}` is already defined"
)));
}
self.aliases.insert((kind, alias.clone()), target.clone());
if let Some(meta) = self.meta.get_mut(&(kind, target))
&& !meta.aliases.contains(&alias)
{
meta.aliases.push(alias);
}
Ok(self)
}
pub fn resolve_name(&self, kind: ComponentKind, name: &str) -> Option<String> {
if self.meta.contains_key(&(kind, name.to_owned())) {
return Some(name.to_owned());
}
let target = self.aliases.get(&(kind, name.to_owned()))?;
if self.meta.contains_key(&(kind, target.clone())) {
Some(target.clone())
} else {
None
}
}
pub fn model(&self, name: &str) -> Option<Arc<dyn ChatModel<State>>> {
let canonical = self.resolve_name(ComponentKind::Model, name)?;
self.models.get(&canonical).cloned()
}
pub fn tool(&self, name: &str) -> Option<Arc<dyn Tool<State>>> {
let canonical = self.resolve_name(ComponentKind::Tool, name)?;
self.tools.get(&canonical).cloned()
}
pub fn graph_blueprint(&self, name: &str) -> Option<&Blueprint> {
let canonical = self.resolve_name(ComponentKind::Graph, name)?;
self.graphs.get(&canonical)
}
pub fn has(&self, kind: ComponentKind, name: &str) -> bool {
self.resolve_name(kind, name).is_some()
}
pub fn names(&self, kind: ComponentKind) -> Vec<String> {
let mut names: Vec<String> = self
.meta
.keys()
.filter(|(k, _)| *k == kind)
.map(|(_, name)| name.clone())
.collect();
names.sort();
names
}
pub fn names_including_aliases(&self, kind: ComponentKind) -> Vec<String> {
let mut names = self.names(kind);
for (k, alias) in self.aliases.keys() {
if *k == kind {
names.push(alias.clone());
}
}
names.sort();
names.dedup();
names
}
pub fn metadata(&self, kind: ComponentKind, name: &str) -> Option<&ComponentMetadata> {
let canonical = self.resolve_name(kind, name)?;
self.meta.get(&(kind, canonical))
}
pub fn to_model_registry(&self) -> ModelRegistry<State> {
let mut registry = ModelRegistry::new();
for (name, model) in &self.models {
registry.register(name.clone(), model.clone());
}
for ((kind, alias), target) in &self.aliases {
if *kind == ComponentKind::Model
&& let Some(model) = self.models.get(target)
{
registry.register(alias.clone(), model.clone());
}
}
registry
}
pub fn to_tool_registry(&self) -> ToolRegistry<State> {
let mut registry = ToolRegistry::new();
for tool in self.tools.values() {
registry.register(tool.clone());
}
registry
}
pub fn capability_resolver(&self) -> CapabilityResolver {
CapabilityResolver::from_registry(self)
}
}
impl<State: Send + Sync> Default for CapabilityRegistry<State> {
fn default() -> Self {
Self::new()
}
}
impl<State: Send + Sync> std::fmt::Debug for CapabilityRegistry<State> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("CapabilityRegistry");
for kind in ComponentKind::ALL {
dbg.field(kind.as_str(), &self.names(kind));
}
dbg.field("aliases", &self.aliases).finish()
}
}
#[cfg(test)]
mod test;