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.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-20-openrouter-cost-balance-readouts.md
|
||||
2026-08-20-openrouter-cost-balance-readouts.md: 339acc4140eb6c34d1c3910a93babbeda9a28cac
|
||||
2026-08-20-openrouter-cost-balance-readouts.zh.md: 16606ae58b346a1eeb10f703eae748665f64ea83
|
||||
2026-08-20-openrouter-cost-balance-readouts.md: 471ca8f3d6552a0fdba680580908b3d7b268b115
|
||||
2026-08-20-openrouter-cost-balance-readouts.zh.md: 5a3a09453bb26ebcc82887bda4209c281f8476bd
|
||||
|
||||
@@ -42,7 +42,14 @@ into `conversation.composer.dock` (the shipped stats line's dock), reading the
|
||||
balance badge registers into `sidebar.footer.action`, polling the balance
|
||||
Remote on a 60s client-side interval. Both hide themselves when their data is
|
||||
absent: cost hides before any priced step, balance renders an empty dashes
|
||||
marker when the snapshot has no value yet.
|
||||
marker when the snapshot has no value yet. The projection's `steps` view field
|
||||
also maps every priced step to its USD cost under `${turn}:${step}` keys, and
|
||||
`dsh-client-ui-trajectory` reads it from the resident projection store to show
|
||||
each priced assistant step's cost on the Trajectory request boundary and in
|
||||
its inspector summary. The projection store follows the session instance's
|
||||
residency ([retention note](../bug-fix/2026-08-20-web-session-projection-store-survives-revival.md)),
|
||||
so the readouts survive switching between chats instead of reverting to
|
||||
blank.
|
||||
|
||||
**The API key is a credential reference, seeded identically to routing.**
|
||||
`apiKeyEnv` (default `OPENROUTER_API_KEY`) resolves through the credentials
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: implemented
|
||||
|
||||
**费用是面向客户端的只读模型投影,绝不是被记录或模型可见的事实。** 网关拉取 OpenRouter 的模型定价表(`GET /models`,`pricing.prompt`/`completion` USD 每 token、flat 的 `request` 费用、披露的缓存费率),并在 `openRouterCost` 的 `SessionProjectionMap` 条目中按各会话已记录的用量折叠它。余额来自 OpenRouter 权威余额端点 `GET /credits`(可用余额 = `total_credits` 减去已花费的 `total_usage`,即仪表盘展示的数字),并与 `GET /auth/key` 合并以获取 key 标签与月度 token 预算(`usage`/`limit`);标准 `sk-or-v1` key 在 `/auth/key` 上**不**返回 `credits` 字段,因此仅靠该端点总会读到空余额。两个请求都使用 `redirect: 'error'` 的 `global.fetch` —— 携带凭证的出站调用的主机标准。
|
||||
|
||||
**Web 表面是两个 slot 条目。**每会话费用读数注册进 `conversation.composer.dock`(随附统计行的 dock),通过 `useProjection` 框架座位读取 `openRouterCost` 投影;实时余额徽章注册进 `sidebar.footer.action`,以客户端 60s 间隔轮询余额 Remote。两者在数据缺失时自我隐藏:费用在任何已计价步骤前隐藏,余额在快照尚无值时渲染空的破折号标记。
|
||||
**Web 表面是两个 slot 条目。**每会话费用读数注册进 `conversation.composer.dock`(随附统计行的 dock),通过 `useProjection` 框架座位读取 `openRouterCost` 投影;实时余额徽章注册进 `sidebar.footer.action`,以客户端 60s 间隔轮询余额 Remote。两者在数据缺失时自我隐藏:费用在任何已计价步骤前隐藏,余额在快照尚无值时渲染空的破折号标记。投影的 `steps` 视图字段还以 `${turn}:${step}` 为键映射每个已计价 step 的美元成本,`dsh-client-ui-trajectory` 从常驻投影存储读取它,在 Trajectory 请求边界与其检查器摘要中显示每个已计价助手 step 的费用。投影存储跟随会话实例的常驻性([保留笔记](../bug-fix/2026-08-20-web-session-projection-store-survives-revival.md)),因此读数在切换聊天时持续存在,不会回退为空白。
|
||||
|
||||
**API key 是凭证引用,与路由的初始化方式一致。**`apiKeyEnv`(默认 `OPENROUTER_API_KEY`)与 `dsh-web-search-deepseek` 完全一样,通过凭证接缝加环境回退解析,按刷新在 thunk 中捕获,使凭证或设置变更无需重建即可在下一周期生效。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user