From 03621f1ed9b5744b1a9eb45570948ded4a79d61b Mon Sep 17 00:00:00 2001 From: peakchao Date: Sat, 27 Jun 2026 18:59:02 +0800 Subject: [PATCH] fix(relay): remove extra blank line from Claude SSE stream - stop appending a trailing newline to Claude stream data frames before CustomEvent writes the SSE terminator - keep Claude native /v1/messages stream output at the standard single blank line between events - add a regression test for consecutive Claude SSE chunks Refs #5402 --- relay/helper/common.go | 2 +- relay/helper/common_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 relay/helper/common_test.go diff --git a/relay/helper/common.go b/relay/helper/common.go index 17ce79d2a10..ae17a4e205a 100644 --- a/relay/helper/common.go +++ b/relay/helper/common.go @@ -68,7 +68,7 @@ func ClaudeData(c *gin.Context, resp dto.ClaudeResponse) error { func ClaudeChunkData(c *gin.Context, resp dto.ClaudeResponse, data string) { c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("event: %s\n", resp.Type)}) - c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("data: %s\n", data)}) + c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("data: %s", data)}) _ = FlushWriter(c) } diff --git a/relay/helper/common_test.go b/relay/helper/common_test.go new file mode 100644 index 00000000000..7411265fbec --- /dev/null +++ b/relay/helper/common_test.go @@ -0,0 +1,36 @@ +package helper + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/QuantumNous/new-api/dto" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/require" +) + +func TestClaudeChunkDataDoesNotEmitExtraBlankLine(t *testing.T) { + oldMode := gin.Mode() + gin.SetMode(gin.TestMode) + t.Cleanup(func() { gin.SetMode(oldMode) }) + + recorder := httptest.NewRecorder() + c, _ := gin.CreateTestContext(recorder) + c.Request = httptest.NewRequest(http.MethodPost, "/v1/messages", nil) + + ClaudeChunkData(c, dto.ClaudeResponse{Type: "message_start"}, `{"type":"message_start"}`) + ClaudeChunkData(c, dto.ClaudeResponse{Type: "content_block_start"}, `{"type":"content_block_start"}`) + + require.Equal(t, strings.Join([]string{ + `event: message_start`, + `data: {"type":"message_start"}`, + ``, + `event: content_block_start`, + `data: {"type":"content_block_start"}`, + ``, + ``, + }, "\n"), recorder.Body.String()) + require.NotContains(t, recorder.Body.String(), "\n\n\n") +}