Appearance
Function Tools
Build tools from plain Python functions using the @tool decorator. The SDK extracts the tool name, description, and parameter schema automatically from the function signature and docstring.
Minimal example
python
from elsai import tool
@tool
def greet(name: str) -> str:
"""Greet a person by name.
Args:
name: The person's name.
"""
return f"Hello, {name}!"Docstring format
The model uses the docstring to understand when and how to call the tool. Write clear, concise descriptions:
python
@tool
def send_email(to: str, subject: str, body: str) -> dict:
"""Send an email to a recipient.
Args:
to: The recipient's email address.
subject: The email subject line.
body: The full email body text.
Returns:
A dict with 'success' bool and 'message_id' string.
"""
# implementation
return {"success": True, "message_id": "msg-123"}Type hints
Always add type hints — they become the JSON schema the model sees:
| Python type | JSON Schema |
|---|---|
str | string |
int | integer |
float | number |
bool | boolean |
list[str] | array of string |
dict | object |
Optional[str] | string (nullable) |
Optional parameters with defaults
python
@tool
def search_web(query: str, max_results: int = 5, safe_search: bool = True) -> list[str]:
"""Search the web and return URLs.
Args:
query: The search query.
max_results: Maximum results to return (default 5).
safe_search: Enable safe search filtering.
"""
...Returning rich content
Return a dict with a "content" key for structured results the model can read:
python
@tool
def get_chart_data(symbol: str) -> dict:
"""Retrieve stock chart data for a ticker symbol.
Args:
symbol: The stock ticker (e.g. AAPL).
"""
data = fetch_from_api(symbol)
return {
"content": [
{"text": f"Price: ${data['price']}, Change: {data['change']}%"},
]
}Async tools
Tools can be async:
python
import httpx
from elsai import tool
@tool
async def fetch_url(url: str) -> str:
"""Fetch the text content of a URL.
Args:
url: The URL to fetch.
"""
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text[:2000] # truncateAccessing agent state inside tools
Add an agent parameter to receive the calling agent:
python
from elsai import tool
@tool
def save_note(content: str, agent) -> str:
"""Save a note to the agent's memory.
Args:
content: The note content to save.
"""
notes = agent.state.get("notes", [])
notes.append(content)
agent.state["notes"] = notes
return f"Saved note #{len(notes)}":::caution Do not include agent in the Args docstring section — it's injected automatically and the model should not try to fill it. :::
Context injection with ToolContext
For dependency injection without modifying the function signature:
python
from elsai import tool
from elsai.tools.tool_provider import ToolProvider
@tool
def query_db(sql: str) -> list:
"""Execute a read-only SQL query.
Args:
sql: The SQL query to run.
"""
# db_connection injected at runtime
...Tool result format
Tools can return:
- A
str— sent as text to the model - A
dictwith a"content"list — rich content blocks - A
list— converted to JSON string - Any Python object — converted to string via
str()
Error handling
Raise an exception to signal a tool error back to the model:
python
@tool
def divide(a: float, b: float) -> float:
"""Divide a by b.
Args:
a: The dividend.
b: The divisor.
"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / bThe agent will catch the exception, format it as a tool error, and let the model decide how to proceed.
Hot-reloading from a directory
Create a ./tools/ directory and place tool files there:
my_project/
├── main.py
└── tools/
├── database.py # contains @tool decorated functions
├── email.py
└── search.pypython
# main.py
from elsai import Agent
agent = Agent(load_tools_from_directory=True)
# Tools are loaded from ./tools/ and reloaded automatically on file changes