Skip to content

Docs needed for PR #1548: framework-adapter plugins, Typer-first CLI dispatch, run_sync() loop safety #276

@MervinPraison

Description

@MervinPraison

Source change

Upstream PR merged to MervinPraison/PraisonAI main on 2026-04-28:

Three architectural gaps were closed in the praisonai wrapper layer. Two of them produce user-visible behaviour that needs documentation, and one introduces a brand-new public extensibility API that has no documentation today. Another agent picking this up will create / update the pages described below.


TL;DR — recommended doc plan

Action Page Folder Reason
CREATE framework-adapter-plugins.mdx docs/features/ New public API: third-party framework adapters via Python entry points (praisonai.framework_adapters group). No existing page covers it. docs/features/plugins.mdx only covers tool/hook/guardrail plugins.
UPDATE docs/cli/cli.mdx and docs/cli/cli-reference.mdx docs/cli/ Typer is now the single CLI dispatcher. The legacy argparse path is now a narrow shim for bare prompt or bare YAML only. Registration errors now fail loud instead of silently falling back.
UPDATE docs/features/thread-safety.mdx docs/features/ praisonai._async_bridge.run_sync() now raises RuntimeError when called from inside a running event loop — previously it auto-fell-back. This is a behaviour change worth a callout. Also note the new thread-safe lazy loaders in praisonai/auto.py and the loop-aware async primitives in async_agent_scheduler.py.
CROSS-LINK docs/framework/crewai.mdx, docs/framework/autogen.mdx, docs/framework/praisonaiagents.mdx docs/framework/ Add a “Custom frameworks” callout linking to the new framework-adapter-plugins.mdx page so readers learn the framework list is now extensible.

Per AGENTS.md §1.8 — all new pages go in docs/features/, NEVER in docs/concepts/. Update docs.json under the Features group only.


Page 1 — NEW: docs/features/framework-adapter-plugins.mdx

Why this is needed

Before PR #1548, the available frameworks (crewai, autogen, autogen_v4, ag2, praisonai) were hardcoded in a FRAMEWORK_ADAPTERS dict inside agents_generator.py. After PR #1548, framework adapters live in a proper registry and third parties can register their own adapters via Python entry points. This is a new public extensibility surface.

SDK ground truth (read these before writing)

In the MervinPraison/PraisonAI repo (NOT in this docs repo):

  • src/praisonai/praisonai/framework_adapters/registry.py — new file, defines FrameworkAdapterRegistry
  • src/praisonai/praisonai/framework_adapters/base.py — defines the FrameworkAdapter Protocol
  • src/praisonai/praisonai/framework_adapters/crewai_adapter.py, autogen_adapter.py, praisonai_adapter.py — built-in adapters (read for shape of what users must implement)
  • src/praisonai/praisonai/agents_generator.py — now uses FrameworkAdapterRegistry.get_instance().create(framework) instead of the hardcoded dict

Key facts from the SDK (verify by reading source — do not paraphrase from this issue)

  • Entry point group name: praisonai.framework_adapters
  • Singleton access: FrameworkAdapterRegistry.get_instance()
  • Public methods: register(name, adapter_class), unregister(name), create(name), list_registered(), is_available(name)
  • Built-in adapters auto-registered: crewai, autogen, autogen_v4, ag2, praisonai (each guarded by lazy try/except ImportError so missing optional deps do not break the registry)
  • Failure mode: if an entry-point plugin fails to load, the registry logs a warning and continues — it does not break framework dispatch
  • Adapter contract: FrameworkAdapter is a Protocol, so users implement it duck-typed. They must provide whatever the protocol requires (read base.py to enumerate methods — at minimum is_available() plus the methods called by agents_generator.py).

Required page sections (per AGENTS.md template)

  1. Frontmattertitle: "Framework Adapter Plugins", icon: "puzzle-piece" (matches existing plugin docs)
  2. Hero Mermaid diagram — third-party package → entry point → registry → CLI --framework <name> dispatch
  3. Quick Start (Steps component) with two steps:
    • Step 1: programmatic registration via FrameworkAdapterRegistry.get_instance().register("myframework", MyAdapter) — the simplest path
    • Step 2: distribute as a pip-installable plugin via pyproject.toml entry point under group praisonai.framework_adapters
  4. How It Works — sequence diagram: pip install → entry-point discovery on first registry access → praisonai --framework myframework agents.yaml resolves through the registry
  5. Configuration / API table for the public FrameworkAdapterRegistry methods listed above
  6. Common Patterns — at least: (a) overriding a built-in adapter, (b) wrapping a non-Python framework via subprocess, (c) availability check that returns False when optional deps are missing
  7. Best Practices (AccordionGroup) — handle missing optional deps inside is_available(); do not raise from import-time module code; log via logging.getLogger(__name__) not print
  8. Related (CardGroup, 2 cards) — link to docs/features/plugins.mdx and docs/framework/crewai.mdx (or whichever framework page is most relevant as a “see how a built-in adapter looks” reference)

Example skeleton (snippets the next agent should adapt after reading the SDK — do not copy verbatim)

# pyproject.toml of the third-party package
[project.entry-points."praisonai.framework_adapters"]
myframework = "myframework_praisonai.adapter:MyFrameworkAdapter"
# Programmatic registration (alternative to entry points)
from praisonai.framework_adapters.registry import FrameworkAdapterRegistry
from myframework_praisonai.adapter import MyFrameworkAdapter

FrameworkAdapterRegistry.get_instance().register("myframework", MyFrameworkAdapter)
praisonai --framework myframework agents.yaml

docs.json change

Add the new page under the Features group only. Do NOT add it under Concepts.


Page 2 — UPDATE: docs/cli/cli.mdx and docs/cli/cli-reference.mdx

What changed

src/praisonai/praisonai/__main__.py was rewritten. The new dispatch rules are:

  1. --version / -V → print version and exit
  2. Legacy shim — only triggers for:
    • A first positional arg containing a space (free-text prompt), or
    • A first positional arg ending in .yaml/.yml that exists on disk
  3. Everything else → Typer

Notable user-visible consequences:

  • --help / -h is no longer special-cased — it goes through Typer (Typer already handles help correctly, so behaviour is the same, but the routing comment in any existing docs/diagrams should reflect this)
  • A YAML path that does not exist on disk no longer falls into the legacy path — it goes to Typer, which will emit a clear unknown-command error instead of silently mis-dispatching
  • Fail loud: if register_commands() raises (e.g. a Typer subcommand has an ImportError), the CLI now propagates the exception instead of silently falling back to legacy dispatch. Users who previously saw mysterious legacy behaviour will now see the real error.

What the doc update should add

  • A short "How CLI dispatch works" subsection (1 paragraph + a small Mermaid graph TB showing the three routing rules)
  • A <Note> callout: "PraisonAI now fails loud on CLI registration errors. If you see a new ImportError after upgrading, a Typer subcommand or one of its dependencies failed to import — fix the import rather than ignoring the error."
  • Verify any existing examples that pass a YAML path also note that the file must exist (no behavioural surprise, but worth being explicit)

Where NOT to change

Do not touch docs/concepts/* (per AGENTS.md §1.8). Stay in docs/cli/.


Page 3 — UPDATE: docs/features/thread-safety.mdx

What changed

  1. praisonai._async_bridge.run_sync() is now strict about event loops. It used to detect a running loop and schedule the coroutine on the background loop in either case. It now raises RuntimeError if called from inside a running event loop, with the message:

    run_sync() cannot be called from a running event loop; await the coroutine directly instead.

    This is a behaviour change. Any user code that called run_sync() from an async context will now break loudly instead of silently working through a background-loop hop.

  2. AsyncAgentScheduler is now loop-aware. start() binds its async primitives (asyncio.Event, asyncio.Lock) to the running loop, and stop() raises RuntimeError if called from a different loop than start().

  3. praisonai/auto.py lazy loaders are now thread-safe. A single _load_optional(key, loader) helper with a module-level lock replaces the previous unguarded module-level globals (_crewai_available, _autogen_module, etc.).

  4. praisonai/integrations/registry.py — the singleton registry now has a per-instance threading.Lock guarding register/unregister/create/list_registered and snapshots the dict before async iteration in get_available().

  5. Telemetry init in praisonai/__init__.py now uses double-checked locking (already commented as such in the new code).

What the doc update should add

  • A new "Behaviour change in PR #1548" <Warning> callout near the top of the page explaining the run_sync() change with a before/after code example:

    # Before PR #1548 (worked, but unsafe)
    async def handler():
        result = run_sync(some_coro())  # silently used background loop
    
    # After PR #1548 (raises RuntimeError)
    async def handler():
        result = await some_coro()  # use this from async context
  • A short note that the wrapper-layer lazy loaders, integration registry, and async scheduler primitives are now thread-safe and loop-aware (one paragraph each, no need to enumerate every file)


Cross-link updates (small)

In each of docs/framework/crewai.mdx, docs/framework/autogen.mdx, docs/framework/praisonaiagents.mdx, add a one-line callout near the top:

Need a framework that isn't listed here? See Framework Adapter Plugins to register your own via Python entry points.


Verification checklist for the implementing agent

Per AGENTS.md §1.1–§1.3 (SDK-first cycle):

  • Read src/praisonai/praisonai/framework_adapters/registry.py from MervinPraison/PraisonAI main (post-merge of #1548) — do not rely on the snippets in this issue
  • Read src/praisonai/praisonai/framework_adapters/base.py to enumerate the exact FrameworkAdapter protocol methods
  • Read at least one built-in adapter (e.g. crewai_adapter.py) to show users a realistic shape
  • Read src/praisonai/praisonai/__main__.py (post-merge) to confirm the exact dispatch rules before updating CLI docs
  • Read src/praisonai/praisonai/_async_bridge.py (post-merge) to confirm the exact RuntimeError message
  • All new pages go in docs/features/ only
  • docs.json updates go under the Features group only
  • Use Mintlify components: <Steps>, <AccordionGroup>, <CardGroup>, <Note>/<Warning>
  • Use the standard Mermaid colour scheme from AGENTS.md §3.1
  • Every code example must run as-is (no your-key-here placeholders)
  • Agent-centric framing — top-of-page example should be the simplest Agent / CLI flow, not a deep API tour

Out of scope (do not document)

  • Internal LRU cache details for OpenAI clients in auto.py — implementation detail, not user-facing
  • Internal _load_optional helper — implementation detail
  • The get_unlocked() method on _BG — internal to _async_bridge.py

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingclaudeTrigger Claude Code analysisdocumentationImprovements or additions to documentationenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions