Files
Coder 99b63abde6 feat: show per-step OpenRouter cost on the Trajectory tab
Extend the openRouterCost projection with a per-step priced-cost map
keyed by turn:step so surfaces can render spend without re-pricing, and
have the Trajectory view read it through the framework useProjection seat:
each priced assistant step shows its USD cost on the request boundary chip
and in the inspector summary, while unpriced steps stay blank. Also fix a
pre-existing exactOptionalPropertyTypes error in extractCacheRates that the
host typecheck surfaced.
2026-08-20 22:14:09 +07:00

64 lines
4.4 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.
# @deepseek-ai/dsh-openrouter-usage
[English](README.md) | 中文
OpenRouter 花费追踪:一个提供账户余额的 Typert Remote 网关,加上 `openRouterCost` 会话投影——后者用 OpenRouter 的模型定价表为日志中的 token 用量计价。
在无法解析到 `OPENROUTER_API_KEY`(即 OpenRouter LLM 路由所用的同一个凭证)时,本包处于惰性状态:无 key 意味着不发起请求、定价表为空(因此每个 step 都保持未计价)、余额为空。
## 配置
```yaml
- id: openrouter-usage
name: '@deepseek-ai/dsh-openrouter-usage'
config:
apiKeyEnv: OPENROUTER_API_KEY
baseURL: https://openrouter.ai/api/v1
syncEnabled: true
pricingRefreshMs: 21600000
balanceRefreshMs: 60000
```
| Key | 默认值 | 含义 |
| --- | --- | --- |
| `apiKeyEnv` | `OPENROUTER_API_KEY` | 每次刷新解析的凭证引用。 |
| `baseURL` | `https://openrouter.ai/api/v1` | OpenRouter API 根;会拼接 `/credits``/models``/auth/key`。 |
| `syncEnabled` | `true` | 是否运行周期性的定价/余额刷新。 |
| `pricingRefreshMs` | `21600000`6 小时) | 定价表刷新间隔(毫秒)。 |
| `balanceRefreshMs` | `60000`60 秒) | 余额刷新间隔(毫秒)。 |
key 经凭证边界(`ctx.credentials`)解析,并以后端环境作为回退,方式与 `dsh-web-search-deepseek` 一致。
## 服务契约
`ctx.openRouterUsage` 是一个 Typert Remote 网关。`snapshot()` 方法返回最近一次成功的余额快照的副本:`balanceUsd` 是来自 `GET /credits` 的可用余额(`total_credits` 减去已花费的 `total_usage`,即 OpenRouter 仪表盘展示的数字),外加来自 `GET /auth/key``label` 与月度 `usageTokens`/`limitTokens` 预算、`isFreeTier``updatedAt` 时间戳。在任何成功获取之前,它返回一个全 `null` 的记录;一次失败的刷新会保留上一次已知快照并记录日志。同一个 key 还会从 `GET /models` 刷新模型定价表USD 每 token 的 `pricing.prompt`/`completion`,以及 flat 的 `request` 费用,若 API 披露时还有可选的 `input_cache_read`/`input_cache_write`)。
`openRouterCost` 投影会按定价表折叠每个会话日志中的 token 用量(`assistant/chunk` 的 usage 与 `assistant/message` 的 usage按 step 去重)。归属优先采用已组装消息自身的 `provider`/`model`;仅有 chunk 的失败step 则按最新的 `request/context` 路由计价。非 `openrouter` provider 上的 step 不属于本域,不会改变任何值;模型没有对应定价条目的 OpenRouter step 会计作未知未计价step。投影的 `steps` 视图字段以 `${turn}:${step}` 为键记录每个已计价 step 的美元成本,便于按 step 展示花费而无需重复计价;未计价的 step 不在该映射中。
## 扩展点
Web 界面读取 `openRouterCost` 投影并通过客户端组装调用余额 Remote`ctx.remote.openRouterUsage.snapshot()`);组件栈由 `dsh-client-ui-openrouter-usage` 提供。网关不需要任何 session 或 agent 接线——一切都依托持久全量日志投影与一次缓存的请求。
## 模型体验
### OpenRouter 花费与余额读数
#### 模型看到什么
什么也看不到。花费与余额只是面向客户端的只读模型:两者都不会进入模型请求、工具 schema 或任何提示词。agent 循环保持不变。
#### Token 影响
不会产生模型 token。计价的数字来自 `assistant/message``assistant/chunk` 事件中早已记录的用量。
#### KV Cache 影响
因为花费只是对既有用量事件的投影而非新的模型可见输入KV cache 不受影响。
## 已知限制与暂缓事项
- **估算,而非逐条计费**——花费是 `tokens × 模型定价`,不是 OpenRouter 逐条 generation 的计费。generation id 并未持久记录,且 pi-ai 适配器丢弃了 `usage.cost`,因此投影是根据 token 数重建花费的。free tier 与促销定价可能与模型表有出入。
- **计价以折叠时刻为准**——投影在单元格折叠时使用当时的模型表计价。刷新定价只会影响其后折叠的单元格;已折叠的历史保持此前的数值。
- **逐 token 近似**——当 API 未披露单独的 cache 费率时cache 读/写回退到 prompt 费率flat 的 request 费用每个 step 记一次。账单可能相差不到一分钱。
- **无设置卡片**——本插件只通过 cordis.yml 暴露配置;设置项界面推迟实现。