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:
@@ -215,20 +215,20 @@ func (c *Client) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo
|
|||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadCodeAssist 获取 project_id
|
// LoadCodeAssist 获取账户信息,返回解析后的结构体和原始 JSON
|
||||||
func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadCodeAssistResponse, error) {
|
func (c *Client) LoadCodeAssist(ctx context.Context, accessToken string) (*LoadCodeAssistResponse, map[string]any, error) {
|
||||||
reqBody := LoadCodeAssistRequest{}
|
reqBody := LoadCodeAssistRequest{}
|
||||||
reqBody.Metadata.IDEType = "ANTIGRAVITY"
|
reqBody.Metadata.IDEType = "ANTIGRAVITY"
|
||||||
|
|
||||||
bodyBytes, err := json.Marshal(reqBody)
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("序列化请求失败: %w", err)
|
return nil, nil, fmt.Errorf("序列化请求失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
url := BaseURL + "/v1internal:loadCodeAssist"
|
url := BaseURL + "/v1internal:loadCodeAssist"
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(string(bodyBytes)))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(string(bodyBytes)))
|
||||||
if err != nil {
|
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("Authorization", "Bearer "+accessToken)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
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)
|
resp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loadCodeAssist 请求失败: %w", err)
|
return nil, nil, fmt.Errorf("loadCodeAssist 请求失败: %w", err)
|
||||||
}
|
}
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBodyBytes, err := io.ReadAll(resp.Body)
|
respBodyBytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
return nil, nil, fmt.Errorf("读取响应失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
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
|
var loadResp LoadCodeAssistResponse
|
||||||
if err := json.Unmarshal(respBodyBytes, &loadResp); err != nil {
|
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 模型配额信息
|
// ModelQuotaInfo 模型配额信息
|
||||||
@@ -278,18 +282,18 @@ type FetchAvailableModelsResponse struct {
|
|||||||
Models map[string]ModelInfo `json:"models"`
|
Models map[string]ModelInfo `json:"models"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchAvailableModels 获取可用模型和配额信息
|
// FetchAvailableModels 获取可用模型和配额信息,返回解析后的结构体和原始 JSON
|
||||||
func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectID string) (*FetchAvailableModelsResponse, error) {
|
func (c *Client) FetchAvailableModels(ctx context.Context, accessToken, projectID string) (*FetchAvailableModelsResponse, map[string]any, error) {
|
||||||
reqBody := FetchAvailableModelsRequest{Project: projectID}
|
reqBody := FetchAvailableModelsRequest{Project: projectID}
|
||||||
bodyBytes, err := json.Marshal(reqBody)
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("序列化请求失败: %w", err)
|
return nil, nil, fmt.Errorf("序列化请求失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiURL := BaseURL + "/v1internal:fetchAvailableModels"
|
apiURL := BaseURL + "/v1internal:fetchAvailableModels"
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, strings.NewReader(string(bodyBytes)))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, strings.NewReader(string(bodyBytes)))
|
||||||
if err != nil {
|
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("Authorization", "Bearer "+accessToken)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
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)
|
resp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fetchAvailableModels 请求失败: %w", err)
|
return nil, nil, fmt.Errorf("fetchAvailableModels 请求失败: %w", err)
|
||||||
}
|
}
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
respBodyBytes, err := io.ReadAll(resp.Body)
|
respBodyBytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
return nil, nil, fmt.Errorf("读取响应失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
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
|
var modelsResp FetchAvailableModelsResponse
|
||||||
if err := json.Unmarshal(respBodyBytes, &modelsResp); err != nil {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func (s *AntigravityOAuthService) ExchangeCode(ctx context.Context, input *Antig
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取 project_id(部分账户类型可能没有)
|
// 获取 project_id(部分账户类型可能没有)
|
||||||
loadResp, err := client.LoadCodeAssist(ctx, tokenResp.AccessToken)
|
loadResp, _, err := client.LoadCodeAssist(ctx, tokenResp.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[AntigravityOAuth] 警告: 获取 project_id 失败: %v\n", err)
|
fmt.Printf("[AntigravityOAuth] 警告: 获取 project_id 失败: %v\n", err)
|
||||||
} else if loadResp != nil && loadResp.CloudAICompanionProject != "" {
|
} else if loadResp != nil && loadResp.CloudAICompanionProject != "" {
|
||||||
|
|||||||
@@ -145,10 +145,16 @@ func (r *AntigravityQuotaRefresher) refreshAccountQuota(ctx context.Context, acc
|
|||||||
|
|
||||||
client := antigravity.NewClient(proxyURL)
|
client := antigravity.NewClient(proxyURL)
|
||||||
|
|
||||||
// 获取账户类型(tier)和 project_id
|
if account.Extra == nil {
|
||||||
loadResp, _ := client.LoadCodeAssist(ctx, accessToken)
|
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 {
|
if loadResp != nil {
|
||||||
r.updateAccountTier(account, loadResp)
|
|
||||||
// 尝试从 API 获取 project_id
|
// 尝试从 API 获取 project_id
|
||||||
if projectID == "" && loadResp.CloudAICompanionProject != "" {
|
if projectID == "" && loadResp.CloudAICompanionProject != "" {
|
||||||
projectID = loadResp.CloudAICompanionProject
|
projectID = loadResp.CloudAICompanionProject
|
||||||
@@ -164,14 +170,21 @@ func (r *AntigravityQuotaRefresher) refreshAccountQuota(ctx context.Context, acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 调用 API 获取配额
|
// 调用 API 获取配额
|
||||||
modelsResp, err := client.FetchAvailableModels(ctx, accessToken, projectID)
|
modelsResp, modelsRaw, err := client.FetchAvailableModels(ctx, accessToken, projectID)
|
||||||
if err != nil {
|
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)
|
r.updateAccountQuota(account, modelsResp)
|
||||||
|
|
||||||
|
account.Extra["last_refresh"] = time.Now().Format(time.RFC3339)
|
||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
return r.accountRepo.Update(ctx, account)
|
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)
|
return time.Now().Add(5 * time.Minute).After(*expiresAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateAccountTier 更新账户类型信息
|
// updateAccountQuota 更新账户的配额信息(前端使用的格式)
|
||||||
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 更新账户的配额信息
|
|
||||||
func (r *AntigravityQuotaRefresher) updateAccountQuota(account *Account, modelsResp *antigravity.FetchAvailableModelsResponse) {
|
func (r *AntigravityQuotaRefresher) updateAccountQuota(account *Account, modelsResp *antigravity.FetchAvailableModelsResponse) {
|
||||||
if account.Extra == nil {
|
|
||||||
account.Extra = make(map[string]any)
|
|
||||||
}
|
|
||||||
|
|
||||||
quota := make(map[string]any)
|
quota := make(map[string]any)
|
||||||
|
|
||||||
for modelName, modelInfo := range modelsResp.Models {
|
for modelName, modelInfo := range modelsResp.Models {
|
||||||
@@ -233,5 +219,4 @@ func (r *AntigravityQuotaRefresher) updateAccountQuota(account *Account, modelsR
|
|||||||
}
|
}
|
||||||
|
|
||||||
account.Extra["quota"] = quota
|
account.Extra["quota"] = quota
|
||||||
account.Extra["last_quota_check"] = time.Now().Format(time.RFC3339)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,11 +403,26 @@ const antigravityClaude45Usage = computed(() =>
|
|||||||
getAntigravityUsage(['claude-sonnet-4-5', 'claude-opus-4-5-thinking'])
|
getAntigravityUsage(['claude-sonnet-4-5', 'claude-opus-4-5-thinking'])
|
||||||
)
|
)
|
||||||
|
|
||||||
// Antigravity 账户类型
|
// Antigravity 账户类型(从 load_code_assist 响应中提取)
|
||||||
const antigravityTier = computed(() => {
|
const antigravityTier = computed(() => {
|
||||||
const extra = props.account.extra as Record<string, unknown> | undefined
|
const extra = props.account.extra as Record<string, unknown> | undefined
|
||||||
if (!extra || typeof extra.tier !== 'string') return null
|
if (!extra) return null
|
||||||
return extra.tier as string
|
|
||||||
|
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
|
||||||
})
|
})
|
||||||
|
|
||||||
// 账户类型显示标签
|
// 账户类型显示标签
|
||||||
|
|||||||
Reference in New Issue
Block a user