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 | // to a user message. buildMessages always prepends the embedded system prompt as |
| 241 | // wire element 0, so any system message Pack returns is a SECOND, non-leading |
| 242 | // system message, which strict OpenAI-compat backends reject outright ("System |
| 243 | // message must be at the beginning"; observed on strict backends like Ollama |
| 244 | // and llama.cpp), the same class of wire-shape 400 the dangling/orphan/userless passes |
| 245 | // guard against. The only system content reaching history is a soft-nudge note |
| 246 | // (the embedded prompt is never stored there), so demoting to user keeps that note, |
| 247 | // automated-check prefix and all, in front of the model while keeping the wire |
| 248 | // legal everywhere. Must run AFTER anchorUserMessage: a demoted nudge would |
| 249 | // otherwise masquerade as a surviving user message and suppress the original-task |
| 250 | // anchor. Mutates only the copied kept slice, never history. |
| 251 | func demoteSystemMessages(kept []Message) []Message { |
| 252 | for i := range kept { |
| 253 | if kept[i].Role == RoleSystem { |
| 254 | kept[i].Role = RoleUser |
| 255 | } |
| 256 | } |
| 257 | return kept |
| 258 | } |
| 259 | |
| 260 | // newestToolGroup returns the assistant that issued the newest tool result |
| 261 | // together with every tool result answering it, chronologically: the minimal |
| 262 | // well-formed unit that honours "always keep the newest" when the newest |
| 263 | // history message is a tool result. nil when the newest message isn't an |
| 264 | // identifiable tool result (the budget walk already keeps non-tool newests) or |
| 265 | // no owning assistant exists (an unpairable tool can't be kept anyway). |
| 266 | func newestToolGroup(history []Message) []Message { |
| 267 | if len(history) == 0 { |
| 268 | return nil |
| 269 | } |
| 270 | last := history[len(history)-1] |
| 271 | if last.Role != RoleTool || last.ToolCallID == "" { |
| 272 | return nil |
| 273 | } |
| 274 | owner := -1 |
| 275 | search: |
| 276 | for i := len(history) - 2; i >= 0; i-- { |
| 277 | if history[i].Role != RoleAssistant { |
| 278 | continue |
| 279 | } |
| 280 | for _, tc := range history[i].ToolCalls { |
| 281 | if tc.ID == last.ToolCallID { |
| 282 | owner = i |
| 283 | break search |
| 284 | } |
| 285 | } |