Skip to content

fix(client): 会话总计改为 Σ 各轮成本(模型名回退最后一轮历史) - #15

Merged
Max-Samson merged 2 commits into
Max-Samson:mainfrom
rezon-aki:pr/sum-round-costs
Sep 23, 2026
Merged

Max-Samson merged 2 commits into
Max-Samson:mainfrom
rezon-aki:pr/sum-round-costs

Conversation

@rezon-aki

Copy link
Copy Markdown
Contributor

这个改动带来什么

  • 把成本计算的颗粒度提高到每一轮对话:每轮按自己发生的时段(高峰/空闲)和模型计价,会话总计 = 各轮徽章之和。
  • 跨高峰与空闲的会话不再虚高:以前在高峰时段打开面板,整个会话都会被按高峰价算一遍(最多差一倍);现在什么时候打开,数字都一样。
  • 没有历史数据时(新会话、读取失败)仍按原来的估算显示,不会出现空白或 0。

现象

指示器行 / 面板头部的会话总计与每条消息尾部「本轮 ≈ ¥0.0x」徽章之和对不上 —— 高峰时段最多差一倍;同一会话在高峰时段打开会比空闲时段看到更大的总计。

根因

总计走的是「会话累计 token × 刊例价」,而计费时段只取单一时刻:指示器用 Date.now(),面板用最后一轮的开始时刻。逐轮徽章则不同 —— 宿主按轮折叠,每轮用自己的开始时刻计价。因此跨高峰/空闲的会话会把一个时段应用到全部 token 上。另外在 dsh >= 0.1.2 上快照节点不再带 provenance / requestConfig,模型名取不到,价格解析静默落到回退档。

修法

  • 新增 sumRoundCosts()(src/client/rounds/types.ts):把每轮各自的成本(双币种)相加。指示器行与面板共用它,总计因此与逐轮徽章天然一致;跨时段的会话按每轮开始时刻分别计价。
  • 旧估算在「仍是唯一选择」的场景原样保留:历史不可用、还没有任何轮次、或任一轮没有成本 —— 这些情况下 sumRoundCosts() 返回 null,走原来的「会话总量 × 刊例价」路径。
  • 快照取不到模型时回退取历史最后一轮的模型(面板原本就优先用宿主折叠的归因,指示器行现在与它一致)。

3 个文件,+51/−7,无新增依赖。

验证

  • npm run typecheck:0 错误。
  • npm test:41/41 通过(node 22,dsh 0.1.5-rc.2)。
  • 本机实测(dsh 0.1.5-rc.2):指示器行总计 = 各轮徽章之和;跨高峰边界的会话不再因为「什么时候打开」而变数字。

说明:本 PR 与 #12 互不依赖(改的是同一文件 UsageIndicator.tsx 的相邻两行),两者中后合并的一个需要一次很小的 rebase。

The session total multiplied the session-wide token totals by the list price
at Date.now(), so every round was priced at the tier in effect when the panel
was opened (up to 2x off during peak hours), and on dsh >= 0.1.2 the model
chip was empty so the estimate fell back to fallback pricing.

The indicator line and the panel now both sum the per-round costs
(sumRoundCosts in src/client/rounds/types.ts), so the total agrees with the
per-turn badges; the old estimate remains as a fallback when history is
unavailable. The indicator model chip falls back to the last round in history.

Verified: npm run typecheck; npm test 41/41.
@Max-Samson
Max-Samson merged commit 42c03f5 into Max-Samson:main Sep 23, 2026
0 of 2 checks passed
@Max-Samson

Copy link
Copy Markdown
Owner

非常感谢你的敏锐洞察和贡献!

会话总成本如果按单一时刻(特别是高峰时段)乘以全量累计 tokens,确实会导致跨峰谷时段的历史会话总计虚高翻倍,与逐轮徽章的金额产生不一致。你能精准定位到这个痛点并提出 $\Sigma$ 各轮成本累加的思路非常棒!

在仔细阅读代码时,我们注意到一个活动会话中的实时流式场景可能需要进一步斟酌:
由于输入框底栏的指示器(UsageIndicator)是常驻挂载且不随轮次卸载的,useHistoryRounds 仅在组件初次挂载时发起一次 HTTP 异步请求。如果在已有历史轮次的会话中继续对话,AI 正在流式生成新回复(或者新一轮刚刚产生但页面未刷新时),totals(来自实时投影 tokenUsage)在不断递增,但 history.rounds 尚未刷新。此时若无条件以 historyCost 优先,底栏展示的成本数字可能会在整个活跃生成期间处于“停滞/冻结”状态,无法实时反映新轮次消耗的 token 费用。

这个痛点本身非常关键,我们可以一起探讨下如何把“历史已封存轮次求和”与“当前未封存/流式增量”做更丝滑的衔接(例如在有新 token 增量时补偿实时估算,或者在轮次推进时触发历史数据的防抖对齐刷新)。

再次由衷感谢你为项目提交的这些高质量贡献!

@Max-Samson

Copy link
Copy Markdown
Owner

非常感谢你的敏锐洞察和贡献!

会话总成本如果按单一时刻(特别是高峰时段)乘以全量累计 tokens,确实会导致跨峰谷时段的历史会话总计虚高翻倍,与逐轮徽章的金额产生不一致。你能精准定位到这个痛点并提出 Σ 各轮成本累加的思路非常棒!

在仔细阅读代码时,我们注意到一个活动会话中的实时流式场景可能需要进一步斟酌: 由于输入框底栏的指示器(UsageIndicator)是常驻挂载且不随轮次卸载的,useHistoryRounds 仅在组件初次挂载时发起一次 HTTP 异步请求。如果在已有历史轮次的会话中继续对话,AI 正在流式生成新回复(或者新一轮刚刚产生但页面未刷新时),totals(来自实时投影 tokenUsage)在不断递增,但 history.rounds 尚未刷新。此时若无条件以 historyCost 优先,底栏展示的成本数字可能会在整个活跃生成期间处于“停滞/冻结”状态,无法实时反映新轮次消耗的 token 费用。

这个痛点本身非常关键,我们可以一起探讨下如何把“历史已封存轮次求和”与“当前未封存/流式增量”做更丝滑的衔接(例如在有新 token 增量时补偿实时估算,或者在轮次推进时触发历史数据的防抖对齐刷新)。

再次由衷感谢你为项目提交的这些高质量贡献!

我检查了当前 main:PR #12、#14、#15 的合并提交都已进入分支,但 #15 修改 UsageIndicator.tsx 时覆盖了 #12 的一处改动。

rezon-aki added a commit to rezon-aki/dsh-usage-chart that referenced this pull request Sep 23, 2026
- 上游 v1.1.7 已收录本 fork 早先提的 4 项修复(PR Max-Samson#10/Max-Samson#12/Max-Samson#14/Max-Samson#15)与合并事故修复
  (9753856),fork 不再重复维护它们;README / FORK_NOTES 改为「已并入上游」的口径。
- fork 独有改动只剩「面板可拖动」:panel-position.ts(持久化 + 包含块夹取)+
  UsageIndicator 把手/双击复位 + i18n/styles + tests/panel-position.test.mjs,
  直接取自上游 v1.1.7 基线上的实现(与本地 live 仓库同一份代码)。
- CHANGELOG 中英新增 [1.1.7-dsh.1] 段。
@rezon-aki
rezon-aki deleted the pr/sum-round-costs branch September 23, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants