Integration Registry

Add custom CLI / managed-agent / hosted-agent integrations via Python entry points

python
from praisonai.integrations import ClaudeCodeIntegration

integration = ClaudeCodeIntegration(workspace="/path/to/project")
result = await integration.execute("Refactor this module")

Import any built-in or plugin-registered integration straight from praisonai.integrations — a static export map serves built-ins, and the praisonai.integrations entry-point group adds third-party names.

Integration Registry lets third-party developers add custom CLI tools, managed agents, and agent backends to PraisonAI, importable from a single module.

Diagram

hit

miss

📦 Third-party Package

🔌 Entry Point\npraisonai.integrations

📥 from praisonai.integrations import X

🗺️ _STATIC_EXPORTS?

✅ built-in class

✅ plugin class

Choose Your Registration Path

Pick a path based on how the integration ships.

Diagram

Yes

No

Yes

No, typed backend

Need a new integration?

Ships with PraisonAI?

Use built-in name
e.g. ClaudeCodeIntegration

Distributed via pip?

Add entry point
praisonai.integrations

Use ExternalAgentRegistry
or ManagedBackendRegistry


Quick Start

  1. Use a built-in integration
    python
    from praisonai.integrations import ClaudeCodeIntegration
    
    integration = ClaudeCodeIntegration(workspace="/path/to/project")
  2. Distribute as a pip plugin
    toml
    # your-plugin/pyproject.toml
    [project.entry-points."praisonai.integrations"]
    acme = "acme_praison:AcmeIntegration"
    python
    # after `pip install your-plugin` — no praisonai code changes needed
    from praisonai.integrations import acme
    integration = acme(workspace="/path/to/project")

    The left-hand side (acme) is the name users import; the right-hand side is the dotted path to the class. Names that collide with a built-in are ignored — built-ins always win.


How It Works

Diagram
praisonai.integrations entry points_STATIC_EXPORTSpraisonai.integrationsUserpraisonai.integrations entry points_STATIC_EXPORTSpraisonai.integrationsUseralt[plugin found][none]alt[built-in hit][miss]from praisonai.integrations import XX in static map?lazy import “module:attr”Xentry point named X?ep.load()XAttributeError
  • __getattr__ on praisonai.integrations first checks the static _STATIC_EXPORTS map (name -> "module:attr"), lazily importing the owning module on first access.
  • If the name isn’t a built-in, it’s looked up in the praisonai.integrations entry-point group and ep.load()-ed.
  • Unknown names raise AttributeError (native Python behaviour).
  • Dunder / underscore-prefixed submodule names (e.g. from praisonai.integrations import base) fall through to normal import machinery.

Precedence

Built-ins always win because they are served by _STATIC_EXPORTS before the entry-point lookup runs. A plugin may only add names, never replace a shipped one.

SourceWhen resolvedOn name collision
Built-in (_STATIC_EXPORTS)Checked first in __getattr__Wins — plugin never consulted
Third-party entry-point pluginOnly if no static-map hitCannot shadow a built-in

Available built-in names

Every key below imports directly: from praisonai.integrations import <name>.

CLI Tools

  • BaseCLIIntegration — base class for CLI tools
  • CLIExecutionError — CLI execution error class
  • get_available_integrations — list available integrations
  • ClaudeCodeIntegration — Claude Code CLI
  • GeminiCLIIntegration — Gemini CLI
  • CodexCLIIntegration — Codex CLI
  • CursorCLIIntegration — Cursor CLI

Managed Agents

  • ManagedAgent (alias ManagedAgentIntegration) — managed agent interface
  • AnthropicManagedAgent — Anthropic managed agent
  • ManagedConfig (alias ManagedBackendConfig) — managed agent configuration

Local & Sandboxed Agents

  • LocalManagedAgent — local managed agent
  • LocalManagedConfig — local agent configuration
  • SandboxedAgent — sandboxed agent execution
  • SandboxedAgentConfig — sandboxed agent configuration

Agent Backends

  • HostedAgent — hosted agent backend
  • HostedAgentConfig — hosted agent configuration
  • LocalAgent — local agent backend
  • LocalAgentConfig — local agent configuration

Registry Functions

  • ExternalAgentRegistry — external agent registry (typed source of truth for --external-agent)
  • get_registry — get the external-agent registry
  • register_integration — register a new integration
  • create_integration — create an integration instance

Typed backend registries

The two typed registries are unchanged by #4651 and remain the sources of truth for their surfaces:

RegistrySurface it drives
ExternalAgentRegistrythe --external-agent CLI short names
ManagedBackendRegistrythe run_on= kwarg

Use these when you need typed enumeration of backends rather than plain module importability.


Migration from the old registry

The internal registry surfaces removed in PR #4651 no longer exist. Update any code that touched them:

RemovedReplacement
from praisonai.integrations._unified_registry import get_integrations_registryfrom praisonai.integrations import <name> (still works for every built-in and plugin)
INTEGRATIONS_REGISTRY module attributenone — import the name you need directly
IntegrationRegistry classnone — built-ins served by _STATIC_EXPORTS, plugins via the entry-point group
registry.get_by_attr(module, attr)getattr(praisonai.integrations, attr) / a normal import
Typed backend enumerationExternalAgentRegistry / ManagedBackendRegistry
python
# ❌ Before (removed)
from praisonai.integrations._unified_registry import get_integrations_registry
cls = get_integrations_registry().get_by_attr(__name__, "ClaudeCodeIntegration")

# ✅ After
from praisonai.integrations import ClaudeCodeIntegration

Advanced Usage

Adding a new built-in backend

Built-in CLI backends (Claude Code, Gemini, Codex, Cursor) are registered from a single canonical module: praisonai/integrations/_cli_loaders.py. It exports BUILTIN_INTEGRATIONS (short-alias → loader) used by ExternalAgentRegistry. Register a new built-in there and add the class to _STATIC_EXPORTS in praisonai/integrations/__init__.py to make it importable.

Build your own namespace

Use create_lazy_getattr(registry) from praisonai._registry to give your own package the same lazy-loading + entry-point dispatch behaviour:

python
# my_pkg/__init__.py
from praisonai._registry import PluginRegistry, create_lazy_getattr

_REG = PluginRegistry(entry_point_group="my_pkg.plugins")
__getattr__ = create_lazy_getattr(_REG)

PluginRegistry.has(name) -> bool (added in #4651) is a cheap, load-free membership check you can use before triggering a lazy import.


Best Practices

Import from the package root, not submodules

Use the public surface so you get built-in-vs-plugin resolution:

python
# ✅ Good
from praisonai.integrations import ClaudeCodeIntegration

# ❌ Bad — internal module path, not the public API
from praisonai.integrations.claude_code import ClaudeCodeIntegration
Never reuse a built-in name

Built-ins are served by _STATIC_EXPORTS before the entry-point lookup runs, so a plugin sharing a built-in name is never reached. Pick a name unique to your package.

Prefer typed registries for enumeration

When you need to list backends (not just import one), use ExternalAgentRegistry / ManagedBackendRegistry — they own the --external-agent and run_on= surfaces.

Upgrade for entry-point discovery

Plugin discovery needs PR #4154 or later. On older releases, only built-ins resolve.


Related