# pi-deepseek-suite

[English](./README.md) | **中文**

给 [pi](https://pi.dev) 用的 DeepSeek 费用核算，一个扩展搞定三件事：

1. **峰谷重定价** —— 每条 DeepSeek 助手消息都按**它自己时间戳**对应的官方费率计费，让 pi 的会话总额、footer、导出结果与 DeepSeek 实际账单一致。
2. **本地账本** —— 每次计价的调用写一条 JSONL 记录，按天、按模型、按时段汇总。就是普通文件，可以直接用 `jq` 查。
3. **账户余额** —— 官方余额接口，带日均消耗和可用天数估算，可选低余额告警。

除余额查询外不发起任何 API 调用。无遥测。对话内容不出本机。

```bash
pi install npm:pi-deepseek-suite
```

然后 `/reload`（或重启 pi）。

---

## 为什么是一个扩展，而不是三个

`message_end` 的处理器是**链式**的：后一个处理器能看到前一个返回的替换结果，而最终被 pi 持久化的就是最后那个替换值。如果「重定价」和「记账」分属**两个**插件，账本的正确性就取决于它们在 `settings.json` 里的**排列顺序** —— 把记账的那个放在前面，它就会永远记下未修正的金额，而且悄无声息。

本扩展把修正后的金额算一次，两个消费方共用，所以根本不存在顺序问题。

## 为什么按 model id 而不是 provider 名匹配

不少 DeepSeek 插件把一切都建立在 `provider === "deepseek"` 上。但 provider 名是**本地配置**：中转和路由工具（cc-switch、new-api、one-api……）会生成 `models.json`，自己取一个 provider 名，并把密钥内联其中。在这类环境里，provider 名判断会**静默地**让整个插件失效 —— footer 就是不出现，也不报错。

本扩展按 **model id** 匹配，并按以下顺序找密钥：`DEEPSEEK_API_KEY` → `auth.json` → **`models.json` 里任何 `baseUrl` 指向 `api.deepseek.com` 的 provider**。所以你的 provider 叫 `deepseek`、`cc-switch-deep-seek` 还是别的什么名字都能用。

## 命令

| 命令 | 内容 |
| --- | --- |
| `/ds [Nd]` | 从账本出报告：总额、调用次数、token 数、峰谷拆分、按天与按模型明细。默认当天；`/ds 7d`、`/ds 30d`。 |
| `/ds-balance` | 账户余额（识别 CNY）、账户是否可用、日均消耗、按此速度的可用天数。 |
| `/ds-tier` | 当前时段、当前模型生效的确切费率、下次切换还有多久。 |

状态栏保持紧凑：

```
off-peak · $0.0340 today · ¥42.50
```

## 定价是怎么算的

权威来源：<https://api-docs.deepseek.com/quick_start/pricing>

- **高峰时段**为 UTC `01:00–04:00` 与 `06:00–10:00`，周一至周五。其余时间（含周末）均为闲时。
- **闲时价恰好是高峰价的一半**，所以内置费率表只维护闲时一列，高峰时做乘法。
- **DeepSeek 的缓存写入不收费。**

内置费率表的闲时价（美元 / 每百万 token）：

| 模型 id | input | output | cacheRead | cacheWrite |
| --- | --- | --- | --- | --- |
| `deepseek-flash` | 0.15 | 0.60 | 0.003 | 0 |
| `deepseek-v4-flash` | 0.15 | 0.60 | 0.003 | 0 |
| `deepseek-v4-flash-vision-exp` | 0.15 | 0.60 | 0.003 | 0 |
| `deepseek-v4-pro` | 0.66 | 1.98 | 0.022 | 0 |

`deepseek-v4-flash` 与 `deepseek-v4-flash-vision-exp` 是已退役的 id，DeepSeek 仍接受请求并按当前 Flash 价计费。

`deepseek-v4-pro` 保留自己的费率。DeepSeek 曾宣布 2026-09-14 将 Pro 改路由到 Flash 计价，随后**撤销了该决定** —— 所以这里没有编码任何切换点。仍在假设那次撤销没发生的插件，从那个日期起会把 Pro 的用量少算约 3–4 倍。

### 不等发版就能更新价格

在配置文件里设置 `rates`。它会**整体替换**内置费率表，所以要写全你用到的模型：

```json
{
  "rates": {
    "deepseek-flash": { "input": 0.15, "output": 0.6, "cacheRead": 0.003, "cacheWrite": 0 },
    "some-new-model": { "input": 1, "output": 2, "cacheRead": 0.1, "cacheWrite": 0 }
  }
}
```

费率一律填**闲时**数字；高峰价由乘法得出。

## 配置

全局：`~/.pi/agent/deepseek-suite.json`
项目：`<cwd>/.pi/deepseek-suite.json`（仅在项目被信任时生效）

项目配置覆盖全局配置。任何格式错误的值会被忽略而不是报错中断。既接受扁平对象，也接受 `{ "deepseekSuite": { ... } }` 包裹形式。

| 键 | 默认值 | 含义 |
| --- | --- | --- |
| `peakMultiplier` | `2` | 高峰时段应用的乘数。 |
| `rates` | `null` | 替换内置费率表。闲时价，美元 / 每百万 token。 |
| `reprice` | `true` | 把修正后的金额写回会话。 |
| `ledger` | `true` | 每条计价的助手消息追加一条 JSONL 记录。 |
| `status` | `true` | 显示状态栏条目。 |
| `currency` | `null` | 账户返回多种货币时优先选哪个。 |
| `lowBalanceThreshold` | `null` | 低于该余额时告警。充值回上方后重新武装。 |
| `balanceRefreshSeconds` | `300` | 余额轮询间隔。 |

## 文件

```
~/.pi/agent/deepseek-suite.json                     配置（全局）
~/.pi/agent/deepseek-suite/ledger/YYYY/MM/DD.jsonl  每次计价调用一行
```

一条账本记录：

```json
{
  "ts": 1789219121803,
  "provider": "cc-switch-deep-seek",
  "model": "deepseek-flash",
  "tier": "offPeak",
  "tokens": { "input": 134, "output": 2, "cacheRead": 4351, "cacheWrite": 0 },
  "cost": { "input": 0.0000201, "output": 0.0000012, "cacheRead": 0.000013053, "cacheWrite": 0, "total": 0.000034353 },
  "ratesSource": "official"
}
```

`provider` 按 pi 上报的原样记录，**绝不改写**。定价只看 `model`。`tier` 是那次调用实际生效的时段 —— 正因如此，「这次调用如果在闲时会花多少」以后还能算得出来。

`jq` 用法示例：

```bash
# 今天的总额
jq -s 'map(.cost.total) | add' ~/.pi/agent/deepseek-suite/ledger/2026/09/12.jsonl

# 本月按时段拆分的花费
jq -s 'group_by(.tier) | map({tier: .[0].tier, total: map(.cost.total) | add})' \
  ~/.pi/agent/deepseek-suite/ledger/2026/09/*.jsonl
```

## 范围与限制

- 只对助手消息计价。工具结果与压缩（compaction）的用量没有自己的模型，因此交给 pi 处理。
- 费率表之外的模型完全不碰：保留 pi 自己算的金额，而不是拿猜测值覆盖。
- 账本按**本地时间**分日；时段边界按 **UTC**，因为 DeepSeek 就是用 UTC 公布的。
- 账本是只追加的，从不重写。改 `rates` 不会追溯重算历史。
- 唯一的网络调用是余额查询，且只在有余额展示需求时发起（`session_start`、`model_select`、轮询定时器，或 `/ds-balance`）。

## 环境要求

- pi `@earendil-works/pi-coding-agent`（较新版本即可，无硬性下限）
- Node 20+（用到 `AbortSignal.timeout`）

## 许可

MIT
