Files
cpa-plugin/docs/modules/data.md
T

430 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据模块
## 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、事实与聚合均明确分离;
- 未知、缺失、零和失败具有不同语义;
- 所有金额可使用定点整数,所有历史收费可追溯到价格快照;
- 所有秘密字段均被排除或明确限定在受保护配置边界;
- 后续模块只能扩展契约版本,不能用自己的私有字段重新定义同一个业务事实。