From e9a25e7b92774712e009c2c54751d436adfc8907 Mon Sep 17 00:00:00 2001 From: nanobabanan <1212@gmail.com> Date: Mon, 18 May 2026 15:10:35 +0800 Subject: [PATCH] fix(apicompat): preserve empty streaming thinking blocks Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- .../pkg/apicompat/anthropic_responses_test.go | 4 ++++ backend/internal/pkg/apicompat/types.go | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/backend/internal/pkg/apicompat/anthropic_responses_test.go b/backend/internal/pkg/apicompat/anthropic_responses_test.go index aa36ef0b..3a0a3bc6 100644 --- a/backend/internal/pkg/apicompat/anthropic_responses_test.go +++ b/backend/internal/pkg/apicompat/anthropic_responses_test.go @@ -744,6 +744,10 @@ func TestStreamingReasoning(t *testing.T) { assert.Equal(t, "content_block_start", events[0].Type) assert.Equal(t, "thinking", events[0].ContentBlock.Type) + sse, err := ResponsesAnthropicEventToSSE(events[0]) + require.NoError(t, err) + assert.Contains(t, sse, `"content_block":{"thinking":"","type":"thinking"}`) + // reasoning text delta events = ResponsesEventToAnthropicEvents(&ResponsesStreamEvent{ Type: "response.reasoning_summary_text.delta", diff --git a/backend/internal/pkg/apicompat/types.go b/backend/internal/pkg/apicompat/types.go index f9cd5a1c..06f0aec6 100644 --- a/backend/internal/pkg/apicompat/types.go +++ b/backend/internal/pkg/apicompat/types.go @@ -75,6 +75,28 @@ type AnthropicContentBlock struct { IsError bool `json:"is_error,omitempty"` } +func (b AnthropicContentBlock) MarshalJSON() ([]byte, error) { + type anthropicContentBlock AnthropicContentBlock + base := struct { + anthropicContentBlock + }{anthropicContentBlock: anthropicContentBlock(b)} + + switch b.Type { + case "text": + return json.Marshal(struct { + Text string `json:"text"` + anthropicContentBlock + }{Text: b.Text, anthropicContentBlock: anthropicContentBlock(b)}) + case "thinking": + return json.Marshal(struct { + Thinking string `json:"thinking"` + anthropicContentBlock + }{Thinking: b.Thinking, anthropicContentBlock: anthropicContentBlock(b)}) + default: + return json.Marshal(base) + } +} + // AnthropicImageSource describes the source data for an image content block. type AnthropicImageSource struct { Type string `json:"type"` // "base64"