20 KiB
数据模块
1. 定位
数据模块是 cpa-ext 的统一数据语言。它只回答三件事:
- 系统需要从 CLIProxyAPI(CPA)及相关来源关注什么数据;
- 这些数据在插件内部以什么稳定语义流转;
- 计费、统计、Key 管理、配额、审计和展示模块可以得到什么输出。
数据模块不负责采集回调、计算费用、执行扣款、生成统计图或决定界面权限。具体模块负责产生和消费数据,数据模块只定义契约。
本模块以两个已经实际工作的项目为需求来源:
cpa-plugin-key-billing已经拿到并使用的数据,全部视为我们需要关注的数据;cpa-usage-keeper已经拿到并使用的数据,全部视为我们需要关注的数据;- 两者已经对外提供的数据产品,全部纳入我们的输出能力全集;
- 第一版可以只实现其中一部分,但数据抽象不能阻止以后补齐其余能力。
2. 两个参考项目的数据总结
2.1 cpa-plugin-key-billing
它拿到的数据
| 数据域 | 实际取得的内容 | 主要来源 |
|---|---|---|
| 下游调用身份 | caller_scope、Key 脱敏预览、标签、Key 是否仍在 CPA 配置中 |
请求 metadata、管理端同步的 CPA Key 列表 |
| 请求关联 | request_id、入口 endpoint、请求格式、上游格式、流式标记、是否真实生成 |
request interceptor、request lifecycle |
| 模型 | 用户/路由模型、实际上游模型、稳定 billing model | 请求阶段、响应阶段 |
| 上游凭证 | auth_index、provider、auth type、OAuth 账户或脱敏 provider API Key |
CPA usage callback |
| 请求终态 | 正常、失败、取消、拒绝 | request lifecycle |
| 原始用量 | OpenAI/Codex、Claude、Gemini、Interactions 等响应中的 usage 和 response ID | 翻译前响应、非流式响应、流式 chunk |
| 标准 Token | 非缓存输入、缓存读取、缓存写入、普通输出、推理输出、总量、无法分类数量 | 自身规范化逻辑 |
| 数据质量 | complete、inconsistent、unclassified、未观察到用量 | 自身规范化逻辑 |
| 价格 | 模型匹配规则、每百万 Token 各分项价格、长上下文门槛与价格、价格来源 | 内置 models.dev 目录、管理员覆盖 |
| 套餐与周期 | 套餐 ID/名称、美元额度、日/周/月/自定义周期、周期开始与结束 | 插件配置与持久化状态 |
| 请求准入快照 | 请求进入时绑定的 Key、套餐和周期 | 请求 interceptor |
它没有把 CPA 的 usage_plugin 当作完整用量事实:该回调缺少 RequestID,所以项目主要从带有请求关联信息的响应 hook 中恢复权威 Token,再在终态回调结算;usage_plugin 主要用来补充上游凭证身份。
它存储的数据
- 价格规则、套餐、Key 状态、上游凭证目录;
- Key 的当前周期和生命周期累计;
- 按模型累计;
- 最近 30 天请求日志;
- 每次请求的标准 Token、金额分项、价格来源、长上下文状态和数据质量;
- 运行时未定价、无 Token、无法分类等诊断计数。
它输出的数据
| 输出域 | 已提供的数据 |
|---|---|
| 请求控制 | 放行或拒绝,额度耗尽时返回明确的 HTTP 错误 |
| Key 目录 | Key 标识/预览/标签、套餐、是否无限、是否阻断、周期额度、已用、使用率、周期结束时间 |
| 套餐管理 | 套餐及金额、周期,绑定、解绑和重置 |
| 请求账目 | 时间、Key、请求 ID、endpoint、上游凭证、模型、结果、Token、金额、价格来源和核算质量 |
| 汇总 | Key 数量、阻断数量、生命周期请求/Token/金额、按模型汇总 |
| 价格管理 | 当前模型价格、自定义覆盖、价格刷新结果 |
| 运维诊断 | 持久化状态、配置状态、未定价/缺失/无法分类计数 |
2.2 cpa-usage-keeper
它拿到的数据
| 数据域 | 实际取得的内容 | 主要来源 |
|---|---|---|
| 用量事件 | provider、endpoint、auth type/index、request ID、API Key、模型/别名、请求与响应 service tier、reasoning effort、executor、时间、失败、延迟、TTFT、完整 Token 分项 | CPA Redis usage queue / HTTP usage queue |
| 请求环境 | client IP、X-Forwarded-For、User-Agent、source、API group key | CPA usage payload |
| 下游 Key | 完整 CPA API Key、展示值、别名、删除/同步状态 | CPA Management API |
| 上游 Auth File | auth index、名称、文件、email、provider、label、状态、priority、disabled、note、account/project、订阅起止与 plan type | CPA Management API |
| Provider API Key 配置 | provider 类型、名称、prefix、base URL、lookup key 等归一化元数据 | CPA 各 provider management API |
| 模型目录 | 模型 ID、owner、创建时间 | CPA /v1/models |
| 上游配额 | Codex 主/次窗口、允许状态、使用率、重置时间、额外限制、reset credits;以及其他 provider 的 quota | CPA 代调用各 provider API |
| 上游订阅 | Codex plan、tier、有效期;其他 provider 的订阅/账户资料 | auth metadata、provider API |
| 请求日志 | 按 request ID 获取的请求日志、预览与下载数据 | CPA request-log management API |
| 价格 | 模型价格快照、条件规则、service tier、reasoning、模型、Key、auth、endpoint、executor 等价格维度 | models.dev、管理员配置 |
它存储的数据
- 原始 Redis inbox 及处理/重试状态;
- 标准化的逐次用量事件和归档;
- CPA API Key 与上游身份目录;
- 模型价格设置、同步快照和条件价格规则;
- 小时/日 overview、activity、latency、identity 等预聚合及各自 checkpoint;
- 配额快照和应用设置;
- 排名等派生结果。
它输出的数据
| 输出域 | 已提供的数据 |
|---|---|
| Overview | 请求数、Token、金额、RPM、TPM、日均值、缓存命中率和时间序列 |
| 实时状态 | Token 速度、请求速度、TTFT/Latency P50/P95、响应分布、当前模型/Key/Auth Top、缓存水平 |
| 事件明细 | 可分页筛选的逐请求记录,包含模型、身份、结果、Token、金额、延迟和请求日志入口 |
| Analysis | Token 时序、模型使用、Key/模型/Auth 构成、Key×模型热力图、金额分项、模型效率 |
| Activity | 日/周/月/年活动格、成功失败、成功率和各 Token 分项 |
| 身份目录 | 下游 Key 与上游账号/provider 的元数据、别名、启停状态、订阅和累计使用情况 |
| 配额 | 各上游账号的窗口、使用率、剩余、重置时间、订阅层级和 reset credits |
| 价格 | 当前价格、价格来源、同步状态、条件规则及预览 |
| 排名与导出 | 本地排名、事件导出、请求日志查看/下载 |
3. 我们的数据关注全集
两个项目的数据取并集后,数据模块定义以下九个数据域。这里的“需要”表示契约需要容纳,不表示所有字段都必须在 MVP 首日采集完成。
3.1 请求与执行
必须同时表达两个层级:
Request:一次下游用户请求;Execution:一次 CPA after-auth 可观察到的上游逻辑执行段。
一个 Request 可以因为重试、故障切换或路由产生多个 Execution。Execution 不保证等于一个物理 HTTP dispatch:当前 CPA 在 auth refresh 后的内部重发不会再次触发 after-auth,应以 subattempt_count/observability=partial 表达,而不是伪造额外 Execution。未来宿主提供 dispatch lifecycle 时再增加 DispatchAttempt。
关注字段:
- request/event/trace/execution ID;
- endpoint、source、source format、upstream format;
- requested model、routed model、upstream model、model alias;
- stream、generate、reasoning effort;
- requested、reported、effective service tier;
- requested、started、first-token、completed 时间;
- outcome、HTTP status、标准错误类别;
- settlement status(与请求 outcome 分开,允许 canceled/failed 请求在迟到 Usage 后结算);
- latency、TTFT;
- 重试序号及最终尝试标记。
3.2 下游身份
关注字段:
- account/member ID;
- credential ID;
- credential secret version/public ID/HMAC key ID/status;
- 自管 Key 的 public key ID、HMAC digest、digest secret version;
- CPA 根据 Principal 派生的稳定 caller scope(仅用于请求期关联,不作为业务主键);
- 兼容模式下 CPA 原生 API Key 的稳定 scope/hash;
- 脱敏预览、别名、标签;
- 启用、删除、同步状态;
- 套餐/计费账户绑定。
自管 Key 的完整值只在创建/轮换成功时展示一次,持久层只保存摘要;CPA 原生 Key 即使在兼容迁移模式由 CPA 配置持有,也不得复制进插件事件、日志或跨模块消息。
3.3 上游身份
关注字段:
- auth ID/index/type;
- provider、executor type;
- 名称、别名、email/account、文件标识;
- provider prefix、base URL、lookup key;
- priority、disabled、status、note;
- account/project ID;
- plan/tier 和订阅有效期。
秘密凭证不属于数据流转契约。
3.4 用量
统一为互不重叠、跨 provider 可比较的字段:
- uncached input;
- cache read;
- cache creation/write;
- non-reasoning output;
- reasoning output;
- total;
- unclassified;
- generate 标记。
同时保留规范化质量:complete、normalized、partial、inconsistent、unclassified、missing,以及修正动作/异常代码。原始 provider 计数可以作为受限审计快照保存,但不能直接成为跨模块语义。
3.5 金额与价格
关注字段:
- settlement currency;
- 金额的定点整数值;
- 未缓存输入、缓存读取、缓存写入、输出的金额分项;
- base amount、final charged amount;
- 每百万 Token 的实际应用价格;
- service tier / Fast multiplier;
- 长上下文门槛及是否命中;
- price version、source、effective time;
- 价格是否可用及不可用原因;
- billing record ID、request/execution ID、usage revision;
- charge、late settlement、credit、refund、adjustment 类型及被修正记录;
- pending/awaiting usage/settled/unmeasured/inconsistent 状态;
- occurred at 与 booked at。
用户侧核心金额只使用最终结算金额;Token、价格与倍率是后台审计数据。
3.6 下游套餐、周期与余额
关注字段:
- plan、额度金额和周期规则;
- BillingAccount 与 plan 的绑定、Credential → BillingAccount 绑定;
- cycle start/end;
- limit、spent、remaining;
- blocked/allowed;
- 管理员调整及其原因;
- 请求准入时使用的周期快照。
3.7 上游配额与订阅
它和我们分配给用户的金额额度是不同概念,必须分开表达。
关注字段:
- provider/account;
- plan/tier、订阅起止;
- primary/secondary/additional windows;
- used、limit、remaining、percentage;
- allowed、limit reached;
- window duration、reset at/after;
- window usage token/cost;
- reset credit 数量、状态和过期时间;
- snapshot time 和数据新鲜度。
3.8 模型、价格与配置目录
关注字段:
- CPA 可用模型及 owner;
- 模型别名和路由映射;
- provider/model 对应关系;
- 价格目录、管理员覆盖和条件规则;
- Key、auth、provider 的当前配置事实;
- 同步时间、来源、版本和删除状态。
3.9 诊断、日志与数据质量
关注字段:
- schema version、producer version;
- source/provenance、observed at、persisted at;
- idempotency key;
- 缺失、修正、冲突、未定价状态;
- inbox/checkpoint/retry/archive 状态;
- 请求日志引用和受控下载信息;
- 原始数据哈希或受限审计引用。
Prompt、模型完整响应、Authorization、Cookie、OAuth token 和 provider API Key 默认不进入通用数据契约。
4. 核心抽象
数据模块不要求所有内容塞进一张表。它定义以下稳定对象,存储模块可以分别落库。
DownstreamAccount
└─ BillingAccount
└─ DownstreamCredential (1..N)
├─ CredentialSecretVersion (1..N)
└─ Request
└─ Execution (1..N)
├─ Usage
├─ Outcome & Performance
└─ UpstreamIdentity
Usage + PricingSnapshot
└─ BillingRecord
└─ LedgerEntry
Usage / BillingRecord / Identity / QuotaSnapshot
└─ Aggregate & View
4.1 RequestRecord
用户视角的一次请求。保存下游身份、入口、请求模型、最终结果、时间以及所有 Execution 的关联,不直接猜测每次上游尝试的细节。
4.2 ExecutionRecord
一次 CPA after-auth 可观察到的逻辑执行段。保存上游凭证、provider、实际模型、tier、格式、终态、性能、重试关系和 observability;不能宣称等于每个物理 HTTP dispatch。
4.3 UsageRecord
一次 Execution 对应的标准 Token 事实,包含质量和来源。Request 层用量由明确规则合并,不覆盖底层执行事实。当前 CPA 对部分 Usage 缺少 RequestID/AttemptID,无法可靠归属时必须保持 unmeasured/未关联事实,不能按时间或上游账户猜配。
4.4 BillingRecord
计费模块对 Usage 应用价格后产生的不可变金额事实。它引用 Usage revision 和价格快照,迟到结算、退款或人工修正通过新增记录表达;金额一旦入账,统计模块不得重新计算并改写历史。
4.5 IdentityRecord
统一承载下游 Key/账户和上游 auth/provider 的非秘密身份信息,但通过明确的 identity kind 区分,不能只靠一个字符串猜类型。
4.6 QuotaSnapshot
某个上游身份在某一时刻的 provider 配额和订阅快照。它是时间点事实,不覆盖历史;也不与下游金额余额混用。
4.7 CatalogSnapshot
模型、价格、路由和配置目录的带版本快照,使历史请求可以解释当时使用的模型和价格。
4.8 AggregateRecord
从不可变事实派生的小时、日、活动、延迟、身份和排名数据。它可以删除并重建,不是账本真相。
4.9 ProjectionEvent
跨 Request、Usage、Billing、Ledger 多表的统一追加变化流。它携带单调 event_seq、唯一 event_id、kind、fact ID/revision、occurred/booked time、可选 supersedes ID 和确定性统计差量。领域事实与对应 ProjectionEvent 必须在同一事务提交;统计 projector 只按这条流推进,不能比较各表互不相关的自增 ID。
5. 统一数据元信息
所有可持久化的事实对象都应携带或可追溯到:
| 字段 | 含义 |
|---|---|
schema_version |
数据契约版本 |
event_id |
全局稳定事件 ID |
idempotency_key |
重复回调或重放时去重 |
source |
CPA callback、response hook、Redis、Management API、provider API 等 |
observed_at |
来源数据被观察到的时间 |
occurred_at |
业务事实真实发生时间 |
persisted_at |
成功落库时间 |
quality |
完整、修正、部分、矛盾、缺失等 |
producer_version |
产生该规范化数据的插件版本 |
字段缺失必须保持“未知”,不能自动等价为零、空字符串或成功。
6. 数据模块输出契约
数据模块定义四类输出,具体模块按需实现和消费。
6.1 事实输出
- Request、Execution、Usage、Billing、Ledger、ProjectionEvent;
- Downstream/Upstream Identity;
- Quota、Subscription、Catalog 快照;
- 数据质量和诊断事件。
6.2 查询输出
- 逐请求和逐执行明细;
- Key、账户、上游身份、模型和价格目录;
- 套餐、周期、余额和账本;
- 配额、订阅和重置时间;
- 请求日志的受控引用。
6.3 聚合输出
完整能力集合至少容纳:
- 请求、Token、金额、RPM、TPM;
- 时间序列和日均;
- 成功率、错误分布;
- TTFT/Latency 分位数和分布;
- 缓存读取率;
- 模型、Key、账户、Auth、Provider 构成及 Top;
- Key×模型热力图;
- 金额分项和模型效率;
- 活动格、排名和历史对比。
6.4 控制输出
- 请求允许/拒绝及稳定错误码;
- Key、套餐、价格和绑定的变更结果;
- 配额刷新/重置结果;
- 同步、归档、重建和诊断结果。
控制输出由业务模块决定,数据模块只定义其可交换的数据形状。
7. 模块边界
| 模块 | 对数据模块的关系 |
|---|---|
| CPA 适配层 | 把 CPA 回调、响应和 Management API 数据翻译成标准事实 |
| 存储模块 | 按契约持久化、查询、去重、归档和迁移 |
| Core/Key 模块 | 维护下游身份、金额账户和绑定关系 |
| 上游账户模块 | 维护 CPA Auth 引用、priority、bindability 和 Quota/Subscription 快照 |
| 价格模块 | 维护不可变 PriceVersion、模型 alias 和计价政策 |
| 计费模块 | 消费 Usage、Pricing、Plan,输出 Billing 和 Ledger |
| 统计模块 | 消费不可变事实,输出可重建的 Aggregate |
| API/UI 模块 | 将同一数据按用户或管理员权限投影,不发明新的业务事实 |
数据模块自身:
- 不依赖 UI;
- 不依赖某一种数据库;
- 不调用 CPA;
- 不计算价格;
- 不聚合统计;
- 不保存或传播秘密;
- 不把参考项目现有字段名直接当成永久领域语义。
8. 如何参考两个项目
8.1 从 cpa-plugin-key-billing 参考
重点查看:
internal/plugin/usage_tracker.go:请求与响应用量关联;internal/plugin/upstream_usage.go:跨 provider Token 语义;internal/billing/pricing.go:标准 Token、数据质量、价格和金额分项;internal/billing/account.go:终态用量到费用记录;internal/billing/state.go:价格、套餐、Key、周期、累计和日志;internal/billing/log.go、keys.go:请求账目和管理输出;internal/billing/credentials.go:上游凭证的安全展示身份。
吸收它对请求关联、Token 不重叠、质量显式化、请求准入周期快照和上游身份脱敏的定义;不要继承其 JSON 大状态、浮点余额、30 天日志限制和把重试只保留为最终 provider usage 的数据损失。
8.2 从 cpa-usage-keeper 参考
重点查看:
internal/entities/usage_event.go:逐请求事件字段;internal/entities/usage_identity.go、cpa_api_key.go:身份目录;internal/service/sync.go:用量规范化、来源和 inbox 处理;internal/service/tokenprocessor/:Token 修正与质量判断;internal/pricing/、internal/entities/model_price_*:价格快照与条件;internal/quota/:上游配额、订阅和 provider 抽象;internal/service/dto/usage.go、analysis.go、usage_activity.go:查询和分析输出;internal/repository/usage*.go:明细、聚合、checkpoint 和归档。
吸收它完整的事件、身份、配额、价格、实时和分析数据面;不要把其 Redis/HTTP 拉取方式、明文 CPA Key 存储方式、GORM 实体或动态历史费用重算直接变成我们的领域契约。
9. 本模块的完成标准
- 两个参考项目实际使用的输入数据均能映射到本模块的数据域;
- 两个参考项目实际提供的输出均能由本模块的事实或派生数据表达;
- Request 与 Execution、下游额度与上游配额、Usage 与 Billing、事实与聚合均明确分离;
- 未知、缺失、零和失败具有不同语义;
- 所有金额可使用定点整数,所有历史收费可追溯到价格快照;
- 所有秘密字段均被排除或明确限定在受保护配置边界;
- 后续模块只能扩展契约版本,不能用自己的私有字段重新定义同一个业务事实。