diff --git a/docs/user-guide/concepts/tools/python-tools.md b/docs/user-guide/concepts/tools/python-tools.md index c6c468eb..6130a3cb 100644 --- a/docs/user-guide/concepts/tools/python-tools.md +++ b/docs/user-guide/concepts/tools/python-tools.md @@ -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 @@ -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 @@ -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 """ @@ -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. @@ -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, @@ -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. @@ -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 \ No newline at end of file +3. If an exception occurs, it's converted to an error response diff --git a/docs/user-guide/concepts/tools/tools_overview.md b/docs/user-guide/concepts/tools/tools_overview.md index 437e51f6..4b93ae25 100644 --- a/docs/user-guide/concepts/tools/tools_overview.md +++ b/docs/user-guide/concepts/tools/tools_overview.md @@ -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 @@ -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 @@ -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"]