omp packages

pi-cost-lens0.1.1

Pi 编码代理的模型 API 费用统计与本地货币显示扩展:按厂商官方价格(人民币/美元)重新计算真实 Token 费用,支持峰谷、阶梯、缓存、推理与自定义价格。

shuo-wang Published 16 days ago MIT ~359 downloads/mo

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.

Did it work for you?

Result

One report per person. A new report replaces your old one.

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. 币种规则

  1. 默认不做汇率换算:官方人民币价就显示 ¥,官方美元价就显示 $。 同一会话里混用两种币种时,费用分别累加,绝不混币相加。
  2. 不会因为“这是中国模型”就强行把美元价换成人民币,也不会因为“这是美国模型”就强行换成美元。
  3. 需要强制单一币种时,在 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. 明确的能力边界

诚实说明,避免误用:

  1. 费用是估算。除 Provider 直接返回账单金额外,UI 一律标注「估算」。 赠送额度、优惠券、企业协议价、充值优惠、税费都不会体现在 Token Usage 里。
  2. 未配置价格时绝不出数。显示 ¥-- /「未配置」,并提供 /cost set。
  3. Mistral 目前无法自动抓取(官方文档站是 SPA,.md 返回 404)。 插件会保留“最后一次成功验证的价格”,否则显示未配置。
  4. 区域。Qwen 只采用「华北2(北京)」;Vertex AI 上的 Gemini / Claude 价格与官方直连不同, 插件不做映射(不把 Vertex 价格冒充 Gemini Developer API 价格)。
  5. Provider 映射只做确定等价的别名(例如 qwen-token-plan-cn → qwen、 moonshotai-cn → moonshot、zai-coding-cn → zai、openai-codex → openai)。 中转站(如 OpenRouter)的加价不会、也无法从官方价推导。
  6. 缓存单价缺失时的策略:官方未单独公布缓存单价时,缓存 Token 按标准输入单价计费 (保守,不低估)。这条策略在 /cost source 的备注里可见。
  7. 长上下文档位阈值未知时(例如 OpenAI 表格里只标注 “Long context” 而没有阈值), 只采用短上下文档位,并在规则备注里明确标注,避免把长上下文价错配到普通请求。
  8. 阿里云百炼的「第三方模型」不解析。百炼上的 DeepSeek / GLM / Kimi 采用「忙时 / 闲时」 双价,其高峰时段定义写在另一篇官方公告里,且不同区域价格不同。为避免猜价, 本插件只解析百炼的千问系列;百炼上的第三方模型显示「未配置」,请用 /cost set 配置。
  9. 国际站 Kimi(moonshotai)故意不映射。国际站是美元定价,把它映射到中国大陆平台的 人民币规则会伪造出「官方人民币价格」。该 Provider 未配置时显示「未配置」。
  10. 订阅制 Provider:kimi-coding 等订阅套餐在 Pi 里会显示 (sub);本插件仍会给出 “按官方 Token 单价估算”的金额,并保留原生的 (sub) 标记,请以实际套餐为准。
  11. 不修改 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

  1. 新建 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[],解析失败返回 [] */ },
};
  1. 在 src/providers/index.ts 的 PROVIDERS 中登记;
  2. 若 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 验证)。 要跑测试请在源码仓库目录执行。

测试覆盖(对应需求清单):

  1. DeepSeek 工作日高峰 2. 工作日空闲 3. 周末 4. Cache Hit 5. Cache Miss
  2. Session 跨 Peak/Off-Peak 7. 中途切换模型 8. Qwen 上下文阶梯 9. 官方价格获取失败
  3. 使用缓存价格 11. 用户自定义价格 12. 未知模型 13. USD Provider 14. CNY Provider
  4. 窄终端窗口 16. 重启后历史 Session 费用一致性 (另含:footer 桥接替换其它扩展 footer 里的金额、桥接幂等与生命周期转发、 推理 Token 单独计价、固定费用/工具调用费用、Batch 折扣、Provider 别名、来源白名单、种子完整性)

License

MIT