use std::collections::HashMap;
use std::sync::Arc;
use lc_chains::base::BaseChain;
pub trait SkillRouter: Send + Sync {
fn chain_for(&self, skill_id: &str) -> Option<Arc<dyn BaseChain>>;
}
#[derive(Default)]
pub struct SkillMapRouter {
skills: HashMap<String, Arc<dyn BaseChain>>,
}
impl SkillMapRouter {
pub fn new() -> Self {
Self::default()
}
pub fn with_skill(mut self, skill_id: impl Into<String>, chain: Arc<dyn BaseChain>) -> Self {
self.skills.insert(skill_id.into(), chain);
self
}
}
impl SkillRouter for SkillMapRouter {
fn chain_for(&self, skill_id: &str) -> Option<Arc<dyn BaseChain>> {
self.skills.get(skill_id).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
use lc_chains::base::{ChainError, ChainResult};
use serde_json::Value;
struct NamedChain(String);
#[async_trait::async_trait]
impl BaseChain for NamedChain {
fn input_keys(&self) -> Vec<&str> {
vec!["input"]
}
fn output_keys(&self) -> Vec<&str> {
vec!["output"]
}
async fn invoke(&self, _inputs: HashMap<String, Value>) -> Result<ChainResult, ChainError> {
let mut out = HashMap::new();
out.insert("output".to_string(), Value::String(self.0.clone()));
Ok(out)
}
fn name(&self) -> &str {
&self.0
}
}
fn arc_named(name: &str) -> Arc<dyn BaseChain> {
Arc::new(NamedChain(name.to_string()))
}
#[test]
fn empty_router_falls_through() {
let router = SkillMapRouter::new();
assert!(router.chain_for("anything").is_none());
}
#[test]
fn routes_by_skill_id() {
let router = SkillMapRouter::new()
.with_skill("research", arc_named("research-chain"))
.with_skill("summarize", arc_named("summary-chain"));
assert!(router.chain_for("research").is_some());
assert!(router.chain_for("summarize").is_some());
assert!(router.chain_for("nope").is_none());
let a = router.chain_for("research").unwrap();
let b = router.chain_for("summarize").unwrap();
assert_ne!(a.name(), b.name());
}
}