Quick Start
Cap every tool call
Set
tool_timeout_ms inside the llm= dict. A slow tool now returns a typed timeout result instead of freezing the turn.How It Works
The executor wraps each tool call, branching to a typed result on timeout or cancellation.| Condition | Behaviour |
|---|---|
timeout_ms is None or <= 0 | Tool runs directly with zero wrapping overhead. |
timeout_ms > 0 | Tool runs on a dedicated ThreadPoolExecutor(max_workers=1); on expiry the worker is abandoned and a typed timeout result is returned. |
cancel_token signalled | Tool never runs; returns a typed cancelled result. |
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
tool_timeout_ms (in llm= dict / extra_settings) | int | None | Per-tool timeout in milliseconds. None or <= 0 disables it. |
cancel_token (kwarg to execute_batch) | threading.Event-like | None | Any object with is_set(), is_cancelled, or cancelled (attribute or callable). When signalled, pending tool calls short-circuit. |
threading.Event and InterruptController-shaped tokens without importing a concrete type.
Structured Error Envelope
ToolResult.structured_error returns a discriminated JSON envelope so the model can tell a timeout from a genuine bug, instead of an opaque "Error executing tool: ..." string.
| Field | Type | Values |
|---|---|---|
error | bool | Always true when present |
kind | str | "timeout" | "cancelled" | "error" |
type | str | Exception class name (ToolTimeoutError, ToolCancelledError, or the tool’s own exception) |
message | str | str(exc) |
tool | str | Tool function name |
kind discriminant lets the model retry a timeout, abort on a cancelled, and report a plain error differently.
Exceptions
| Exception | Module | When raised |
|---|---|---|
ToolTimeoutError | praisonaiagents.tools | Sync worker exceeded timeout_ms |
ToolCancelledError | praisonaiagents.tools | cancel_token was signalled before / between calls |
ToolResult.error with the matching error_kind — they are not thrown to your calling code.
Common Patterns
Bound a whole batch with one timeout — most users’ entry point:Best Practices
Pick the right timeout layer
Pick the right timeout layer
Use millisecond
tool_timeout_ms for user-facing latency budgets (a chat turn should not hang). Use wrapper-level tool_timeout (seconds) for hard operational cutoffs in YAML/CLI crews.Background work may continue
Background work may continue
A timed-out sync tool’s daemon worker is abandoned, not killed. Assume the tool may still be running. Never use the timeout to enforce security-critical bounds.
Reserve cancel tokens for interactive loops
Reserve cancel tokens for interactive loops
Use
cancel_token for interactive or streaming loops (bot, CLI, gateway) where a user can hit stop. Background jobs should rely on tool_timeout_ms alone.Let the model see structured_error
Let the model see structured_error
Pass
ToolResult.structured_error to the LLM instead of flattening it to a string. The discriminated kind is what lets the agent choose retry vs. abort vs. report.Related
Async Tool Safety
Wrapper-level timeouts and safe async tool execution.
Tool Config
Configure timeouts across all three enforcement layers.
Concurrency
Parallel tool execution and per-agent limits.

