LiteLLM 用量接入实战:CodexBar 虚拟密钥方案的数据源、配置与安全边界
2026/9/13 5:19:17 网站建设 项目流程

LiteLLM 用量接入实战:CodexBar 虚拟密钥方案的数据源、配置与安全边界

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

导读

本文以 CodexBar 仓库中的 LiteLLM 接入文档为主线,系统讲解如何为 LiteLLM 代理网关配置用量统计:从虚拟密钥(virtual key)加代理 Base URL 的认证模型,到/key/info/user/info/team/info三类管理端点的数据读取链路,再到 HTTPS 校验、密钥存储等安全设计。读完本文,你将掌握在 CodexBar 中为 LiteLLM 配置 API Key 与 Base URL 的完整步骤、理解个人预算与团队预算如何映射为菜单栏指标,并能依据源码与测试定位常见配置与权限问题。

LiteLLM 在 CodexBar 中的定位

LiteLLM 是一款流行的 LLM 代理网关,通过虚拟密钥(virtual key)对外统一暴露多个上游模型,同时集中管理每个密钥对应的预算与消费。CodexBar 将 LiteLLM 作为一个可选的用量统计 Provider,其核心思路是:不依赖主密钥(master key),而是让虚拟密钥通过 LiteLLM 自带的管理端点读取自身的身份、花费与预算数据。这一点在文档中被明确表述为:

LiteLLM uses a virtual key plus the proxy base URL. The key reads its own identity and budget data through LiteLLM's authenticated information endpoints.

从源码角度看,该 Provider 由三部分组成(Sources/CodexBarCore/Providers/LiteLLM/):

  • LiteLLMProviderDescriptor.swift:Provider 的注册描述、凭据适配、取数策略与菜单栏展示规则;
  • LiteLLMSettingsReader.swift:读取并清洗LITELLM_API_KEYLITELLM_BASE_URL两个配置源;
  • LiteLLMUsageFetcher.swift:实现三类管理端点的请求、解析与快照转换,是数据链路的执行核心。

此外,Sources/CodexBar/Providers/LiteLLM/LiteLLMProviderImplementation.swift 负责在应用侧呈现设置表单字段并监听配置变化。

配置方式:图形界面与配置文件双通道

CodexBar 支持两种等价配置途径。

方式一:图形界面

Settings -> Providers -> LiteLLM中填写两个字段(对应源码中的settingsFields实现):

字段说明占位示例
API keyLiteLLM 虚拟密钥,用于读取该密钥自身的花费与预算sk-…
Base URLLiteLLM 代理 Base URL;允许带/v1后缀,管理端点会自动剥离https://litellm.example.com

方式二:~/.codexbar/config.json

{ "id": "litellm", "enabled": true, "apiKey": "<LITELLM_API_KEY>", "enterpriseHost": "https://litellm.example.com" }

方式三:环境变量

export LITELLM_API_KEY=sk-... export LITELLM_BASE_URL=https://litellm.example.com

两个环境变量的键名在源码中硬编码于 LiteLLMSettingsReader.swift:

public static let apiKeyEnvironmentKey = "LITELLM_API_KEY" public static let baseURLEnvironmentKey = "LITELLM_BASE_URL"

值得注意的细节:环境变量的读取会先做空白与引号清理——源码中的cleaned(_:)trimmingCharacters去掉首尾空白,并且如果值被成对的双引号或单引号包裹,也会一并剥离(对应测试settings reader trims quoted environment values,见 Tests/CodexBarTests/LiteLLMUsageFetcherTests.swift)。因此LITELLM_API_KEY='sk-test'这类带引号的写法也能被正确解析。

另外,CodexBar 还支持在 token-account 存储中保存多把 LiteLLM API Key(描述器中的TokenAccountSupport注明 "Store multiple LiteLLM API keys."),通过环境注入方式把选定密钥传给取数流程。

Base URL 的/v1处理与校验规则

/v1后缀自动剥离

LiteLLM 的代理地址通常形如https://host/v1,而管理端点(/key/info等)并不在/v1前缀之下。CodexBar 在构造管理端点 URL 时会对 Base URL 做归一化:managementBaseURL(_:)会检查路径的最后一段是否为v1,若是则移除该段后再拼接管理路径(LiteLLMUsageFetcher.swift)。

该行为有测试直接验证(management urls accept root or v1 base urls):

  • https://litellm.example.comhttps://litellm.example.com/key/info
  • https://litellm.example.com/v1https://litellm.example.com/key/info
  • https://gateway.example.com/litellm/v1/https://gateway.example.com/litellm/user/info?user_id=user-123(嵌套路径同样支持,只剥离最后一段v1

协议与凭证校验

文档明确:Base URL 必须满足以下条件,否则被视为非法并被拒绝取数:

  • 必须使用 HTTPS,除非该地址指向回环地址(loopback)、私网地址(RFC 1918)、链路本地地址(link-local)或 IPv6 唯一本地地址(unique-local),或者是一个.localmDNS 主机名;
  • 不得内嵌凭证(如https://user:pass@host),因为 API Key 会以 Bearer token 形式发送到该地址;
  • 纯 HTTP 仅对自建代理可用,且限回环、RFC 1918、链路本地、IPv6 唯一本地网络。

这些规则由ProviderEndpointOverrideValidator().validatedURLAllowingPrivateNetworkHTTP(raw)统一实施(LiteLLMSettingsReader.swift),与其他 Provider 的端点覆盖校验共用同一套逻辑。

值得一提的设计是:当 Base URL 配置了但未通过校验时,CodexBar 会明确抛出LiteLLMUsageError.invalidEndpointOverride(LITELLM_BASE_URL),并提示使用 HTTPS 或仅对回环/私网地址使用 HTTP、且不得内嵌凭证(见 LiteLLMUsageError)。为了做到这一点,hasBaseURLOverride(environment:)专门区分"从未配置"与"已配置但被拒绝"两种状态,取数策略据此决定抛invalidEndpointOverride还是missingBaseURL(LiteLLMProviderDescriptor.swift),避免 Provider 静默不可用让用户无从排查。

此外文档还提到一条约束:原生取数器(native fetcher)仍是权威路径;配置的插件源(plugin origins)覆盖 HTTPS 与回环 HTTP,但在没有更宽泛的主机网络策略时,并不覆盖既有的私网与.localHTTP 契约。即插件化替代方案目前不能完全替代原生取数器对私网 HTTP 场景的支持。

数据源与取数流程

三阶段请求链路

文档给出的调用序列,与LiteLLMUsageFetcher.fetchUsage的实现完全一致(LiteLLMUsageFetcher.swift):

  1. GET /key/info:读取当前虚拟密钥自身的user_idteam_id。注意源码注释明确指出:"Virtual keys may read their own metadata; omit?key=to avoid requiring or exposing a master key."——即请求不带?key=参数,从而避免暴露主密钥。
  2. GET /user/info?user_id=<user_id>:当密钥绑定了用户时,读取个人花费、预算与团队列表。
  3. GET /team/info?team_id=<team_id>:当密钥是"仅团队"密钥(无user_id只有team_id)时,读取团队花费与预算。

所有请求均为GET,请求头固定为:

Authorization: Bearer <apiKey> Accept: application/json

源码中request(url:apiKey:)的实现(LiteLLMUsageFetcher.swift)还确认:CodexBar 从不请求也不存储 LiteLLM master key,仅使用虚拟密钥本身的 Bearer 认证。发送前还会对 API Key 做空白清理(fetchUsage中先trimmingCharacters再校验非空)。

响应字段映射

/key/info的响应解析为LiteLLMKeyInfoSnapshot,关注字段:

JSON 字段快照属性说明
info.key_namekeyName密钥名称(脱敏展示用)
info.spendspendUSD该密钥累计花费(USD)
info.expiresexpiresAt密钥过期时间
info.user_iduserID绑定的用户 ID
info.team_idteamID绑定的团队 ID

/user/info的响应解析关注user_info下的user_emailuser_aliasmax_budgetspendbudget_reset_at以及metadata.preferred_username,账户邮箱会按"邮箱 > 别名 > preferred_username"的优先级取第一个非空值(firstNonEmpty)。团队信息则从teams数组中按team_id精确匹配/key/info中声明的团队(preferredTeam)。

/team/info的响应解析关注team_info下的team_aliasteam_idmax_budgetspendbudget_reset_atbudget_duration

身份一致性校验

CodexBar 会把/key/info返回的user_id/team_id与后续/user/info/team/info响应中的 ID 做交叉比对,不一致时直接抛parseFailed("user_id did not match /key/info")parseFailed("team_id did not match /key/info")(LiteLLMUsageFetcher.swift)。这一设计保证展示的预算确实属于当前密钥,防止越权或串号数据被误展示。

用量窗口的映射逻辑

个人密钥:个人窗口为主、团队窗口为辅

对于绑定了用户的密钥:

  • 主窗口(primary):个人花费与个人预算(user_info.max_budget);
  • 次窗口(secondary):与/key/info团队 ID精确匹配的团队预算;
  • 菜单栏自动指标:由于团队预算是该密钥实际被执行的约束,自动模式(.automatic)下菜单栏优先展示团队预算窗口。

这一逻辑在描述器的menuBarWindowResolver中实现:自动模式下依次选择exhausted(primary, secondary) ?? secondary ?? primary(LiteLLMProviderDescriptor.swift),即优先展示已耗尽的窗口,否则优先团队窗口。

仅团队密钥:团队预算为唯一窗口

/key/info没有user_id只有team_id时,CodexBar 直接走/team/info分支,个人窗口为空,团队预算成为唯一用量窗口(providerCostSnapshotperiod显示为 "Team budget")。

无预算配置:保留花费可见性

当 LiteLLM 没有为该用户或团队配置预算(max_budget为空或为 0)时,CodexBar 仍以"API 花费行"(API-spend row)展示实际花费,而不是隐藏整个 Provider。这在providerCostSnapshot()中体现为limit: 0period显示为 "Personal spend" 或 "Team spend"(LiteLLMUsageFetcher.swift)。对应测试preserves personal spend when no budget is configured验证了used == 12.5, limit == 0的行为。

快照中的其他信息

LiteLLMUsageSnapshot.toUsageSnapshot()还会填充:

  • subscriptionExpiresAt← 密钥过期时间(info.expires);
  • identity.accountEmail← 解析出的邮箱/别名;
  • identity.accountOrganization← 团队别名(team_alias);
  • identity.loginMethod"api"

菜单卡片中,ProviderCostPresentation依据limit <= 0选择apiSpend样式,否则为hidden(即预算场景下花费并入主/次窗口展示,不重复显示花费行)。

权限要求与错误排查

虚拟密钥所需的最小权限

要让 CodexBar 正确读取用量,虚拟密钥必须被允许访问:

  • 自身的/key/info数据;
  • 对应user_id/user/info数据(用户绑定的密钥);
  • 或对应team_id/team/info数据(仅团队密钥)。

任何一端返回 401/403 都会导致取数失败。测试fetch surfaces rejected virtual key验证了 401 响应会抛出LiteLLMUsageError.apiError("HTTP 401: ..."),且只发起一次请求(Tests/CodexBarTests/LiteLLMUsageFetcherTests.swift)。

常见错误与对应提示

场景错误类型提示文案
未配置 API KeymissingCredentialsMissing LiteLLM API key. Set apiKey in~/.codexbar/config.jsonorLITELLM_API_KEY.
未配置 Base URLmissingBaseURLMissing LiteLLM base URL. Set enterpriseHost in~/.codexbar/config.jsonorLITELLM_BASE_URL.
Base URL 非法(HTTP 公网 / 内嵌凭证)invalidEndpointOverride提示改用 HTTPS,或仅对回环/私网地址与.local主机使用 HTTP,且不得内嵌凭证
/key/infouser_idteam_idmissingUserIDLiteLLM key info did not include a user_id or team_id.
HTTP 非 2xxapiErrorLiteLLM API error: HTTP <status>: <响应摘要前 500 字节>

安全边界

文档的 Security 一节虽然简短,但结合源码可以确认以下事实:

  1. 密钥按秘密对待:LiteLLM 密钥只存于 Provider 配置或 token-account 存储(后者支持多密钥管理),不落盘到其他位置;
  2. 密钥只发送给配置的 Base URL:所有请求统一走Authorization: Bearer头,且 URL 校验禁止内嵌凭证,避免密钥被当作 URL 的一部分泄露到日志或 Referer;
  3. 不使用主密钥/key/info请求有意省略?key=参数,既满足虚拟密钥读取自身信息的需要,又从设计上杜绝了主密钥的获取与存储;
  4. 私网 HTTP 白名单:自建代理在回环、RFC 1918、链路本地与 IPv6 唯一本地网络可继续使用明文 HTTP,兼顾本地部署的便利性与公网传输的保密性。

小结

CodexBar 的 LiteLLM 接入以"虚拟密钥自读信息"为设计主线:一份 Base URL 加一把虚拟密钥,即可通过三个管理端点获得个人/团队的花费与预算,并自动映射为菜单栏主次窗口与预算/花费两种展示形态。配置上支持图形界面、~/.codexbar/config.json与环境变量三种途径;安全上通过 URL 校验、禁止内嵌凭证、不使用主密钥以及 ID 交叉比对,把密钥暴露面控制在最小。若需继续深入,建议阅读 LiteLLMUsageFetcher.swift 的完整解析逻辑与 Tests/CodexBarTests/LiteLLMUsageFetcherTests.swift 的六组测试用例,它们覆盖了个人/团队密钥、无预算场景、/v1归一化、引号清理、401 拒绝等全部关键路径。

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

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

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

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

立即咨询