CodexBar 中 Vertex AI Provider 的实现剖析:gcloud ADC 凭证、Cloud Monitoring 配额查询与 Claude 日志成本识别
2026/9/13 4:26:24 网站建设 项目流程

CodexBar 中 Vertex AI Provider 的实现剖析:gcloud ADC 凭证、Cloud Monitoring 配额查询与 Claude 日志成本识别

【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar

CodexBar 的 Vertex AI Provider 通过 gcloud Application Default Credentials(ADC)完成免登录认证,调用 Cloud Monitoring timeSeries API 计算配额使用率,并借助本地 Claude Code 日志(~/.claude/projects/)中的vrtx标识与@格式模型名识别 Vertex AI 的 token 成本。读完本文,你能完整理解从凭证加载、token 刷新、监控指标过滤到配额序列匹配与成本归类的整条数据链路,并能独立排查「无配额数据」「无成本数据」「认证失败」三类典型问题。

数据源与取数路径

Vertex AI 的用量获取只有一条路径:OAuth via gcloud ADC,即从 gcloud 配置目录读取application_default_credentials.json,再基于 Cloud Monitoring 的 time-series 指标计算配额用量。这一点在 Provider 描述文件中可以直接印证——取数管线只注册了一个策略:

fetchPlan: ProviderFetchPlan( sourceModes: [.auto, .oauth], pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [VertexAIOAuthFetchStrategy()] })),

见 VertexAIProviderDescriptor.swift。由于不存在第二个策略,该 Provider 的usesAccountFallback也为false,认证失败时不会走任何账号回退逻辑,只能依靠策略自身的shouldFallback判定(凭证缺失、401/403 时视为可回退,即直接标记为不可用)。

OAuth 凭证:ADC 文件的定位与解析

认证命令

按 docs/vertexai.md 的说明,前置条件只有两条命令:

# 1) 完成 OAuth 登录,生成 application_default_credentials.json gcloud auth application-default login # 2) 指定默认项目 gcloud config set project PROJECT_ID

CodexBar 内置的登录流程会弹出一个提示窗口,点击 "Open Terminal" 后实际执行的是带完整 scope 的版本(见 VertexAILoginFlow.swift):

gcloud auth application-default login \ --scopes=openid,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/cloud-platform

登录窗口关闭后,StatusItemController会等待 2 秒并触发一次store.refresh(),无需手动刷新。

凭证文件查找顺序

VertexAIOAuthCredentials.swift 中的credentialsFilePath()按以下优先级定位 ADC 文件:

  1. 环境变量GOOGLE_APPLICATION_CREDENTIALS指向的显式路径(可指向服务账号 JSON);
  2. 环境变量CLOUDSDK_CONFIG指定的 gcloud 配置目录下的application_default_credentials.json
  3. 默认位置~/.config/gcloud/application_default_credentials.json

用户凭证解析时要求client_idclient_secretrefresh_token三者齐全,缺失则分别抛出missingClientCredentials/missingTokens错误;access_token允许缺失(随后走刷新流程),token_expiry字段以 ISO8601 格式解析,email则从id_token的 JWT payload 中直接解码提取(避免额外网络请求)。

项目 ID 的解析顺序

loadProjectId()的取值链路是:

  1. 读取 gcloud 的默认配置(~/.config/gcloud/configurations/config_default,或CLOUDSDK_CONFIG下的同名文件),解析 INI 风格的project = PROJECT_ID行——这正是gcloud config set project写入的内容;
  2. 配置文件不存在或无 project 行时,依次回退到环境变量GOOGLE_CLOUD_PROJECTGCLOUD_PROJECTCLOUDSDK_CORE_PROJECT

两者都拿不到项目 ID 时,取数会直接抛出noProject错误,提示执行gcloud config set project PROJECT_ID

服务账号支持

从源码结构看,ADC 文件若包含client_email+private_key(服务账号特征),loadForFetch不会尝试解析刷新令牌,而是通过子进程执行:

gcloud auth application-default print-access-token

由 gcloud 自身完成签名换 token,CodexBar 只负责清洗输出并构造一个 50 分钟有效期的临时凭证。这意味着服务账号路径依赖本机 gcloud CLI 可用。

Token 刷新

VertexAITokenRefresher.swift 实现了标准 OAuth 刷新:

  • 触发条件:needsRefresh,即距token_expiry不足 5 分钟(无过期时间则视为需要刷新);
  • 端点:POST https://oauth2.googleapis.com/tokengrant_type=refresh_token,携带client_id/client_secret/refresh_token
  • 错误映射:invalid_grant→ refresh token 过期,unauthorized_client→ refresh token 被吊销,两者都提示重新执行gcloud auth application-default login
  • 刷新成功后更新access_token、按expires_in(默认 3600 秒)重算过期时间,并尝试从新的id_token刷新账号 email。

需要说明的是,凭证刷新结果只在内存中缓存(save()是空实现),应用不会修改 gcloud 自己的凭证文件。

API 端点:Cloud Monitoring timeSeries

配额数据来自两个监控指标,过滤器在 VertexAIUsageFetcher.swift 中硬编码:

指标过滤条件
用量serviceruntime.googleapis.com/quota/allocation/usageresource.type="consumer_quota" AND resource.label.service="aiplatform.googleapis.com"
限额serviceruntime.googleapis.com/quota/limit同上

请求构造为GET https://monitoring.googleapis.com/v3/projects/{PROJECT_ID}/timeSeries,关键查询参数为:

  • filter:上表两个过滤器之一;
  • interval.startTime/interval.endTime:最近24 小时窗口(usageWindowSeconds = 24 * 60 * 60);
  • aggregation.alignmentPeriod=3600saggregation.perSeriesAligner=ALIGN_MAX:按 1 小时对齐取峰值;
  • view=FULL,并循环处理nextPageToken直到取完所有分页。

响应按 HTTP 状态码分类处理:401 →unauthorized(提示重跑gcloud auth application-default login),403 →forbidden(提示检查 IAM 中 Cloud Monitoring 的访问权限),其余非 200 一律包装响应体为invalidResponse

配额序列的匹配与聚合逻辑

原始 timeSeries 不能直接使用,makeQuotaUsageResponse完成了文档中 "Mapping" 一节描述的全部语义:

1. 构建序列键。每条序列提取为三元组QuotaKey(quotaMetric, limitName, location)

  • quotaMetric取自metric.labels["quota_metric"],回退到resource.labels["quota_id"]
  • limitName取自metric.labels["limit_name"](用量序列通常没有,为空串);
  • location取自resource.labels["location"],缺省为global

2. 按键聚合。同一键的多条序列取点值最大值(ALIGN_MAX之外的二次保护,兼容doubleValueint64Value两种点值类型)。

3. 用量与限额配对。matchingLimit采用两级策略:

  • 精确匹配:用量键与限额键三元组完全一致且限额 > 0;
  • 唯一候选回退:当用量序列没有limit_name时,在同quotaMetric+ 同location的限额中,若恰好只有 1 个候选则采用,存在多个候选(区域限额歧义)则放弃该序列。

4. 汇报最大值。对所有成功配对的序列计算usage / limit * 100,最终requestsUsedPercent为其中的最大值——即文档所述 "Reports the highest usage percent across matched series"。两侧序列均缺失或无一配对时抛noData

这个noData在策略层被刻意降级:VertexAIProviderDescriptor.swift 中捕获noData后将 usage 置空,继续返回快照——因为本地 token 成本不依赖 Cloud Monitoring,即使近 24 小时没有 Vertex 请求,成本面板仍可用。取数成功后UsageSnapshot.identity会携带账号 email 与项目 ID(loginMethod: "gcloud"),用于菜单栏展示身份。

对应的回归测试见 VertexAIUsageFetcherTests.swift,fixtures 覆盖了精确命名匹配、无 limit_name 匹配与区域限额歧义三种场景,例如 exact-named-usage.json 与 ambiguous-regional-limits.json。

Token 成本跟踪:如何从 Claude 日志中识别 Vertex AI 条目

Vertex AI 上的 Claude 使用记录与直接调用 Anthropic API 的记录写入同一批本地文件(~/.claude/projects/下的.jsonl)。区分两者是成本归类的核心,识别逻辑集中在 CostUsageScanner+Claude.swift 的isVertexAIUsageEntry,实际是三层判定(代码中的顺序比文档更细,文档强调的后两层完全一致):

第一层:vrtx消息/请求 ID(代码中标注为 Primary)。Vertex AI 的 message ID 与 request ID 带vrtx前缀,例如msg_vrtx_0154LUXjFVzQGUca3yK2RUeoreq_vrtx_011CWjK86SWeFuXqZKUtgB1H。只要message.id或顶层requestId包含_vrtx_即判为 Vertex AI 条目。这是最可靠的信号,因为不受模型名归一化影响。

第二层:模型名@版本分隔符。文档标注的 primary 判据:

  • Vertex AI:claude-opus-4-5@20251101
  • Anthropic API:claude-opus-4-5-20251101

实现上modelNameLooksVertex要求模型名以claude-开头且包含@。注意文档的告警依然成立:如果 Claude Code 在写日志时把模型名归一化为-格式且没有vrtxID 或元数据兜底,条目将无法与直接 API 用量区分。

第三层:元数据字段(fallback)。对整条记录做递归遍历,命中即判定:

  • 键名或值包含vertex(大小写不敏感的字节级扫描,避免 Unicode 组合字符干扰),顶层键名同时匹配gcp
  • 命中一组预定义「提供者类」键(providerplatformbackendapi_providersourcevendorclient等)且其值为vertex标记——即文档中metadata.provider: "vertexai"这类场景。

扫描器还提供vertexAIOnly/excludeVertexAI两种过滤模式,供成本口径选择「仅统计 Vertex AI」或「排除 Vertex AI」。分类器行为有专门测试:CostUsageClaudeVertexClassifierTests.swift。

看到 Vertex AI 成本的前置条件

按文档要求,需要在 CodexBar 中同时满足:

  1. Settings → Providers 中启用Vertex AI(该 Provider 默认关闭,defaultEnabled: false);
  2. Settings → General 中开启 "Show cost summary";
  3. Claude Code 实际走 Vertex AI(例如cv别名设置ANTHROPIC_MODEL=claude-opus-4-5@20251101),且日志保留@格式或vrtxID。

启用后,菜单卡片会以内联 token 成本仪表盘呈现(描述文件中supportsTokenCost: truesupportsInlineTokenCostDashboard: true);若无成本数据,提示文案为 "No Vertex AI cost data found in Claude logs. Ensure entries include Vertex metadata."。

故障排查

结合文档的三条排查项与源码错误类型,完整对照如下:

症状可能原因处理
无配额数据项目未开启 Cloud Monitoring API,或账号缺少相应 IAM 权限确认所选项目可用 Cloud Monitoring;403 时检查 IAM 权限
无配额数据(且无报错)近 24 小时无 Vertex 请求属正常noData降级,本地 token 成本仍会展示
无成本数据~/.claude/projects/不存在或缺少带 Vertex 标记的.jsonl检查 Claude Code 日志目录,确认模型名含@或存在vrtxID / vertex 元数据
认证问题ADC 文件缺失、refresh token 过期或被吊销重跑gcloud auth application-default login;服务账号场景检查 gcloud CLI 可用性
No Google Cloud project configured未设置默认项目gcloud config set project PROJECT_ID,或设置GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT/CLOUDSDK_CORE_PROJECT

相关实现与测试入口:凭证解析 VertexAIOAuthCredentials.swift(测试 VertexAIOAuthCredentialsTests.swift)、监控取数与匹配 VertexAIUsageFetcher.swift、成本分类 CostUsageScanner+Claude.swift。

【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询