package ai import ( "encoding/json" "strings" "testing" ) func TestRepairChatCompletionBody_preservesToolCalls(t *testing.T) { body := []byte(`{ "model":"gpt-4o", "messages":[ {"role":"user","content":"list mail"}, {"role":"assistant","content":"","tool_calls":[{"id":"call_1","type":"function","function":{"name":"mail_list","arguments":"{\"limit\":3}"}}]}, {"role":"tool","tool_call_id":"call_1","content":"{\"items\":[]}"} ] }`) repaired, err := repairChatCompletionBody(body) if err != nil { t.Fatalf("repairChatCompletionBody() error = %v", err) } var parsed map[string]any if err := json.Unmarshal(repaired, &parsed); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } messages := parsed["messages"].([]any) assistant := messages[1].(map[string]any) toolCalls, ok := assistant["tool_calls"].([]any) if !ok || len(toolCalls) != 1 { t.Fatalf("expected assistant tool_calls preserved, got %#v", assistant) } } func TestRepairChatCompletionBody_insertsMissingAssistantToolCalls(t *testing.T) { body := []byte(`{ "model":"gpt-4o", "messages":[ {"role":"system","content":"prompt"}, {"role":"user","content":"list mail"}, {"role":"tool","tool_call_id":"call_abc","content":"{\"items\":[]}"} ] }`) repaired, err := repairChatCompletionBody(body) if err != nil { t.Fatalf("repairChatCompletionBody() error = %v", err) } var parsed map[string]any if err := json.Unmarshal(repaired, &parsed); err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } messages := parsed["messages"].([]any) if len(messages) != 4 { t.Fatalf("messages len = %d, want 4", len(messages)) } assistant := messages[2].(map[string]any) if messageRole(assistant) != "assistant" { t.Fatalf("messages[2].role = %q, want assistant", messageRole(assistant)) } toolCalls, ok := assistant["tool_calls"].([]any) if !ok || len(toolCalls) != 1 { t.Fatalf("expected inserted tool_calls, got %#v", assistant) } call := toolCalls[0].(map[string]any) if call["id"] != "call_abc" { t.Fatalf("tool_call id = %v, want call_abc", call["id"]) } } func TestRepairChatCompletionBody_stripsNothingWhenAlreadyValid(t *testing.T) { body := []byte(`{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}`) repaired, err := repairChatCompletionBody(body) if err != nil { t.Fatalf("repairChatCompletionBody() error = %v", err) } if string(repaired) != string(body) { t.Fatalf("expected unchanged body, got %s", repaired) } } func TestRepairToolMessages_doesNotDuplicateAssistant(t *testing.T) { messages := []any{ map[string]any{"role": "user", "content": "list"}, map[string]any{ "role": "assistant", "content": "", "tool_calls": []any{ map[string]any{"id": "call_1", "type": "function"}, }, }, map[string]any{"role": "tool", "tool_call_id": "call_1", "content": "{}"}, } repaired := repairToolMessages(messages) if len(repaired) != 3 { t.Fatalf("len = %d, want 3", len(repaired)) } if !strings.Contains(string(mustJSON(repaired)), "call_1") { t.Fatal("expected original tool_call_id preserved") } } func mustJSON(v any) []byte { b, err := json.Marshal(v) if err != nil { panic(err) } return b }