Use the default registry for normal CLI / single-tenant apps — that’s still the simplest path. Reach for a custom registry only when you need: (a) per-tenant adapter overrides, (b) test isolation between parallel test cases, or (c) sandboxed runs that must not leak custom registrations into the rest of the process.
Experimental adapters don’t leak into other processes:
def experiment_with_adapter(): experiment_registry = FrameworkAdapterRegistry() experiment_registry.register("experimental", ExperimentalAdapter) # This won't affect other parts of the application return AgentsGenerator( adapter_registry=experiment_registry, framework="experimental", # ... )
Library code should accept registries as parameters rather than directly accessing defaults:
# ✅ Good - accepts registry parameterdef process_with_framework(framework: str, registry: FrameworkAdapterRegistry): adapter = registry.create(framework) return adapter.run(...)# ❌ Bad - hardcoded to defaultdef process_with_framework(framework: str): from praisonai.framework_adapters.registry import get_default_registry registry = get_default_registry() adapter = registry.create(framework) return adapter.run(...)
Thread Safety Considerations
Don’t share a custom registry across threads without understanding what you’ve registered:
# ✅ Safe - each thread gets its own registrydef worker_thread(): thread_registry = FrameworkAdapterRegistry() thread_registry.register("crewai", ThreadSpecificAdapter) # ...# ⚠️ Careful - shared registry needs thread-safe adaptersshared_registry = FrameworkAdapterRegistry()def worker_thread(): # Registry operations are thread-safe, but adapter classes may not be adapter = shared_registry.create("crewai")
Error Handling
Built-in plugins are loaded lazily; failures importing a single plugin do not break others:
registry = FrameworkAdapterRegistry()registry.register("broken", BrokenAdapter)registry.register("working", WorkingAdapter)# This will work even if BrokenAdapter fails to loadworking = registry.create("working")