Files

153 lines
9.0 KiB
Markdown
Raw Permalink 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.
# billing
`billing` 是 CLIProxyAPI 的收敛式计费插件,提供持久化用量与价格展示、下游 Key 管理、模型准入和上游凭证定向路由。一个 Key 代表一个调用用户;管理员可以创建、禁用或永久归档 Key,并按 Key 查看统计。
每个 Key 同时拥有独立的美元额度账户。管理员可以分配本周期额度、设置日/周/月自动重置和并发上限,并查看不可变的扣费、额度调整与重置账目。额度采用请求结束后结算的软限制:余额为正时放行,实际 Usage 到达后扣费,最后一个或多个在途请求可能产生负余额,之后的新请求返回 429。
## 一次请求如何通过 CPA
```mermaid
sequenceDiagram
autonumber
participant Client as 用户 / Codex
participant CPA as CLIProxyAPI
participant Billing as billing
participant DB as SQLite
participant Upstream as 实际模型服务
Client->>CPA: 携带下游 Key 发起模型请求
CPA->>Billing: frontend_auth.authenticate
Billing->>DB: 查询 Key 与状态
DB-->>Billing: 用户访问配置
alt Key 无效或已停用
Billing-->>CPA: 认证失败
CPA-->>Client: 401 Unauthorized
else 认证成功
Billing-->>CPA: 返回用户身份(稳定 Key ID)
CPA->>Billing: request.intercept_before
Billing->>DB: 检查模型权限、余额与并发并占用名额
alt 模型、额度或并发不允许
Billing-->>CPA: 终止请求(403 / 429 / 503
CPA-->>Client: 返回结构化错误
else 请求准入
Billing-->>CPA: 允许继续
CPA->>Billing: scheduler.pick(可用上游候选)
alt 自动路由
Billing-->>CPA: 委托 CPA 内置调度
else 严格路由
Billing->>DB: 读取该用户绑定的上游账号
Billing-->>CPA: 指定唯一 Auth ID
end
CPA->>Billing: request.intercept_after
Billing-->>CPA: 复核价格配置与实际上游
alt 价格缺失或严格路由失效
CPA-->>Client: 503 Service Unavailable
else 复核通过
CPA->>Upstream: 使用选定凭证转发请求
Upstream-->>CPA: 模型响应 / 流式输出
CPA-->>Client: 返回模型结果
CPA->>Billing: request.complete(请求终态)
Billing->>DB: 释放并发名额,保存成功、失败或取消结果
CPA->>Billing: usage.handle(如有最终 Token 用量)
Billing->>Billing: 按模型价格计算成本
Billing->>DB: 保存用量、扣减余额并写入账目
end
end
end
```
CLIProxyAPI 负责 HTTP 接入、协议转换、上游凭证和实际请求执行;`billing` 通过插件回调参与请求决策与记录,不直接连接模型服务。`request.complete``usage.handle` 是独立事实回调,实际到达顺序不作为数据正确性的前提。
## 当前兼容目标
- CLIProxyAPI 基线:`v7.2.139` / `0a14eb70ce19fac1d114bcdb4a476d61adc819e2`,由 `.externals/CLIProxyAPI` submodule 锁定,需依次应用 `patch/` 中三项宿主补丁
- 插件版本:`0.1.0`
- Native ABI`1`
- RPC schema:最高 `4`,注册时按宿主版本向下协商;schema v4 提供 Usage 请求身份
- 插件 ID / 动态库文件名:`billing`
- 已声明能力:`frontend_auth_provider`(独占)、`scheduler``request_interceptor``request_lifecycle_plugin``usage_plugin``management_api`
契约来源以本仓库内 `.externals/CLIProxyAPI/sdk/pluginabi/types.go``.externals/CLIProxyAPI/sdk/pluginapi/types.go``.externals/CLIProxyAPI/internal/pluginhost/rpc_schema.go` 为准。
## 环境与构建(WSL/Linux
需要支持 toolchain 自动切换的 Go 1.24+ 和 GCC;项目固定使用已包含安全修复的 Go 1.26.6 构建。Ubuntu/Debian 可先准备 C 工具链:
```bash
sudo apt-get update
sudo apt-get install -y build-essential
./scripts/check-env.sh
./scripts/build.sh
```
从 PowerShell 也可调用 WSL 构建入口:
```powershell
./scripts/build.ps1
```
生成 Linux/amd64 发布包和 SHA-256 校验文件:
```bash
bash ./scripts/package.sh 0.1.0
```
产物位于 `dist/billing_0.1.0_linux_amd64.tar.gz`,只包含动态库、版本元数据、README 和示例配置。
产物为 `bin/billing.so`,适用于运行在 WSL/Linux 的 CLIProxyAPI。SQLite 驱动依赖 CGO,普通开发测试也应在已经安装 GCC 的 WSL/Linux 环境执行:
```bash
CGO_ENABLED=1 go test ./...
```
## 加载到 CLIProxyAPI
1.`bin/billing.so` 放进 Linux CLIProxyAPI 配置的插件目录(默认 `plugins/`)。
2. 合并 `config.example.yaml` 中的 `plugins` 配置。
3. 重启宿主,确认日志中成功加载插件 `billing`
4. 首次启动会创建名称为 `default`、值为 `000000` 的下游 Key,保持现有 Codex 配置可用。
5. 发起一个 Codex 请求,确认管理页面出现该 Key 的用量和实际上游 Auth ID。
## Key 管理与路由
管理页面提供:
- 自动生成或手动输入至少 6 位的 Key,列表脱敏显示,点击脱敏文本可复制完整 Key;
- `active` / `disabled` 状态切换,以及不可恢复但保留历史的归档;
- CPA 自动选择上游,或严格绑定一个 OAuth/API Key 上游凭证;
- 按客户端请求模型配置允许列表;
- 模型允许列表支持精确名称和 `*` 通配符,例如 `deepseek-*`
- 分配美元额度、设置自动重置时间和每用户并发上限;
- 查看当前额度、已用、余额以及最近扣费账目;
- 累计、今日和最近请求统计。
管理页面资源可以直接反向代理。未填写 Management Key 时展示脱敏后的真实只读数据并隐藏全部修改入口;输入有效 Management Key 后才显示完整管理操作。匿名只读接口不会返回完整下游 Key,历史 Usage 的旧 `api_key` 字段也会被清空。只读展示仅需代理 `/v0/resource/plugins/billing/ui`(数据请求复用同一路径的查询参数);若要在外部页面执行管理操作,还需同源代理 `/v0/management/plugins/billing/*`
严格绑定的账号不可用或不支持目标模型时请求直接失败,不回退到其他账号。新 Key 在创建时复制 `default` 当时的路由与模型规则,之后独立维护。
现有和新建 Key 的初始额度都是 `$0`,默认不自动重置、并发上限为 4。管理员分配额度后才能发起模型请求。金额以微美元整数保存;修改额度不会清空本周期已用金额,手动或自动重置不会结转旧余额。允许模型没有价格配置时,请求会在触达上游前以 503 拒绝。
价格页面可以手动从 `models.dev/catalog.json` 更新供应商级参考目录,并把任意参考模型的价格导入任意本地模型。例如本地的 `deepseek-v4-flash` 可以显式采用 `OpenAI / gpt-5.6-sol` 价格。目录和本地有效价格彼此独立;刷新只展示变化,管理员确认后才更新已关联价格,人工保存则解除目录关联。全零目录项不作为免费价格导入,下载失败继续保留上次缓存且不影响请求链路。
`config_yaml` 中包含宿主补充的 `enabled``priority`;插件会解析 `codex_only`、数据库路径、`models_dev_url`、目录缓存路径和首次导入 Key。目录缓存默认与 SQLite 数据库放在同一目录。完整下游 Key 只通过受 CPA Management Key 保护的 Key 管理接口返回,不写入普通日志或错误消息;上游 Token、Cookie 和原始凭证不会由插件读取或保存。
## 请求明细与汇总
请求明细由服务端分页,每页 100 条,默认按请求时间和稳定 ID 倒序展示。管理接口支持时间范围、用户 Key、模型、结果、上游 Auth ID、端点和完整 Request ID 筛选;连续翻页使用与筛选条件绑定的游标,数字页码跳转使用 SQLite 索引定位。用户页的今日汇总、各用户用量和近 7 日 Token 由数据库独立聚合,不受当前明细页影响。
升级已有数据库时,插件会从原始 Usage 和请求终态事实自动建立轻量查询投影。事实表、计费账目和历史统计保持不变;没有 Usage 的取消或失败请求仍然可见。patched CPA 使用 RPC schema v4 向 billing 传递 Usage Request ID 和 Trace ID,可以按 Request ID 精确关联请求终态。旧宿主或旧 schema 没有请求身份时,只有模型和时间足够接近且匹配关系唯一才关联;存在并发歧义时保留为独立记录。
## 工程布局
- `cmd/billing`:仅负责 C ABI、请求字节复制和 C 内存释放。
- `internal/plugin`RPC dispatcher、契约 DTO、原子配置与 Usage 接入。
- `internal/modelcatalog`models.dev 下载、规范化、搜索和最后可用缓存。
- `scripts`:环境检查与可复现构建。
- `.externals/CLIProxyAPI`:锁定官方基线的宿主 submodule,用于构建、契约核对和集成测试;本项目的宿主改动由 `patch/` 维护。