Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.praison.ai/llms.txt

Use this file to discover all available pages before exploring further.

ToolResolver is the one place PraisonAI looks for tools.py — whether it ships callables or BaseTool classes.

Quick Start

1

Basic Usage

Drop a tools.py next to your YAML/script, set the environment variable, and the resolver picks both kinds up automatically:
export PRAISONAI_ALLOW_LOCAL_TOOLS=true
from praisonaiagents import Agent

# Tools from tools.py are automatically loaded
agent = Agent(
    name="Tool User",
    instructions="Use available tools to help the user"
)

agent.start("Calculate something using my tools")
2

Direct Python Usage

When embedding PraisonAI in your own Python code:
from praisonai.tool_resolver import ToolResolver

resolver = ToolResolver()  # defaults to ./tools.py
callables = resolver.get_local_callables()       # path A: functions
tool_classes = resolver.get_local_tool_classes() # path B: BaseTool instances

print(f"Found {len(callables)} functions")
print(f"Found {len(tool_classes)} tool classes")

How It Works

The resolver delegates to _safe_loader.load_user_module for consistent environment variable checking and CWD path-traversal guard. The loaded module is reflected to extract either plain functions or tool class instances, then cached as an immutable view for thread safety.

Two Flavours of tools.py

What’s in tools.pyMethodReturned shape
Plain Python functionsget_local_callables()List[Callable]
praisonai_tools.BaseTool / praisonai.tools.BaseTool / langchain_community.tools.* classesget_local_tool_classes()Dict[str, ToolInstance] (instantiated)
Example tools.py with functions:
# tools.py - Plain functions
def calculate_sum(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

def get_weather(city: str) -> str:
    """Get weather for a city."""
    return f"Weather in {city}: sunny"
Example tools.py with BaseTool classes:
# tools.py - BaseTool classes
from praisonai_tools import BaseTool

class CalculatorTool(BaseTool):
    name = "calculator"
    description = "Perform basic math operations"
    
    def _run(self, operation: str) -> str:
        # Implementation here
        return "42"

class WeatherTool(BaseTool):
    name = "weather"
    description = "Get weather information"
    
    def _run(self, city: str) -> str:
        return f"Weather in {city}: sunny"

Configuration Options

ParameterTypeDefaultDescription
tools_py_pathOptional[str]"tools.py"Path to tools.py file to load
# Load from non-default path
resolver = ToolResolver(tools_py_path="/abs/path/to/my_tools.py")

Common Patterns

from praisonai.tool_resolver import ToolResolver

# Load from specific file
resolver = ToolResolver(tools_py_path="/project/utils/custom_tools.py")
callables = resolver.get_local_callables()

Security

Security enforcement is handled by _safe_loader.load_user_module:
  • Environment gate: Requires PRAISONAI_ALLOW_LOCAL_TOOLS=true
  • CWD constraint: Refuses paths outside current working directory
  • Path traversal protection: Prevents ../ style attacks
See Security Environment Variables for details.
# These will be refused even with PRAISONAI_ALLOW_LOCAL_TOOLS=true
resolver = ToolResolver(tools_py_path="../outside_cwd/tools.py")  # ❌
resolver = ToolResolver(tools_py_path="/tmp/tools.py")           # ❌

# This works when inside your project directory
resolver = ToolResolver(tools_py_path="./utils/tools.py")        # ✅

Caching Behaviour

  • First call: Loads and caches tools.py content
  • Subsequent calls: Returns cached immutable view (MappingProxyType)
  • Thread safety: Uses locks for concurrent access
  • Cache clearing: clear_cache() resets for reloading
resolver = ToolResolver()

# First call loads from disk
tools1 = resolver.get_local_callables()  # Loads tools.py

# Second call uses cache
tools2 = resolver.get_local_callables()  # Uses cache

# Clear and reload
resolver.clear_cache()
tools3 = resolver.get_local_callables()  # Loads tools.py again

Best Practices

Place tools.py in your current working directory. Paths outside CWD are refused even with the environment variable set. This prevents path traversal attacks from HTTP API callers.
# Good structure
project/
├── agents.yaml
├── tools.py          # ✅ In CWD
└── utils/
    └── helpers.py

# Bad structure  
project/
├── agents.yaml
└── ../tools/
    └── tools.py      # ❌ Outside CWD
Use plain Python functions for praisonaiagents agents. Reserve BaseTool classes for crewai-style flows or when you need complex tool state management.
# Simple and effective
def process_data(data: str) -> str:
    """Process some data."""
    return data.upper()

# Only when you need complex behavior
class DataProcessorTool(BaseTool):
    name = "data_processor"
    
    def __init__(self):
        self.state = {}  # Complex state management
Don’t import ToolResolver from praisonaiagents — it lives in the wrapper at praisonai.tool_resolver. The wrapper handles YAML-based tool resolution.
# Correct
from praisonai.tool_resolver import ToolResolver

# Incorrect - won't work
from praisonaiagents.tool_resolver import ToolResolver  # ❌
Set PRAISONAI_ALLOW_LOCAL_TOOLS=true only in development or trusted deployment environments. This prevents arbitrary code execution from untrusted working directories.
# Development
export PRAISONAI_ALLOW_LOCAL_TOOLS=true

# Production - keep disabled unless absolutely necessary
# unset PRAISONAI_ALLOW_LOCAL_TOOLS

Security Environment Variables

Environment variable security controls

Tools

General tools documentation