diff --git a/internal/openai/responses_compat.go b/internal/openai/responses_compat.go index cd65fec..8037be7 100644 --- a/internal/openai/responses_compat.go +++ b/internal/openai/responses_compat.go @@ -202,6 +202,30 @@ func sanitizeInputItem(item map[string]any) map[string]any { out := clone(item) delete(out, "internal_chat_message_metadata_passthrough") delete(out, "phase") + if content, exists := out["content"]; exists && String(out, "type", "") == "message" { + out["content"] = sanitizeMessageContent(content) + } + return out +} + +func sanitizeMessageContent(content any) any { + parts, ok := content.([]any) + if !ok { + return content + } + out := make([]any, 0, len(parts)) + for _, raw := range parts { + part, ok := raw.(map[string]any) + if !ok { + out = append(out, raw) + continue + } + clean := clone(part) + if String(clean, "type", "") == "output_text" { + clean["type"] = "input_text" + } + out = append(out, clean) + } return out } diff --git a/internal/openai/responses_compat_test.go b/internal/openai/responses_compat_test.go index 87c4003..14f8b3c 100644 --- a/internal/openai/responses_compat_test.go +++ b/internal/openai/responses_compat_test.go @@ -126,6 +126,46 @@ func TestPrepareCompatibleResponsesDropsUnknownInput(t *testing.T) { } } +func TestPrepareCompatibleResponsesRewritesAssistantOutputTextHistory(t *testing.T) { + wire, _, err := PrepareCompatibleResponses(map[string]any{ + "model": "grok-4.5", + "input": []any{ + map[string]any{ + "type": "message", "role": "assistant", "id": "msg_1", "status": "completed", + "content": []any{map[string]any{"type": "output_text", "text": "first response"}}, + }, + map[string]any{"type": "message", "role": "user", "content": []any{map[string]any{"type": "input_text", "text": "continue"}}}, + }, + }) + if err != nil { + t.Fatal(err) + } + input := wire["input"].([]any) + message := input[0].(map[string]any) + content := message["content"].([]any) + part := content[0].(map[string]any) + if part["type"] != "input_text" || part["text"] != "first response" { + t.Fatalf("assistant history content = %#v", content) + } +} + +func TestPrepareCompatibleResponsesDoesNotAddMissingMessageContent(t *testing.T) { + wire, _, err := PrepareCompatibleResponses(map[string]any{ + "model": "grok-4.5", + "input": []any{ + map[string]any{"type": "message", "role": "assistant", "tool_calls": []any{map[string]any{"id": "call_1", "type": "function"}}}, + }, + }) + if err != nil { + t.Fatal(err) + } + input := wire["input"].([]any) + message := input[0].(map[string]any) + if _, exists := message["content"]; exists { + t.Fatalf("missing content was added: %#v", message) + } +} + func TestPrepareCompatibleResponsesNormalizesCodexHostedTools(t *testing.T) { wire, _, err := PrepareCompatibleResponses(map[string]any{ "model": "grok-4.5", "input": "hello",