Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions docs/user-guide/concepts/tools/python-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ from strands import tool
@tool
def weather_forecast(city: str, days: int = 3) -> str:
"""Get weather forecast for a city.

Args:
city: The name of the city
days: Number of days for the forecast
Expand Down Expand Up @@ -48,7 +48,7 @@ You can also optionally override the tool name or description by providing them
@tool(name="get_weather", description="Retrieves weather forecast for a specified location")
def weather_forecast(city: str, days: int = 3) -> str:
"""Implementation function for weather forecasting.

Args:
city: The name of the city
days: Number of days for the forecast
Expand All @@ -65,7 +65,7 @@ By default, your function's return value is automatically formatted as a text re
@tool
def fetch_data(source_id: str) -> dict:
"""Fetch data from a specified source.

Args:
source_id: Identifier for the data source
"""
Expand All @@ -88,6 +88,31 @@ def fetch_data(source_id: str) -> dict:

For more details, see the [Tool Response Format](#tool-response-format) section below.

### Async Invocation

Decorated tools may also be defined async. Strands will invoke all async tools concurrently.

```Python
import asyncio
from strands import Agent, tool


@tool
async def call_api() -> str:
"""Call API asynchronously."""

await asyncio.sleep(5) # simulated api call
return "API result"


async def async_example():
agent = Agent(tools=[call_api])
await agent.invoke_async("Can you call my API?")


asyncio.run(async_example())
```

## Python Modules as Tools

An alternative approach is to define a tool as a Python module with a specific structure. This enables creating tools that don't depend on the SDK directly.
Expand Down Expand Up @@ -132,14 +157,14 @@ def weather_forecast(tool, **kwargs: Any):
# Extract tool parameters
tool_use_id = tool["toolUseId"]
tool_input = tool["input"]

# Get parameter values
city = tool_input.get("city", "")
days = tool_input.get("days", 3)

# Tool implementation
result = f"Weather forecast for {city} for the next {days} days..."

# Return structured response
return {
"toolUseId": tool_use_id,
Expand Down Expand Up @@ -171,6 +196,34 @@ agent = Agent(
)
```

### Async Invocation

Similar to decorated tools, users may define their module tools async.

```Python
TOOL_SPEC = {
"name": "call_api",
"description": "Call my API asynchronously.",
"inputSchema": {
"json": {
"type": "object",
"properties": {},
"required": []
}
}
}

async def call_api(tool, **kwargs):
await asyncio.sleep(5) # simulated api call
result = "API result"

return {
"toolUseId": tool["toolUseId"],
"status": "success",
"content": [{"text": result}],
}
```

### Tool Response Format

Tools can return responses in various formats using the [`ToolResult`](../../../api-reference/types.md#strands.types.tools.ToolResult) structure. This structure provides flexibility for returning different types of content while maintaining a consistent interface.
Expand Down Expand Up @@ -227,4 +280,4 @@ When using the [`@tool`](../../../api-reference/tools.md#strands.tools.decorator

1. If you return a string or other simple value, it's wrapped as `{"text": str(result)}`
2. If you return a dictionary with the proper [`ToolResult`](../../../api-reference/types.md#strands.types.tools.ToolResult) structure, it's used directly
3. If an exception occurs, it's converted to an error response
3. If an exception occurs, it's converted to an error response
44 changes: 26 additions & 18 deletions docs/user-guide/concepts/tools/tools_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,21 @@ Build your own Python tools using the Strands SDK's tool interfaces.
Function decorated tools can be placed anywhere in your codebase and imported in to your agent's list of tools. Define any Python function as a tool by using the [`@tool`](../../../api-reference/tools.md#strands.tools.decorator.tool) decorator.

```python
import asyncio
from strands import Agent, tool


@tool
def get_user_location() -> str:
"""Get the user's location
"""
"""Get the user's location."""

# Implement user location lookup logic here
return "Seattle, USA"


@tool
def weather(location: str) -> str:
"""Get weather information for a location
"""Get weather information for a location.

Args:
location: City or location name
Expand All @@ -115,30 +117,35 @@ def weather(location: str) -> str:
# Implement weather lookup logic here
return f"Weather for {location}: Sunny, 72°F"

agent = Agent(tools=[get_user_location, weather])

# Use the agent with the custom tools
agent("What is the weather like in my location?")
```
@tool
async def call_api() -> str:
"""Call API asynchronously.

#### Module-Based Approach
Strands will invoke all async tools concurrently.
"""

Tool modules can contain function decorated tools, in this example `get_user_location.py`:
await asyncio.sleep(5) # simulated api call
return "API result"

```python
# get_user_location.py

from strands import tool
def basic_example():
agent = Agent(tools=[get_user_location, weather])
agent("What is the weather like in my location?")

@tool
def get_user_location() -> str:
"""Get the user's location
"""

# Implement user location lookup logic here
return "Seattle, USA"
async def async_example():
agent = Agent(tools=[call_api])
await agent.invoke_async("Can you call my API?")


def main():
basic_example()
asyncio.run(async_example())
```

#### Module-Based Approach

Tool modules can also provide single tools that don't use the decorator pattern, instead they define the `TOOL_SPEC` variable and a function matching the tool's name. In this example `weather.py`:

```python
Expand All @@ -165,6 +172,7 @@ TOOL_SPEC = {
}

# Function name must match tool name
# May also be defined async similar to decorated tools
def weather(tool: ToolUse, **kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
location = tool["input"]["location"]
Expand Down