From d78e366db552a6dd361701981af2dc3065d2734a Mon Sep 17 00:00:00 2001 From: IanShaw <131567472+IanShaw027@users.noreply.github.com> Date: Thu, 20 Aug 2026 07:31:19 -0700 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=BD=90=20Grok=20Realtime=20?= =?UTF-8?q?=E6=8F=A1=E6=89=8B=E5=A4=B1=E8=B4=A5=E8=B4=A6=E5=8F=B7=E5=86=B7?= =?UTF-8?q?=E5=8D=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/handler/grok_audio.go | 6 ++++++ backend/internal/service/grok_audio.go | 23 +++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/backend/internal/handler/grok_audio.go b/backend/internal/handler/grok_audio.go index 08a8ff3c6..c95b5ce55 100644 --- a/backend/internal/handler/grok_audio.go +++ b/backend/internal/handler/grok_audio.go @@ -98,6 +98,12 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) { cancelProbe() if openErr != nil { reqLog.Warn("grok_realtime.pre_accept_failed", zap.Int64("account_id", account.ID), zap.Error(openErr)) + statusCode := http.StatusBadGateway + var dialErr *service.GrokRealtimeDialError + if errors.As(openErr, &dialErr) && dialErr.StatusCode > 0 { + statusCode = dialErr.StatusCode + } + h.gatewayService.HandleGrokRealtimeUpstreamError(c.Request.Context(), account, statusCode, []byte(openErr.Error())) release() release = nil failed[account.ID] = struct{}{} diff --git a/backend/internal/service/grok_audio.go b/backend/internal/service/grok_audio.go index a2fb33f12..771cae09f 100644 --- a/backend/internal/service/grok_audio.go +++ b/backend/internal/service/grok_audio.go @@ -139,6 +139,16 @@ func (s *OpenAIGatewayService) ProxyGrokRealtime(ctx context.Context, c *gin.Con type GrokRealtimeUpstream struct{ conn openAIWSClientConn } +// GrokRealtimeDialError preserves an HTTP status returned before WebSocket +// upgrade so handlers can apply the normal Grok account policy. +type GrokRealtimeDialError struct { + StatusCode int + Err error +} + +func (e *GrokRealtimeDialError) Error() string { return e.Err.Error() } +func (e *GrokRealtimeDialError) Unwrap() error { return e.Err } + func (u *GrokRealtimeUpstream) Close() error { if u == nil || u.conn == nil { return nil @@ -171,13 +181,22 @@ func (s *OpenAIGatewayService) OpenGrokRealtime(ctx context.Context, account *Ac if account.ProxyID != nil && account.Proxy != nil { proxyURL = account.Proxy.URL() } - conn, _, _, err := s.getOpenAIWSPassthroughDialer().Dial(ctx, u.String(), headers, proxyURL) + conn, status, _, err := s.getOpenAIWSPassthroughDialer().Dial(ctx, u.String(), headers, proxyURL) if err != nil { - return nil, err + return nil, &GrokRealtimeDialError{StatusCode: status, Err: err} } return &GrokRealtimeUpstream{conn: conn}, nil } +// HandleGrokRealtimeUpstreamError applies the shared Grok account policy to a +// failed pre-accept WebSocket handshake. +func (s *OpenAIGatewayService) HandleGrokRealtimeUpstreamError(ctx context.Context, account *Account, statusCode int, body []byte) { + if statusCode <= 0 { + statusCode = http.StatusBadGateway + } + s.handleGrokAccountUpstreamError(ctx, account, statusCode, nil, body) +} + func (s *OpenAIGatewayService) ProxyGrokRealtimeConn(ctx context.Context, c *gin.Context, client *coderws.Conn, upstream *GrokRealtimeUpstream) (bool, error) { if s == nil || client == nil || upstream == nil || upstream.conn == nil { return false, fmt.Errorf("realtime connection is required")