Documentation Index
Fetch the complete documentation index at: https://docs.definable.ai/llms.txt
Use this file to discover all available pages before exploring further.
Hooks let you run custom logic around tool execution — validate inputs, transform outputs, log calls, enforce permissions, or trigger side effects.
Pre-Hooks and Post-Hooks
Use pre_hook and post_hook on the @tool decorator:
from definable.tool.decorator import tool
def log_before(tool_name, args):
print(f"Calling {tool_name} with {args}")
def log_after(tool_name, args, result):
print(f"{tool_name} returned: {result}")
@tool(pre_hook=log_before, post_hook=log_after)
def calculate(expression: str) -> str:
"""Evaluate a math expression."""
return str(eval(expression))
Pre-Hook Signature
def pre_hook(tool_name: str, args: dict) -> None:
...
Pre-hooks receive the tool name and arguments before execution. They can:
- Log the call
- Validate arguments beyond schema checks
- Raise an exception to prevent execution
Post-Hook Signature
def post_hook(tool_name: str, args: dict, result) -> None:
...
Post-hooks receive the tool name, arguments, and the result after execution. They can:
- Log or audit the result
- Send notifications
- Update metrics
Hook Chains
Use tool_hooks to attach a list of hooks that run in order:
def audit_hook(tool_name, args):
audit_log.append({"tool": tool_name, "args": args})
def rate_limit_hook(tool_name, args):
if not check_rate_limit(tool_name):
raise Exception(f"Rate limit exceeded for {tool_name}")
@tool(tool_hooks=[audit_hook, rate_limit_hook])
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email."""
return f"Email sent to {to}"
All hooks in the list run before execution. If any hook raises an exception, the tool is not called.
Practical Examples
Permission Check
def require_admin(tool_name, args):
if not current_user.is_admin:
raise PermissionError(f"Admin required for {tool_name}")
@tool(pre_hook=require_admin)
def delete_user(user_id: str) -> str:
"""Delete a user account."""
return f"Deleted user {user_id}"
Result Sanitization
def redact_pii(tool_name, args, result):
# Redact any email addresses from tool results
import re
return re.sub(r'\S+@\S+', '[REDACTED]', str(result))
@tool(post_hook=redact_pii)
def lookup_customer(customer_id: str) -> str:
"""Look up customer details."""
return "John Doe, [email protected], Account #12345"
Timing
import time
_timings = {}
def start_timer(tool_name, args):
_timings[tool_name] = time.perf_counter()
def stop_timer(tool_name, args, result):
elapsed = time.perf_counter() - _timings.pop(tool_name, 0)
print(f"{tool_name} took {elapsed:.3f}s")
@tool(pre_hook=start_timer, post_hook=stop_timer)
def slow_search(query: str) -> str:
"""Search with timing."""
time.sleep(1)
return f"Results for {query}"
Async Hooks
Hooks can be async functions when used with async agent execution:
async def async_audit(tool_name, args):
await audit_service.log(tool_name, args)
@tool(pre_hook=async_audit)
async def fetch_data(url: str) -> str:
"""Fetch data from a URL."""
async with httpx.AsyncClient() as client:
resp = await client.get(url)
return resp.text