Execute multiple tool calls in parallel with optional callbacks. Uses chuk-tool-processor's ToolCall/ToolResult models for consistency. Results are returned in completion order (faster tools return first). Args: manager: ToolManager instance to execute tools calls:
(
manager: ToolManager,
calls: list[CTPToolCall],
timeout: float | None = None,
on_tool_start: Callable[[CTPToolCall], Awaitable[None]] | None = None,
on_tool_result: Callable[[CTPToolResult], Awaitable[None]] | None = None,
max_concurrency: int = 4,
batch_timeout: float | None = None,
)
| 30 | |
| 31 | |
| 32 | async def execute_tools_parallel( |
| 33 | manager: ToolManager, |
| 34 | calls: list[CTPToolCall], |
| 35 | timeout: float | None = None, |
| 36 | on_tool_start: Callable[[CTPToolCall], Awaitable[None]] | None = None, |
| 37 | on_tool_result: Callable[[CTPToolResult], Awaitable[None]] | None = None, |
| 38 | max_concurrency: int = 4, |
| 39 | batch_timeout: float | None = None, |
| 40 | ) -> list[CTPToolResult]: |
| 41 | """ |
| 42 | Execute multiple tool calls in parallel with optional callbacks. |
| 43 | |
| 44 | Uses chuk-tool-processor's ToolCall/ToolResult models for consistency. |
| 45 | Results are returned in completion order (faster tools return first). |
| 46 | |
| 47 | Args: |
| 48 | manager: ToolManager instance to execute tools |
| 49 | calls: List of CTPToolCall objects to execute |
| 50 | timeout: Timeout per tool execution (uses default if not specified) |
| 51 | on_tool_start: Async callback invoked when each tool starts |
| 52 | on_tool_result: Async callback invoked when each tool completes |
| 53 | max_concurrency: Maximum concurrent executions (default: 4) |
| 54 | batch_timeout: Global timeout for entire batch (auto-computed if None) |
| 55 | |
| 56 | Returns: |
| 57 | List of CTPToolResult objects in completion order |
| 58 | """ |
| 59 | if not calls: |
| 60 | return [] |
| 61 | |
| 62 | effective_timeout = timeout or manager.tool_timeout |
| 63 | sem = asyncio.Semaphore(max_concurrency) |
| 64 | results: list[CTPToolResult] = [] |
| 65 | |
| 66 | async def execute_single(call: CTPToolCall) -> CTPToolResult: |
| 67 | """Execute a single tool call with semaphore control.""" |
| 68 | start_time = datetime.now(UTC) |
| 69 | |
| 70 | async with sem: |
| 71 | # Invoke start callback |
| 72 | if on_tool_start: |
| 73 | try: |
| 74 | await on_tool_start(call) |
| 75 | except Exception as e: |
| 76 | logger.warning( |
| 77 | f"on_tool_start callback failed for {call.tool}: {e}" |
| 78 | ) |
| 79 | |
| 80 | # Execute the tool |
| 81 | tool_result = await manager.execute_tool( |
| 82 | call.tool, |
| 83 | call.arguments, |
| 84 | namespace=call.namespace if call.namespace != "default" else None, |
| 85 | timeout=effective_timeout, |
| 86 | ) |
| 87 | |
| 88 | end_time = datetime.now(UTC) |
| 89 |