diff --git a/backend/internal/handler/openai_gateway_handler.go b/backend/internal/handler/openai_gateway_handler.go index b787d0f976..ea60b26692 100644 --- a/backend/internal/handler/openai_gateway_handler.go +++ b/backend/internal/handler/openai_gateway_handler.go @@ -2696,7 +2696,11 @@ func (h *OpenAIGatewayHandler) ensureForwardErrorResponse(c *gin.Context, stream imageKeepalivePaddingOnly = adjustedSize < 0 imageKeepaliveResponseWritten = adjustedSize >= 0 } - if service.IsResponseCommitted(c) || (!compactKeepaliveCommitted && imageKeepaliveResponseWritten) { + compactKeepaliveHasMeaningfulOutput := compactKeepaliveCommitted && service.OpenAICompactKeepaliveAdjustedWrittenSize(c) > 0 + // Compact keepalive may have committed 200 headers without writing a + // semantic SSE event. In that case the Responses stream still needs its + // protocol-correct terminal response.failed event. + if (service.IsResponseCommitted(c) && (!compactKeepaliveCommitted || compactKeepaliveHasMeaningfulOutput)) || (!compactKeepaliveCommitted && imageKeepaliveResponseWritten) { return false } if c.Writer.Written() && !imageKeepalivePaddingOnly { diff --git a/backend/internal/handler/openai_gateway_handler_test.go b/backend/internal/handler/openai_gateway_handler_test.go index 543cda1e55..82e530cbe4 100644 --- a/backend/internal/handler/openai_gateway_handler_test.go +++ b/backend/internal/handler/openai_gateway_handler_test.go @@ -334,6 +334,26 @@ func TestOpenAIEnsureForwardErrorResponse_AfterDeltaAppendsSingleValidResponseFa require.Equal(t, 1, errorEvents) } +func TestOpenAIEnsureForwardErrorResponse_CompactKeepaliveOnlyWritesResponseFailed(t *testing.T) { + gin.SetMode(gin.TestMode) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, EndpointResponses, nil) + service.MarkOpenAICompactClientStream(c) + + stop := service.StartOpenAICompactSSEKeepalive(c, 5*time.Millisecond) + defer stop() + before := service.OpenAICompactKeepaliveAdjustedWrittenSize(c) + require.Eventually(t, c.Writer.Written, time.Second, time.Millisecond) + require.Equal(t, before, service.OpenAICompactKeepaliveAdjustedWrittenSize(c)) + + h := &OpenAIGatewayHandler{} + require.True(t, h.ensureForwardErrorResponse(c, false)) + require.Equal(t, http.StatusOK, w.Code) + require.Contains(t, w.Body.String(), "event: response.failed\n") + require.NotContains(t, w.Body.String(), "event: error\n") +} + func TestOpenAIEnsureForwardErrorResponse_ImageJSONKeepaliveWritesSingleJSONFallback(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder()