plugin-usage-stats — 用量统计
包名: @aalis/plugin-usage-stats
源码: packages/plugin-usage-stats/src/index.ts
子系统: llm
概述
记账插件:订阅 api-llm 的 llm:usage,把每次模型调用记进数据目录的账本,经页面契约提供「用量统计」页,按天、按周、按模型列出输入、缓存命中与输出 token。配置里填了单价才有费用一列;有服务商的模型实现了余额查询才有「余额」一页。
不装 webui-server 时照样记账,只是没有页面。
插件声明
ts
definePlugin({
name: '@aalis/plugin-usage-stats',
displayName: '用量统计',
subsystem: 'llm',
configSchema,
uses: {
config,
logger,
lifecycle,
events,
paths: hostPaths,
webui: optional(webuiServer),
llm: optional(llm),
plugins: optional(pluginsService),
},
apply: run,
});配置
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
prices | map | {} | 单价:每百万 token 的价格,键是模型 id,值依次写输入、缓存命中、输出三个价,用逗号隔开,如 deepseek-chat: 2, 0.5, 8。填了才有费用一列,没填单价的模型不算进费用。写错(不是三个不小于 0 的数)时插件起不来,错误写明是哪个模型 |
currency | string | 'CNY' | 费用一列标的币种,只用于显示 |
账本
- 位置:
state/usage/llm-calls.jsonl(经 host-paths 取数据目录),只追加,一次调用一行:at(毫秒时刻)与llm:usage的各字段(服务商、模型、调用方、会话、用途、输入、缓存命中、输出)。后一行等前一行写完;追加失败记 warn,这一次只留在内存里的合计中。停用或停机时等排着的写完。 - 启动时读回: 按行流式读回(不把整个账本读成一个字符串),按本地时区的日期与(服务商,模型)合计。读不懂的行(不是 JSON、缺字段、数不对)跳过并记一条 warn 写明几行;账本读不出来(不是不存在,如权限、路径是目录)时插件起不来,不照空账本往下记。
- 不清理: 账本按调用增长(一行约 230 字节),启动读回的耗时随之增长;要清掉旧记录就删这个文件(重启后从空起步)。
「用量统计」页
位置为插件详情(/p/@aalis%2Fplugin-usage-stats/usage-stats),声明式。按天、按周、按模型三个标签各一张表,有余额时另有「余额」:
| 标签 | 行 | 首列 |
|---|---|---|
| 按天 | 每天一行,新的在前 | 日期(2026-10-04) |
| 按周 | 周一起算,新的在前 | 周(2026-09-28 – 10-04) |
| 按模型 | 每个(服务商,模型)一行,按输入加输出从多到少 | 模型,另有一列服务商(插件的显示名,非默认实例带实例名,插件已不在的写实例 id) |
| 余额 | 有服务商的模型实现了余额查询才有:每个服务商查一次,一个币种一项;查不到的写「查不到:原因」 | — |
- 数列是输入、缓存命中、输出,填了单价另有费用(四舍五入到分)。缓存命中只加服务商报了的,一行里一次都没报时空着(与 0 不同)。费用只算填了单价的模型,一行里一个都没有时空着;表格上写明「费用按配置里的单价估算,没填单价的模型不算在内」。
- 窄时(手机、340 宽的容器)只留首列、输入,以及费用(填了单价时)或输出(没填时)。
- 每次记账后作废按天、按周、按模型三张表,开着的页面重取;余额不跟着记账重查,只在打开或手动刷新时查,时限 10 秒。
- llm 服务可能变了(
service:changed)时重看有没有服务商实现余额查询,有无变了就以同一个 key 重新登记这一页,「余额」随之出现或消失。