@@ -930,7 +930,7 @@ func (s *AntigravityGatewayService) applyErrorPolicy(p antigravityRetryLoopParam
930930 case ErrorPolicyTempUnscheduled :
931931 slog .Info ("temp_unschedulable_matched" ,
932932 "prefix" , p .prefix , "status_code" , statusCode , "account_id" , p .account .ID )
933- return true , statusCode , & AntigravityAccountSwitchError {OriginalAccountID : p .account .ID , IsStickySession : p .isStickySession }
933+ return true , statusCode , & AntigravityAccountSwitchError {OriginalAccountID : p .account .ID , RateLimitedModel : p . requestedModel , IsStickySession : p .isStickySession }
934934 }
935935 return false , statusCode , nil
936936}
@@ -1001,8 +1001,9 @@ type TestConnectionResult struct {
10011001 MappedModel string // 实际使用的模型
10021002}
10031003
1004- // TestConnection 测试 Antigravity 账号连接(非流式,无重试、无计费)
1005- // 支持 Claude 和 Gemini 两种协议,根据 modelID 前缀自动选择
1004+ // TestConnection 测试 Antigravity 账号连接。
1005+ // 复用 antigravityRetryLoop 的完整重试 / credits overages / 智能重试逻辑,
1006+ // 与真实调度行为一致。差异:不做账号切换(测试指定账号)、不记录 ops 错误。
10061007func (s * AntigravityGatewayService ) TestConnection (ctx context.Context , account * Account , modelID string ) (* TestConnectionResult , error ) {
10071008
10081009 // 获取 token
@@ -1026,10 +1027,8 @@ func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account
10261027 // 构建请求体
10271028 var requestBody []byte
10281029 if strings .HasPrefix (modelID , "gemini-" ) {
1029- // Gemini 模型:直接使用 Gemini 格式
10301030 requestBody , err = s .buildGeminiTestRequest (projectID , mappedModel )
10311031 } else {
1032- // Claude 模型:使用协议转换
10331032 requestBody , err = s .buildClaudeTestRequest (projectID , mappedModel )
10341033 }
10351034 if err != nil {
@@ -1042,64 +1041,63 @@ func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account
10421041 proxyURL = account .Proxy .URL ()
10431042 }
10441043
1045- baseURL := resolveAntigravityForwardBaseURL ()
1046- if baseURL == "" {
1047- return nil , errors .New ("no antigravity forward base url configured" )
1048- }
1049- availableURLs := []string {baseURL }
1050-
1051- var lastErr error
1052- for urlIdx , baseURL := range availableURLs {
1053- // 构建 HTTP 请求(总是使用流式 endpoint,与官方客户端一致)
1054- req , err := antigravity .NewAPIRequestWithURL (ctx , baseURL , "streamGenerateContent" , accessToken , requestBody )
1055- if err != nil {
1056- lastErr = err
1057- continue
1058- }
1059-
1060- // 调试日志:Test 请求信息
1061- logger .LegacyPrintf ("service.antigravity_gateway" , "[antigravity-Test] account=%s request_size=%d url=%s" , account .Name , len (requestBody ), req .URL .String ())
1062-
1063- // 发送请求
1064- resp , err := s .httpUpstream .Do (req , proxyURL , account .ID , account .Concurrency )
1065- if err != nil {
1066- lastErr = fmt .Errorf ("请求失败: %w" , err )
1067- if shouldAntigravityFallbackToNextURL (err , 0 ) && urlIdx < len (availableURLs )- 1 {
1068- logger .LegacyPrintf ("service.antigravity_gateway" , "[antigravity-Test] URL fallback: %s -> %s" , baseURL , availableURLs [urlIdx + 1 ])
1069- continue
1070- }
1071- return nil , lastErr
1072- }
1073-
1074- // 读取响应
1075- respBody , err := io .ReadAll (io .LimitReader (resp .Body , 2 << 20 ))
1076- _ = resp .Body .Close () // 立即关闭,避免循环内 defer 导致的资源泄漏
1077- if err != nil {
1078- return nil , fmt .Errorf ("读取响应失败: %w" , err )
1079- }
1080-
1081- // 检查是否需要 URL 降级
1082- if shouldAntigravityFallbackToNextURL (nil , resp .StatusCode ) && urlIdx < len (availableURLs )- 1 {
1083- logger .LegacyPrintf ("service.antigravity_gateway" , "[antigravity-Test] URL fallback (HTTP %d): %s -> %s" , resp .StatusCode , baseURL , availableURLs [urlIdx + 1 ])
1084- continue
1044+ // 复用 antigravityRetryLoop:完整的重试 / credits overages / 智能重试
1045+ prefix := fmt .Sprintf ("[antigravity-Test] account=%d(%s)" , account .ID , account .Name )
1046+ p := antigravityRetryLoopParams {
1047+ ctx : ctx ,
1048+ prefix : prefix ,
1049+ account : account ,
1050+ proxyURL : proxyURL ,
1051+ accessToken : accessToken ,
1052+ action : "streamGenerateContent" ,
1053+ body : requestBody ,
1054+ c : nil , // 无 gin.Context → 跳过 ops 追踪
1055+ httpUpstream : s .httpUpstream ,
1056+ settingService : s .settingService ,
1057+ accountRepo : s .accountRepo ,
1058+ requestedModel : modelID ,
1059+ handleError : testConnectionHandleError ,
1060+ }
1061+
1062+ result , err := s .antigravityRetryLoop (p )
1063+ if err != nil {
1064+ // AccountSwitchError → 测试时不切换账号,返回友好提示
1065+ var switchErr * AntigravityAccountSwitchError
1066+ if errors .As (err , & switchErr ) {
1067+ return nil , fmt .Errorf ("该账号模型 %s 当前限流中,请稍后重试" , switchErr .RateLimitedModel )
10851068 }
1069+ return nil , err
1070+ }
10861071
1087- if resp .StatusCode >= 400 {
1088- return nil , fmt .Errorf ("API 返回 %d: %s" , resp .StatusCode , string (respBody ))
1089- }
1072+ if result == nil || result .resp == nil {
1073+ return nil , errors .New ("upstream returned empty response" )
1074+ }
1075+ defer func () { _ = result .resp .Body .Close () }()
10901076
1091- // 解析流式响应,提取文本
1092- text := extractTextFromSSEResponse (respBody )
1077+ respBody , err := io .ReadAll (io .LimitReader (result .resp .Body , 2 << 20 ))
1078+ if err != nil {
1079+ return nil , fmt .Errorf ("读取响应失败: %w" , err )
1080+ }
10931081
1094- // 标记成功的 URL,下次优先使用
1095- antigravity .DefaultURLAvailability .MarkSuccess (baseURL )
1096- return & TestConnectionResult {
1097- Text : text ,
1098- MappedModel : mappedModel ,
1099- }, nil
1082+ if result .resp .StatusCode >= 400 {
1083+ return nil , fmt .Errorf ("API 返回 %d: %s" , result .resp .StatusCode , string (respBody ))
11001084 }
11011085
1102- return nil , lastErr
1086+ text := extractTextFromSSEResponse (respBody )
1087+ return & TestConnectionResult {Text : text , MappedModel : mappedModel }, nil
1088+ }
1089+
1090+ // testConnectionHandleError 是 TestConnection 使用的轻量 handleError 回调。
1091+ // 仅记录日志,不做 ops 错误追踪或粘性会话清除。
1092+ func testConnectionHandleError (
1093+ _ context.Context , prefix string , account * Account ,
1094+ statusCode int , _ http.Header , body []byte ,
1095+ requestedModel string , _ int64 , _ string , _ bool ,
1096+ ) * handleModelRateLimitResult {
1097+ logger .LegacyPrintf ("service.antigravity_gateway" ,
1098+ "%s test_handle_error status=%d model=%s account=%d body=%s" ,
1099+ prefix , statusCode , requestedModel , account .ID , truncateForLog (body , 200 ))
1100+ return nil
11031101}
11041102
11051103// buildGeminiTestRequest 构建 Gemini 格式测试请求
0 commit comments