Limit parallel agent runs and bound tool execution time
Concurrency controls let you limit parallel agent execution and set timeouts for tool calls to prevent resource exhaustion.
from praisonaiagents import Agentagent = Agent( name="worker", instructions="Respect concurrency limits across tool calls",)agent.start("Process these jobs without overloading APIs")
The user runs parallel work; concurrency controls cap simultaneous operations and tool timeouts.
Control how many instances of the same agent can run concurrently:
from praisonaiagents import Agentfrom praisonaiagents.agent.concurrency import ConcurrencyRegistryregistry = ConcurrencyRegistry()registry.set_limit("researcher", 2) # at most 2 concurrent runsagent = Agent(name="researcher", instructions="Research topics")# Sync contextregistry.acquire_sync("researcher")try: agent.start("Research Mars exploration")finally: registry.release("researcher")
2
Same, async
Use async context for better resource utilization:
await registry.acquire("researcher")try: await agent.astart("Research Mars exploration")finally: registry.release("researcher")
3
Bound tool time with ToolConfig
Prevent slow tools from blocking agent execution:
from praisonaiagents import Agentfrom praisonaiagents.config.feature_configs import ToolConfigagent = Agent( name="Assistant", instructions="Use tools to help users", tools=["get_weather"], tool_config=ToolConfig(timeout=30), # seconds; slow tools return a timeout dict)agent.start("What's the weather in Tokyo?")
The concurrency registry enforces strict separation between sync and async contexts:
Context
Method
What happens if you mix
Sync
registry.acquire_sync(name)
✅ Works correctly
Async
await registry.acquire(name)
✅ Works correctly
Mixed
acquire_sync() in async
❌ Raises RuntimeError
Calling acquire_sync() from an async context raises RuntimeError("acquire_sync('<agent_name>') cannot be called with a running event loop; use async acquire() in async contexts."). Use await acquire() instead.
When tool_config=ToolConfig(timeout=...) is set, tools run in a dedicated executor with these characteristics:In YAML the field name is still tool_timeout:; in Python use tool_config=ToolConfig(timeout=…).
On timeout, each layer surfaces the timeout differently:
Layer
Trigger
Behaviour
SDK Agent executor
Agent(tool_config=ToolConfig(timeout=30)) directly in Python
{"error": "Tool timed out after 30s", "timeout": True}
Wrapper boundary
YAML tool_timeout: 30 or CLI --tool-timeout 30 (framework: praisonai)
Raises ToolTimeoutError (a TimeoutError subclass) with tool_name, timeout_seconds, and background_work_may_continue. Framework adapters catch it and translate it per framework. See Async Tool Safety → Wrapper-Level Timeout.
When multiple tool_timeout values are declared, the wrapper resolves a single effective timeout applied to every tool in the shared tool dict:
CLI wins. An explicit --tool-timeout N on the command line (or cli_config={"tool_timeout": N} when embedding) is used verbatim.
Otherwise, the largest per-role/per-agent value. The wrapper picks max(tool_timeout) across every entry under roles: and agents: in the YAML. This is the safest default for a shared tool dict — a slow-tool role won’t have its tools killed by another role’s stricter timeout.
Otherwise, no wrapping. If nothing declares a timeout, tools run without wrapper-layer enforcement (the SDK executor-layer enforcement still applies if tool_config=ToolConfig(timeout=…) is set in Python).
The resolver is AgentsGenerator._resolve_effective_tool_timeout(config) — see praisonai/agents_generator.py.
YAML boolean values are ignored, not coerced. Because bool subclasses int in Python, tool_timeout: yes or tool_timeout: true used to silently become a 1-second cap on every tool. As of PR #2609 the resolver explicitly rejects bool values — such entries are treated as “not declared” and fall through to the next precedence level. Use an integer or float (e.g. tool_timeout: 30).
Any tool that does network IO should have a timeout:
from praisonaiagents import Agentfrom praisonaiagents.config.feature_configs import ToolConfig# Tools that need timeoutsnetwork_tools = ["web_search", "api_call", "download_file"]local_tools = ["calculate", "format_text", "parse_json"]agent = Agent( name="Assistant", tools=network_tools + local_tools, tool_config=ToolConfig(timeout=30) # Protects against slow network)
Use thread names for debugging
Filter logs by agent name using the thread prefix:
# Filter tool execution logs by agentgrep "tool-researcher" app.log# Or in Python loggingimport logginglogging.basicConfig(format='%(threadName)s: %(message)s')