fix(antigravity): 修复 429 限流处理逻辑
- 只有 5 次重试全部失败后才标记账户限流 - 使用 Gemini 格式解析 429 响应中的重试时间 - Claude 模型无重试时间时默认 1 分钟,Gemini 默认 5 分钟 - 添加生图模型映射 gemini-3-pro-image-preview
This commit is contained in:
@@ -51,23 +51,27 @@ var antigravityModelMapping = map[string]string{
|
|||||||
"claude-haiku-4-5": "gemini-3-flash",
|
"claude-haiku-4-5": "gemini-3-flash",
|
||||||
"claude-3-haiku-20240307": "gemini-3-flash",
|
"claude-3-haiku-20240307": "gemini-3-flash",
|
||||||
"claude-haiku-4-5-20251001": "gemini-3-flash",
|
"claude-haiku-4-5-20251001": "gemini-3-flash",
|
||||||
|
// 生图模型:官方名 → Antigravity 内部名
|
||||||
|
"gemini-3-pro-image-preview": "gemini-3-pro-image",
|
||||||
}
|
}
|
||||||
|
|
||||||
// AntigravityGatewayService 处理 Antigravity 平台的 API 转发
|
// AntigravityGatewayService 处理 Antigravity 平台的 API 转发
|
||||||
type AntigravityGatewayService struct {
|
type AntigravityGatewayService struct {
|
||||||
|
accountRepo AccountRepository
|
||||||
tokenProvider *AntigravityTokenProvider
|
tokenProvider *AntigravityTokenProvider
|
||||||
rateLimitService *RateLimitService
|
rateLimitService *RateLimitService
|
||||||
httpUpstream HTTPUpstream
|
httpUpstream HTTPUpstream
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAntigravityGatewayService(
|
func NewAntigravityGatewayService(
|
||||||
_ AccountRepository,
|
accountRepo AccountRepository,
|
||||||
_ GatewayCache,
|
_ GatewayCache,
|
||||||
tokenProvider *AntigravityTokenProvider,
|
tokenProvider *AntigravityTokenProvider,
|
||||||
rateLimitService *RateLimitService,
|
rateLimitService *RateLimitService,
|
||||||
httpUpstream HTTPUpstream,
|
httpUpstream HTTPUpstream,
|
||||||
) *AntigravityGatewayService {
|
) *AntigravityGatewayService {
|
||||||
return &AntigravityGatewayService{
|
return &AntigravityGatewayService{
|
||||||
|
accountRepo: accountRepo,
|
||||||
tokenProvider: tokenProvider,
|
tokenProvider: tokenProvider,
|
||||||
rateLimitService: rateLimitService,
|
rateLimitService: rateLimitService,
|
||||||
httpUpstream: httpUpstream,
|
httpUpstream: httpUpstream,
|
||||||
@@ -402,14 +406,15 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
|
|||||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
|
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode == 429 {
|
|
||||||
s.handleUpstreamError(ctx, account, resp.StatusCode, resp.Header, respBody)
|
|
||||||
}
|
|
||||||
if attempt < antigravityMaxRetries {
|
if attempt < antigravityMaxRetries {
|
||||||
log.Printf("Antigravity account %d: upstream status %d, retry %d/%d", account.ID, resp.StatusCode, attempt, antigravityMaxRetries)
|
log.Printf("Antigravity account %d: upstream status %d, retry %d/%d", account.ID, resp.StatusCode, attempt, antigravityMaxRetries)
|
||||||
sleepAntigravityBackoff(attempt)
|
sleepAntigravityBackoff(attempt)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// 所有重试都失败,标记限流状态
|
||||||
|
if resp.StatusCode == 429 {
|
||||||
|
s.handleUpstreamError(ctx, account, resp.StatusCode, resp.Header, respBody)
|
||||||
|
}
|
||||||
if action == "countTokens" {
|
if action == "countTokens" {
|
||||||
estimated := estimateGeminiCountTokens(body)
|
estimated := estimateGeminiCountTokens(body)
|
||||||
c.JSON(http.StatusOK, map[string]any{"totalTokens": estimated})
|
c.JSON(http.StatusOK, map[string]any{"totalTokens": estimated})
|
||||||
@@ -526,6 +531,23 @@ func sleepAntigravityBackoff(attempt int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AntigravityGatewayService) handleUpstreamError(ctx context.Context, account *Account, statusCode int, headers http.Header, body []byte) {
|
func (s *AntigravityGatewayService) handleUpstreamError(ctx context.Context, account *Account, statusCode int, headers http.Header, body []byte) {
|
||||||
|
// 429 使用 Gemini 格式解析(从 body 解析重置时间)
|
||||||
|
if statusCode == 429 {
|
||||||
|
resetAt := ParseGeminiRateLimitResetTime(body)
|
||||||
|
if resetAt == nil {
|
||||||
|
// 解析失败:Gemini 有重试时间用 5 分钟,Claude 没有用 1 分钟
|
||||||
|
defaultDur := 1 * time.Minute
|
||||||
|
if bytes.Contains(body, []byte("Please retry in")) || bytes.Contains(body, []byte("retryDelay")) {
|
||||||
|
defaultDur = 5 * time.Minute
|
||||||
|
}
|
||||||
|
ra := time.Now().Add(defaultDur)
|
||||||
|
_ = s.accountRepo.SetRateLimited(ctx, account.ID, ra)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = s.accountRepo.SetRateLimited(ctx, account.ID, time.Unix(*resetAt, 0))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 其他错误码继续使用 rateLimitService
|
||||||
if s.rateLimitService == nil {
|
if s.rateLimitService == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1883,7 +1883,7 @@ func (s *GeminiMessagesCompatService) handleGeminiUpstreamError(ctx context.Cont
|
|||||||
if statusCode != 429 {
|
if statusCode != 429 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resetAt := parseGeminiRateLimitResetTime(body)
|
resetAt := ParseGeminiRateLimitResetTime(body)
|
||||||
if resetAt == nil {
|
if resetAt == nil {
|
||||||
ra := time.Now().Add(5 * time.Minute)
|
ra := time.Now().Add(5 * time.Minute)
|
||||||
_ = s.accountRepo.SetRateLimited(ctx, account.ID, ra)
|
_ = s.accountRepo.SetRateLimited(ctx, account.ID, ra)
|
||||||
@@ -1892,7 +1892,8 @@ func (s *GeminiMessagesCompatService) handleGeminiUpstreamError(ctx context.Cont
|
|||||||
_ = s.accountRepo.SetRateLimited(ctx, account.ID, time.Unix(*resetAt, 0))
|
_ = s.accountRepo.SetRateLimited(ctx, account.ID, time.Unix(*resetAt, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGeminiRateLimitResetTime(body []byte) *int64 {
|
// ParseGeminiRateLimitResetTime 解析 Gemini 格式的 429 响应,返回重置时间的 Unix 时间戳
|
||||||
|
func ParseGeminiRateLimitResetTime(body []byte) *int64 {
|
||||||
// Try to parse metadata.quotaResetDelay like "12.345s"
|
// Try to parse metadata.quotaResetDelay like "12.345s"
|
||||||
var parsed map[string]any
|
var parsed map[string]any
|
||||||
if err := json.Unmarshal(body, &parsed); err == nil {
|
if err := json.Unmarshal(body, &parsed); err == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user