fix(grok): fail over silent-refusal empty response.completed on Grok Responses streams

Issue #5009 added a failover guard for OpenAI Responses streams that
terminate with an empty response.completed (no output, no usage, no
error), but the guard is gated on PlatformOpenAI. Grok platform
accounts reach the same handler (forwardGrokResponses ->
handleStreamingResponseWithReasoning) and exhibit the same upstream
silent-refusal shape, so their empty completions are relayed to the
client as successful empty turns: Responses SDK clients map the empty
completed to finishReason stop and accept the turn with no retry.

Extend the guard to PlatformGrok, matching the unconditional guard on
the passthrough path (openai_gateway_passthrough.go).

Fixes #7774
This commit is contained in:
YahooYuan666
2026-10-01 02:16:22 +08:00
parent 42bc7f6cff
commit 3613174cf6
@@ -687,8 +687,12 @@ func (s *OpenAIGatewayService) handleStreamingResponseWithReasoning(ctx context.
// OpenAI Responses streams that terminate with an empty
// response.completed (no output, no usage, no error, nothing sent
// to the client) are silent upstream refusals: fail over instead of
// recording a successful 0/0 usage turn (issue #5009).
if account != nil && account.Platform == PlatformOpenAI &&
// recording a successful 0/0 usage turn (issue #5009). Grok
// subscriptions behind vendor gateways show the same silent-refusal
// shape on /v1/responses (forwardGrokResponses reaches this same
// handler), so cover PlatformGrok too — the passthrough-path guard
// is already unconditional.
if account != nil && (account.Platform == PlatformOpenAI || account.Platform == PlatformGrok) &&
(eventType == "response.completed" || eventType == "response.done") &&
!sawFailedEvent && !responsesSemanticOutputSeen && !clientOutputStarted &&
openAIResponsesCompletedEventIsEmpty(dataBytes, usage) {