From 98b5f5a5c3db4171a1f4deaa0c5d7ab0eec4ed12 Mon Sep 17 00:00:00 2001 From: chiyi Date: Wed, 8 Jul 2026 13:13:25 +0800 Subject: [PATCH] =?UTF-8?q?chatgot=E6=9B=B4=E6=96=B0=E6=8D=A2=E5=8F=B7?= =?UTF-8?q?=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/provider/chatgpt/client.go | 3 ++- backend/internal/service/v1.go | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/internal/provider/chatgpt/client.go b/backend/internal/provider/chatgpt/client.go index 1af0bf6..51458bd 100644 --- a/backend/internal/provider/chatgpt/client.go +++ b/backend/internal/provider/chatgpt/client.go @@ -898,7 +898,8 @@ func (c *Client) startImageGeneration(ctx context.Context, session tlsclient.Htt // image generation task actually started" signal, so when it is absent (and // nothing was streamed inline) treat the attempt as a transient upstream // failure. That is retryable: a fresh submission reliably engages the pipeline, - // so the pool's same-account retry recovers instead of failing the request. + // so the pool retries the same account a few times and then fails over to + // another account (换号重试) instead of failing the request. if !asyncStarted && len(fileIDs) == 0 && len(sedimentIDs) == 0 { return "", nil, nil, fmt.Errorf("%w: image generation did not start (no async marker)", ErrTemporaryUpstream) } diff --git a/backend/internal/service/v1.go b/backend/internal/service/v1.go index 50a5523..1394890 100644 --- a/backend/internal/service/v1.go +++ b/backend/internal/service/v1.go @@ -2318,8 +2318,11 @@ func (s *V1Service) generateChatGPTImage(ctx context.Context, eventID string, mo return nil, err } - // Round-robin order; same-account retry on transient errors, fail over to the - // next account on auth/quota (see runPoolWithFailover). + // Round-robin order; on a transient upstream error (e.g. "image generation + // did not start (no async marker)") retry the same account a few times then + // FAIL OVER to the next account (tempFailover=true, capped at + // maxTempDeadAccounts) — never mark the account dead. Auth/quota fail over + // immediately (see runPoolWithFailover). return s.runPoolWithFailover(ctx, eventID, "chatgpt", active, "image", func(token model.TokenAccount) ([]byte, error) { data, _, genErr := s.chatgpt.GenerateImage(ctx, token.Value, in.Prompt, modelItem.ID, aspectRatio, resolution, refs) if genErr == nil { @@ -2330,7 +2333,7 @@ func (s *V1Service) generateChatGPTImage(ctx context.Context, eventID string, mo return data, genErr }, func(e error) (bool, bool, bool, bool) { return errors.Is(e, chatgpt.ErrAuth), errors.Is(e, chatgpt.ErrQuotaExhausted), errors.Is(e, chatgpt.ErrTemporaryUpstream), false - }, nil, false) // chatgpt token IS the credential — no cookie to refresh + }, nil, true) // chatgpt token IS the credential — no cookie to refresh; switch accounts on transient errors } // leonardoResetAfter returns when a Leonardo account's daily free tokens renew.