newestToolGroup returns the assistant that issued the newest tool result together with every tool result answering it, chronologically: the minimal well-formed unit that honours "always keep the newest" when the newest history message is a tool result. nil when the newest message isn't an identifiab
(history []Message)
| 240 | // history message is a tool result. nil when the newest message isn't an |
| 241 | // identifiable tool result (the budget walk already keeps non-tool newests) or |
| 242 | // no owning assistant exists (an unpairable tool can't be kept anyway). |
| 243 | func newestToolGroup(history []Message) []Message { |
| 244 | if len(history) == 0 { |
| 245 | return nil |
| 246 | } |
| 247 | last := history[len(history)-1] |
| 248 | if last.Role != RoleTool || last.ToolCallID == "" { |
| 249 | return nil |
| 250 | } |
| 251 | owner := -1 |
| 252 | search: |
| 253 | for i := len(history) - 2; i >= 0; i-- { |
| 254 | if history[i].Role != RoleAssistant { |
| 255 | continue |
| 256 | } |
| 257 | for _, tc := range history[i].ToolCalls { |
| 258 | if tc.ID == last.ToolCallID { |
| 259 | owner = i |
| 260 | break search |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | if owner < 0 { |
| 265 | return nil |
| 266 | } |
| 267 | // Parallel tool calls put [assistant(c1,c2), tool(c1), tool(c2)] at the tail, |
| 268 | // so collect every tool result whose id the owning assistant issued, not |
| 269 | // just the immediately-preceding one. |
| 270 | ids := map[string]bool{} |
| 271 | for _, tc := range history[owner].ToolCalls { |
| 272 | if tc.ID != "" { |
| 273 | ids[tc.ID] = true |
| 274 | } |
| 275 | } |
| 276 | group := []Message{history[owner]} |
| 277 | for i := owner + 1; i < len(history); i++ { |
| 278 | if history[i].Role == RoleTool && ids[history[i].ToolCallID] { |
| 279 | group = append(group, history[i]) |
| 280 | } |
| 281 | } |
| 282 | return group |
| 283 | } |
| 284 | |
| 285 | // newestToolIndex returns the index of the newest tool-result message in |