Source change
Tracks merged PR MervinPraison/PraisonAI#1505 which fixes issue #1500.
PR #1505 fixed three gaps in src/praisonai/praisonai/:
| Gap |
File |
Nature |
User-facing? |
| 1 |
agents_generator.py — unresolved tool-class NameError |
bug fix (internal) |
No (crash fix only) |
| 2 |
_async_bridge.py — daemon-thread leak, no shutdown, infinite run_sync waits |
bug fix + new public API + new env var |
Yes |
| 3 |
5 parallel tool registries consolidated into one ToolResolver |
internal refactor |
No (behaviour becomes consistent, but no new user API) |
Only Gap 2 introduces new user-facing surface. Gaps 1 and 3 are internal — no documentation work is required for them beyond, optionally, mentioning the security-gate parity improvement (see §4 below).
What's new and user-facing (from Gap 2)
Confirmed by reading the merged diff in src/praisonai/praisonai/_async_bridge.py:
1. New environment variable — PRAISONAI_RUN_SYNC_TIMEOUT
# src/praisonai/praisonai/_async_bridge.py
_DEFAULT_TIMEOUT = float(os.environ.get("PRAISONAI_RUN_SYNC_TIMEOUT", "300"))
- Name:
PRAISONAI_RUN_SYNC_TIMEOUT
- Type: float (seconds)
- Default:
300 (5 minutes)
- Effect: default
timeout for every praisonai._async_bridge.run_sync(coro) call — this function is invoked from ~77 call sites across agents_generator.py, bots/_approval_base.py, endpoints/a2u_server.py, integrations/base.py, integrations/registry.py, integrations/managed_local.py, gateway/server.py, mcp_server/, scheduler/, etc.
- Previously: default was
None, meaning unbounded waits — deadlocked integrations could freeze a server indefinitely.
- Override: per-call via
run_sync(coro, timeout=<seconds>); globally via the env var.
2. New public API — praisonai._async_bridge.shutdown()
def shutdown() -> None:
"""Public hook for servers (gateway, a2u, mcp_server) to stop the bridge cleanly."""
_BG.shutdown()
- Registered automatically via
atexit.
- Intended for long-running servers (gateway, a2u, mcp_server, scheduler) that want explicit, deterministic shutdown of the background event loop — cancels outstanding tasks, stops the loop, joins the thread, closes the loop.
- Safe to call multiple times; idempotent after first call.
3. Behaviour changes (transparent to users — document as "what changed")
- Background async thread is now
daemon=False with an explicit atexit shutdown → finally blocks run, HTTP/DB clients close cleanly, OpenTelemetry exporters flush.
run_sync no longer uses asyncio.run(coro) on the cheap path — everything goes through a single reused _BackgroundLoop. Consequence: connection pools / contextvars survive across calls. Users running hot paths (e.g., integrations/registry.py:get_available()) will see a small latency win but no API change.
Scope of documentation work
Type: mostly content update on 2 existing pages, plus optionally 1 new page (thin). No new concepts page.
1. UPDATE — docs/features/security-environment-variables.mdx (primary change)
This page is the canonical list of PRAISONAI_* env vars. Add a new ### PRAISONAI_RUN_SYNC_TIMEOUT section after the existing PRAISONAI_BROWSER_ALLOW_REMOTE section, using the same format already established in the file:
- One-sentence description of what it controls
- Default value and safe/recommended range
export / unset snippet
- Short note that it applies to the
praisonai CLI and wrapper-based servers (gateway, a2u, mcp_server), not to SDK-only code (praisonaiagents.utils.async_bridge has its own semantics, documented separately in docs/features/async-bridge.mdx).
- Cross-link to the new
shutdown() hook (see §3 below).
Proposed content skeleton (follow the style of the existing entries in the same file — keep it terse, no "In this section we will"):
### PRAISONAI_RUN_SYNC_TIMEOUT
Default maximum seconds the wrapper's sync-to-async bridge will wait for a coroutine to complete.
**Default:** `300` (5 minutes)
```bash
# Tighten for latency-sensitive servers
export PRAISONAI_RUN_SYNC_TIMEOUT=30
# Loosen for long-running batch jobs
export PRAISONAI_RUN_SYNC_TIMEOUT=3600
Applies to every praisonai CLI entry and wrapper-based server (gateway, a2u, mcp_server, scheduler). The SDK (praisonaiagents) uses a separate bridge — see Async Bridge.
> Note: place this even though it is an *operational* (not security) variable — `security-environment-variables.mdx` is the only existing page that lists all `PRAISONAI_*` env vars end-to-end. If a dedicated env var reference page is preferred, propose one, but do not duplicate.
### 2. UPDATE — `docs/cli/env.mdx`
Current file only covers `praisonai env show` / `praisonai env check` commands. Add a short "Related environment variables" section at the bottom with a table and a link to `/docs/features/security-environment-variables` for full details. Include `PRAISONAI_RUN_SYNC_TIMEOUT`, `PRAISONAI_ALLOW_LOCAL_TOOLS`, `PRAISONAI_ALLOW_JOB_WORKFLOWS`, `PRAISONAI_BROWSER_ALLOW_REMOTE`.
### 3. UPDATE — `docs/features/gateway.mdx` (and `docs/deploy/servers/a2u.mdx` if it documents lifecycle)
Add a "Graceful shutdown" subsection covering the new `shutdown()` hook. Keep it short — this is an operator-facing note, not a concept:
```mdx
### Graceful shutdown
Long-running servers can stop the wrapper's background async loop explicitly:
```python
from praisonai._async_bridge import shutdown
# On SIGTERM / server stop
shutdown()
shutdown() is also registered via atexit, so it runs automatically on interpreter exit. Call it manually when you need deterministic cleanup (flushing telemetry exporters, closing async HTTP/DB clients) before the process exits.
Repeat the same block (or a `/snippets`-style include if this repo supports them — verify before assuming) in:
- `docs/deploy/servers/a2u.mdx`
- `docs/features/bot-gateway.mdx` — only if it covers process lifecycle; skim first.
- Any `mcp_server` / `scheduler` operational doc. Grep first: `grep -ri "graceful\|SIGTERM\|lifecycle" docs/features/ docs/deploy/`.
### 4. OPTIONAL NEW PAGE — `docs/features/async-bridge-wrapper.mdx`
`docs/features/async-bridge.mdx` already exists but documents the **SDK** bridge at `praisonaiagents.utils.async_bridge`. The wrapper bridge at `praisonai._async_bridge` is a **different module** with different semantics (single background loop, non-daemon thread, atexit cleanup). If and only if reviewers feel the distinction is confusing, add a thin page:
- Title: "Wrapper Async Bridge"
- Clarifies the two bridges co-exist and when each is used
- Documents `run_sync(coro, timeout=...)` and `shutdown()`
- Links to `PRAISONAI_RUN_SYNC_TIMEOUT` env var entry
Otherwise, a single `<Note>` block inside the existing `docs/features/async-bridge.mdx` that points to the env var entry is sufficient. Prefer the lighter option.
### 5. (Out of scope — do NOT document) Gap 1 & Gap 3
- Gap 1 was a crash bug on a code path that users couldn't successfully use anyway. No doc mention needed.
- Gap 3 consolidates 5 internal tool registries into `praisonai.tool_resolver.ToolResolver`. The consolidation is **internal**; public tool-loading semantics for users don't change. However, one side effect is worth a sentence in `docs/features/security-environment-variables.mdx` under the existing `PRAISONAI_ALLOW_LOCAL_TOOLS` section:
> As of PraisonAI v[version], the `PRAISONAI_ALLOW_LOCAL_TOOLS` gate is enforced consistently across the CLI (`praisonai run`), YAML workflows, and Python (`AgentsGenerator`) entry points. Previously it was only enforced on one path.
This is a security-posture improvement users will want to know about.
---
## Non-goals / guard-rails for the documenting agent
- **DO NOT** modify `docs/concepts/` — this whole change is `docs/features/` + `docs/cli/` + `docs/deploy/` only (per AGENTS.md §1.8).
- **DO NOT** edit `docs/js/` or `docs/rust/` — `_async_bridge.py` is Python-only and the TS/Rust SDKs have their own async models.
- **DO NOT** invent a new concept page. The bridge is not a concept, it's a small operational knob.
- **DO NOT** document internal classes (`_BackgroundLoop`, `_BG`). Only `run_sync`, `shutdown`, and the env var are public.
- **DO NOT** change the style of `docs/features/security-environment-variables.mdx` entries — match the existing format exactly (terse, `export`/`unset` snippets, one-line description).
- Verify against source before writing: the SDK-truth file is `praisonai-package/src/praisonai/praisonai/_async_bridge.py` on `main` post-merge of PR #1505 (head SHA `6af2f40e`).
- After edits, run `docs.json` validation (valid JSON) and the parity script if any `docs/js/` / `docs/rust/` entries get touched (they shouldn't for this task).
---
## Checklist for the documenting agent
- [ ] Read `src/praisonai/praisonai/_async_bridge.py` at head `6af2f40e` end-to-end before writing anything.
- [ ] Add `### PRAISONAI_RUN_SYNC_TIMEOUT` section to `docs/features/security-environment-variables.mdx` matching the file's existing style.
- [ ] Add a "Related environment variables" section to `docs/cli/env.mdx` linking out to the full list.
- [ ] Add a "Graceful shutdown" subsection covering `praisonai._async_bridge.shutdown()` to `docs/features/gateway.mdx`. Extend to other server docs only after confirming they discuss lifecycle.
- [ ] Add a one-line note under `PRAISONAI_ALLOW_LOCAL_TOOLS` in `docs/features/security-environment-variables.mdx` about cross-entry-point parity (Gap 3 side effect).
- [ ] Decide on §4 (optional new page) — prefer `<Note>` in existing `async-bridge.mdx` unless there's clear reader confusion.
- [ ] Verify `docs.json` remains valid JSON and that any new pages are registered under the **Features** group, NOT Concepts.
- [ ] Do not add files under `docs/concepts/`, `docs/js/`, or `docs/rust/`.
- [ ] Keep all code examples minimal, runnable, and copy-paste-ready (AGENTS.md §5).
- [ ] Use the standard Mintlify components where appropriate (`<Steps>`, `<Note>`, `<CardGroup>` per AGENTS.md §4).
---
## Links
- Source PR: https://github.com/MervinPraison/PraisonAI/pull/1505
- Source issue: https://github.com/MervinPraison/PraisonAI/issues/1500
- Head SHA: `6af2f40e0a0df1d2ee0c50045c89e3600195dd80`
- Key file: `src/praisonai/praisonai/_async_bridge.py`
- Existing SDK-bridge doc (different module): `docs/features/async-bridge.mdx`
- Canonical env var list: `docs/features/security-environment-variables.mdx`
Source change
Tracks merged PR MervinPraison/PraisonAI#1505 which fixes issue #1500.
PR #1505 fixed three gaps in
src/praisonai/praisonai/:agents_generator.py— unresolved tool-classNameError_async_bridge.py— daemon-thread leak, no shutdown, infiniterun_syncwaitsToolResolverOnly Gap 2 introduces new user-facing surface. Gaps 1 and 3 are internal — no documentation work is required for them beyond, optionally, mentioning the security-gate parity improvement (see §4 below).
What's new and user-facing (from Gap 2)
Confirmed by reading the merged diff in
src/praisonai/praisonai/_async_bridge.py:1. New environment variable —
PRAISONAI_RUN_SYNC_TIMEOUTPRAISONAI_RUN_SYNC_TIMEOUT300(5 minutes)timeoutfor everypraisonai._async_bridge.run_sync(coro)call — this function is invoked from ~77 call sites acrossagents_generator.py,bots/_approval_base.py,endpoints/a2u_server.py,integrations/base.py,integrations/registry.py,integrations/managed_local.py,gateway/server.py,mcp_server/,scheduler/, etc.None, meaning unbounded waits — deadlocked integrations could freeze a server indefinitely.run_sync(coro, timeout=<seconds>); globally via the env var.2. New public API —
praisonai._async_bridge.shutdown()atexit.3. Behaviour changes (transparent to users — document as "what changed")
daemon=Falsewith an explicitatexitshutdown →finallyblocks run, HTTP/DB clients close cleanly, OpenTelemetry exporters flush.run_syncno longer usesasyncio.run(coro)on the cheap path — everything goes through a single reused_BackgroundLoop. Consequence: connection pools / contextvars survive across calls. Users running hot paths (e.g.,integrations/registry.py:get_available()) will see a small latency win but no API change.Scope of documentation work
Type: mostly content update on 2 existing pages, plus optionally 1 new page (thin). No new concepts page.
1. UPDATE —
docs/features/security-environment-variables.mdx(primary change)This page is the canonical list of
PRAISONAI_*env vars. Add a new### PRAISONAI_RUN_SYNC_TIMEOUTsection after the existingPRAISONAI_BROWSER_ALLOW_REMOTEsection, using the same format already established in the file:export/unsetsnippetpraisonaiCLI and wrapper-based servers (gateway, a2u, mcp_server), not to SDK-only code (praisonaiagents.utils.async_bridgehas its own semantics, documented separately indocs/features/async-bridge.mdx).shutdown()hook (see §3 below).Proposed content skeleton (follow the style of the existing entries in the same file — keep it terse, no "In this section we will"):
Applies to every
praisonaiCLI entry and wrapper-based server (gateway, a2u, mcp_server, scheduler). The SDK (praisonaiagents) uses a separate bridge — see Async Bridge.shutdown()is also registered viaatexit, so it runs automatically on interpreter exit. Call it manually when you need deterministic cleanup (flushing telemetry exporters, closing async HTTP/DB clients) before the process exits.