diff --git a/backend/internal/service/gateway_forward_as_chat_completions.go b/backend/internal/service/gateway_forward_as_chat_completions.go index 1df450d6b4..16c10a3298 100644 --- a/backend/internal/service/gateway_forward_as_chat_completions.go +++ b/backend/internal/service/gateway_forward_as_chat_completions.go @@ -318,6 +318,12 @@ func (s *GatewayService) handleCCBufferedFromAnthropic( if s.responseHeaderFilter != nil { responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter) } + // 非流式响应必须是 application/json。上游被强制流式后会返回 + // Content-Type: text/event-stream,经 WriteFilteredHeaders 透传后会污染 + // 响应头;而 c.Data/c.JSON 走 Gin 的 writeContentType(仅当头不存在时才设置), + // 无法覆盖已存在的 SSE 头。这里显式 Set 强制改回 JSON,避免下游中间层 + // (如 new-api)按 Content-Type 误判为流式。 + c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8") // Marshal then bytes-replace so tool name mapping is reversed at byte level // (parity with Parrot non-stream flow that marshals → restore → emit). if respBytes, err := json.Marshal(ccResp); err == nil { diff --git a/backend/internal/service/gateway_forward_as_responses.go b/backend/internal/service/gateway_forward_as_responses.go index 22951b88b1..f883ae870f 100644 --- a/backend/internal/service/gateway_forward_as_responses.go +++ b/backend/internal/service/gateway_forward_as_responses.go @@ -337,6 +337,12 @@ func (s *GatewayService) handleResponsesBufferedStreamingResponse( if s.responseHeaderFilter != nil { responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter) } + // 非流式响应必须是 application/json。上游被强制流式后会返回 + // Content-Type: text/event-stream,经 WriteFilteredHeaders 透传后会污染 + // 响应头;而 c.Data/c.JSON 走 Gin 的 writeContentType(仅当头不存在时才设置), + // 无法覆盖已存在的 SSE 头。这里显式 Set 强制改回 JSON,避免下游中间层 + // (如 new-api)按 Content-Type 误判为流式。 + c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8") if respBytes, err := json.Marshal(responsesResp); err == nil { respBytes = reverseToolNamesIfPresent(c, respBytes) c.Data(http.StatusOK, "application/json; charset=utf-8", respBytes) diff --git a/backend/internal/service/openai_gateway_chat_completions.go b/backend/internal/service/openai_gateway_chat_completions.go index 9a534c1bb3..ddc717f2f7 100644 --- a/backend/internal/service/openai_gateway_chat_completions.go +++ b/backend/internal/service/openai_gateway_chat_completions.go @@ -407,6 +407,11 @@ func (s *OpenAIGatewayService) handleChatBufferedStreamingResponse( if s.responseHeaderFilter != nil { responseheaders.WriteFilteredHeaders(c.Writer.Header(), resp.Header, s.responseHeaderFilter) } + // 非流式响应必须为标准 JSON。上游被强制流式,其响应头 Content-Type 为 + // text/event-stream,会经 WriteFilteredHeaders 透传进来;而 c.JSON 走 Gin 的 + // writeContentType 仅在头不存在时才设置,无法覆盖。这里显式 Set 强制改回 JSON, + // 否则下游"看头判流式"的中间层(如 new-api)会把本应聚合的 JSON 当成 SSE 处理。 + c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8") c.JSON(http.StatusOK, chatResp) return &OpenAIForwardResult{