Files
cpa-plugin/docs/modules/billing.md
T
chuan 6a461bf1f3 feat: 添加持久化用量计费与管理面板
- 使用 SQLite 保存关联后的请求生命周期和用量记录
- 支持长上下文阶梯价格和可配置的 Fast 计费倍率
- 添加管理接口和可配置列的用量面板
- 保留失败、取消、重试和 compact 请求,便于计费核对
2026-08-14 21:08:02 +08:00

30 KiB
Raw Blame History

计费模块

1. 模块目标

计费模块服务于一个核心场景:管理员把 CPA 的模型能力分享给其他人,为每位用户分发独立 API Key,并以金额额度控制其使用。

完整闭环:

管理员创建或同步 API Key
  → 设置金额套餐与周期
  → 请求进入时检查剩余金额
  → CPA 执行模型请求
  → 按真实 Usage、价格和倍率计算金额
  → 原子记账并更新余额
  → 额度耗尽后拒绝新请求

本模块包含:

  • API Key 身份识别和脱敏标识;
  • 金额套餐、周期、额度和余额;
  • 请求准入、并发状态和终态清理;
  • Usage 归一化、价格解析和金额计算;
  • Fast/priority、长上下文等计价规则;
  • 不可重复结算的金额账本;
  • 管理端的套餐、绑定、充值、重置、停用和账单查询;
  • 向统计模块输出标准计费事件。

本模块不负责趋势图、排行榜、热力图、RPM/TPM、延迟分析等统计产品能力。

2. 不可违背的金额原则

2.1 唯一计费单位

系统对共享用户提供的计费单位有且只有金额

用户获得、看到和理解的内容只能是:

  • 套餐金额;
  • 已消费金额;
  • 剩余金额;
  • 本次请求金额;
  • 周期内金额明细。

用户侧不得出现以下额度概念:

  • Credits、点数或积分;
  • Token 配额或 Token 余额;
  • 按请求次数折算的额度;
  • 隐藏的第二套扣费单位。

Token、缓存、模型单价、倍率和价格版本是后台计算与审计信息。管理员可以查看,统计模块也可以分析,但它们不能成为用户的余额单位。

2.2 结算币种

一个部署实例只使用一种结算币种。第一版固定使用 USD;未来如支持其他币种,必须通过明确的汇率版本换算为该实例的唯一结算币种,不能让同一账户同时持有多种余额。

所有金额必须使用定点表示:

type Money struct {
    Currency string // v1: USD
    Micros   int64  // 1 USD = 1,000,000 micros
}

持久化、比较、扣减和额度判断全部使用整数 Microsfloat64 只允许出现在外部价格解析边界,进入领域层前必须按统一规则转换并检查溢出,不能直接用于余额和账本。

2.3 用户视图与管理员视图

用户视图只返回金额和必要状态:

{
  "limit": "10.00",
  "spent": "3.27",
  "remaining": "6.73",
  "currency": "USD",
  "cycle_ends_at": "2026-09-01T00:00:00Z",
  "enabled": true
}

管理员视图可以额外包含计价依据:模型、输入/缓存/输出 Token、基础价格、Fast 倍率、长上下文价格、最终金额和价格版本。

3. 模块边界

3.1 计费模块拥有

  • 套餐和周期定义;
  • API Key 与套餐绑定;
  • 周期金额额度、已用金额和余额;
  • 请求准入结果;
  • 请求与最终 Usage 的关联;
  • 每笔金额账本;
  • 消费 pricing.md 返回的不可变 ResolvedPricePolicy,并把实际使用的价格政策快照固化到每笔账单;
  • 充值、退款、人工调整和重置记录;
  • 幂等结算键。

价格来源、候选、模型 alias、长上下文/tier 政策、版本发布和回滚属于 价格目录与计价政策模块。Billing 不能在结算时自行拉取价格或修改 active version。

3.2 统计模块拥有

  • 原始事件的长期保存策略;
  • 小时、天和实时聚合;
  • 趋势、构成、排行、热力图;
  • RPM、TPM、成功率、TTFT、延迟;
  • 按 API Key、模型、Provider、Auth 等维度查询;
  • 导出和只读分析页面。

统计模块只能消费计费模块已经确定的 charged_amount,不能在查询时重新计算历史账单。价格修改不得悄悄改变已经结算的金额。

3.3 CLIProxyAPI 能力

预期使用以下最小能力组合,最终以目标 CLIProxyAPI 源码为准:

能力 用途
frontend_auth_provider 校验插件签发的下游 Key,并向 CPA 返回不含明文 Key 的稳定身份
request_interceptor 识别稳定下游身份、检查金额额度并拒绝请求
request_lifecycle_plugin 接收成功、失败、拒绝、取消等终态,释放并发并触发幂等结算
response_before_translator、response interceptors 取得带 RequestID 的上游 Usage,并关联非流式或流式响应
usage_plugin 补充 CPA 标准 Usage、上游身份、性能和失败信息;当前不能单独承担按请求结算
scheduler 按 Key 的账户绑定选择上游 AuthID
management_api 暴露计费管理 API 和管理页面

主路径使用 CPA 的 frontend_auth_provider,由插件自己签发、校验、禁用和轮换下游 Key。插件只保存高熵 Key 的安全校验值,认证成功后把稳定 credential_id 作为 Principal 返回给 CPACPA 再基于该 Principal 产生不可逆 caller_scope。这样 Key 生命周期、金额账户和路由绑定可以在同一业务事务内维护,不依赖修改 CPA 配置文件。

生产模式必须声明 frontend_auth_provider_exclusive: true,并在真实 CPA 中验证本插件是唯一生效的认证路径,避免 CPA 原生 Key 绕过插件准入。CPA 原生 api-keys 只保留为显式兼容/迁移模式;该模式下任何不能映射到本地 Credential 的调用都必须按配置拒绝或标为受控例外,不能默认无限使用。

4. 核心领域模型

4.1 API Key 标识

插件不持久化明文 Key。签发时从明文生成或提取:

  • public key ID 与 HMAC 校验摘要:用于认证 lookup;
  • 脱敏 key_preview:用于管理员辨认;
  • 可选用户标签。

一个逻辑 Credential 可以包含多个 CredentialSecretVersion。secret 轮换新增 version、保留同一 credential_id 和 BillingAccountoverlap 后撤销旧 version;只有独立新 Key 才创建新 Credential。认证成功后向 CPA 返回稳定 credential_id PrincipalCPA 的 caller_scope 是该 Principal 的不可逆请求期标识。业务表使用 credential_id/billing_account_id 外键,不把 caller scope 当作唯一长期主键。

额度实际挂在 BillingAccount,每个 Credential 必须绑定一个 BillingAccount。第一版创建用户 Key 时默认一并创建独立 BillingAccount,因此产品表现仍是“每个 Key 可分配金额额度”;轮换 Key 时绑定同一 BillingAccount 即可保留余额和历史。未来多个 Key 需要共享额度时只调整绑定,不改变账本模型。

4.2 套餐

BillingPlan
├─ id
├─ name
├─ amount_micros
├─ currency
├─ period_kind        fixed / never
├─ period_seconds     daily/weekly 等 UI 预设都换算为固定秒数
├─ enabled
└─ timestamps

周期在该 BillingAccount 第一次成功准入时开始,沿用 cpa-plugin-key-billing 的简单行为。日历月、统一账单日和时区对齐留给后续明确需求,第一版不暗中引入复杂规则。

4.3 周期账户

BillingAccountCycle
├─ billing_account_id
├─ plan_id
├─ cycle_id
├─ starts_at
├─ ends_at
├─ limit_micros
├─ spent_micros       # 可重建消费投影
├─ balance_micros     # 可重建余额投影
└─ status

请求准入时必须记录其 plan_idcycle_id。延迟结束的请求只能计入准入时所属周期,不能误扣到新周期或新套餐。

4.4 账本

账本是金额事实来源,不以可变聚合字段作为唯一依据:

BillingLedgerEntry
├─ id
├─ idempotency_key
├─ billing_account_id
├─ credential_id      # 请求归因;充值可为空
├─ request_id
├─ execution_id
├─ billing_record_id
├─ cycle_id
├─ kind               charge / late_settlement / credit / refund / adjustment
├─ spend_delta_micros
├─ balance_delta_micros
├─ currency
├─ occurred_at
├─ booked_at
├─ pricing_version
├─ pricing_snapshot
└─ usage_snapshot

每次结算必须按 (request_id, execution_id, settlement_kind, usage_revision) 或等价稳定规则生成 idempotency_key。同一 Usage revision 的重复、迟到或重放不能产生第二次扣费;新的可靠 Usage revision 只能追加差额账本。

账本用两个带符号字段避免把“用户消费”和“账户余额变化”混成一个数字:

kind spend_delta_micros balance_delta_micros
charge / late_settlement 正数 负数
与 Usage 关联的refund 负数 正数
充值credit 0 正数
非 Usage 人工增减 0 按方向正/负
纠正历史费用的 adjustment 按消费方向正/负 与其相反

用户消费趋势汇总 spend_delta_micros;余额从 balance_delta_micros 推导。充值和普通账户调整没有 model/Auth/endpoint 维度,不能塞进请求消费趋势。

5. 计价管线

计价管线必须是单向、可审计的:

原始 Usage
  → Token 语义归一化
  → 确定计费模型
  → 解析版本化基础价格
  → 应用长上下文价格
  → 确定 effective service tier
  → 应用 Fast/priority 倍率
  → 转换为整数金额
  → 写入不可变账本
  → 发布 BillingEvent

5.1 Token 归一化

计价使用四个互不重叠的区段:

普通输入 + 缓存读取 + 缓存写入 + 输出

必须识别供应商字段语义:

  • 缓存 Token 是否已包含在 input_tokens 中;
  • Reasoning Token 是否已包含在 output_tokens 中;
  • total_tokens 与各区段是否一致;
  • 重试和流式响应是否上报了重复 Usage。

信息不完整或矛盾时必须标记 accounting_quality,并采用以下固定策略:

  • complete / normalized:正常结算;
  • partial:只结算语义明确且未与其他字段重叠的已确认部分,状态保持 partial,后续 revision 只追加差额;
  • missing / unclassified / inconsistent:不猜测金额,进入 awaiting_usagereview_required,当前展示金额为 0 但不标记为免费;
  • 准入时找不到可信价格:在触达上游前返回 503 billing_price_unavailable
  • 实际模型/tier 与准入快照不同而在执行后才发现无价格:保存 unpriced 事实、告警并阻止该 credential/model 的后续新请求,管理员发布可追溯价格版本后通过追加结算处理。

不得用“按最高价保守扣费”或“把未知都当零”代替真实事实。

5.2 基础金额公式

基础金额 =
  普通输入 Token × 输入单价
  + 缓存读取 Token × 缓存读取单价
  + 缓存写入 Token × 缓存写入单价
  + 输出 Token × 输出单价

Reasoning Token 用于后台明细,但若上游语义表明其已经包含在输出中,不得再次相加收费。

5.3 Fast / priority 2.5×

Fast/priority 是金额倍率,不是另一种余额单位。OpenAI/Codex 通过 service_tier=priority 表达,Anthropic 通过 speed=fast 表达:

Fast 最终金额 = 基础金额 × 2.5

不能同时对请求 service_tier=priority 和响应 response_service_tier=priority 各乘一次。应先得到唯一的有效速度层级:

effective_service_tier =
  响应明确确认的层级
  否则请求指定的层级
  否则 standard

两种表达先归一化为一个 fast_requested 事实,然后只应用一次倍率。价格配置可以关闭 Fast 加价;关闭后请求仍使用 Fast,但金额不乘倍率。倍率和是否实际应用必须保存在账本价格快照中。

Fast 规则必须是版本化价格政策的一部分,不能依赖管理员每次手工补规则。实现时需要用当前 OpenAI 官方资料再次核对支持模型和倍率。

5.4 长上下文

价格目录包含长上下文阈值时,使用归一化后的总输入 Token 判断是否进入阶梯价;阈值、命中的价格和判断输入必须写入价格快照。

Fast 倍率作用于长上下文基础金额之后:

最终金额 = 长上下文规则计算出的基础金额 × Fast 倍率

5.5 价格来源与优先级

价格不能无条件信任第三方目录。当前已观察到 models.dev 的部分 GPT-5.6 Terra/Luna openai 条目与 OpenAI 官方价格不一致。

推荐优先级:

  1. 管理员明确覆盖并确认的价格;
  2. 项目维护的、带生效日期和来源链接的官方价格表;
  3. models.dev 自动同步的候选价格;
  4. 无法定价,进入显式待处理状态。

自动同步必须是“预览 → 确认 → 发布新价格版本”,不能在后台静默修改生产计费。

每笔账单至少保存:

  • 实际计费模型和匹配方式;
  • 四段 Token 数;
  • 四段实际单价;
  • 长上下文状态;
  • effective service tier
  • 最终倍率;
  • 价格来源和版本;
  • 舍入前结果与最终 amount_micros

5.6 Usage revision 与差额算法

usage_revision 由插件生成,不是 CPA/provider 传入:

  1. 每个 Execution 保存 canonical cumulative vector(四段 Token)、source rank、response ID 与 canonical hash
  2. 同一 vector/hash 的重复 callback 不创建 revision
  3. 只有 SQLite 事务内 CAS 确认 canonical vector 改变后才递增 revision
  4. CPA usage、翻译前响应和翻译后响应是择优/交叉校验来源,绝不能三份相加;
  5. 新应收额使用该 Execution 的原始 admission/price policy 重算,差额为“新 canonical total 应收 - 该 Execution 已入账 usage spend”;
  6. 更小或矛盾 vector 标为 inconsistent,自动差额为 0;退款只能由显式 refund/adjustment 账本产生。

Request 级汇总不能参与单个 Execution 的差额,避免重试费用互相抵消。

6. 请求准入与结算

6.1 准入

请求进入时:

  1. 使用 frontend auth 已确认的 credential_id,并校验 CPA caller_scope 映射;
  2. 查询有效套餐和周期;
  3. 结算已过期周期;
  4. 检查账户启用状态和剩余金额;
  5. 保存 pending request、周期和价格政策版本;
  6. 放行或返回结构化错误。

未绑定套餐的策略必须可配置。开发/迁移时可以显式使用“记录金额但不限制”的 observe-only 模式;生产默认必须是“未绑定即拒绝”,避免新建或漏同步的 Key 意外获得无限额度。

额度耗尽时返回 HTTP 429 和稳定错误码 billing_quota_exhausted。响应只描述金额余额,不泄露内部 Token、价格表或凭证信息。

6.2 并发透支

请求开始时无法知道最终金额。第一版沿用 cpa-plugin-key-billing 的务实策略:

  • 余额大于零即可准入;
  • 请求结束后按真实金额结算;
  • 单次或并发请求可以造成有限负余额;
  • 余额不再为正时拒绝新的请求。

这项行为必须在管理界面明确说明为“软金额额度/请求后结算”,不能承诺绝不超额。MVP 为每个 BillingAccount 设置默认并发上限 1,管理员可显式调高;这只能把最坏超额限制在少量在途请求,仍不是硬额度。严格预授权、输入成本估算和按允许最大输出预占属于后续增强,不能伪装成已经做到严格不透支。

6.3 终态

成功、失败、取消、客户端断开、流式中止和宿主关闭都必须结束运行中的并发占用。request.complete 是请求终态信号,但终态类型本身不决定是否收费;是否扣费只由上游实际产生且能够可靠确认的 Usage 决定。

统一规则:

场景 金额处理 记录处理
本地准入拒绝,未到达上游 不扣费 outcome=rejected,无 Usage、无消费账本
请求在上游执行前取消 不扣费 outcome=canceled,金额为 0
成功完成且有可靠 Usage 按实际 Usage 扣费 正常结算
执行失败但上游报告了 Usage 按实际 Usage 扣费 outcome=failed 与金额同时保留
用户取消/断开,但取消前已经产生可靠 Usage 按实际 Usage 扣费 outcome=canceled 与金额同时保留
流式输出一部分后取消 按上游累计报告的实际 Usage 扣费 不能只按已发送到下游的 chunk 猜测
已触达上游但没有取得可靠 Usage 不猜测、不虚构金额,暂记 0 标记unmeasured 并进入迟到 Usage 观察

取消自动免单会形成明显漏洞:调用方可以在最后一个流式事件前主动断开,从而反复使用已经由上游执行的计算。因此用户侧可以看到“已取消”和本次实际金额,但不能把取消理解为退款。

6.3.1 取消时的处理顺序

  1. 幂等接收 request.complete,保存 outcome=canceled
  2. 立即释放该请求的并发占用或预授权资源;
  3. 封存此时已经取得的 Execution 与 Usage 快照;
  4. 有可靠 Usage 时按同一价格快照正常结算;
  5. 没有 Usage 时写入 settlement_status=awaiting_usageaccounting_quality=unmeasured,用户当前金额显示为 $0
  6. 保留有界的持久化关联窗口,等待可能迟到的 response/usage 事实;
  7. 到期仍无 Usage 时转为 unmeasured_final,保留诊断和风险计数,绝不能伪造 Token。

第一版 late_usage_ttl 默认 24 小时,与持久化 pending/recovery 机制配合;到期只清理内存关联并把状态转为 unmeasured_final,不是“24 小时后永远免费”的边界。只要以后到达的 Usage 仍能用 RequestID/ExecutionID 可靠关联,仍可通过追加账本进行补记。

6.3.2 迟到 Usage 与补记

回调不能假定同步、严格有序或只调用一次。终态后到达的新 Usage 不得直接修改已经提交的 BillingRecord 或 LedgerEntry,而应:

  1. request_id + execution_id + usage_revision 去重;
  2. 对新的累计 Usage 生成新的计费版本;
  3. 计算“新确认应收金额 - 已入账金额”的差额;
  4. 差额为正时追加 late_settlement 账本;
  5. 出现更小或矛盾的 Usage 时不自动退款,标记 inconsistent 等待管理员复核;
  6. 原请求的展示投影汇总原账与补记账,账本历史保持不可变。

部分可确认 Usage 可以先按确认部分结算并标记 partial,迟到的新增部分仍走差额补记。重复回调、相同 revision 或相同幂等键必须产生零次新扣费。

6.3.3 防止取消逃费

系统按 Key/账户统计以下诊断:

  • canceled 请求数量与占比;
  • canceled + unmeasured 数量与连续次数;
  • 迟到补记金额;
  • 取消发生时是否已经选择上游账户、收到首字或输出 chunk。

偶发无 Usage 不惩罚用户;高频、持续的 canceled + unmeasured 可以触发并发收紧、暂时限流或管理员告警。风控动作属于 Core 的准入策略,计费模块只提供事实和计数,不能凭猜测生成金额。

7. 提供给统计模块的数据

计费模块不承担详细分析,只发布足以支撑未来统计的标准事件。建议字段:

type BillingEvent struct {
    EventID              string
    IdempotencyKey       string
    RequestID            string
    ExecutionID          string
    CredentialID         string
    KeyPreview           string
    BillingAccountID     string
    PlanID               string
    CycleID              string
    Kind                 string // charge/late_settlement/refund/credit/adjustment

    Provider             string
    ExecutorType         string
    Model                 string
    ModelAlias            string
    AuthID                string
    AuthIndex             string
    Source                string
    Endpoint              string
    ReasoningEffort       string
    RequestedServiceTier  string
    ResponseServiceTier   string
    EffectiveServiceTier  string

    RequestedAt          time.Time
    CompletedAt          time.Time
    OccurredAt           time.Time
    BookedAt             time.Time
    Latency              time.Duration
    TTFT                 time.Duration
    Failed               bool
    FailureStatusCode    int
    Outcome              string // succeeded/failed/rejected/canceled

    InputTokens          int64
    OutputTokens         int64
    ReasoningTokens      int64
    CacheReadTokens      int64
    CacheCreationTokens  int64
    TotalTokens          int64
    AccountingQuality    string

    Currency             string
    BaseAmountMicros      int64
    AppliedMultiplier     string // 十进制定点文本,例如 "2.5"
    BillingAmountMicros   int64 // 当前 BillingRecord 的完整应收快照
    SpendDeltaMicros      int64 // 本事件对消费趋势的带符号变化
    BalanceDeltaMicros    int64 // 本事件对余额的带符号变化
    PricingVersion        string
    PricingSource         string
    LongContext           bool
    SettlementStatus      string // settled/awaiting_usage/unmeasured_final/corrected
    UsageRevision         int64
    CorrectionOfEventID   string
}

安全要求:统计事件不得包含明文 API Key、Bearer Token、上游凭证、原始请求体或失败响应中的敏感内容。

持久化层以 EventID/IdempotencyKey 去重,并在同一领域事务中把事件转换为带单调 event_seqProjectionEvent。统计模块聚合 SpendDeltaMicros;余额投影聚合 BalanceDeltaMicros。它可以展示 Token 和倍率,但不能根据 Token 重新计算或覆盖已经入账的金额。

8. 如何参考现有项目

8.1 cpa-plugin-key-billing

定位:实时金额额度控制和插件生命周期的首要参考。

优先阅读:

主题 源码位置
插件装配与 RPC 分发 cpa-plugin-key-billing/internal/plugin/app.go
请求准入与 Key 识别 internal/plugin/intercept.gointernal/billing/enforce.go
Request/Usage 关联 internal/plugin/usage_tracker.go
上游 Usage 归一化 internal/plugin/upstream_usage.go
四段计价与长上下文 internal/billing/pricing.go
套餐、周期与绑定 internal/billing/plan.gokeys.goaccount.go
并发安全与持久化 internal/billing/store.gopending.go
管理 API 与 UI internal/plugin/management.goadmin.goui.html
C ABI 入口 cmd/cpa-key-billing/main.go

适合直接吸收的设计:

  • Key 摘要和脱敏,不保存明文;
  • 准入周期快照,避免跨周期误扣;
  • 请求、Usage 和终态关联;
  • 先可靠结算能用 RequestID/response ID 关联的最终 Usage;其覆盖失败尝试的限制不能作为长期完整计费模型;
  • 429 拒绝流程;
  • 原子热重配、幂等关闭和无后台 goroutine 的插件约束;
  • 长上下文阶梯价与不确定 Usage 的保守处理。

不能直接照搬:

  • float64 作为余额和累计金额,必须改为整数定点金额;
  • JSON 状态文件作为长期账本,目标实现应使用 SQLite 和不可变账本;
  • 缺少 Fast/priority 计价;
  • models.dev 自动价格不能直接视为权威;
  • 当前简单累计统计不能成为统计模块基础;
  • 固定 SchemaVersion=2 且忽略宿主 lifecycle schema_version 的实现;
  • 无条件同时声明 response-before 与 stream chunk hook:这是为弥补 Usage 缺少 RequestID/WebSocket 同协议透传的过渡关联方案,会把 JSON/base64/C ABI 成本放到每个流式帧;
  • 仅把 schema 改成 3 就宣称性能问题解决:schema 3 不会消除 response-before 请求体和 HistoryChunks 的重复传输。

8.2 cpa-usage-keeper

定位:持久化、Token 语义修正、价格快照、条件倍率以及未来统计模块的首要参考。

计费模块优先阅读:

主题 源码位置
四段费用计算 cpa-usage-keeper/internal/helper/usage_cost.go
Token 语义修正 internal/service/tokenprocessor/
价格快照与校验 internal/pricing/snapshot.gocatalog.go
条件倍率 internal/pricing/fields.goresolver.go
价格持久化与规则 internal/repository/pricing.gopricing_rules.go
models.dev 同步 internal/service/pricing_metadata_sync.go
Usage 事件实体 internal/entities/usage_event.go

未来统计模块重点参考:

  • internal/repository/usage_*:SQLite 事件、实时/小时/天聚合;
  • internal/service/usage.goOverview、Activity 和 Analysis 服务;
  • internal/api/usage_*:查询接口;
  • web/src/components/usage/:请求、Token、费用、健康和延迟展示;
  • web/src/features/ranking/:排名能力,是否纳入产品需另行决定。

适合吸收的设计:

  • SQLite 和迁移体系;
  • Provider 感知的 Token 归一化;
  • 原子发布的只读价格快照;
  • service_tier 等条件倍率;
  • 价格修改后各查询路径使用一致快照;
  • 完整的统计维度和前端组件体系。

不能直接照搬:

  • 价格规则同时命中多个字段时全部相乘,Fast 请求/响应 tier 必须先合并成唯一有效 tier;
  • 当前自动价格同步忽略长上下文 tiers;
  • models.dev 候选价格必须经过官方表或管理员确认;
  • Keeper 是独立服务,后台 worker、定时器和运行模型不能原样搬入 c-shared 插件;
  • 统计查询时动态重算费用的行为不能改变已经落账的历史金额。

8.3 复用规则

两个参考项目均使用 MIT License。复制或修改核心代码时必须:

  • 保留相应版权与许可证声明;
  • 在仓库 NOTICE/THIRD_PARTY 文档中记录来源文件和上游提交;
  • 优先按领域模块移植并补测试,不做无法追踪来源的大段拼贴;
  • CLIProxyAPI ABI、RPC DTO 和能力名称始终以目标 CLIProxyAPI 源码为准,两个下游项目不能覆盖宿主契约。

当前检查基线:

  • cpa-plugin-key-billing: 25b534ae386f830f537cca9215cff5586e630b3a
  • cpa-usage-keeper: d62cad3f345ae574089a14a4ac75cca023c7ead6

9. 验收标准

计费模块完成必须至少证明:

  • 用户所有额度、余额、扣费和账单均以金额显示;
  • 余额与账本使用整数定点金额,无浮点累计误差;
  • 普通输入、缓存读、缓存写和输出不会重复计价;
  • Reasoning 不会因字段语义误判而重复计价;
  • GPT-5.6 Sol/Terra/Luna 使用经过确认、带版本的价格;
  • Fast/priority 只应用一次 2.5×;
  • 长上下文与 Fast 可以正确组合;
  • 本地拒绝不收费,失败或取消只按可确认的实际上游 Usage 收费;
  • 取消但没有可靠 Usage 时不猜费,并能记录 unmeasured
  • 迟到 Usage 通过幂等追加账本补记,重复 Usage 不会重复扣费;
  • 重试产生的每个可可靠关联上游 Execution 用量都能独立去重和结算;当前 CPA 无 RequestID 的 attempt usage 必须标为限制/unmeasured,不能猜配;
  • 延迟完成不会扣到错误周期;
  • 额度耗尽时稳定拒绝新请求;
  • 明确为软额度,默认 BillingAccount 并发 1,最后一个在途请求可能形成有限负余额;
  • Credential secret 轮换不创建新 BillingAccount/cycle,也不刷新额度;
  • 相同 canonical Usage 重放不增加 revision,多来源不会重复相加;
  • 重启后账本、周期和余额一致;
  • 统计事件足以支持后续统计模块,且不含敏感信息;
  • 实际动态库通过单元测试、竞态测试和 CLIProxyAPI 端到端加载测试。

10. MVP 产品默认值

为避免实现阶段再次产生不同口径,第一版固定:

  1. 生产未绑定账户/套餐/价格一律拒绝;observe-only 只允许显式开发/迁移模式;
  2. 周期从第一次成功准入开始,按 plan 的固定时长推进;不实现自然月/统一账单日;
  3. 允许管理员充值或扣减,但必须追加 credit/adjustment 账本和审计原因;
  4. 用户可以查看自己的逐请求时间、模型、状态和最终金额,也可以看周期汇总;
  5. 负余额按真实负金额展示,并说明软额度语义,不伪装成 0.00
  6. 结算币种只支持 USD。