From 69c4b17a9b05b57d07914dfb0be7655db70447c9 Mon Sep 17 00:00:00 2001 From: song Date: Sat, 17 Jan 2026 01:54:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(antigravity):=20=E5=8A=A8=E6=80=81=20URL?= =?UTF-8?q?=20=E6=8E=92=E5=BA=8F=EF=BC=8C=E6=9C=80=E8=BF=91=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E7=9A=84=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - URLAvailability 新增 lastSuccess 字段追踪最近成功的 URL - GetAvailableURLs 返回列表时优先放置 lastSuccess - 所有 Antigravity API 调用成功后调用 MarkSuccess 更新优先级 --- backend/internal/pkg/antigravity/client.go | 4 +++ backend/internal/pkg/antigravity/oauth.go | 29 +++++++++++++++++-- .../service/antigravity_gateway_service.go | 16 ++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/backend/internal/pkg/antigravity/client.go b/backend/internal/pkg/antigravity/client.go index 454d3438..fd6cac58 100644 --- a/backend/internal/pkg/antigravity/client.go +++ b/backend/internal/pkg/antigravity/client.go @@ -358,6 +358,8 @@ func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadC var rawResp map[string]any _ = json.Unmarshal(respBodyBytes, &rawResp) + // 标记成功的 URL,下次优先使用 + DefaultURLAvailability.MarkSuccess(baseURL) return &loadResp, rawResp, nil } @@ -449,6 +451,8 @@ func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectI var rawResp map[string]any _ = json.Unmarshal(respBodyBytes, &rawResp) + // 标记成功的 URL,下次优先使用 + DefaultURLAvailability.MarkSuccess(baseURL) return &modelsResp, rawResp, nil } diff --git a/backend/internal/pkg/antigravity/oauth.go b/backend/internal/pkg/antigravity/oauth.go index 9d4baa6c..ee2a6c1a 100644 --- a/backend/internal/pkg/antigravity/oauth.go +++ b/backend/internal/pkg/antigravity/oauth.go @@ -51,11 +51,12 @@ var BaseURLs = []string{ // BaseURL 默认 URL(保持向后兼容) var BaseURL = BaseURLs[0] -// URLAvailability 管理 URL 可用性状态(带 TTL 自动恢复) +// URLAvailability 管理 URL 可用性状态(带 TTL 自动恢复和动态优先级) type URLAvailability struct { mu sync.RWMutex unavailable map[string]time.Time // URL -> 恢复时间 ttl time.Duration + lastSuccess string // 最近成功请求的 URL,优先使用 } // DefaultURLAvailability 全局 URL 可用性管理器 @@ -76,6 +77,15 @@ func (u *URLAvailability) MarkUnavailable(url string) { u.unavailable[url] = time.Now().Add(u.ttl) } +// MarkSuccess 标记 URL 请求成功,将其设为优先使用 +func (u *URLAvailability) MarkSuccess(url string) { + u.mu.Lock() + defer u.mu.Unlock() + u.lastSuccess = url + // 成功后清除该 URL 的不可用标记 + delete(u.unavailable, url) +} + // IsAvailable 检查 URL 是否可用 func (u *URLAvailability) IsAvailable(url string) bool { u.mu.RLock() @@ -87,14 +97,29 @@ func (u *URLAvailability) IsAvailable(url string) bool { return time.Now().After(expiry) } -// GetAvailableURLs 返回可用的 URL 列表(保持优先级顺序) +// GetAvailableURLs 返回可用的 URL 列表 +// 最近成功的 URL 优先,其他按默认顺序 func (u *URLAvailability) GetAvailableURLs() []string { u.mu.RLock() defer u.mu.RUnlock() now := time.Now() result := make([]string, 0, len(BaseURLs)) + + // 如果有最近成功的 URL 且可用,放在最前面 + if u.lastSuccess != "" { + expiry, exists := u.unavailable[u.lastSuccess] + if !exists || now.After(expiry) { + result = append(result, u.lastSuccess) + } + } + + // 添加其他可用的 URL(按默认顺序) for _, url := range BaseURLs { + // 跳过已添加的 lastSuccess + if url == u.lastSuccess { + continue + } expiry, exists := u.unavailable[url] if !exists || now.After(expiry) { result = append(result, url) diff --git a/backend/internal/service/antigravity_gateway_service.go b/backend/internal/service/antigravity_gateway_service.go index a0e845ee..fc0008ea 100644 --- a/backend/internal/service/antigravity_gateway_service.go +++ b/backend/internal/service/antigravity_gateway_service.go @@ -266,6 +266,8 @@ func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account // 解析流式响应,提取文本 text := extractTextFromSSEResponse(respBody) + // 标记成功的 URL,下次优先使用 + antigravity.DefaultURLAvailability.MarkSuccess(baseURL) return &TestConnectionResult{ Text: text, MappedModel: mappedModel, @@ -551,8 +553,10 @@ func (s *AntigravityGatewayService) Forward(ctx context.Context, c *gin.Context, // 重试循环 var resp *http.Response + var usedBaseURL string // 追踪成功使用的 URL urlFallbackLoop: for urlIdx, baseURL := range availableURLs { + usedBaseURL = baseURL for attempt := 1; attempt <= antigravityMaxRetries; attempt++ { // 检查 context 是否已取消(客户端断开连接) select { @@ -628,6 +632,11 @@ urlFallbackLoop: } defer func() { _ = resp.Body.Close() }() + // 请求成功,标记 URL 供后续优先使用 + if resp.StatusCode < 400 && usedBaseURL != "" { + antigravity.DefaultURLAvailability.MarkSuccess(usedBaseURL) + } + if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20)) @@ -1097,8 +1106,10 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co // 重试循环 var resp *http.Response + var usedBaseURL string // 追踪成功使用的 URL urlFallbackLoop: for urlIdx, baseURL := range availableURLs { + usedBaseURL = baseURL for attempt := 1; attempt <= antigravityMaxRetries; attempt++ { // 检查 context 是否已取消(客户端断开连接) select { @@ -1177,6 +1188,11 @@ urlFallbackLoop: } }() + // 请求成功,标记 URL 供后续优先使用 + if resp.StatusCode < 400 && usedBaseURL != "" { + antigravity.DefaultURLAvailability.MarkSuccess(usedBaseURL) + } + // 处理错误响应 if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20))