1use std::collections::HashMap;
11use std::sync::{Arc, RwLock};
12use tokio::sync::broadcast;
13
14use rpi_ai::model::Model;
15use rpi_ai::Provider;
16
17#[derive(Debug, Clone)]
19pub enum ModelRegistryEvent {
20 ProviderRegistered { provider_id: String },
22 ProviderUnregistered { provider_id: String },
24 CatalogRefreshed,
26 AuthStatusChanged {
28 provider_id: String,
29 authenticated: bool,
30 },
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum AuthStatus {
36 Unauthenticated,
38 Authenticated,
40 Expired,
42 Unknown,
44}
45
46#[derive(Debug, Clone)]
48pub struct ProviderInfo {
49 pub id: String,
51 pub name: String,
53 pub builtin: bool,
55 pub auth_status: AuthStatus,
57 pub models: Vec<Model>,
59}
60
61pub struct ModelRegistry {
63 providers: Arc<RwLock<HashMap<String, ProviderInfo>>>,
65 provider_instances: Arc<RwLock<HashMap<String, Arc<dyn Provider>>>>,
67 event_tx: broadcast::Sender<ModelRegistryEvent>,
69 last_refresh: Arc<RwLock<Option<std::time::Instant>>>,
71}
72
73impl ModelRegistry {
74 pub fn new() -> Self {
76 let (event_tx, _) = broadcast::channel(100);
77 Self {
78 providers: Arc::new(RwLock::new(HashMap::new())),
79 provider_instances: Arc::new(RwLock::new(HashMap::new())),
80 event_tx,
81 last_refresh: Arc::new(RwLock::new(None)),
82 }
83 }
84
85 pub fn register_builtin_provider(
87 &self,
88 id: String,
89 name: String,
90 models: Vec<Model>,
91 ) -> Result<(), RegistryError> {
92 let info = ProviderInfo {
93 id: id.clone(),
94 name,
95 builtin: true,
96 auth_status: AuthStatus::Unknown,
97 models,
98 };
99
100 let mut providers = self
101 .providers
102 .write()
103 .map_err(|_| RegistryError::LockPoisoned)?;
104 providers.insert(id.clone(), info);
105
106 let _ = self
107 .event_tx
108 .send(ModelRegistryEvent::ProviderRegistered { provider_id: id });
109
110 Ok(())
111 }
112
113 pub fn register_extension_provider(
115 &self,
116 provider: Arc<dyn Provider>,
117 ) -> Result<(), RegistryError> {
118 let id = provider.id().to_string();
119 let models = provider.models().to_vec();
120
121 let info = ProviderInfo {
122 id: id.clone(),
123 name: id.clone(), builtin: false,
125 auth_status: AuthStatus::Unknown,
126 models,
127 };
128
129 let mut providers = self
130 .providers
131 .write()
132 .map_err(|_| RegistryError::LockPoisoned)?;
133 providers.insert(id.clone(), info);
134
135 let mut instances = self
136 .provider_instances
137 .write()
138 .map_err(|_| RegistryError::LockPoisoned)?;
139 instances.insert(id.clone(), provider);
140
141 let _ = self
142 .event_tx
143 .send(ModelRegistryEvent::ProviderRegistered { provider_id: id });
144
145 Ok(())
146 }
147
148 pub fn unregister_provider(&self, provider_id: &str) -> Result<(), RegistryError> {
150 let mut providers = self
151 .providers
152 .write()
153 .map_err(|_| RegistryError::LockPoisoned)?;
154 let mut instances = self
155 .provider_instances
156 .write()
157 .map_err(|_| RegistryError::LockPoisoned)?;
158
159 if providers.remove(provider_id).is_some() {
160 instances.remove(provider_id);
161 let _ = self
162 .event_tx
163 .send(ModelRegistryEvent::ProviderUnregistered {
164 provider_id: provider_id.to_string(),
165 });
166 Ok(())
167 } else {
168 Err(RegistryError::ProviderNotFound(provider_id.to_string()))
169 }
170 }
171
172 pub fn get_provider(&self, provider_id: &str) -> Option<ProviderInfo> {
174 let providers = self.providers.read().ok()?;
175 providers.get(provider_id).cloned()
176 }
177
178 pub fn list_providers(&self) -> Vec<ProviderInfo> {
180 let providers = self
181 .providers
182 .read()
183 .ok()
184 .map(|p| p.values().cloned().collect());
185 providers.unwrap_or_default()
186 }
187
188 pub fn list_models(&self) -> Vec<Model> {
190 let providers = match self.providers.read() {
191 Ok(p) => p,
192 Err(_) => return Vec::new(),
193 };
194
195 providers
196 .values()
197 .flat_map(|info| info.models.clone())
198 .collect()
199 }
200
201 pub fn list_provider_models(&self, provider_id: &str) -> Vec<Model> {
203 let providers = match self.providers.read() {
204 Ok(p) => p,
205 Err(_) => return Vec::new(),
206 };
207
208 providers
209 .get(provider_id)
210 .map(|info| info.models.clone())
211 .unwrap_or_default()
212 }
213
214 pub fn update_auth_status(
216 &self,
217 provider_id: &str,
218 status: AuthStatus,
219 ) -> Result<(), RegistryError> {
220 let mut providers = self
221 .providers
222 .write()
223 .map_err(|_| RegistryError::LockPoisoned)?;
224
225 if let Some(info) = providers.get_mut(provider_id) {
226 info.auth_status = status.clone();
227
228 let authenticated = matches!(status, AuthStatus::Authenticated);
229 let _ = self.event_tx.send(ModelRegistryEvent::AuthStatusChanged {
230 provider_id: provider_id.to_string(),
231 authenticated,
232 });
233
234 Ok(())
235 } else {
236 Err(RegistryError::ProviderNotFound(provider_id.to_string()))
237 }
238 }
239
240 pub fn get_auth_status(&self, provider_id: &str) -> Option<AuthStatus> {
242 let providers = self.providers.read().ok()?;
243 providers
244 .get(provider_id)
245 .map(|info| info.auth_status.clone())
246 }
247
248 pub async fn refresh_catalog(&self) -> Result<(), RegistryError> {
250 let config = crate::config::load_models_config()
252 .map_err(|e| RegistryError::InvalidConfiguration(e.to_string()))?;
253
254 let mut providers = self
256 .providers
257 .write()
258 .map_err(|_| RegistryError::LockPoisoned)?;
259
260 for (provider_id, provider_config) in &config.providers {
265 if let Some(models) = crate::config::provider_to_models(provider_id, provider_config) {
266 if let Some(info) = providers.get_mut(provider_id) {
267 info.models = models;
268 continue;
269 }
270 }
271 let models =
273 crate::config::provider_to_models(provider_id, provider_config).unwrap_or_default();
274 providers.insert(
275 provider_id.clone(),
276 ProviderInfo {
277 id: provider_id.clone(),
278 name: provider_config
279 .name
280 .clone()
281 .unwrap_or_else(|| provider_id.clone()),
282 builtin: false,
283 auth_status: AuthStatus::Unknown,
284 models,
285 },
286 );
287 }
288
289 drop(providers);
290
291 let mut last_refresh = self
293 .last_refresh
294 .write()
295 .map_err(|_| RegistryError::LockPoisoned)?;
296 *last_refresh = Some(std::time::Instant::now());
297
298 let _ = self.event_tx.send(ModelRegistryEvent::CatalogRefreshed);
300
301 Ok(())
302 }
303
304 pub fn last_refresh_time(&self) -> Option<std::time::Instant> {
306 self.last_refresh.read().ok().and_then(|t| *t)
307 }
308
309 pub fn subscribe(&self) -> broadcast::Receiver<ModelRegistryEvent> {
311 self.event_tx.subscribe()
312 }
313
314 pub fn get_provider_instance(&self, provider_id: &str) -> Option<Arc<dyn Provider>> {
316 let instances = self.provider_instances.read().ok()?;
317 instances.get(provider_id).cloned()
318 }
319
320 pub fn has_provider(&self, provider_id: &str) -> bool {
322 let providers = match self.providers.read() {
323 Ok(p) => p,
324 Err(_) => return false,
325 };
326 providers.contains_key(provider_id)
327 }
328
329 pub fn provider_count(&self) -> usize {
331 self.providers.read().map(|p| p.len()).unwrap_or(0)
332 }
333
334 pub fn model_count(&self) -> usize {
336 let providers = match self.providers.read() {
337 Ok(p) => p,
338 Err(_) => return 0,
339 };
340 providers.values().map(|info| info.models.len()).sum()
341 }
342}
343
344impl Default for ModelRegistry {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350#[derive(Debug, thiserror::Error)]
352pub enum RegistryError {
353 #[error("Lock poisoned")]
354 LockPoisoned,
355
356 #[error("Provider not found: {0}")]
357 ProviderNotFound(String),
358
359 #[error("Provider already registered: {0}")]
360 ProviderAlreadyRegistered(String),
361
362 #[error("Invalid provider configuration: {0}")]
363 InvalidConfiguration(String),
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_registry_creation() {
372 let registry = ModelRegistry::new();
373 assert_eq!(registry.provider_count(), 0);
374 assert_eq!(registry.model_count(), 0);
375 }
376
377 #[test]
378 fn test_provider_registration() {
379 let registry = ModelRegistry::new();
380
381 assert!(registry
382 .register_builtin_provider("test".to_string(), "Test Provider".to_string(), vec![])
383 .is_ok());
384 assert_eq!(registry.provider_count(), 1);
385 assert!(registry.has_provider("test"));
386 }
387
388 #[test]
389 fn test_provider_unregistration() {
390 let registry = ModelRegistry::new();
391
392 registry
393 .register_builtin_provider("test".to_string(), "Test Provider".to_string(), vec![])
394 .unwrap();
395 assert!(registry.unregister_provider("test").is_ok());
396 assert_eq!(registry.provider_count(), 0);
397 }
398
399 #[test]
400 fn test_auth_status_update() {
401 let registry = ModelRegistry::new();
402
403 registry
404 .register_builtin_provider("test".to_string(), "Test Provider".to_string(), vec![])
405 .unwrap();
406
407 assert!(registry
408 .update_auth_status("test", AuthStatus::Authenticated)
409 .is_ok());
410 assert_eq!(
411 registry.get_auth_status("test"),
412 Some(AuthStatus::Authenticated)
413 );
414 }
415}