Find indices of tool_call/tool_result groups that must stay together. An assistant message may contain multiple tool_calls, each with a corresponding tool result message. All messages in such a group must be dropped or kept together. Returns a mapping where every index in a group m
(
messages: list[Message]
)
| 183 | |
| 184 | |
| 185 | def _find_tool_pair_indices( |
| 186 | messages: list[Message] |
| 187 | ) -> dict[int, frozenset[int]]: |
| 188 | """Find indices of tool_call/tool_result groups that must stay together. |
| 189 | |
| 190 | An assistant message may contain multiple tool_calls, each with a |
| 191 | corresponding tool result message. All messages in such a group |
| 192 | must be dropped or kept together. |
| 193 | |
| 194 | Returns a mapping where every index in a group maps to the full |
| 195 | set of indices in that group. |
| 196 | |
| 197 | Args: |
| 198 | messages: The message list. |
| 199 | |
| 200 | Returns: |
| 201 | Dict mapping index -> frozenset of all indices in the group. |
| 202 | """ |
| 203 | groups: dict[int, frozenset[int]] = {} |
| 204 | |
| 205 | for i, msg in enumerate(messages): |
| 206 | if msg.role == Role.ASSISTANT and msg.tool_calls: |
| 207 | tool_call_ids = {tc.id for tc in msg.tool_calls} |
| 208 | group_indices = {i} |
| 209 | for j in range(i + 1, len(messages)): |
| 210 | if messages[j].role == Role.TOOL: |
| 211 | for tr in messages[j].tool_results: |
| 212 | if tr.tool_call_id in tool_call_ids: |
| 213 | group_indices.add(j) |
| 214 | break |
| 215 | group = frozenset(group_indices) |
| 216 | for idx in group: |
| 217 | groups[idx] = group |
| 218 | |
| 219 | return groups |
| 220 | |
| 221 | |
| 222 | def compact_history( |