refactor(Antigravity): 保存完整 API 响应到 extra 字段

- LoadCodeAssist/FetchAvailableModels 返回原始 JSON
- extra 新增 load_code_assist 和 available_models 保存原始响应
- 前端 tier 从 load_code_assist.paidTier.id 提取
- 删除冗余的 updateAccountTier 函数
This commit is contained in:
song
2025-12-31 00:15:25 +08:00
parent 0a4e0edc85
commit f284ea72fc
4 changed files with 65 additions and 57 deletions

View File

@@ -215,20 +215,20 @@ func (c *Client) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo
return &userInfo, nil
}
// LoadCodeAssist 获取 project_id
func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadCodeAssistResponse, error) {
// LoadCodeAssist 获取账户信息,返回解析后的结构体和原始 JSON
func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadCodeAssistResponse, map[string]any, error) {
reqBody := LoadCodeAssistRequest{}
reqBody.Metadata.IDEType = "ANTIGRAVITY"
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
return nil, nil, fmt.Errorf("序列化请求失败: %w", err)
}
url := BaseURL + "/v1internal:loadCodeAssist"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(string(bodyBytes)))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
return nil, nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
@@ -236,25 +236,29 @@ func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadC
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("loadCodeAssist 请求失败: %w", err)
return nil, nil, fmt.Errorf("loadCodeAssist 请求失败: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
return nil, nil, fmt.Errorf("读取响应失败: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("loadCodeAssist 失败 (HTTP %d): %s", resp.StatusCode, string(respBodyBytes))
return nil, nil, fmt.Errorf("loadCodeAssist 失败 (HTTP %d): %s", resp.StatusCode, string(respBodyBytes))
}
var loadResp LoadCodeAssistResponse
if err := json.Unmarshal(respBodyBytes, &loadResp); err != nil {
return nil, fmt.Errorf("响应解析失败: %w", err)
return nil, nil, fmt.Errorf("响应解析失败: %w", err)
}
return &loadResp, nil
// 解析原始 JSON 为 map
var rawResp map[string]any
_ = json.Unmarshal(respBodyBytes, &rawResp)
return &loadResp, rawResp, nil
}
// ModelQuotaInfo 模型配额信息
@@ -278,18 +282,18 @@ type FetchAvailableModelsResponse struct {
Models map[string]ModelInfo `json:"models"`
}
// FetchAvailableModels 获取可用模型和配额信息
func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectID string) (*FetchAvailableModelsResponse, error) {
// FetchAvailableModels 获取可用模型和配额信息,返回解析后的结构体和原始 JSON
func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectID string) (*FetchAvailableModelsResponse, map[string]any, error) {
reqBody := FetchAvailableModelsRequest{Project: projectID}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
return nil, nil, fmt.Errorf("序列化请求失败: %w", err)
}
apiURL := BaseURL + "/v1internal:fetchAvailableModels"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, strings.NewReader(string(bodyBytes)))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
return nil, nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
@@ -297,23 +301,27 @@ func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectI
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("fetchAvailableModels 请求失败: %w", err)
return nil, nil, fmt.Errorf("fetchAvailableModels 请求失败: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
return nil, nil, fmt.Errorf("读取响应失败: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("fetchAvailableModels 失败 (HTTP %d): %s", resp.StatusCode, string(respBodyBytes))
return nil, nil, fmt.Errorf("fetchAvailableModels 失败 (HTTP %d): %s", resp.StatusCode, string(respBodyBytes))
}
var modelsResp FetchAvailableModelsResponse
if err := json.Unmarshal(respBodyBytes, &modelsResp); err != nil {
return nil, fmt.Errorf("响应解析失败: %w", err)
return nil, nil, fmt.Errorf("响应解析失败: %w", err)
}
return &modelsResp, nil
// 解析原始 JSON 为 map
var rawResp map[string]any
_ = json.Unmarshal(respBodyBytes, &rawResp)
return &modelsResp, rawResp, nil
}

View File

@@ -142,7 +142,7 @@ func (s *AntigravityOAuthService) ExchangeCode(ctx context.Context, input *Antig
}
// 获取 project_id部分账户类型可能没有
loadResp, err := client.LoadCodeAssist(ctx, tokenResp.AccessToken)
loadResp, _, err := client.LoadCodeAssist(ctx, tokenResp.AccessToken)
if err != nil {
fmt.Printf("[AntigravityOAuth] 警告: 获取 project_id 失败: %v\n", err)
} else if loadResp != nil && loadResp.CloudAICompanionProject != "" {

View File

@@ -145,10 +145,16 @@ func (r *AntigravityQuotaRefresher) refreshAccountQuota(ctx context.Context, acc
client := antigravity.NewClient(proxyURL)
// 获取账户类型tier和 project_id
loadResp, _ := client.LoadCodeAssist(ctx, accessToken)
if account.Extra == nil {
account.Extra = make(map[string]any)
}
// 获取账户信息tier、project_id 等)
loadResp, loadRaw, _ := client.LoadCodeAssist(ctx, accessToken)
if loadRaw != nil {
account.Extra["load_code_assist"] = loadRaw
}
if loadResp != nil {
r.updateAccountTier(account, loadResp)
// 尝试从 API 获取 project_id
if projectID == "" && loadResp.CloudAICompanionProject != "" {
projectID = loadResp.CloudAICompanionProject
@@ -164,14 +170,21 @@ func (r *AntigravityQuotaRefresher) refreshAccountQuota(ctx context.Context, acc
}
// 调用 API 获取配额
modelsResp, err := client.FetchAvailableModels(ctx, accessToken, projectID)
modelsResp, modelsRaw, err := client.FetchAvailableModels(ctx, accessToken, projectID)
if err != nil {
return err
return r.accountRepo.Update(ctx, account) // 保存已有的 load_code_assist 信息
}
// 解析配额数据并更新 extra 字段
// 保存完整的配额响应
if modelsRaw != nil {
account.Extra["available_models"] = modelsRaw
}
// 解析配额数据为前端使用的格式
r.updateAccountQuota(account, modelsResp)
account.Extra["last_refresh"] = time.Now().Format(time.RFC3339)
// 保存到数据库
return r.accountRepo.Update(ctx, account)
}
@@ -187,35 +200,8 @@ func (r *AntigravityQuotaRefresher) isTokenExpired(account *Account) bool {
return time.Now().Add(5 * time.Minute).After(*expiresAt)
}
// updateAccountTier 更新账户类型信息
func (r *AntigravityQuotaRefresher) updateAccountTier(account *Account, loadResp *antigravity.LoadCodeAssistResponse) {
if account.Extra == nil {
account.Extra = make(map[string]any)
}
tier := loadResp.GetTier()
if tier != "" {
account.Extra["tier"] = tier
}
// 保存不符合条件的原因(如 INELIGIBLE_ACCOUNT
if len(loadResp.IneligibleTiers) > 0 && loadResp.IneligibleTiers[0] != nil {
ineligible := loadResp.IneligibleTiers[0]
if ineligible.ReasonCode != "" {
account.Extra["ineligible_reason_code"] = ineligible.ReasonCode
}
if ineligible.ReasonMessage != "" {
account.Extra["ineligible_reason_message"] = ineligible.ReasonMessage
}
}
}
// updateAccountQuota 更新账户的配额信息
// updateAccountQuota 更新账户的配额信息(前端使用的格式)
func (r *AntigravityQuotaRefresher) updateAccountQuota(account *Account, modelsResp *antigravity.FetchAvailableModelsResponse) {
if account.Extra == nil {
account.Extra = make(map[string]any)
}
quota := make(map[string]any)
for modelName, modelInfo := range modelsResp.Models {
@@ -233,5 +219,4 @@ func (r *AntigravityQuotaRefresher) updateAccountQuota(account *Account, modelsR
}
account.Extra["quota"] = quota
account.Extra["last_quota_check"] = time.Now().Format(time.RFC3339)
}

View File

@@ -403,11 +403,26 @@ const antigravityClaude45Usage = computed(() =>
getAntigravityUsage(['claude-sonnet-4-5', 'claude-opus-4-5-thinking'])
)
// Antigravity 账户类型
// Antigravity 账户类型(从 load_code_assist 响应中提取)
const antigravityTier = computed(() => {
const extra = props.account.extra as Record<string, unknown> | undefined
if (!extra || typeof extra.tier !== 'string') return null
return extra.tier as string
if (!extra) return null
const loadCodeAssist = extra.load_code_assist as Record<string, unknown> | undefined
if (!loadCodeAssist) return null
// 优先取 paidTier否则取 currentTier
const paidTier = loadCodeAssist.paidTier as Record<string, unknown> | undefined
if (paidTier && typeof paidTier.id === 'string') {
return paidTier.id
}
const currentTier = loadCodeAssist.currentTier as Record<string, unknown> | undefined
if (currentTier && typeof currentTier.id === 'string') {
return currentTier.id
}
return null
})
// 账户类型显示标签