fix(antigravity): restore url fallback behavior
This commit is contained in:
@@ -129,31 +129,10 @@ urlFallbackLoop:
|
|||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
// "Resource has been exhausted" 是 URL 级别限流,切换 URL
|
// "Resource has been exhausted" 是 URL 级别限流,切换 URL
|
||||||
if isURLLevelRateLimit(respBody) {
|
if isURLLevelRateLimit(respBody) && urlIdx < len(availableURLs)-1 {
|
||||||
upstreamMsg := strings.TrimSpace(extractAntigravityErrorMessage(respBody))
|
antigravity.DefaultURLAvailability.MarkUnavailable(baseURL)
|
||||||
upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg)
|
log.Printf("%s URL fallback (429): %s -> %s", p.prefix, baseURL, availableURLs[urlIdx+1])
|
||||||
appendOpsUpstreamError(p.c, OpsUpstreamErrorEvent{
|
continue urlFallbackLoop
|
||||||
Platform: p.account.Platform,
|
|
||||||
AccountID: p.account.ID,
|
|
||||||
AccountName: p.account.Name,
|
|
||||||
UpstreamStatusCode: resp.StatusCode,
|
|
||||||
UpstreamRequestID: resp.Header.Get("x-request-id"),
|
|
||||||
Kind: "retry",
|
|
||||||
Message: upstreamMsg,
|
|
||||||
Detail: getUpstreamDetail(respBody),
|
|
||||||
})
|
|
||||||
if urlIdx < len(availableURLs)-1 {
|
|
||||||
log.Printf("%s URL fallback (HTTP 429): %s -> %s body=%s", p.prefix, baseURL, availableURLs[urlIdx+1], truncateForLog(respBody, 200))
|
|
||||||
continue urlFallbackLoop
|
|
||||||
}
|
|
||||||
log.Printf("%s status=429 url_rate_limited base_url=%s body=%s", p.prefix, baseURL, truncateForLog(respBody, 200))
|
|
||||||
resp = &http.Response{
|
|
||||||
StatusCode: resp.StatusCode,
|
|
||||||
Header: resp.Header.Clone(),
|
|
||||||
Body: io.NopCloser(bytes.NewReader(respBody)),
|
|
||||||
Request: resp.Request,
|
|
||||||
}
|
|
||||||
break urlFallbackLoop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 账户/模型配额限流,重试 3 次(指数退避)
|
// 账户/模型配额限流,重试 3 次(指数退避)
|
||||||
@@ -853,20 +832,11 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
|
|||||||
if txErr != nil {
|
if txErr != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
retryResult, retryErr := antigravityRetryLoop(antigravityRetryLoopParams{
|
retryReq, buildErr := antigravity.NewAPIRequest(ctx, action, accessToken, retryGeminiBody)
|
||||||
ctx: ctx,
|
if buildErr != nil {
|
||||||
prefix: prefix,
|
continue
|
||||||
account: account,
|
}
|
||||||
proxyURL: proxyURL,
|
retryResp, retryErr := s.httpUpstream.Do(retryReq, proxyURL, account.ID, account.Concurrency)
|
||||||
accessToken: accessToken,
|
|
||||||
action: action,
|
|
||||||
body: retryGeminiBody,
|
|
||||||
quotaScope: quotaScope,
|
|
||||||
c: c,
|
|
||||||
httpUpstream: s.httpUpstream,
|
|
||||||
settingService: s.settingService,
|
|
||||||
handleError: s.handleUpstreamError,
|
|
||||||
})
|
|
||||||
if retryErr != nil {
|
if retryErr != nil {
|
||||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||||
Platform: account.Platform,
|
Platform: account.Platform,
|
||||||
@@ -880,7 +850,6 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
retryResp := retryResult.resp
|
|
||||||
if retryResp.StatusCode < 400 {
|
if retryResp.StatusCode < 400 {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
resp = retryResp
|
resp = retryResp
|
||||||
@@ -890,13 +859,6 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
|
|||||||
|
|
||||||
retryBody, _ := io.ReadAll(io.LimitReader(retryResp.Body, 2<<20))
|
retryBody, _ := io.ReadAll(io.LimitReader(retryResp.Body, 2<<20))
|
||||||
_ = retryResp.Body.Close()
|
_ = retryResp.Body.Close()
|
||||||
if retryResp.StatusCode == http.StatusTooManyRequests {
|
|
||||||
retryBaseURL := ""
|
|
||||||
if retryResp.Request != nil && retryResp.Request.URL != nil {
|
|
||||||
retryBaseURL = retryResp.Request.URL.Scheme + "://" + retryResp.Request.URL.Host
|
|
||||||
}
|
|
||||||
log.Printf("%s status=429 rate_limited base_url=%s retry_stage=%s body=%s", prefix, retryBaseURL, stage.name, truncateForLog(retryBody, 200))
|
|
||||||
}
|
|
||||||
kind := "signature_retry"
|
kind := "signature_retry"
|
||||||
if strings.TrimSpace(stage.name) != "" {
|
if strings.TrimSpace(stage.name) != "" {
|
||||||
kind = "signature_retry_" + strings.ReplaceAll(stage.name, "+", "_")
|
kind = "signature_retry_" + strings.ReplaceAll(stage.name, "+", "_")
|
||||||
@@ -941,15 +903,9 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
|
|||||||
|
|
||||||
// 处理错误响应(重试后仍失败或不触发重试)
|
// 处理错误响应(重试后仍失败或不触发重试)
|
||||||
if resp.StatusCode >= 400 {
|
if resp.StatusCode >= 400 {
|
||||||
urlLevelRateLimit := resp.StatusCode == http.StatusTooManyRequests && isURLLevelRateLimit(respBody)
|
s.handleUpstreamError(ctx, prefix, account, resp.StatusCode, resp.Header, respBody, quotaScope)
|
||||||
if !urlLevelRateLimit {
|
|
||||||
s.handleUpstreamError(ctx, prefix, account, resp.StatusCode, resp.Header, respBody, quotaScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.shouldFailoverUpstreamError(resp.StatusCode) {
|
if s.shouldFailoverUpstreamError(resp.StatusCode) {
|
||||||
if urlLevelRateLimit {
|
|
||||||
return nil, s.writeMappedClaudeError(c, account, resp.StatusCode, resp.Header.Get("x-request-id"), respBody)
|
|
||||||
}
|
|
||||||
upstreamMsg := strings.TrimSpace(extractAntigravityErrorMessage(respBody))
|
upstreamMsg := strings.TrimSpace(extractAntigravityErrorMessage(respBody))
|
||||||
upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg)
|
upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg)
|
||||||
logBody := s.settingService != nil && s.settingService.cfg != nil && s.settingService.cfg.Gateway.LogUpstreamErrorBody
|
logBody := s.settingService != nil && s.settingService.cfg != nil && s.settingService.cfg.Gateway.LogUpstreamErrorBody
|
||||||
@@ -1559,10 +1515,7 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
|
|||||||
if unwrapErr != nil || len(unwrappedForOps) == 0 {
|
if unwrapErr != nil || len(unwrappedForOps) == 0 {
|
||||||
unwrappedForOps = respBody
|
unwrappedForOps = respBody
|
||||||
}
|
}
|
||||||
urlLevelRateLimit := resp.StatusCode == http.StatusTooManyRequests && isURLLevelRateLimit(unwrappedForOps)
|
s.handleUpstreamError(ctx, prefix, account, resp.StatusCode, resp.Header, respBody, quotaScope)
|
||||||
if !urlLevelRateLimit {
|
|
||||||
s.handleUpstreamError(ctx, prefix, account, resp.StatusCode, resp.Header, respBody, quotaScope)
|
|
||||||
}
|
|
||||||
upstreamMsg := strings.TrimSpace(extractAntigravityErrorMessage(unwrappedForOps))
|
upstreamMsg := strings.TrimSpace(extractAntigravityErrorMessage(unwrappedForOps))
|
||||||
upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg)
|
upstreamMsg = sanitizeUpstreamErrorMessage(upstreamMsg)
|
||||||
|
|
||||||
@@ -1580,9 +1533,6 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
|
|||||||
setOpsUpstreamError(c, resp.StatusCode, upstreamMsg, upstreamDetail)
|
setOpsUpstreamError(c, resp.StatusCode, upstreamMsg, upstreamDetail)
|
||||||
|
|
||||||
if s.shouldFailoverUpstreamError(resp.StatusCode) {
|
if s.shouldFailoverUpstreamError(resp.StatusCode) {
|
||||||
if urlLevelRateLimit {
|
|
||||||
return nil, s.writeGoogleError(c, resp.StatusCode, upstreamMsg)
|
|
||||||
}
|
|
||||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||||
Platform: account.Platform,
|
Platform: account.Platform,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
|
|||||||
Reference in New Issue
Block a user