Repository: pydantic/pydantic-ai
File: pydantic_ai_slim/pydantic_ai/_agent_graph.py (Python)

Code before:

        async def handle_call_or_result(
            coro_or_task: Coroutine[
                Any, Any, tuple[_messages.ToolReturnPart | _messages.RetryPromptPart, _messages.UserPromptPart | None]
            ]
            | Task[tuple[_messages.ToolReturnPart | _messages.RetryPromptPart, _messages.UserPromptPart | None]],
            index: int,
        ) -> _messages.HandleResponseEvent | None:
            try:
                tool_part, tool_user_part = (
                    (await coro_or_task) if isinstance(coro_or_task, Coroutine) else coro_or_task.result()
                )
            except exceptions.CallDeferred:
                deferred_calls_by_index[index] = 'external'
            except exceptions.ApprovalRequired:
                deferred_calls_by_index[index] = 'unapproved'
            else:
                tool_parts_by_index[index] = tool_part
                if tool_user_part:
                    user_parts_by_index[index] = tool_user_part

                return _messages.FunctionToolResultEvent(tool_part)

        if tool_manager.should_sequential_tool_call(tool_calls):
            for call in tool_calls:
                index = tool_calls.index(call)
                if event := await handle_call_or_result(
                    _call_tool(tool_manager, call, deferred_tool_results.get(call.tool_call_id), usage_limits),
                    index,
                ):
                    yield event

        else:
            tasks = [
                asyncio.create_task(
                    _call_tool(tool_manager, call, deferred_tool_results.get(call.tool_call_id), usage_limits),
                    name=call.tool_name,
                )
                for call in tool_calls
            ]

            pending = tasks
            while pending:
                done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
                for task in done:
                    index = tasks.index(task)
                    if event := await handle_call_or_result(coro_or_task=task, index=index):
                        yield event

    # We append the results at the end, rather than as they are received, to retain a consistent ordering
    # This is mostly just to simplify testing

Review comment (suggestion):
```suggestion
            for index, call in enumerate(tool_calls):
```

Code after:
    ):

        async def handle_call_or_result(
            coro_or_task: Awaitable[
                tuple[_messages.ToolReturnPart | _messages.RetryPromptPart, _messages.UserPromptPart | None]
            ]
            | Task[tuple[_messages.ToolReturnPart | _messages.RetryPromptPart, _messages.UserPromptPart | None]],
            index: int,
        ) -> _messages.HandleResponseEvent | None:
            try:
                tool_part, tool_user_part = (
                    (await coro_or_task) if inspect.isawaitable(coro_or_task) else coro_or_task.result()
                )
            except exceptions.CallDeferred:
                deferred_calls_by_index[index] = 'external'
            except exceptions.ApprovalRequired:
                deferred_calls_by_index[index] = 'unapproved'
            else:
                tool_parts_by_index[index] = tool_part
                if tool_user_part:
                    user_parts_by_index[index] = tool_user_part

                return _messages.FunctionToolResultEvent(tool_part)

        if tool_manager.should_call_sequentially(tool_calls):
            for index, call in enumerate(tool_calls):
                if event := await handle_call_or_result(
                    _call_tool(tool_manager, call, deferred_tool_results.get(call.tool_call_id), usage_limits),
                    index,
                ):
                    yield event

        else:
            tasks = [
                asyncio.create_task(
                    _call_tool(tool_manager, call, deferred_tool_results.get(call.tool_call_id), usage_limits),
                    name=call.tool_name,
                )
                for call in tool_calls
            ]

            pending = tasks
            while pending:
                done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
                for task in done:
                    index = tasks.index(task)
                    if event := await handle_call_or_result(coro_or_task=task, index=index):
                        yield event

    # We append the results at the end, rather than as they are received, to retain a consistent ordering
    # This is mostly just to simplify testing