@@ -20,11 +20,27 @@ import (
const (
antigravityStickySessionTTL = time . Hour
antigravityMaxRetries = 5
antigravityMaxRetries = 3
antigravityRetryBaseDelay = 1 * time . Second
antigravityRetryMaxDelay = 16 * time . Second
)
// getSessionID 从 gin.Context 获取 session_id( 用于日志追踪)
func getSessionID ( c * gin . Context ) string {
if c == nil {
return ""
}
return c . GetHeader ( "session_id" )
}
// logPrefix 生成统一的日志前缀
func logPrefix ( sessionID , accountName string ) string {
if sessionID != "" {
return fmt . Sprintf ( "[antigravity-Forward] session=%s account=%s" , sessionID , accountName )
}
return fmt . Sprintf ( "[antigravity-Forward] account=%s" , accountName )
}
// Antigravity 直接支持的模型(精确匹配透传)
var antigravitySupportedModels = map [ string ] bool {
"claude-opus-4-5-thinking" : true ,
@@ -46,10 +62,11 @@ var antigravityPrefixMapping = []struct {
target string
} {
// 长前缀优先
{ "gemini-3-pro -image" , "gemini-3-pro-image" } , // gemini-3-pro-image-preview 等
{ "claude-3-5-sonnet" , "claude-sonnet-4-5 " } , // 旧版 claude-3-5-sonnet-xxx
{ "claude-sonnet-4-5 " , "claude-sonnet-4-5" } , // claude-sonnet-4-5- xxx
{ "claude-haiku -4-5" , "claude-sonnet-4-5" } , // claude-haiku -4-5-xxx → sonnet
{ "gemini-2.5-flash -image" , "gemini-3-pro-image" } , // gemini-2.5-flash-image → 3-pro-image
{ "gemini-3-pro-image" , "gemini-3-pro-image " } , // gemini-3-pro-image-preview 等
{ "claude-3-5- sonnet" , "claude-sonnet-4-5" } , // 旧版 claude-3-5- sonnet-xxx
{ "claude-sonnet -4-5" , "claude-sonnet-4-5" } , // claude-sonnet -4-5-xxx
{ "claude-haiku-4-5" , "claude-sonnet-4-5" } , // claude-haiku-4-5-xxx → sonnet
{ "claude-opus-4-5" , "claude-opus-4-5-thinking" } ,
{ "claude-3-haiku" , "claude-sonnet-4-5" } , // 旧版 claude-3-haiku-xxx → sonnet
{ "claude-sonnet-4" , "claude-sonnet-4-5" } ,
@@ -310,6 +327,8 @@ func (s *AntigravityGatewayService) unwrapV1InternalResponse(body []byte) ([]byt
// Forward 转发 Claude 协议请求( Claude → Gemini 转换)
func ( s * AntigravityGatewayService ) Forward ( ctx context . Context , c * gin . Context , account * Account , body [ ] byte ) ( * ForwardResult , error ) {
startTime := time . Now ( )
sessionID := getSessionID ( c )
prefix := logPrefix ( sessionID , account . Name )
// 解析 Claude 请求
var claudeReq antigravity . ClaudeRequest
@@ -364,10 +383,11 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
resp , err = s . httpUpstream . Do ( upstreamReq , proxyURL , account . ID , account . Concurrency )
if err != nil {
if attempt < antigravityMaxRetries {
log . Printf ( "Antigravity account %d: upstream request failed, retry %d/%d: %v" , account . ID , attempt , antigravityMaxRetries , err )
log . Printf ( "%s status= request_ failed retry= %d/%d error= %v" , prefix , attempt , antigravityMaxRetries , err )
sleepAntigravityBackoff ( attempt )
continue
}
log . Printf ( "%s status=request_failed retries_exhausted error=%v" , prefix , err )
return nil , s . writeClaudeError ( c , http . StatusBadGateway , "upstream_error" , "Upstream request failed after retries" )
}
@@ -376,13 +396,13 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
_ = resp . Body . Close ( )
if attempt < antigravityMaxRetries {
log . Printf ( "Antigravity account %d: upstream status %d, retry %d/%d" , account . ID , resp . StatusCode , attempt , antigravityMaxRetries )
log . Printf ( "%s status= %d retry= %d/%d" , prefix , resp . StatusCode , attempt , antigravityMaxRetries )
sleepAntigravityBackoff ( attempt )
continue
}
// 所有重试都失败,标记限流状态
if resp . StatusCode == 429 {
s . handleUpstreamError ( ctx , account , resp . StatusCode , resp . Header , respBody )
s . handleUpstreamError ( ctx , prefix , account , resp . StatusCode , resp . Header , respBody )
}
// 最后一次尝试也失败
resp = & http . Response {
@@ -400,7 +420,7 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
// 处理错误响应
if resp . StatusCode >= 400 {
respBody , _ := io . ReadAll ( io . LimitReader ( resp . Body , 2 << 20 ) )
s . handleUpstreamError ( ctx , account , resp . StatusCode , resp . Header , respBody )
s . handleUpstreamError ( ctx , prefix , account , resp . StatusCode , resp . Header , respBody )
if s . shouldFailoverUpstreamError ( resp . StatusCode ) {
return nil , & UpstreamFailoverError { StatusCode : resp . StatusCode }
@@ -419,6 +439,7 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
if claudeReq . Stream {
streamRes , err := s . handleClaudeStreamingResponse ( c , resp , startTime , originalModel )
if err != nil {
log . Printf ( "%s status=stream_error error=%v" , prefix , err )
return nil , err
}
usage = streamRes . usage
@@ -443,6 +464,8 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context,
// ForwardGemini 转发 Gemini 协议请求
func ( s * AntigravityGatewayService ) ForwardGemini ( ctx context . Context , c * gin . Context , account * Account , originalModel string , action string , stream bool , body [ ] byte ) ( * ForwardResult , error ) {
startTime := time . Now ( )
sessionID := getSessionID ( c )
prefix := logPrefix ( sessionID , account . Name )
if strings . TrimSpace ( originalModel ) == "" {
return nil , s . writeGoogleError ( c , http . StatusBadRequest , "Missing model in URL" )
@@ -518,10 +541,11 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
resp , err = s . httpUpstream . Do ( upstreamReq , proxyURL , account . ID , account . Concurrency )
if err != nil {
if attempt < antigravityMaxRetries {
log . Printf ( "Antigravity account %d: upstream request failed, retry %d/%d: %v" , account . ID , attempt , antigravityMaxRetries , err )
log . Printf ( "%s status= request_ failed retry= %d/%d error= %v" , prefix , attempt , antigravityMaxRetries , err )
sleepAntigravityBackoff ( attempt )
continue
}
log . Printf ( "%s status=request_failed retries_exhausted error=%v" , prefix , err )
return nil , s . writeGoogleError ( c , http . StatusBadGateway , "Upstream request failed after retries" )
}
@@ -530,13 +554,13 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
_ = resp . Body . Close ( )
if attempt < antigravityMaxRetries {
log . Printf ( "Antigravity account %d: upstream status %d, retry %d/%d" , account . ID , resp . StatusCode , attempt , antigravityMaxRetries )
log . Printf ( "%s status= %d retry= %d/%d" , prefix , resp . StatusCode , attempt , antigravityMaxRetries )
sleepAntigravityBackoff ( attempt )
continue
}
// 所有重试都失败,标记限流状态
if resp . StatusCode == 429 {
s . handleUpstreamError ( ctx , account , resp . StatusCode , resp . Header , respBody )
s . handleUpstreamError ( ctx , prefix , account , resp . StatusCode , resp . Header , respBody )
}
resp = & http . Response {
StatusCode : resp . StatusCode ,
@@ -558,7 +582,7 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
// 处理错误响应
if resp . StatusCode >= 400 {
respBody , _ := io . ReadAll ( io . LimitReader ( resp . Body , 2 << 20 ) )
s . handleUpstreamError ( ctx , account , resp . StatusCode , resp . Header , respBody )
s . handleUpstreamError ( ctx , prefix , account , resp . StatusCode , resp . Header , respBody )
if s . shouldFailoverUpstreamError ( resp . StatusCode ) {
return nil , & UpstreamFailoverError { StatusCode : resp . StatusCode }
@@ -580,6 +604,7 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
if stream || upstreamAction == "streamGenerateContent" {
streamRes , err := s . handleGeminiStreamingResponse ( c , resp , startTime )
if err != nil {
log . Printf ( "%s status=stream_error error=%v" , prefix , err )
return nil , err
}
usage = streamRes . usage
@@ -628,7 +653,7 @@ func sleepAntigravityBackoff(attempt int) {
sleepGeminiBackoff ( attempt ) // 复用 Gemini 的退避逻辑
}
func ( s * AntigravityGatewayService ) handleUpstreamError ( ctx context . Context , account * Account , statusCode int , headers http . Header , body [ ] byte ) {
func ( s * AntigravityGatewayService ) handleUpstreamError ( ctx context . Context , prefix string , account * Account , statusCode int , headers http . Header , body [ ] byte ) {
// 429 使用 Gemini 格式解析(从 body 解析重置时间)
if statusCode == 429 {
resetAt := ParseGeminiRateLimitResetTime ( body )
@@ -639,17 +664,23 @@ func (s *AntigravityGatewayService) handleUpstreamError(ctx context.Context, acc
defaultDur = 5 * time . Minute
}
ra := time . Now ( ) . Add ( defaultDur )
log . Printf ( "%s status=429 rate_limited reset_in=%v (fallback)" , prefix , defaultDur )
_ = s . accountRepo . SetRateLimited ( ctx , account . ID , ra )
return
}
_ = s . accountRepo . SetRateLimited ( ctx , account . ID , time . Unix ( * resetAt , 0 ) )
resetTime := time . Unix ( * resetAt , 0 )
log . Printf ( "%s status=429 rate_limited reset_at=%v reset_in=%v" , prefix , resetTime . Format ( "15:04:05" ) , time . Until ( resetTime ) . Truncate ( time . Second ) )
_ = s . accountRepo . SetRateLimited ( ctx , account . ID , resetTime )
return
}
// 其他错误码继续使用 rateLimitService
if s . rateLimitService == nil {
return
}
s . rateLimitService . HandleUpstreamError ( ctx , account , statusCode , headers , body )
shouldDisable := s. rateLimitService . HandleUpstreamError ( ctx , account , statusCode , headers , body )
if shouldDisable {
log . Printf ( "%s status=%d marked_error" , prefix , statusCode )
}
}
type antigravityStreamResult struct {
@@ -758,7 +789,7 @@ func (s *AntigravityGatewayService) writeClaudeError(c *gin.Context, status int,
func ( s * AntigravityGatewayService ) writeMappedClaudeError ( c * gin . Context , upstreamStatus int , body [ ] byte ) error {
// 记录上游错误详情便于调试
log . Printf ( "A ntigravity upstream error %d: %s" , upstreamStatus , string ( body ) )
log . Printf ( "[a ntigravity-Forward] upstream_ error status=%d body= %s" , upstreamStatus , string ( body ) )
var statusCode int
var errType , errMsg string
@@ -832,7 +863,7 @@ func (s *AntigravityGatewayService) handleClaudeNonStreamingResponse(c *gin.Cont
// 转换 Gemini 响应为 Claude 格式
claudeResp , agUsage , err := antigravity . TransformGeminiToClaude ( body , originalModel )
if err != nil {
log . Printf ( "Transform Gemini to Claude failed: %v, body: %s" , err , string ( body ) )
log . Printf ( "[antigravity-Forward] transform_error error= %v body= %s" , err , string ( body ) )
return nil , s . writeClaudeError ( c , http . StatusBadGateway , "upstream_error" , "Failed to parse upstream response" )
}