TestPackDropsDanglingAssistantToolCalls: a turn cancelled mid-tool leaves an assistant.tool_calls in history with no answering tool message (the TUI appended it on round close, then endTurn dropped the pending call on Ctrl+C). On the next request Pack must strip that dangling assistant, otherwise th
(t *testing.T)
| 283 | // On the next request Pack must strip that dangling assistant, otherwise the |
| 284 | // wire carries an unanswered tool_calls and every OpenAI-compat backend 400s. |
| 285 | func TestPackDropsDanglingAssistantToolCalls(t *testing.T) { |
| 286 | history := []Message{ |
| 287 | {Role: RoleUser, Content: "run a long thing"}, |
| 288 | {Role: RoleAssistant, ToolCalls: []ToolCall{{ID: "c1", Name: "bash"}}}, |
| 289 | // no tool(c1): the user Ctrl+C'd before it finished |
| 290 | {Role: RoleUser, Content: "actually, do this instead"}, |
| 291 | } |
| 292 | r := Pack(history, 100000) |
| 293 | for _, m := range r.Messages { |
| 294 | if m.Role == RoleAssistant && len(m.ToolCalls) > 0 { |
| 295 | t.Fatalf("dangling assistant.tool_calls survived pack: %+v", r.Messages) |
| 296 | } |
| 297 | } |
| 298 | // Both user messages must remain, only the unpaired assistant is dropped. |
| 299 | if len(r.Messages) != 2 { |
| 300 | t.Fatalf("want 2 user messages kept, got %d: %+v", len(r.Messages), r.Messages) |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | // TestPackDropsDanglingPartialParallelGroup: cancelling a parallel round after |
| 305 | // only some results return leaves assistant(c1,c2)+tool(c1). The assistant has |