Parallel Tool Calls

Several Tools in One Turn

When the calls do not depend on each other, the model can request several tools in a single response: for "compare the Stride Pro and Cushion Jogger" it may return two tool_use blocks. Run them all (concurrently if you like) and send all results in one user message, each with its matching tool_use_id.

Note: To allow at most one call per turn, set tool_choice={"type": "auto", "disable_parallel_tool_use": True}.

Handling Multiple tool_use Blocks

Example

A stand-in model response with two tool calls; they run concurrently in a thread pool and the results go back together, in the original order:

import json
from concurrent.futures import ThreadPoolExecutor

PRODUCTS = {"stride-pro": {"price": 2499, "rating": 4.4}, "cushion-jogger": {"price": 2999, "rating": 4.1}}
def get_product(slug):
    return PRODUCTS[slug]

response_content = [   # stand-in for one model reply with two parallel tool calls
    {"type": "text", "text": "I'll look up both shoes."},
    {"type": "tool_use", "id": "toolu_A", "name": "get_product", "input": {"slug": "stride-pro"}},
    {"type": "tool_use", "id": "toolu_B", "name": "get_product", "input": {"slug": "cushion-jogger"}},
]
calls = [b for b in response_content if b["type"] == "tool_use"]

with ThreadPoolExecutor() as pool:
    outputs = list(pool.map(lambda b: get_product(**b["input"]), calls))

tool_results = [{"type": "tool_result", "tool_use_id": b["id"], "content": json.dumps(out)}
                for b, out in zip(calls, outputs)]
print(json.dumps({"role": "user", "content": tool_results}, indent=1))

Output

{
 "role": "user",
 "content": [
  {
   "type": "tool_result",
   "tool_use_id": "toolu_A",
   "content": "{\"price\": 2499, \"rating\": 4.4}"
  },
  {
   "type": "tool_result",
   "tool_use_id": "toolu_B",
   "content": "{\"price\": 2999, \"rating\": 4.1}"
  }
 ]
}