430 lines
20 KiB
Markdown
430 lines
20 KiB
Markdown
# 数据模块
|
||
|
||
## 1. 定位
|
||
|
||
数据模块是 `cpa-ext` 的统一数据语言。它只回答三件事:
|
||
|
||
1. 系统需要从 CLIProxyAPI(CPA)及相关来源关注什么数据;
|
||
2. 这些数据在插件内部以什么稳定语义流转;
|
||
3. 计费、统计、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. 核心抽象
|
||
|
||
数据模块不要求所有内容塞进一张表。它定义以下稳定对象,存储模块可以分别落库。
|
||
|
||
```text
|
||
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、事实与聚合均明确分离;
|
||
- 未知、缺失、零和失败具有不同语义;
|
||
- 所有金额可使用定点整数,所有历史收费可追溯到价格快照;
|
||
- 所有秘密字段均被排除或明确限定在受保护配置边界;
|
||
- 后续模块只能扩展契约版本,不能用自己的私有字段重新定义同一个业务事实。
|