-
Notifications
You must be signed in to change notification settings - Fork 0
Support parallel tool execution in ToolInvoker.invoke_all #112
Copy link
Copy link
Closed
Labels
prio:highImportant, should be prioritizedImportant, should be prioritizedscope:smallLess than 1 day of workLess than 1 day of workspec:toolsDESIGN_SPEC Section 11 - Tool & Capability SystemDESIGN_SPEC Section 11 - Tool & Capability Systemtype:featureNew feature implementationNew feature implementation
Milestone
Description
Problem
ToolInvoker.invoke_all currently executes tool calls sequentially:
async def invoke_all(self, tool_calls):
return tuple([await self.invoke(call) for call in tool_calls])When the LLM returns multiple tool calls (e.g. search + read_file), they wait for each other unnecessarily. This becomes a real bottleneck with I/O-bound tools (HTTP requests, file reads, MCP calls).
Proposed Solution
Use asyncio.TaskGroup (Python 3.11+) for structured concurrency:
async def invoke_all(self, tool_calls, *, max_concurrency: int | None = None):
calls = list(tool_calls)
results: list[ToolResult] = [None] * len(calls) # type: ignore[list-item]
sem = asyncio.Semaphore(max_concurrency) if max_concurrency else None
async def _run(idx: int, call: ToolCall) -> None:
if sem:
async with sem:
results[idx] = await self.invoke(call)
else:
results[idx] = await self.invoke(call)
async with asyncio.TaskGroup() as tg:
for i, call in enumerate(calls):
tg.create_task(_run(i, call))
return tuple(results)Key design points:
- Results returned in input order regardless of completion order
max_concurrencyparameter for bounded parallelismmax_concurrency=1preserves current sequential behavior- Non-recoverable errors (
MemoryError,RecursionError) propagate viaExceptionGroup - Recoverable errors captured as
ToolResult(is_error=True)without cancelling siblings
Acceptance Criteria
-
invoke_allexecutes tools concurrently by default -
max_concurrencyparameter for bounded parallelism -
max_concurrency=1preserves current sequential behavior - Results are returned in input order regardless of completion order
- Non-recoverable errors propagate correctly
- Recoverable errors don't cancel other in-flight tool calls
- Tests for concurrent, bounded, and sequential modes
- Verify DESIGN_SPEC.md §11.1.1 and CLAUDE.md async concurrency convention remain consistent after implementation
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
prio:highImportant, should be prioritizedImportant, should be prioritizedscope:smallLess than 1 day of workLess than 1 day of workspec:toolsDESIGN_SPEC Section 11 - Tool & Capability SystemDESIGN_SPEC Section 11 - Tool & Capability Systemtype:featureNew feature implementationNew feature implementation