Skip to content

docs: document PRAISONAI_RUN_SYNC_TIMEOUT env var and praisonai._async_bridge.shutdown() (tracks PraisonAI PR #1505) #227

@MervinPraison

Description

@MervinPraison

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`

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions