Tool Decorator API Reference¶
Quick reference for the @tool decorator and tag filtering API.
Decorator Syntax¶
from agentflow.utils import tool
@tool(
name: str | None = None,
description: str | None = None,
tags: list[str] | set[str] | None = None,
provider: str | None = None,
capabilities: list[str] | None = None,
metadata: dict[str, Any] | None = None
)
def my_function(...):
"""Function implementation."""
pass
Tag Filtering API¶
❌ INCORRECT (Does Not Exist)¶
# This method does NOT exist - will cause AttributeError
tool_node.filter_by_tags(["database"]) # ❌ ERROR
✅ CORRECT Usage¶
from agentflow.graph import ToolNode
# Define tools with tags
@tool(tags=["database", "read"])
def read_db(id: int): pass
@tool(tags=["database", "write"])
def write_db(data: dict): pass
@tool(tags=["web", "search"])
def search_web(query: str): pass
# Create ToolNode
all_tools = [read_db, write_db, search_web]
tool_node = ToolNode(all_tools)
# CORRECT: Pass tags parameter to all_tools() or all_tools_sync()
db_tools = await tool_node.all_tools(tags={"database"}) # ✅ Returns read_db, write_db schemas
read_tools = await tool_node.all_tools(tags={"read"}) # ✅ Returns read_db schema
web_tools = tool_node.all_tools_sync(tags={"web"}) # ✅ Sync version
# Tags parameter accepts a set[str]
filtered = await tool_node.all_tools(tags={"database", "web"})
How Tag Filtering Works¶
# From schema.py implementation:
def get_local_tool(self, tags: set[str] | None = None) -> list[dict]:
"""Generate tool schemas for registered functions."""
tools = []
for name, fn in self._funcs.items():
fun_tags = getattr(fn, "_py_tool_tags", None)
# Skip tools that don't match the filter
if tags and fun_tags and tags.isdisjoint(fun_tags):
continue
# Include this tool
tools.append(...)
return tools
Key Points:
- Intersection check: Tools are included if they have ANY tag from the filter set
isdisjoint()logic: Tool is SKIPPED if filter tags and tool tags have NO overlap- Empty filter: If
tags=None, all tools are returned - No tags on tool: Tools without tags are always included (unless filter is provided)
Complete Examples¶
Async Context¶
from agentflow.graph import ToolNode, StateGraph
from agentflow.utils import tool
from litellm import acompletion
@tool(tags=["safe", "read"])
def read_data(id: int): pass
@tool(tags=["dangerous", "write"])
def delete_data(id: int): pass
tool_node = ToolNode([read_data, delete_data])
# In an async agent node
async def agent_node(state, config):
# Get only safe tools for this agent
safe_tools = await tool_node.all_tools(tags={"safe"})
response = await acompletion(
model="gpt-4o-mini",
messages=[...],
tools=safe_tools # Only safe tools available
)
return response
Sync Context¶
from litellm import completion
# Sync version for non-async code
def agent_node_sync(state, config):
safe_tools = tool_node.all_tools_sync(tags={"safe"})
response = completion(
model="gpt-4o-mini",
messages=[...],
tools=safe_tools
)
return response
Multiple Tag Filters¶
# Tools with multiple tags
@tool(tags=["database", "user", "read"])
def get_user(id: int): pass
@tool(tags=["database", "user", "write"])
def create_user(name: str): pass
@tool(tags=["database", "product", "read"])
def get_product(id: int): pass
tool_node = ToolNode([get_user, create_user, get_product])
# Filter by category
user_tools = await tool_node.all_tools(tags={"user"}) # get_user, create_user
product_tools = await tool_node.all_tools(tags={"product"}) # get_product
read_tools = await tool_node.all_tools(tags={"read"}) # get_user, get_product
# Multiple tags = OR logic (ANY match includes the tool)
db_or_user = await tool_node.all_tools(tags={"database", "user"}) # All three tools
Utility Functions¶
from agentflow.utils import get_tool_metadata, has_tool_decorator
@tool(name="example", description="Test", tags=["demo"])
def my_tool(x: int): return x * 2
# Check if decorated
if has_tool_decorator(my_tool):
print("Tool has decorator")
# Get all metadata
metadata = get_tool_metadata(my_tool)
print(metadata)
# Output:
# {
# 'name': 'example',
# 'description': 'Test',
# 'tags': {'demo'},
# 'provider': None,
# 'capabilities': None,
# 'metadata': None
# }
Common Patterns¶
Role-Based Tool Access¶
@tool(tags=["admin", "dangerous"])
def delete_user(id: int): pass
@tool(tags=["user", "safe"])
def view_profile(id: int): pass
def get_tools_for_role(tool_node, role: str):
"""Get tools appropriate for user role."""
if role == "admin":
return tool_node.all_tools_sync() # All tools
else:
return tool_node.all_tools_sync(tags={"safe"}) # Only safe tools
Environment-Specific Tools¶
@tool(tags=["production", "external"])
def call_payment_api(): pass
@tool(tags=["development", "mock"])
def mock_payment_api(): pass
# Select based on environment
import os
env = os.getenv("ENVIRONMENT", "development")
tools = await tool_node.all_tools(tags={env})
See Also¶
- Tool Decorator Tutorial - Comprehensive guide
- Tools Concept Guide - Deep dive into ToolNode
- React Examples - Working code examples