> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool Configuration

> Complete reference for tool timeout settings and performance tuning

# Tool Configuration

This page provides comprehensive documentation for configuring tools in PraisonAI, including timeout settings, performance optimization, error handling, and resource management.

<Note>
  In YAML the field name is still `tool_timeout:`; in Python pass tool execution settings through `tool_config=ToolConfig(timeout=…)`. The standalone `tool_timeout`, `tool_retry_policy`, and `parallel_tool_calls` kwargs were removed and raise `TypeError`.
</Note>

## Tool Timeout Configuration

### Basic Timeout Settings

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Tool

# Configure tool with timeout
tool = Tool(
    name="web_scraper",
    description="Scrape web pages",
    function=scrape_webpage,
    config={
        "timeout": 30,  # seconds
        "retry_on_timeout": True,
        "timeout_message": "Web scraping timed out"
    }
)

from praisonaiagents.config.feature_configs import ToolConfig

agent = Agent(
    name="Researcher",
    tools=[tool],
    tool_config=ToolConfig(timeout=30),
)
```

## Consolidated ToolConfig (new in PraisonAI 0.0.6x)

PraisonAI now provides a unified `ToolConfig` dataclass that consolidates tool-related parameters into a single configuration object. This replaces the previous pattern of separate `tool_timeout`, `parallel_tool_calls`, and `tool_retry_policy` parameters.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import ToolConfig
from praisonaiagents.tools.retry import RetryPolicy

agent = Agent(
    name="researcher",
    tools=[web_search],
    tool_config=ToolConfig(
        timeout=30,
        parallel=True,
        retry_policy=RetryPolicy(max_attempts=3),
    ),
)
```

### ToolConfig Options

| Option                    | Type          | Default  | Description                                                              |
| ------------------------- | ------------- | -------- | ------------------------------------------------------------------------ |
| `timeout`                 | `int`         | `None`   | Tool execution timeout in seconds                                        |
| `parallel`                | `bool`        | `False`  | Enable parallel tool execution                                           |
| `retry_policy`            | `RetryPolicy` | `None`   | Tool retry configuration                                                 |
| `output_limit`            | `int`         | `16000`  | Maximum bytes of tool output before spilling to the artifact store       |
| `output_max_lines`        | `int`         | `None`   | Maximum lines before spilling (alternative to byte limit)                |
| `output_direction`        | `str`         | `"both"` | Truncation direction shown to the agent: `"head"`, `"tail"`, or `"both"` |
| `enable_artifacts`        | `bool`        | `False`  | Master switch — enables artifact spillover for large tool outputs        |
| `artifact_retention_days` | `int`         | `7`      | Days to keep artifacts before garbage collection on agent destruction    |
| `artifact_store`          | `Any`         | `None`   | Custom store instance implementing `ArtifactStoreProtocol`               |
| `redact_secrets`          | `bool`        | `True`   | Redact common secret patterns before writing artifacts to disk           |

### Artifact Storage

When `enable_artifacts=True`, tool outputs that exceed `output_limit` are saved to disk and the agent gains tools to page through them on demand — preventing silent data loss for large web scrapes, file reads, or query results.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import ToolConfig

agent = Agent(tool_config=ToolConfig(enable_artifacts=True))
```

<Card title="Artifact Storage" icon="database" href="/docs/features/artifact-storage">
  Full documentation: how overflow is stored, the six retrieval tools the agent gains, storage layout, and best practices.
</Card>

### Configuration Shortcuts

ToolConfig supports convenient shorthand notation:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import ToolConfig

# Boolean shorthand
agent = Agent(tool_config=True)   # Uses default ToolConfig()
agent = Agent(tool_config=False)  # Disables tool config

# Dict expansion
agent = Agent(tool_config={
    "timeout": 30,
    "parallel": True,
    "retry_policy": {"max_attempts": 3},
    "enable_artifacts": True,
})
```

### Removed legacy kwargs

The standalone `tool_timeout`, `parallel_tool_calls`, and `tool_retry_policy` keyword arguments on `Agent(...)` have been removed. Passing them raises `TypeError`:

```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
TypeError: Agent.__init__() got an unexpected keyword argument 'tool_timeout'
```

Pass these settings through `tool_config=ToolConfig(...)` instead.

| Removed kwarg                        | New location                                            |
| ------------------------------------ | ------------------------------------------------------- |
| `tool_timeout=30`                    | `tool_config=ToolConfig(timeout=30)`                    |
| `parallel_tool_calls=True`           | `tool_config=ToolConfig(parallel=True)`                 |
| `tool_retry_policy=RetryPolicy(...)` | `tool_config=ToolConfig(retry_policy=RetryPolicy(...))` |

YAML and the CLI are unaffected — `tool_timeout:` in YAML and `--tool-timeout` on the CLI remain valid; the wrapper translates them into a `ToolConfig` before constructing the `Agent`.

### Agent-Level Tool Timeout

Each agent can set `tool_config=ToolConfig(timeout=…)` (or `tool_config=True` for defaults) which applies to all tool executions. When a tool times out, it returns a standardized error dict:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import ToolConfig

agent = Agent(
    name="Assistant", 
    tools=["web_search"],
    tool_config=ToolConfig(timeout=30)  # seconds
)

# If web_search takes >30s, it returns:
# {"error": "Tool timed out after 30s", "timeout": True}
```

**Timeout behavior details:**

* Each agent has its own 2-thread tool executor (reused across calls)
* Timed-out tools return `{"error": "Tool timed out after Ns", "timeout": True}` rather than raising
* Thread names are prefixed `tool-<agent_name>` — useful for debugging

#### Why the change?

The consolidation groups timeout + retry + parallel execution settings into one config object instead of three loose kwargs. This provides better organization and prevents parameter conflicts.

<Note>
  For complete concurrency control documentation including per-agent limits and sync/async patterns, see [Concurrency](/docs/features/concurrency).
</Note>

### Wrapper-Level Tool Timeout (YAML / CLI — Defense-in-Depth)

When you set `tool_timeout` in YAML (`framework: praisonai`) **or** pass `--tool-timeout` on the CLI, the wrapper now wraps every tool callable with a timeout-enforcing shim **before** handing the agent to the SDK.

This means the `tool_timeout` knob is enforced even if the downstream SDK ignores it, even if the tool is a blocking C extension, and even if the tool is a subprocess that does not honour `CancelledError`.

The wrapper handles **sync and async tools** differently:

* **Sync tools** run in an instance-owned `ThreadPoolExecutor` (see `_get_tool_timeout_executor` in `agents_generator.py`); on timeout the future is best-effort cancelled. A call that already started cannot be interrupted, so the worker may keep running.
* **Async tools** are wrapped with `asyncio.wait_for(...)`, which cancels the underlying task cleanly.

**Exception raised on wrapper-level timeout:** on timeout the wrapper **raises `ToolTimeoutError`** (a `TimeoutError` subclass), not a JSON dict. This preserves each tool's declared return-type contract — a typed return value is never silently downgraded to a string. Framework adapters catch it and translate it per framework.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonai.agents_generator import ToolTimeoutError

def slow_lookup(query: str) -> str:
    ...  # a tool that may hang

agent = Agent(
    name="Researcher",
    instructions="Look things up.",
    tools=[slow_lookup],
    tool_config={"timeout": 5},  # ToolConfig(timeout=5) also works
)

try:
    agent.start("Look up X")
except ToolTimeoutError as e:
    print(e.tool_name, e.timeout_seconds, e.background_work_may_continue)
```

`ToolTimeoutError` carries three attributes:

| Attribute                      | Type    | Description                                                                                         |
| ------------------------------ | ------- | --------------------------------------------------------------------------------------------------- |
| `tool_name`                    | `str`   | Name of the tool that timed out                                                                     |
| `timeout_seconds`              | `float` | The per-call limit that was exceeded                                                                |
| `background_work_may_continue` | `bool`  | `True` only when a started sync worker could not be cancelled; async tools cancel cleanly (`False`) |

Crucially, the wrapper **also keeps passing** `tool_timeout` to the SDK Agent, so both layers are active — hence "defense in depth". The SDK's executor catches the common case; the wrapper catches the pathological one.

**Example usage:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
framework: praisonai
topic: Fetch and summarise a slow upstream API

config:
  tool_timeout: 15  # Wrapper-level + SDK-level enforcement

roles:
  fetcher:
    role: API Fetcher
    goal: Pull data without wedging the crew on a slow upstream
    backstory: Reliability-focused engineer.
    tools:
      - fetch_url
    tasks:
      fetch:
        description: Fetch {topic}
        expected_output: Summarised payload, or a translated timeout message if the upstream is slow.
```

When `fetch_url` blocks for more than 15 s, the wrapper raises `ToolTimeoutError` instead of hanging indefinitely; the framework adapter catches it and translates it into a value its framework understands, so the crew keeps moving.

**Precedence note:** When both wrapper-level and SDK-level timeouts apply, the **shorter** of the two wins in practice (whichever fires first). Both currently read from the same `tool_timeout` value, so they fire at the same time; if a user later configures different values at different layers, the first-to-fire semantics still hold.

#### Which value the wrapper uses

The wrapper resolves a single effective timeout per invocation:

| Source                                                           | Precedence                                                                                    |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `--tool-timeout N` on the CLI                                    | Wins outright                                                                                 |
| `tool_timeout: N` inside any `roles:` or `agents:` entry in YAML | `max(N)` across all entries, used when the CLI does not specify one                           |
| Nothing declared                                                 | No wrapper-layer wrapping (SDK-level timeout still applies if `tool_config` is set in Python) |

Boolean YAML values (`tool_timeout: yes`, `tool_timeout: true`) are **explicitly ignored** — they used to be silently coerced to a 1-second cap because `bool` subclasses `int`. Use integers or floats (e.g. `tool_timeout: 30`).

<Note>
  `PRAISONAI_TOOL_TIMEOUT_WORKERS` caps the named thread pool that runs sync tools under timeout (default `32`). Invalid or non-positive values fall back to `32` with a warning log line. Once half the pool's workers are permanently leaked to stuck sync tools, the pool is automatically recycled: leaked threads continue until their syscall returns, but new tool calls get a fresh pool instead of queueing behind them.
</Note>

### Advanced Timeout Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
timeout_config = {
    # Per-tool timeout settings
    "tool_timeouts": {
        "web_search": {
            "timeout": 30,
            "connect_timeout": 5,
            "read_timeout": 25,
            "retry_on_timeout": True,
            "max_retries": 2
        },
        "database_query": {
            "timeout": 120,
            "statement_timeout": 60,  # SQL statement timeout
            "lock_timeout": 10,       # Lock acquisition timeout
            "idle_timeout": 300       # Connection idle timeout
        },
        "api_call": {
            "timeout": 45,
            "dynamic_timeout": {
                "base": 30,
                "per_item": 0.5,  # Additional time per item
                "max": 120
            }
        }
    },
    
    # Global timeout settings
    "global_config": {
        "default_timeout": 60,
        "max_total_time": 300,  # Maximum time for all tool executions
        "parallel_timeout": 120,  # Timeout for parallel executions
        "timeout_buffer": 5      # Buffer time for cleanup
    }
}
```

### Dynamic Timeout Calculation

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
def calculate_dynamic_timeout(tool_name, input_data):
    """Calculate timeout based on input complexity"""
    base_timeouts = {
        "data_processor": 30,
        "ml_model": 60,
        "web_scraper": 45
    }
    
    base = base_timeouts.get(tool_name, 60)
    
    # Adjust based on input size
    if hasattr(input_data, '__len__'):
        size_factor = len(input_data) / 100
        return min(base + (size_factor * 10), 300)
    
    return base

# Configure dynamic timeouts
dynamic_timeout_config = {
    "timeout_calculator": calculate_dynamic_timeout,
    "enable_dynamic": True,
    "cache_calculations": True
}
```

## Performance Tuning

### Resource Management

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
performance_config = {
    # CPU and memory limits
    "resource_limits": {
        "max_cpu_percent": 80,
        "max_memory_mb": 2048,
        "max_threads": 10,
        "max_processes": 4
    },
    
    # Execution optimization
    "execution": {
        "parallel_tools": True,
        "max_parallel": 5,
        "queue_size": 100,
        "priority_queue": True,
        "batch_processing": True,
        "batch_size": 10
    },
    
    # Caching configuration
    "cache": {
        "enabled": True,
        "cache_size": 1000,
        "ttl": 3600,
        "cache_strategy": "lru",
        "serialize_method": "pickle"
    }
}
```

### Tool Execution Strategies

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Sequential execution (default)
sequential_config = {
    "execution_strategy": "sequential",
    "fail_fast": True,
    "continue_on_error": False
}

# Parallel execution
parallel_config = {
    "execution_strategy": "parallel",
    "max_workers": 5,
    "thread_pool": True,  # Use threads instead of processes
    "chunk_size": 10,
    "ordered_results": True
}

# Async execution
async_config = {
    "execution_strategy": "async",
    "event_loop": "asyncio",
    "max_concurrent": 10,
    "semaphore_limit": 5,
    "gather_errors": True
}

# Pipeline execution
pipeline_config = {
    "execution_strategy": "pipeline",
    "stages": [
        {"name": "fetch", "parallel": True},
        {"name": "process", "parallel": False},
        {"name": "store", "parallel": True}
    ],
    "buffer_size": 100,
    "backpressure": True
}
```

### Performance Monitoring

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
monitoring_config = {
    "metrics": {
        "track_execution_time": True,
        "track_memory_usage": True,
        "track_cpu_usage": True,
        "track_io_operations": True
    },
    
    "profiling": {
        "enabled": False,  # Enable for debugging
        "profile_type": "cProfile",
        "output_format": "stats",
        "top_functions": 20
    },
    
    "alerts": {
        "slow_execution": {
            "threshold": 30,  # seconds
            "action": "log_warning"
        },
        "high_memory": {
            "threshold": 1024,  # MB
            "action": "garbage_collect"
        },
        "repeated_timeouts": {
            "threshold": 3,
            "window": 300,  # 5 minutes
            "action": "circuit_break"
        }
    }
}
```

## Tool-Specific Configurations

### Web Scraping Tools

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
web_scraping_config = {
    "timeout": 45,
    "page_load_timeout": 30,
    "script_timeout": 20,
    "implicit_wait": 10,
    
    "retry_config": {
        "max_retries": 3,
        "retry_on": ["timeout", "connection_error"],
        "backoff_factor": 2
    },
    
    "performance": {
        "headless": True,
        "disable_images": True,
        "disable_javascript": False,
        "use_cache": True,
        "connection_pool_size": 10
    },
    
    "rate_limiting": {
        "requests_per_second": 2,
        "burst_size": 5,
        "respect_robots_txt": True
    }
}
```

### Database Tools

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
database_config = {
    "timeout": 60,
    "connection_timeout": 10,
    "statement_timeout": 50,
    
    "pool_config": {
        "min_connections": 2,
        "max_connections": 20,
        "connection_lifetime": 3600,
        "idle_timeout": 600,
        "retry_on_connection_failure": True
    },
    
    "query_optimization": {
        "use_prepared_statements": True,
        "batch_size": 1000,
        "fetch_size": 100,
        "use_compression": True
    },
    
    "transaction_config": {
        "isolation_level": "READ_COMMITTED",
        "auto_commit": False,
        "lock_timeout": 5
    }
}
```

### API Tools

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
api_tool_config = {
    "timeout": 30,
    "connect_timeout": 5,
    "read_timeout": 25,
    
    "retry_config": {
        "max_retries": 3,
        "retry_on_status": [429, 500, 502, 503],
        "exponential_backoff": True,
        "respect_retry_after": True
    },
    
    "rate_limiting": {
        "calls_per_minute": 60,
        "calls_per_hour": 1000,
        "burst_allowance": 10,
        "queue_excess": True
    },
    
    # Note: Tool circuit breakers are automatic - see /features/tool-circuit-breaker
    "circuit_breaker": {
        "failure_threshold": 5,
        "recovery_timeout": 60.0,
        "success_threshold": 2,
        "timeout": 30.0,
        "graceful_degradation": True
    }
}
```

### File Processing Tools

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
file_processing_config = {
    "timeout": 120,
    "chunk_size": 8192,  # bytes
    
    "file_limits": {
        "max_file_size": 104857600,  # 100MB
        "allowed_extensions": [".txt", ".csv", ".json", ".xml"],
        "scan_for_malware": True
    },
    
    "processing": {
        "use_memory_mapping": True,
        "parallel_processing": True,
        "compression": "auto",  # auto-detect
        "encoding": "utf-8",
        "error_handling": "replace"
    },
    
    "optimization": {
        "buffer_size": 65536,
        "read_ahead": True,
        "use_native_operations": True
    }
}
```

## Error Handling Configuration

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
error_handling_config = {
    "global_error_handler": "graceful",  # or "strict", "logging"
    
    "error_strategies": {
        "TimeoutError": {
            "retry": True,
            "max_retries": 2,
            "increase_timeout": 1.5,
            "fallback_tool": "simple_fetcher"
        },
        "RateLimitError": {
            "retry": True,
            "use_exponential_backoff": True,
            "switch_endpoint": True
        },
        "ValidationError": {
            "retry": False,
            "log_level": "ERROR",
            "user_message": "Invalid input provided"
        }
    },
    
    "recovery_strategies": {
        "checkpoint_enabled": True,
        "save_partial_results": True,
        "resume_on_restart": True
    }
}
```

## Resource Pooling

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
pooling_config = {
    "connection_pools": {
        "http": {
            "pool_size": 20,
            "max_overflow": 10,
            "timeout": 30,
            "recycle": 3600,
            "pre_ping": True
        },
        "database": {
            "pool_size": 10,
            "max_overflow": 5,
            "timeout": 30,
            "recycle": 1800
        }
    },
    
    "thread_pools": {
        "default": {
            "min_threads": 2,
            "max_threads": 10,
            "queue_size": 100,
            "thread_name_prefix": "tool-"
        }
    },
    
    "resource_sharing": {
        "share_connections": True,
        "share_sessions": True,
        "cleanup_interval": 300
    }
}
```

## Complete Tool Configuration Example

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Tool

# Configure a web search tool with comprehensive settings
web_search_tool = Tool(
    name="advanced_web_search",
    description="Advanced web search with optimizations",
    function=perform_web_search,
    config={
        # Timeout settings
        "timeout": 30,
        "connect_timeout": 5,
        "read_timeout": 25,
        
        # Performance settings
        "cache_enabled": True,
        "cache_ttl": 3600,
        "parallel_requests": 5,
        "batch_size": 10,
        
        # Error handling
        "retry_on_timeout": True,
        "max_retries": 3,
        "exponential_backoff": True,
        
        # Rate limiting
        "rate_limit": 60,  # per minute
        "burst_size": 10,
        
        # Resource limits
        "max_memory_mb": 512,
        "max_response_size": 10485760  # 10MB
    }
)

# Create agent with tool configuration
from praisonaiagents.config.feature_configs import ToolConfig
from praisonaiagents.tools.retry import RetryPolicy

agent = Agent(
    name="SearchAgent",
    tools=[web_search_tool],
    tool_config=ToolConfig(
        timeout=30,
        parallel=True,
        retry_policy=RetryPolicy(max_attempts=3),
    ),
)
```

## Environment Variables

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Tool timeout settings
export PRAISONAI_TOOL_TIMEOUT="60"
export PRAISONAI_TOOL_TIMEOUT_WORKERS="32"  # cap sync tool timeout thread pool (default 32)
export PRAISONAI_TOOL_CONNECT_TIMEOUT="5"
export PRAISONAI_TOOL_READ_TIMEOUT="55"

# Performance settings
export PRAISONAI_TOOL_PARALLEL="true"
export PRAISONAI_TOOL_MAX_WORKERS="5"
export PRAISONAI_TOOL_CACHE_ENABLED="true"
export PRAISONAI_TOOL_CACHE_SIZE="1000"

# Resource limits
export PRAISONAI_TOOL_MAX_MEMORY="2048"  # MB
export PRAISONAI_TOOL_MAX_CPU="80"       # Percent

# Error handling
export PRAISONAI_TOOL_MAX_RETRIES="3"
export PRAISONAI_TOOL_RETRY_DELAY="2"

# Monitoring
export PRAISONAI_TOOL_METRICS="true"
export PRAISONAI_TOOL_PROFILE="false"

# Tool Security & Filtering
export ALLOWED_TOOLS="search,send_message"      # Whitelist specific tools (prevents collisions)
export HERMES_ONLY_TOOLS="search,send_message"  # Deprecated alias for ALLOWED_TOOLS
```

<Note>
  For comprehensive tool whitelisting and collision prevention, see the [Allowed Tools](/features/allowed-tools) feature documentation.
</Note>

## Best Practices

### Timeout Guidelines

1. **Set appropriate timeouts based on tool type**
   * Fast operations: 5-15 seconds
   * API calls: 15-30 seconds
   * Web scraping: 30-60 seconds
   * Data processing: 60-300 seconds

2. **Use dynamic timeouts for variable workloads**
   ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
   timeout = base_timeout + (item_count * per_item_timeout)
   ```

3. **Always set connection timeouts lower than read timeouts**
   ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
   connect_timeout = 5  # Quick failure if can't connect
   read_timeout = 25    # More time for actual operation
   ```

### Performance Optimization

1. **Enable caching for idempotent operations**
2. **Use connection pooling for repeated operations**
3. **Circuit breakers for tools are automatic** - see [Tool Circuit Breaker](/features/tool-circuit-breaker)
4. **Monitor and alert on performance degradation**

## See Also

* [Tool Development](/tools/custom) - Creating custom tools
* [Agent Configuration](/configuration/agent-config) - Agent-level tool settings
* [Best Practices](/configuration/best-practices) - Configuration guidelines
