pi-cost-lens0.1.1
Pi 编码代理的模型 API 费用统计与本地货币显示扩展:按厂商官方价格(人民币/美元)重新计算真实 Token 费用,支持峰谷、阶梯、缓存、推理与自定义价格。
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-cost-lens
Pi 编码代理的模型 API 费用统计与本地货币显示扩展。
它解决一个具体问题:Pi 原生 footer 把用量乘上内置目录里的单一美元单价,于是 DeepSeek / Kimi / GLM / Qwen 这些官方用人民币计价的模型,会得到一个既不是官方人民币价、 也没有考虑峰谷/阶梯/缓存的美元数字:
Pi 原生: ↑315K ↓120K R300 CH50.0% $0.574 31.6%/1.0M (auto) deepseek-flash • thinking off
本插件: ↑315K ↓120K R300 CH50.0% ¥3.82 31.6%/1.0M (auto) deepseek-flash • thinking off
注意:这不是把
$换成¥。金额是用「每次请求真实的 Token 用量 + 该模型官方单价 + 该次请求真实发生的时间(峰谷)」重新算出来的。
1. 核心特性
| 能力 | 说明 |
|---|---|
| 真实重算 | 使用 input / cacheRead / cacheWrite / output / reasoning 各项 Token 分别乘以对应官方单价 |
| 官方价格源 | 只从厂商官方 Pricing 页面 / 官方 API 文档抓取,带域名白名单 |
| 峰谷定价 | DeepSeek 按每次请求发生时间判定 Peak / Off-Peak(时区用 IANA,不依赖本机时间) |
| 阶梯定价 | Qwen、GLM、OpenAI、xAI 的上下文长度阶梯(整单按落档单价结算) |
| 缓存分项 | Cache Hit / Cache Miss / Cache Write(含 Anthropic 1h TTL、Kimi 5min/1h)按各自单价计费 |
| 模式与区域 | 思考 / 非思考、Batch / Flex / Fast、区域价格均可表达为数据 |
| 本地缓存 | ~/.pi/agent/model-pricing/,默认 TTL 24h,离线可用,抓取失败自动回落缓存 |
| 绝不猜价 | 新模型 / 中转站 / 解析失败 → 显示 ¥-- 或「未配置」,不会拿相似模型的价格顶上 |
| 自定义价格 | /cost set 写入 custom-pricing.json,优先级高于自动抓取,override=true 永不被覆盖 |
| 历史可追溯 | 每次请求生成一条 CostRecord(含 pricingVersion),官方涨价后历史费用不变 |
| 账号额度 | API 登录显示余额;OAuth/订阅登录显示 5 小时与周额度剩余百分比,查询失败明确显示 --,不拿 Token 用量猜额度 |
| 币种规则 | 尊重官方价格源币种;不默认做任何汇率换算 |
| TUI 安全 | 使用 visibleWidth() / truncateToWidth(),不写死终端宽度,中文/Emoji/ANSI 均正确 |
2. 安装
2.1 本机开发/试用(不写入 settings)
pi -e /absolute/path/to/pi-cost-lens
2.2 正式安装为 Pi Package
pi install /absolute/path/to/pi-cost-lens # 本地目录
pi install git:github.com/<you>/pi-cost-lens@v0.1.0 # 或 git / npm
pi list
pi config # 启用/禁用资源
安装到 ~/.pi/agent/npm/,重启 Pi 或执行 /reload 生效。
2.3 打包到其它电脑
三种方式,任选:
A. 目录拷贝(最简单)
# 打包时排除 node_modules(本插件运行时零依赖)
tar --exclude node_modules -czf pi-cost-lens-0.1.0.tgz pi-cost-lens
# 目标机器解压后:
pi install /path/to/pi-cost-lens
B. 发布到 npm
cd pi-cost-lens
npm publish # package.json 里已设置 publishConfig.access=public
# 若账号启用了 2FA,npm 会给出网页认证链接;或使用 npm publish --otp=<6位验证码>
# 发布前会自动跑一遍测试(prepublishOnly 钩子)
# 其它电脑:
pi install npm:pi-cost-lens
C. git 仓库
git init && git add -A && git commit -m "feat: pi-cost-lens v0.1.0"
git remote add origin git@github.com:<you>/pi-cost-lens.git
git push -u origin main
# 其它电脑:
pi install git:github.com/<you>/pi-cost-lens
跨机器同步注意:价格缓存与自定义价格在 ~/.pi/agent/model-pricing/。
换机时把该目录拷贝过去,或在新机器上执行 /cost update 重新抓取官方价格
(custom-pricing.json 建议手动同步,它是你的私有价格配置)。
3. 命令
命令名为英文,说明与输出为中文。
| 命令 | 作用 |
|---|---|
/cost |
当前会话费用报告(峰谷次数、缓存/输入/输出 Token 与各自费用、总费用、最近明细) |
/cost update |
强制刷新所有官方价格(忽略 TTL) |
/cost models |
查看已识别的模型与价格、来源状态 |
/cost source |
查看当前模型的价格来源(URL、类型、匹配方式、规则版本、更新时间) |
/cost quota |
立即刷新并查看当前账号余额/订阅额度 |
/cost set |
交互式配置价格;也支持一行式 |
/cost reset [provider] [model] |
取消自定义价格,恢复官方价格 |
/cost footer |
查看 footer 费用集成状态(含安装顺序诊断,被其它扩展接管时给出修复方法) |
/cost order |
一键修复:把本扩展调到 packages 第一位($ 金额不被替换时用这个) |
/cost help |
用法 |
/cost set 一行式
/cost set <provider> <model> <currency> <input> <cacheRead> <cacheWrite> <output>
单价单位:每 100 万 Token。
/cost set my-deepseek deepseek-v4-custom CNY 2.00 0.04 0 8.00
交互式(直接 /cost set)会逐项询问:
Provider → Model → Currency → Cache Miss Input → Cache Hit Input → Cache Write → Output。
写入结果示例(~/.pi/agent/model-pricing/custom-pricing.json):
{
"schemaVersion": 1,
"rules": [
{
"id": "my-deepseek:deepseek-v4-custom",
"provider": "my-deepseek",
"model": "deepseek-v4-custom",
"currency": "CNY",
"custom": true,
"override": true,
"variants": [
{ "id": "default", "tiers": [
{ "rates": { "input": 2, "cacheRead": 0.04, "cacheWrite": 0, "output": 8 }, "label": "用户自定义" }
]}
],
"source": { "url": "custom-pricing.json", "type": "custom", "title": "用户自定义价格" },
"lastUpdated": "2026-09-21T10:00:00.000Z",
"sourceVersion": "…"
}
]
}
也可以直接手写这个文件(支持多档位、峰谷、Batch 等完整规则结构),然后重启或 /reload。
3.5 footer 集成(重要:与 powerline-footer 等扩展共存)
费用要显示在 footer 上,但 Pi 允许扩展用 ctx.ui.setFooter() 完全接管 footer。
你安装的 pi-agent-extensions 里的 powerline-footer 就是这么做的,而它自己用
$${totalCost.toFixed(3)} 打印金额(数据源是 Pi 内置目录里的美元固定价)。
所以本插件有 三条通道,按优先级自动选择:
| 通道 | 触发条件 | 效果 |
|---|---|---|
| ① 内置 FooterComponent 包装 | Pi 使用内置 footer | 把 footer 里的 $0.574 换成 ¥3.82 |
| ② footer 桥接(bridge) | 其它扩展用 ctx.ui.setFooter() 接管了 footer |
把对方 footer 里的 $0.009 换成 ¥3.82,其余内容(git / 时长 / venv / 时间 / 状态栏)原样保留 |
| ③ 扩展状态栏通道 | ①② 都来不及(加载顺序问题) | 通过 ctx.ui.setStatus() 显示费用(powerline 会把状态栏追加在行尾),并给出一次性提示 |
⚠️ 加载顺序决定 ② 能否生效
Pi 的扩展加载顺序是:
1. CLI `-e` ← 最先
2. ~/.pi/agent/extensions/ ← 全局扩展目录
3. settings.json 的 packages[] ← 按数组顺序(pi-agent-extensions 通常在最前)
如果本扩展排在 pi-agent-extensions 后面(pi install 默认追加到末尾),
那么 powerline-footer 会先接管 footer,② 就来不及了,会退化为 ③。
三种修复方式(任选一种):
# 方式 1:用 CLI 扩展启动(CLI 扩展永远最先加载)
pi -e /path/to/pi-cost-lens
# 方式 2:把本包移到 ~/.pi/agent/settings.json 的 packages 数组第一位,重启 Pi
# "packages": ["npm:pi-cost-lens", "npm:pi-agent-extensions", ...]
# 方式 3:生成全局扩展目录引导文件(全局目录早于所有 package)
cd /path/to/pi-cost-lens
node scripts/install-bootstrap.ts
# 卸载:node scripts/install-bootstrap.ts --remove
随时用 /cost footer 查看当前走的是哪条通道、以及该怎么修。
footerMode 配置
{ "footerMode": "patch" }
| 值 | 说明 |
|---|---|
patch(默认) |
通道 ① + ②,并按需降级到 ③ |
status |
只用通道 ③(扩展状态栏),完全不碰 footer |
own |
本扩展自己接管 footer(会替换掉其它扩展的 footer 内容) |
custom |
同 own(兼容旧配置) |
off |
完全不显示费用 |
3.6 账号额度显示
额度会显示在费用后面,不改变 Pi 原有 footer 的其它信息;在 custom/own 模式下还会以右对齐、带颜色和剩余量格子的独立小行展示。例如:
… | ¥4.77 | API · 余额 ¥12.34 | 4m (base)
… | $0.23 | 订阅 · 5h剩余 86% · 周剩余 92% | 4m (base)
当前自动适配:
- DeepSeek API 登录:调用 DeepSeek 官方余额接口,显示人民币余额;
- OpenAI Codex / Anthropic OAuth 登录:显示 5 小时与 7 天(周)窗口剩余百分比;
- Kimi Coding:显示官方 5 小时与 7 天窗口剩余百分比。
额度只保存在内存中,默认每 5 分钟刷新,并在切换模型、turn_end 后按缓存策略刷新。
插件通过 Pi 的 ModelRegistry 读取当前已登录凭据,不读取或写入 auth.json,也不会把凭据写入日志、会话条目或价格缓存。
仅向 provider 官方额度接口发请求;接口不可用时保留费用显示,并在额度位置显示 -- 或 /cost quota 报告错误原因。
关闭 footer 额度显示:
{ "showQuotaInFooter": false }
3.7 故障排查:装了包,右下角还是 $
典型现象:
DeepSeek V4.1 Flash 762K/1.0M (76.3%) | $1.937 | 13h47m 10:32
↑ 还是美元符号
按顺序检查三步:
第 1 步:扩展到底有没有被 Pi 加载?
在 Pi 里敲 /cost。
| 结果 | 说明 | 处理 |
|---|---|---|
| 提示「未知命令 / unknown command」 | Pi 根本没加载它 —— 多半是执行了 npm install pi-cost-lens(这只装进当前项目的 node_modules,Pi 不认识),而不是 pi install |
执行 pi install npm:pi-cost-lens,然后重启 Pi;用 pi list 确认 |
| 正常显示费用报告 | 扩展已加载 → 进入第 2 步 |
npm install pi-cost-lens是往你的 Node 项目里装依赖;pi install npm:pi-cost-lens才是往Pi 的扩展目录里装(写进~/.pi/agent/settings.json)。两者完全不同。
第 2 步:看 footer 通道 + 安装顺序
敲 /cost footer:
footerMode:patch
内置 FooterComponent 包装:未在渲染
外部 footer 桥接:已安装
扩展状态栏通道:启用
────────────────────────────────
安装位置:packages[7] / 共 8 项 ⚠️ 不是最前
配置文件:C:\Users\你\.pi\agent\settings.json
────────────────────────────────
⚠️ 费用金额没有被替换,原因通常是:另一个扩展(例如 pi-agent-extensions 的
powerline-footer)用 ctx.ui.setFooter() 完全接管了 footer,而它比本扩展先加载。
第 3 步:一键修复
敲 /cost order(会自动备份 settings.json.bak 并调整顺序),然后重启 Pi。
重启后 /cost footer 会显示:
外部 footer 桥接:已安装,且已拦截到其它扩展安装的 footer
────────────────────────────────
✅ 当前 footer 里的费用金额会被替换成本地货币(按官方价格重算)。
也可以不用 Pi,直接在命令行修:
node ~/.pi/agent/npm/node_modules/pi-cost-lens/scripts/switch-install-to-npm.ts
# 或用全局扩展目录引导(早于所有 package 加载)
node ~/.pi/agent/npm/node_modules/pi-cost-lens/scripts/install-bootstrap.ts
另一个坑:/cost 命令直接失效 / 输入被当成提问发给模型
如果本扩展被加载了两份(例如既 pi install 了 npm 包、又放了
~/.pi/agent/extensions/ 引导文件;或同时存在本地路径安装与 npm 安装),
Pi 的命令分派会把同名命令全部改名(dist/core/extensions/runner.js):
invocationName = 同名命令数 > 1 ? `${name}:${occurrence}` : name
结果就是没有任何叫 cost 的命令,/cost 会直接落回普通提问发给模型:
注册结果: cost:1 ← cli
cost:2 ← npm:pi-cost-lens
插件会在启动时检测到并弹出错误提示:
pi-cost-lens:检测到 2 份本扩展同时加载,Pi 已把命令改名为 cost:1 / cost:2,
裸 /cost 会失效。请只保留一份:在 Pi 里执行 /cost order 去掉 settings.json 里的重复项;
若还用过 scripts/install-bootstrap.ts,请同时执行 node scripts/install-bootstrap.ts --remove。
修复:
# 1) 去掉 settings.json 里的重复项并排到第一位
# 在 Pi 里执行 /cost order (或命令行)
node ~/.pi/agent/npm/node_modules/pi-cost-lens/scripts/switch-install-to-npm.ts
# 2) 如果用了全局目录引导文件,删掉它(只保留一条加载路径)
node ~/.pi/agent/npm/node_modules/pi-cost-lens/scripts/install-bootstrap.ts --remove
install-bootstrap.ts现在会主动拦截这种误用:检测到 packages 里已有本扩展时会中止并说明, 需要--force才能强行同时使用。
兜底(顺序实在改不了时)
即使顺序不对,插件也不会静默失败:它会改用扩展状态栏通道,
powerline-footer 会把状态栏追加在行尾,所以你会看到 … | $1.937 | 13h47m 10:32 | 费用 ¥1.94。
想彻底不让 $ 出现,可以设置:
{ "footerMode": "own" }
由本扩展自己接管 footer(会替换掉其它扩展的 footer 内容)。
4. 价格来源(官方,自动抓取)
| Provider | 官方来源 | 币种 | 解析方式 |
|---|---|---|---|
| DeepSeek | api-docs.deepseek.com/zh-cn/quick_start/pricing |
CNY | 官方页 HTML 表格 |
| 阿里云百炼 Qwen | help.aliyun.com/zh/model-studio/billing-for-model-studio.md |
CNY | 官方文档 Markdown(HTML 表格,已处理 rowSpan/colSpan) |
| Kimi / Moonshot | platform.kimi.com/docs/pricing/chat.md |
CNY | 官方文档 Markdown 的 <DocTable> |
| 智谱 GLM | docs.bigmodel.cn/cn/guide/start/pricing.md |
CNY | 官方文档 Markdown 表格 |
| OpenAI | platform.openai.com/docs/pricing.md |
USD | 官方文档 Markdown 表格(Standard / Batch / Flex / Fast) |
| Anthropic | docs.claude.com/en/docs/about-claude/pricing.md |
USD | 官方文档 Markdown 表格 |
| Google Gemini | ai.google.dev/gemini-api/docs/pricing |
USD | 官方页“模型段落”解析(含“截至/自某日起”生效期判定) |
| xAI | docs.x.ai/developers/models.md |
USD | 官方文档 Markdown 表格 |
| Groq | console.groq.com/docs/models.md |
USD | 官方文档 Markdown 表格 |
| Mistral | docs.mistral.ai/.../pricing |
USD | ⚠️ 官方文档站为 SPA,目前静态抓取 404 → 显示「未配置」,请 /cost set |
抓取器只接受厂商官方域名(每个 Provider 自带 allowHosts 白名单)。
博客、Reddit、第三方中转站、价格聚合站一律不会被采用。
5. 币种规则
- 默认不做汇率换算:官方人民币价就显示
¥,官方美元价就显示$。 同一会话里混用两种币种时,费用分别累加,绝不混币相加。 - 不会因为“这是中国模型”就强行把美元价换成人民币,也不会因为“这是美国模型”就强行换成美元。
- 需要强制单一币种时,在
config.json里显式配置:
{
"displayCurrencyOverride": "CNY",
"fx": { "base": "USD", "quote": "CNY", "rate": 7.1, "note": "用户自定义固定汇率" }
}
只有提供 fx 时才会换算,并且 /cost 报告会明确标注这是换算结果。
默认不使用任何实时汇率。
6. DeepSeek 峰谷定价(重点)
DeepSeek 官方规则(中文官方页原文):
空闲时段价格为高峰时段价格的一半。北京时间周一至周五(不含中国法定节假日) 9:00 - 12:00、14:00 - 18:00 为高峰时段;其余时段,包括周末及中国法定节假日全天均为空闲时段。
本插件的实现方式:
- 内部一律用 UTC 时间戳保存请求时间(
CostRecord.timestamp); - 判定峰谷时把 UTC 时间戳换算到规则声明的 IANA 时区(
Asia/Shanghai), 通过Intl.DateTimeFormat实现,不依赖 Windows 当前显示的本地时间; - 按每一次请求真实发生的时间决定该次请求的价格,而不是按会话结束时间统一套价。
因此下面这种会话会被正确计费:
10:00 请求 → Peak ¥2.00(cache miss 1M)
13:00 请求 → Off-Peak ¥1.00
15:00 请求 → Peak ¥2.00
总费用 = 2 + 1 + 2 = ¥5.00 (若按会话结束时间统一套价会得到 ¥6 或 ¥3)
法定节假日
官方规则排除中国法定节假日。节假日清单无法从价格页推导,因此不由插件猜测:
- 官方页说明里有“不含中国法定节假日”时,规则窗口会带
holidays: true; - 你把已知节假日写进
~/.pi/agent/model-pricing/holidays.json即可生效:
{ "schemaVersion": 1, "dates": ["2026-10-01", "2026-10-02", "2026-10-03"] }
- 未维护该文件时,峰谷按“工作日 + 时段”判定,
/cost与规则备注中会说明这一注意事项。
缓存分别计价
DeepSeek 返回 prompt_cache_hit_tokens / prompt_cache_miss_tokens 时,Pi 已把它们归一化为
usage.cacheRead / usage.input,本插件分别乘以对应单价:
费用 = cacheRead × cacheHit单价 + input × cacheMiss单价 + output × output单价
不会退化成 prompt_tokens × input单价。
当前官方(人民币)单价(用于测试用例,插件本身以最新抓取结果为准):
| Cache Hit | Cache Miss | Output | |
|---|---|---|---|
| Peak | ¥0.04 / 1M | ¥2 / 1M | ¥8 / 1M |
| Off-Peak | ¥0.02 / 1M | ¥1 / 1M | ¥4 / 1M |
7. Qwen 阶梯计价
阿里云百炼按「单次请求的输入 Token 总量」落档,整单按该档单价结算(不是分段累进):
| 输入 Token | qwen3.7-plus 输入 | 输出 |
|---|---|---|
| 0 < Token ≤ 256K | ¥2 / 1M | ¥8 / 1M |
| 256K < Token ≤ 1M | ¥6 / 1M | ¥24 / 1M |
其他细节:
- 思考模式与非思考模式单价不同时,会生成两套档位(
when.thinking); - 官方页的价格单元格里含“原价 X 元(限时 8 折)”时,采用官方原价 X,并把促销信息写入规则备注(不把活动价当成长期官方价);
- 官方页明确说明「输入单价不含缓存单价」,因此插件不臆造缓存折扣(缓存 Token 按标准输入单价保守计费,见 §9);
- 区域价格不同:插件默认取「华北2(北京)」,不冒用其它区域价格。需要其它区域请
/cost set。
8. 配置文件
~/.pi/agent/model-pricing/ 目录结构:
model-pricing/
├── config.json 插件配置(可选,缺省用默认值)
├── pricing.json 官方价格缓存(自动抓取结果)
├── sources.json 各官方来源的抓取状态、TTL、最近错误
├── custom-pricing.json 用户自定义价格(优先级最高)
├── holidays.json 峰谷判定的节假日(可选)
└── records.jsonl 每次请求的费用记录(审计,可追溯)
config.json 全量字段:
{
"ttlHours": 24,
"autoRefresh": true,
"footerMode": "patch",
"showCostInFooter": true,
"showQuotaInFooter": true,
"quotaRefreshMinutes": 5,
"decimals": { "CNY": 2, "USD": 3 },
"displayCurrencyOverride": null,
"fx": null,
"qwenRegion": "华北2(北京)",
"holidays": [],
"unpricedPlaceholder": "",
"writeAuditLog": true
}
| 字段 | 说明 |
|---|---|
ttlHours |
官方价格缓存有效期,默认 24h |
autoRefresh |
启动时若缓存过期则后台刷新(失败不影响使用) |
footerMode |
patch(默认:内置 footer 包装 + 外部 footer 桥接)/ status / own / custom / off,详见 §3.5 |
showQuotaInFooter |
是否在费用旁显示余额/订阅额度,默认 true |
quotaRefreshMinutes |
额度刷新间隔,默认 5 分钟,最小 1 分钟 |
decimals |
各币种小数位:人民币 2 位、美元 3 位 |
displayCurrencyOverride |
强制币种,需配合 fx |
fx |
显式固定汇率(默认 null,不使用实时汇率) |
qwenRegion |
千问价格区域 |
holidays |
与 holidays.json 合并 |
unpricedPlaceholder |
自定义“无价格”占位符,例如 "¥--"(留空则按币种自动生成) |
writeAuditLog |
是否写 records.jsonl |
9. 明确的能力边界
诚实说明,避免误用:
- 费用是估算。除 Provider 直接返回账单金额外,UI 一律标注「估算」。 赠送额度、优惠券、企业协议价、充值优惠、税费都不会体现在 Token Usage 里。
- 未配置价格时绝不出数。显示
¥--/「未配置」,并提供/cost set。 - Mistral 目前无法自动抓取(官方文档站是 SPA,
.md返回 404)。 插件会保留“最后一次成功验证的价格”,否则显示未配置。 - 区域。Qwen 只采用「华北2(北京)」;Vertex AI 上的 Gemini / Claude 价格与官方直连不同, 插件不做映射(不把 Vertex 价格冒充 Gemini Developer API 价格)。
- Provider 映射只做确定等价的别名(例如
qwen-token-plan-cn → qwen、moonshotai-cn → moonshot、zai-coding-cn → zai、openai-codex → openai)。 中转站(如 OpenRouter)的加价不会、也无法从官方价推导。 - 缓存单价缺失时的策略:官方未单独公布缓存单价时,缓存 Token 按标准输入单价计费
(保守,不低估)。这条策略在
/cost source的备注里可见。 - 长上下文档位阈值未知时(例如 OpenAI 表格里只标注 “Long context” 而没有阈值), 只采用短上下文档位,并在规则备注里明确标注,避免把长上下文价错配到普通请求。
- 阿里云百炼的「第三方模型」不解析。百炼上的 DeepSeek / GLM / Kimi 采用「忙时 / 闲时」
双价,其高峰时段定义写在另一篇官方公告里,且不同区域价格不同。为避免猜价,
本插件只解析百炼的千问系列;百炼上的第三方模型显示「未配置」,请用
/cost set配置。 - 国际站 Kimi(
moonshotai)故意不映射。国际站是美元定价,把它映射到中国大陆平台的 人民币规则会伪造出「官方人民币价格」。该 Provider 未配置时显示「未配置」。 - 订阅制 Provider:
kimi-coding等订阅套餐在 Pi 里会显示(sub);本插件仍会给出 “按官方 Token 单价估算”的金额,并保留原生的(sub)标记,请以实际套餐为准。 - 不修改 Pi 核心源码,不修改
node_modules。footer 集成通过包装 Pi 已导出的FooterComponent.prototype.render实现;若某个 Pi 版本不再导出该类,则自动降级为ctx.ui.setFooter()的 custom 模式。
10. 目录结构
pi-cost-lens/
├── package.json Pi Package 清单(pi.extensions 指向 src/index.ts)
├── README.md
├── LICENSE
├── data/
│ └── pricing.seed.json 内置官方价格种子(离线可用,由脚本从官方页抓取生成)
├── scripts/
│ ├── refresh-prices.ts 构建期抓取官方价格 → data/pricing.seed.json
│ ├── install-bootstrap.ts 生成全局扩展目录引导文件(保证早于 packages 加载)
│ ├── demo-session.ts 用真实 Pi 会话文件跑一遍费用计算并打印 footer / /cost
│ ├── verify-integration.ts Pi 外部的静态检查
│ └── verify-in-pi.ts 在 Pi 运行时内部验证 footer 包装与报告渲染
├── src/
│ ├── index.ts 扩展入口(事件、footer 集成、命令注册)
│ ├── pricing-engine.ts Pricing Engine(规则匹配 / 峰谷 / 阶梯 / 缓存 / 计费)
│ ├── cost-service.ts 会话级服务(价格库、配置、汇总、footer 文本)
│ ├── quota-service.ts Provider 余额/订阅额度查询、缓存与 footer 文本
│ ├── usage-tracker.ts 从 Pi message/entry 采集用量、生成 CostRecord、重建历史
│ ├── currency.ts 币种、符号、格式化、显式汇率
│ ├── time.ts IANA 时区换算与峰谷时间窗口判定
│ ├── config.ts 插件配置
│ ├── commands.ts /cost 命令族
│ ├── providers/ 各厂商的官方价格解析器(新增 Provider 只加文件)
│ │ ├── index.ts Provider 注册表 + Provider id 别名映射
│ │ ├── deepseek.ts qwen.ts kimi.ts glm.ts openai.ts
│ │ └── anthropic.ts google.ts xai.ts mistral.ts groq.ts
│ ├── pricing-sources/
│ │ ├── http.ts 官方页面抓取(白名单、Cookie、重定向、登录中间页重试)
│ │ ├── parse-utils.ts Markdown/HTML 表格解析、区间与金额解析
│ │ └── refresh.ts 刷新编排(TTL、失败回落缓存、状态记录)
│ ├── storage/
│ │ ├── paths.ts ~/.pi/agent/model-pricing/
│ │ └── store.ts 读写、合并、TTL、审计日志
│ └── tui/
│ ├── width.ts visibleWidth / truncateToWidth(注入 pi-tui 官方实现)
│ ├── format.ts 金额/Token 格式化
│ ├── footer.ts footer patch(费用/额度片段替换 + 宽度再平衡)
│ ├── footer-bridge.ts 包装其它扩展的 setFooter,替换其 footer 里的金额
│ ├── report.ts /cost 报告文本
│ └── components.ts 宽度安全的多行 TUI 组件
└── tests/
├── helpers.ts
├── pricing-engine.test.ts 峰谷 / 缓存 / 阶梯 / 模式 / 未知模型 / 币种
├── cost-tracking.test.ts 抓取失败回落缓存 / 自定义价格 / 额度 / 窄终端 / 历史一致性
└── seed.test.ts 内置价格表完整性 + 官方来源白名单
新增一个 Provider
- 新建
src/providers/xxx.ts,导出一个ProviderPriceSource:
export const xxxSource: ProviderPriceSource = {
provider: "xxx", // 与 Pi 的 provider id(或规范 id)一致
title: "XXX 官方价格",
urls: ["https://official.example.com/pricing.md"],
allowHosts: ["official.example.com"], // 只有这些域名会被抓取
type: "official-pricing-page",
parse(ctx) { /* 返回 PricingRule[],解析失败返回 [] */ },
};
- 在
src/providers/index.ts的PROVIDERS中登记; - 若 Pi 的 provider id 与规范 id 不同,在
PROVIDER_ALIASES中加一行映射。
Pricing Engine 与 UI 无需任何改动。
11. 开发与测试
npm test # node --test(39 个用例,需在源码目录运行)
npm run build:seed # 重新抓取官方价格,生成 data/pricing.seed.json
node scripts/demo-session.ts <session.jsonl> # 用真实会话验证
pi -e ./scripts/verify-in-pi.ts -p "ok" --model deepseek/deepseek-flash --no-session
关于在
node_modules里跑测试:Node 24 原生 TS 类型剥离会拒绝对node_modules目录下的.ts文件生效(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING)。 因此pi install npm:pi-cost-lens之后,直接进~/.pi/agent/npm/node_modules/pi-cost-lens跑npm test会失败 —— 这是 Node 的限制, 与包本身无关。Pi 加载扩展使用 jiti 编译 TS,不受此限制, 所以已安装的副本在 Pi 里完全正常(可用/cost与/cost footer验证)。 要跑测试请在源码仓库目录执行。
测试覆盖(对应需求清单):
- DeepSeek 工作日高峰 2. 工作日空闲 3. 周末 4. Cache Hit 5. Cache Miss
- Session 跨 Peak/Off-Peak 7. 中途切换模型 8. Qwen 上下文阶梯 9. 官方价格获取失败
- 使用缓存价格 11. 用户自定义价格 12. 未知模型 13. USD Provider 14. CNY Provider
- 窄终端窗口 16. 重启后历史 Session 费用一致性 (另含:footer 桥接替换其它扩展 footer 里的金额、桥接幂等与生命周期转发、 推理 Token 单独计价、固定费用/工具调用费用、Batch 折扣、Provider 别名、来源白名单、种子完整性)
License
MIT