Overview
The Tool Override system allows loading custom tools from Python files, modules, and directories at runtime. This enables extending PraisonAI with custom functionality without modifying the core package.
Python API
from praisonai.templates import ToolOverrideLoader
loader = ToolOverrideLoader()
# Load tools from a Python file
tools = loader.load_from_file("./my_tools.py")
print(f"Loaded tools: {list(tools.keys())}")
# Load tools from a module
tools = loader.load_from_module("mypackage.tools")
# Load tools from a directory
tools = loader.load_from_directory("~/.praisonai/tools")
Context Manager Pattern
Use the context manager for temporary tool overrides:
from praisonai.templates import ToolOverrideLoader
loader = ToolOverrideLoader()
with loader.override_context(
files=["./custom_tools.py"],
directories=["./more_tools"]
) as tools:
# Tools are available within this context
print(f"Available tools: {list(tools.keys())}")
# Tools are cleaned up after context exits
from praisonai.templates.tool_override import create_tool_registry_with_overrides
# Create registry with custom tools
registry = create_tool_registry_with_overrides(
override_files=["./my_tools.py"],
override_dirs=["~/.praisonai/tools"],
include_defaults=True # Include built-in tools
)
# Resolution order (highest priority first):
# 1. Override files (explicit)
# 2. Override directories
# 3. Default custom dirs (~/.praisonai/tools)
# 4. Built-in tools
PraisonAI checks these locations for custom tools:
| Priority | Path | Description |
|---|
| 1 | ./tools.py | Current working directory |
| 2 | ~/.praisonai/tools | Primary user tools |
| 3 | ~/.config/praison/tools | XDG-friendly location |
If a tools.py file exists in your current working directory, it will be automatically loaded when running templates or agents. This is the easiest way to add project-specific tools.
loader = ToolOverrideLoader()
default_dirs = loader.get_default_tool_dirs()
for d in default_dirs:
print(f"Default dir: {d}")
Discover tool names without importing/executing code:
loader = ToolOverrideLoader()
# Uses AST parsing - safe, no code execution
tool_names = loader.discover_tools_in_directory("./tools")
print(f"Found tools: {tool_names}")
Security
Local Paths Only
Remote URLs are rejected by default:
from praisonai.templates import ToolOverrideLoader, SecurityError
loader = ToolOverrideLoader()
try:
# This will raise SecurityError
loader.load_from_file("https://example.com/tools.py")
except SecurityError as e:
print(f"Blocked: {e}")
Safe Defaults
- Only local file paths are allowed
- No automatic code execution on import
- Discovery uses AST parsing (no execution)
- Context manager ensures cleanup
Create a Python file with functions:
# my_tools.py
def my_search_tool(query: str) -> str:
"""Search for information."""
return f"Results for: {query}"
def my_calculator(expression: str) -> float:
"""Evaluate a math expression."""
return eval(expression) # Use safely in production
# Private functions (starting with _) are ignored
def _helper():
pass