turul_a2a_patterns/
registry.rs1use std::collections::BTreeMap;
8use std::sync::{Arc, Mutex};
9
10use async_trait::async_trait;
11use serde_json::Value;
12
13use turul_a2a_proto::AgentSkill;
14
15use crate::error::SkillError;
16use crate::handler::SkillHandler;
17use crate::manifest::SkillCard;
18
19#[derive(Debug, Clone)]
27#[non_exhaustive]
28pub struct SkillDescriptor {
29 pub id: String,
30 pub agent_skill: AgentSkill,
31 pub params_schema: Option<Value>,
32}
33
34#[async_trait]
36pub trait SkillRegistry: Send + Sync {
37 async fn register_manifest(
41 &self,
42 card: SkillCard,
43 handler: Arc<dyn SkillHandler>,
44 ) -> Result<(), SkillError>;
45
46 async fn register_programmatic(
50 &self,
51 agent_skill: AgentSkill,
52 params_schema: Option<Value>,
53 handler: Arc<dyn SkillHandler>,
54 ) -> Result<(), SkillError>;
55
56 async fn describe(&self, id: &str) -> Option<SkillDescriptor>;
58
59 async fn list(&self) -> Vec<SkillDescriptor>;
61
62 async fn handler(&self, id: &str) -> Option<Arc<dyn SkillHandler>>;
64}
65
66struct Registration {
67 descriptor: SkillDescriptor,
68 handler: Arc<dyn SkillHandler>,
69}
70
71#[derive(Default)]
78pub struct InMemorySkillRegistry {
79 inner: Mutex<BTreeMap<String, Registration>>,
80}
81
82impl InMemorySkillRegistry {
83 pub fn new() -> Self {
84 Self::default()
85 }
86}
87
88#[async_trait]
89impl SkillRegistry for InMemorySkillRegistry {
90 async fn register_manifest(
91 &self,
92 card: SkillCard,
93 handler: Arc<dyn SkillHandler>,
94 ) -> Result<(), SkillError> {
95 let agent_skill = card.to_agent_skill();
98 let descriptor = SkillDescriptor {
99 id: card.id.clone(),
100 agent_skill,
101 params_schema: card.input_schema.clone(),
102 };
103 self.insert(descriptor, handler)
104 }
105
106 async fn register_programmatic(
107 &self,
108 agent_skill: AgentSkill,
109 params_schema: Option<Value>,
110 handler: Arc<dyn SkillHandler>,
111 ) -> Result<(), SkillError> {
112 let descriptor = SkillDescriptor {
113 id: agent_skill.id.clone(),
114 agent_skill,
115 params_schema,
116 };
117 self.insert(descriptor, handler)
118 }
119
120 async fn describe(&self, id: &str) -> Option<SkillDescriptor> {
121 let guard = self.inner.lock().ok()?;
122 guard.get(id).map(|r| r.descriptor.clone())
123 }
124
125 async fn list(&self) -> Vec<SkillDescriptor> {
126 match self.inner.lock() {
127 Ok(g) => g.values().map(|r| r.descriptor.clone()).collect(),
128 Err(_) => Vec::new(),
129 }
130 }
131
132 async fn handler(&self, id: &str) -> Option<Arc<dyn SkillHandler>> {
133 let guard = self.inner.lock().ok()?;
134 guard.get(id).map(|r| r.handler.clone())
135 }
136}
137
138impl InMemorySkillRegistry {
139 fn insert(
140 &self,
141 descriptor: SkillDescriptor,
142 handler: Arc<dyn SkillHandler>,
143 ) -> Result<(), SkillError> {
144 let mut guard = self
145 .inner
146 .lock()
147 .map_err(|e| SkillError::Internal(format!("registry mutex poisoned: {e}")))?;
148 if descriptor.id.is_empty() {
149 return Err(SkillError::InvalidRequest(
150 "skill id must not be empty".to_string(),
151 ));
152 }
153 guard.insert(
154 descriptor.id.clone(),
155 Registration {
156 descriptor,
157 handler,
158 },
159 );
160 Ok(())
161 }
162}