Manages accumulation of tool calls across streaming chunks. Uses Pydantic models for type-safe state management. Handles fragmented JSON and validates tool call structure.
| 84 | |
| 85 | |
| 86 | class ToolCallAccumulator: |
| 87 | """Manages accumulation of tool calls across streaming chunks. |
| 88 | |
| 89 | Uses Pydantic models for type-safe state management. |
| 90 | Handles fragmented JSON and validates tool call structure. |
| 91 | """ |
| 92 | |
| 93 | def __init__(self) -> None: |
| 94 | self._accumulated: list[ToolCallData] = [] |
| 95 | |
| 96 | def process_chunk_tool_calls(self, chunk_tool_calls: list[dict[str, Any]]) -> None: |
| 97 | """Process tool calls from a chunk and accumulate them. |
| 98 | |
| 99 | Args: |
| 100 | chunk_tool_calls: Tool calls extracted from current chunk |
| 101 | """ |
| 102 | for tc_dict in chunk_tool_calls: |
| 103 | # Convert dict to Pydantic model |
| 104 | tc = ToolCallData.from_dict(tc_dict) |
| 105 | |
| 106 | # Find or create accumulator for this tool call |
| 107 | existing = self._find_accumulated_call(tc.id, tc.index) |
| 108 | |
| 109 | if existing: |
| 110 | # Merge chunk data using intelligent JSON merging |
| 111 | self._merge_tool_call(existing, tc) |
| 112 | else: |
| 113 | # New tool call - add to accumulated list |
| 114 | self._accumulated.append(tc) |
| 115 | |
| 116 | def _find_accumulated_call(self, tc_id: str, tc_index: int) -> ToolCallData | None: |
| 117 | """Find existing accumulated tool call by ID or index.""" |
| 118 | for tc in self._accumulated: |
| 119 | if tc.id == tc_id or tc.index == tc_index: |
| 120 | return tc |
| 121 | return None |
| 122 | |
| 123 | def _merge_tool_call(self, existing: ToolCallData, new: ToolCallData) -> None: |
| 124 | """Merge new tool call data into existing accumulator.""" |
| 125 | # Update function name if provided |
| 126 | if new.function.name: |
| 127 | existing.function.name = new.function.name |
| 128 | |
| 129 | # Accumulate arguments using intelligent JSON merging |
| 130 | if new.function.arguments: |
| 131 | merged_args = self._merge_json_strings( |
| 132 | existing.function.arguments, new.function.arguments |
| 133 | ) |
| 134 | existing.function.arguments = merged_args |
| 135 | |
| 136 | def _merge_json_strings(self, current: str, new: str) -> str: |
| 137 | """Merge two JSON strings intelligently. |
| 138 | |
| 139 | Tries multiple strategies: |
| 140 | 1. Parse both and merge dicts |
| 141 | 2. Concatenate and validate |
| 142 | 3. Fix common issues |
| 143 | """ |
no outgoing calls