Skip to content

Commit 7fa5931

Browse files
authored
async python tools (#148)
1 parent d9ac806 commit 7fa5931

File tree

2 files changed

+86
-25
lines changed

2 files changed

+86
-25
lines changed

docs/user-guide/concepts/tools/python-tools.md

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ from strands import tool
2020
@tool
2121
def weather_forecast(city: str, days: int = 3) -> str:
2222
"""Get weather forecast for a city.
23-
23+
2424
Args:
2525
city: The name of the city
2626
days: Number of days for the forecast
@@ -48,7 +48,7 @@ You can also optionally override the tool name or description by providing them
4848
@tool(name="get_weather", description="Retrieves weather forecast for a specified location")
4949
def weather_forecast(city: str, days: int = 3) -> str:
5050
"""Implementation function for weather forecasting.
51-
51+
5252
Args:
5353
city: The name of the city
5454
days: Number of days for the forecast
@@ -65,7 +65,7 @@ By default, your function's return value is automatically formatted as a text re
6565
@tool
6666
def fetch_data(source_id: str) -> dict:
6767
"""Fetch data from a specified source.
68-
68+
6969
Args:
7070
source_id: Identifier for the data source
7171
"""
@@ -88,6 +88,31 @@ def fetch_data(source_id: str) -> dict:
8888

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

91+
### Async Invocation
92+
93+
Decorated tools may also be defined async. Strands will invoke all async tools concurrently.
94+
95+
```Python
96+
import asyncio
97+
from strands import Agent, tool
98+
99+
100+
@tool
101+
async def call_api() -> str:
102+
"""Call API asynchronously."""
103+
104+
await asyncio.sleep(5) # simulated api call
105+
return "API result"
106+
107+
108+
async def async_example():
109+
agent = Agent(tools=[call_api])
110+
await agent.invoke_async("Can you call my API?")
111+
112+
113+
asyncio.run(async_example())
114+
```
115+
91116
## Python Modules as Tools
92117

93118
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.
@@ -132,14 +157,14 @@ def weather_forecast(tool, **kwargs: Any):
132157
# Extract tool parameters
133158
tool_use_id = tool["toolUseId"]
134159
tool_input = tool["input"]
135-
160+
136161
# Get parameter values
137162
city = tool_input.get("city", "")
138163
days = tool_input.get("days", 3)
139-
164+
140165
# Tool implementation
141166
result = f"Weather forecast for {city} for the next {days} days..."
142-
167+
143168
# Return structured response
144169
return {
145170
"toolUseId": tool_use_id,
@@ -171,6 +196,34 @@ agent = Agent(
171196
)
172197
```
173198

199+
### Async Invocation
200+
201+
Similar to decorated tools, users may define their module tools async.
202+
203+
```Python
204+
TOOL_SPEC = {
205+
"name": "call_api",
206+
"description": "Call my API asynchronously.",
207+
"inputSchema": {
208+
"json": {
209+
"type": "object",
210+
"properties": {},
211+
"required": []
212+
}
213+
}
214+
}
215+
216+
async def call_api(tool, **kwargs):
217+
await asyncio.sleep(5) # simulated api call
218+
result = "API result"
219+
220+
return {
221+
"toolUseId": tool["toolUseId"],
222+
"status": "success",
223+
"content": [{"text": result}],
224+
}
225+
```
226+
174227
### Tool Response Format
175228

176229
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.
@@ -227,4 +280,4 @@ When using the [`@tool`](../../../api-reference/tools.md#strands.tools.decorator
227280

228281
1. If you return a string or other simple value, it's wrapped as `{"text": str(result)}`
229282
2. If you return a dictionary with the proper [`ToolResult`](../../../api-reference/types.md#strands.types.tools.ToolResult) structure, it's used directly
230-
3. If an exception occurs, it's converted to an error response
283+
3. If an exception occurs, it's converted to an error response

docs/user-guide/concepts/tools/tools_overview.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,21 @@ Build your own Python tools using the Strands SDK's tool interfaces.
9494
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.
9595

9696
```python
97+
import asyncio
9798
from strands import Agent, tool
9899

100+
99101
@tool
100102
def get_user_location() -> str:
101-
"""Get the user's location
102-
"""
103+
"""Get the user's location."""
103104

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

108+
107109
@tool
108110
def weather(location: str) -> str:
109-
"""Get weather information for a location
111+
"""Get weather information for a location.
110112
111113
Args:
112114
location: City or location name
@@ -115,30 +117,35 @@ def weather(location: str) -> str:
115117
# Implement weather lookup logic here
116118
return f"Weather for {location}: Sunny, 72°F"
117119

118-
agent = Agent(tools=[get_user_location, weather])
119120

120-
# Use the agent with the custom tools
121-
agent("What is the weather like in my location?")
122-
```
121+
@tool
122+
async def call_api() -> str:
123+
"""Call API asynchronously.
123124
124-
#### Module-Based Approach
125+
Strands will invoke all async tools concurrently.
126+
"""
125127

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

128-
```python
129-
# get_user_location.py
130131

131-
from strands import tool
132+
def basic_example():
133+
agent = Agent(tools=[get_user_location, weather])
134+
agent("What is the weather like in my location?")
132135

133-
@tool
134-
def get_user_location() -> str:
135-
"""Get the user's location
136-
"""
137136

138-
# Implement user location lookup logic here
139-
return "Seattle, USA"
137+
async def async_example():
138+
agent = Agent(tools=[call_api])
139+
await agent.invoke_async("Can you call my API?")
140+
141+
142+
def main():
143+
basic_example()
144+
asyncio.run(async_example())
140145
```
141146

147+
#### Module-Based Approach
148+
142149
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`:
143150

144151
```python
@@ -165,6 +172,7 @@ TOOL_SPEC = {
165172
}
166173

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

0 commit comments

Comments
 (0)