Coverage for fastblocks/mcp/registry.py: 36%
137 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-09 00:47 -0700
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-09 00:47 -0700
1"""Central registry for FastBlocks adapter management."""
3from contextlib import suppress
4from typing import Any
5from uuid import UUID
7from acb.depends import depends
9from .discovery import AdapterDiscoveryServer, AdapterInfo
12class AdapterRegistry:
13 """Central registry for managing FastBlocks adapters."""
15 def __init__(self) -> None:
16 """Initialize adapter registry."""
17 self.discovery = AdapterDiscoveryServer()
18 self._active_adapters: dict[str, Any] = {}
19 self._adapter_dependencies: dict[str, set[str]] = {}
20 self._adapter_config: dict[str, dict[str, Any]] = {}
22 async def initialize(self) -> None:
23 """Initialize the registry by discovering all adapters."""
24 await self.discovery.discover_adapters()
26 async def register_adapter(self, adapter_name: str, adapter_instance: Any) -> bool:
27 """Register an adapter instance in the registry."""
28 try:
29 # Register with ACB system
30 with suppress(Exception):
31 depends.set(adapter_instance)
33 # Track in our registry
34 self._active_adapters[adapter_name] = adapter_instance
36 return True
37 except Exception:
38 return False
40 async def unregister_adapter(self, adapter_name: str) -> bool:
41 """Unregister an adapter from the registry."""
42 try:
43 if adapter_name in self._active_adapters:
44 del self._active_adapters[adapter_name]
46 # TODO: Remove from ACB registry if possible
47 return True
48 except Exception:
49 return False
51 async def get_adapter(self, adapter_name: str) -> Any | None:
52 """Get an adapter instance by name."""
53 # Try active registry first
54 if adapter_name in self._active_adapters:
55 return self._active_adapters[adapter_name]
57 # Try ACB registry
58 with suppress(Exception): # Try to instantiate from discovery
59 adapter = depends.get(adapter_name)
60 if adapter:
61 self._active_adapters[adapter_name] = adapter
62 return adapter
64 adapter = await self.discovery.instantiate_adapter(adapter_name)
65 if adapter:
66 await self.register_adapter(adapter_name, adapter)
67 return adapter
69 return None
71 async def get_adapter_info(self, adapter_name: str) -> AdapterInfo | None:
72 """Get adapter information by name."""
73 return await self.discovery.get_adapter_by_name(adapter_name)
75 async def list_available_adapters(self) -> dict[str, AdapterInfo]:
76 """List all available adapters."""
77 return await self.discovery.discover_adapters()
79 async def list_active_adapters(self) -> dict[str, Any]:
80 """List currently active (instantiated) adapters."""
81 return self._active_adapters.copy()
83 async def get_adapters_by_category(self, category: str) -> list[AdapterInfo]:
84 """Get all adapters in a specific category."""
85 return await self.discovery.get_adapters_by_category(category)
87 async def get_categories(self) -> list[str]:
88 """Get all available adapter categories."""
89 return await self.discovery.get_all_categories()
91 def _validate_module_id(self, adapter: Any, result: dict[str, Any]) -> None:
92 """Validate adapter MODULE_ID attribute."""
93 if not hasattr(adapter, "MODULE_ID"):
94 result["warnings"].append("Missing MODULE_ID attribute")
95 elif not isinstance(adapter.MODULE_ID, UUID):
96 result["errors"].append("MODULE_ID is not a valid UUID")
98 def _validate_module_status(self, adapter: Any, result: dict[str, Any]) -> None:
99 """Validate adapter MODULE_STATUS attribute."""
100 if not hasattr(adapter, "MODULE_STATUS"):
101 result["warnings"].append("Missing MODULE_STATUS attribute")
102 elif adapter.MODULE_STATUS not in (
103 "stable",
104 "beta",
105 "alpha",
106 "experimental",
107 ):
108 result["warnings"].append(f"Unknown MODULE_STATUS: {adapter.MODULE_STATUS}")
110 def _validate_settings(self, adapter: Any, result: dict[str, Any]) -> None:
111 """Validate adapter settings configuration."""
112 if hasattr(adapter, "settings"):
113 result["info"]["has_settings"] = True
114 settings = adapter.settings
115 if hasattr(settings, "__dict__"):
116 result["info"]["settings_properties"] = list(settings.__dict__.keys())
117 else:
118 result["warnings"].append("No settings attribute found")
120 async def validate_adapter(self, adapter_name: str) -> dict[str, Any]:
121 """Validate an adapter's configuration and functionality."""
122 result: dict[str, Any] = {
123 "valid": False,
124 "errors": [],
125 "warnings": [],
126 "info": {},
127 }
129 try:
130 # Get adapter info
131 info = await self.get_adapter_info(adapter_name)
132 if not info:
133 result["errors"].append(f"Adapter '{adapter_name}' not found")
134 return result
136 result["info"] = info.to_dict()
138 # Try to get/instantiate adapter
139 adapter = await self.get_adapter(adapter_name)
140 if not adapter:
141 result["errors"].append(
142 f"Failed to instantiate adapter '{adapter_name}'"
143 )
144 return result
146 # Validate adapter attributes
147 self._validate_module_id(adapter, result)
148 self._validate_module_status(adapter, result)
149 self._validate_settings(adapter, result)
151 # If we got here without errors, adapter is valid
152 if not result["errors"]:
153 result["valid"] = True
155 except Exception as e:
156 result["errors"].append(f"Validation error: {e}")
158 return result
160 async def auto_register_available_adapters(self) -> dict[str, bool]:
161 """Automatically register all available adapters."""
162 results = {}
163 available_adapters = await self.list_available_adapters()
165 for adapter_name in available_adapters:
166 try:
167 adapter = await self.discovery.instantiate_adapter(adapter_name)
168 if adapter:
169 success = await self.register_adapter(adapter_name, adapter)
170 results[adapter_name] = success
171 else:
172 results[adapter_name] = False
173 except Exception:
174 results[adapter_name] = False
176 return results
178 async def get_adapter_statistics(self) -> dict[str, Any]:
179 """Get comprehensive statistics about adapters."""
180 available = await self.list_available_adapters()
181 active = await self.list_active_adapters()
182 categories = await self.get_categories()
184 stats: dict[str, Any] = {
185 "total_available": len(available),
186 "total_active": len(active),
187 "total_categories": len(categories),
188 "categories": {},
189 "status_breakdown": {},
190 "active_adapters": list(active.keys()),
191 "inactive_adapters": [name for name in available if name not in active],
192 }
194 # Category breakdown
195 categories_dict: dict[str, dict[str, Any]] = {}
196 for category in categories:
197 category_adapters = await self.get_adapters_by_category(category)
198 categories_dict[category] = {
199 "total": len(category_adapters),
200 "adapters": [adapter.name for adapter in category_adapters],
201 }
202 stats["categories"] = categories_dict
204 # Status breakdown
205 status_breakdown: dict[str, int] = {}
206 for adapter_info in available.values():
207 status = adapter_info.module_status
208 if status not in status_breakdown:
209 status_breakdown[status] = 0
210 status_breakdown[status] += 1
211 stats["status_breakdown"] = status_breakdown
213 return stats
215 def configure_adapter(self, adapter_name: str, config: dict[str, Any]) -> None:
216 """Configure an adapter with custom settings."""
217 self._adapter_config[adapter_name] = config
219 def get_adapter_config(self, adapter_name: str) -> dict[str, Any]:
220 """Get configuration for an adapter."""
221 return self._adapter_config.get(adapter_name, {})
223 def add_adapter_dependency(self, adapter_name: str, dependency: str) -> None:
224 """Add a dependency relationship between adapters."""
225 if adapter_name not in self._adapter_dependencies:
226 self._adapter_dependencies[adapter_name] = set()
227 self._adapter_dependencies[adapter_name].add(dependency)
229 def get_adapter_dependencies(self, adapter_name: str) -> set[str]:
230 """Get dependencies for an adapter."""
231 return self._adapter_dependencies.get(adapter_name, set())
233 async def resolve_dependencies(self, adapter_name: str) -> list[str]:
234 """Resolve and load all dependencies for an adapter."""
235 dependencies = self.get_adapter_dependencies(adapter_name)
236 loaded = []
238 for dep in dependencies:
239 adapter = await self.get_adapter(dep)
240 if adapter:
241 loaded.append(dep)
243 return loaded