Handle execution of tool calls returned by the LLM. Delegates parallel execution to ToolManager.stream_execute_tools(), handling only CLI-specific concerns: UI, conversation history, confirmation. Uses ToolProcessorContext protocol for type-safe context access.
| 53 | |
| 54 | |
| 55 | class ToolProcessor: |
| 56 | """ |
| 57 | Handle execution of tool calls returned by the LLM. |
| 58 | |
| 59 | Delegates parallel execution to ToolManager.stream_execute_tools(), |
| 60 | handling only CLI-specific concerns: UI, conversation history, confirmation. |
| 61 | |
| 62 | Uses ToolProcessorContext protocol for type-safe context access. |
| 63 | """ |
| 64 | |
| 65 | def __init__( |
| 66 | self, |
| 67 | context: ToolProcessorContext, |
| 68 | ui_manager: UIManagerProtocol, |
| 69 | *, |
| 70 | max_concurrency: int = 4, |
| 71 | ) -> None: |
| 72 | self.context = context |
| 73 | self.ui_manager = ui_manager |
| 74 | self.max_concurrency = max_concurrency |
| 75 | |
| 76 | # Tool manager for execution - access via protocol attribute |
| 77 | self.tool_manager: ToolManager | None = context.tool_manager |
| 78 | |
| 79 | # Track transport failures for recovery detection |
| 80 | self._transport_failures = 0 |
| 81 | self._consecutive_transport_failures = 0 |
| 82 | |
| 83 | # Track state for callbacks |
| 84 | self._call_metadata: dict[str, ToolCallMetadata] = {} |
| 85 | self._cancelled = False |
| 86 | |
| 87 | # Track which tool_call_ids have received results (for orphan detection) |
| 88 | self._result_ids_added: set[str] = set() |
| 89 | |
| 90 | # Track page_fault calls within a conversation to prevent re-fault loops |
| 91 | self._faulted_page_ids: set[str] = set() |
| 92 | |
| 93 | # Give the context a back-pointer for Ctrl-C cancellation |
| 94 | # Note: This is the one place we set an attribute on context |
| 95 | context.tool_processor = self |
| 96 | |
| 97 | async def process_tool_calls( |
| 98 | self, |
| 99 | tool_calls: list[Any], |
| 100 | name_mapping: dict[str, str] | None = None, |
| 101 | reasoning_content: str | None = None, |
| 102 | ) -> None: |
| 103 | """ |
| 104 | Execute tool_calls in parallel using ToolManager.stream_execute_tools(). |
| 105 | |
| 106 | Args: |
| 107 | tool_calls: List of tool call objects from the LLM |
| 108 | name_mapping: Mapping from LLM tool names to actual tool names |
| 109 | reasoning_content: Optional reasoning content from the LLM |
| 110 | """ |
| 111 | if not tool_calls: |
| 112 | logger.warning("Empty tool_calls list received.") |
no outgoing calls