恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
DeepSeek Harness sessionStats 投影:让聊天统计条呈现与分页无关的全会话回合与步骤计数
首页
资讯中心
/
DeepSeek Harness sessionStats 投影:让聊天统计条呈现与分页无关的全会话回合与步骤计数
DeepSeek Harness sessionStats 投影:让聊天统计条呈现与分页无关的全会话回合与步骤计数
发布时间:2026/9/20 16:00:48
DeepSeek Harness sessionStats 投影让聊天统计条呈现与分页无关的全会话回合与步骤计数【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness本文讲解 DeepSeek Harness 中一次已落地的 bug 修复与技术决策通过新增函数插件deepseek-ai/dsh-session-stats注册sessionStats会话投影单元把 Web 聊天统计条中的「N turns · M steps」、LLM/工具墙钟时间、TTFT/吞吐平均值从已加载窗口折叠改为全会话持久投影使任何数字都不再随历史分页而变化。读完本文你将掌握 session-projection 能力缝的用法、sessionStats投影单元每个字段的精确折叠语义、客户端回退策略以及该方案在源码与测试中的完整验证依据。问题背景分页窗口导致统计条数字越翻越多Web 聊天界面的统计条stats strip由StatsLine组件渲染其中除 token 账目外的每个非 token 数字——「N 轮 · M 步」计数、LLM 与工具墙钟时间、TTFT/吞吐平均值——都来自对已加载会话窗口的即时折叠deriveStats遍历chat.legacy.nodes见 StatsLine.tsx。问题在于历史消息按每页 50 条分页每点一次「加载更早」Load earlier窗口变大所有数字随之增长。原文档给出了一个直观的例子7 turns · 44 steps 在翻一页后变成 10 turns · 89 stepsLLM 时长也以同样方式攀升。而产品预期是统计条应展示与客户端加载了多少历史无关的全会话数字。值得对照的是同一条统计条里的 token 账目早已采用了正确的架构——它读的是持久的tokenUsage投影useProjection(tokenUsage)因此不受分页影响。本次修复的目标就是把其余数字全部迁移到同一条架构轨道上。架构决策注册sessionStats投影单元解决方案是在 packages/session/session-stats 中新增函数插件deepseek-ai/dsh-session-stats在ctx.sessionProjections上注册sessionStats投影单元并作为 web-app bundle 的一行挂载。插件入口非常精简src/index.ts/** Cordis plugin name. */ export const name session-stats /** The projection registry is the plugins whole purpose; without it the fiber stays pending. */ export const inject [sessionProjections] export function apply(ctx: Context): void { ctx.sessionProjections.register(sessionStatsProjectionDefinition) }关键设计点投影单元只负责折叠投递完全复用现有的投影缝——history 尾页块、session/projection推送帧、列表行——apiproxy、wire schema 与客户端运行时零改动。插件在 web-app bundle 中挂载在 cordis.patch.yml 中有一行- id: session-stats、name: deepseek-ai/dsh-session-stats注释明确说明它是为聊天统计条服务的全会话 turn/step 计数sessionStats 投影键投影注册表本身是 base 层的行。无注册表时单元保持惰性inject: [sessionProjections]使 fiber 在缺少该服务时保持 pending、什么都不注册因此未组合投影注册表的装配不会获得sessionStats键。卸载即移除注册是插件 fiber 上的 effect插件卸载时键随之消失HMR 安全。数据模型与字段语义sessionStats投影的值携带统计条完整的非 token 数字集字段名与客户端窗口折叠一一对应以便两者整体互换字段含义turns携带至少一条已关闭步的不同 turn 数被拒绝或空的 turn 不计数steps已关闭步数——完成、失败、取消、max-tokens 的步全部计入llmMs组装出消息的步上step/start→assistant/message的模型墙钟时间总和toolMs按 callId 配对的tool/call→tool/result墙钟时间总和ttftMs/ttftSteps首 token 延迟总和以及记录到首 token 的步数decodeMs/decodeTokens上报 usage 的步上解码墙钟时间与提供商输出 token 的总和每个字段在首个贡献事件落地前都是 0已组合注册表的装配始终提供该键客户端直接读值而不是检测键是否存在。类型声明位于 src/types.ts 的SessionStatsProjection接口并通过declare module deepseek-ai/dsh-session-projection/types合并进SessionProjectionMap成为该投影键的唯一归属处。折叠规则从会话事件流到全会话数字折叠实现位于 src/projection.ts是一个纯同步的事件折叠。状态包含八个总计turns、steps、llmMs、toolMs、ttftMs、ttftSteps、decodeMs、decodeTokens外加三个在途边界lastTurn——最近一次被计数的step/end所在 turn。因为 turn 号由 host 单调分配一个槽位即可判定新 turn 的首个关闭步openStep——当前打开步的边界事实turn、step、startTime、firstTokenTime由该步的assistant/message关闭pendingCalls——尚未返回结果的工具调用派发时间按 callId。折叠的具体规则与单元测试一一对应不相关事件返回同一个 state 引用注册表用Object.is门控变更流因此无关事件不会产生下游推送。TTFT 记录每步首个非空 delta chunk且在同一步内的llm/retry后仍然保留——这与窗口折叠的resetForRetry对齐空 delta、非 token chunk、步外 chunk 一律忽略。decode 时长覆盖首 token → 已组装消息仅在同时上报了有效 usage 的步上累计畸形 usage 报告会被忽略与窗口折叠对节点 usage 的防御一致重复的assistant/message不会二次累计。toolMs按 callId 配对tool/call→tool/result孤儿 result无对应派发记录被忽略未解决的调用在turn/end时清空——因为 result 一定在其 turn 内落地丢弃残留可以避免持久化状态无限增长。此外用Object.hasOwn做自有键检查callId 来自模型/工具 JSON 边界若一个未记录调用恰好叫constructor之类的原型属性名必须读作未匹配而非继承函数防止toolMs被 NaN 污染。负时钟偏移被钳制为 0Math.max(0, ...)。为什么计数事件选step/end而不是assistant/message这是本次设计中最重要的正确性决策。直觉方案是按已组装消息计数但在评审时发现两个缺陷max-tokens 步会追加一条空内容的assistant/message它仅用于承载 usage从不进入 surface按消息计数会把 transcript 上看不到的步计进去多计。被取消的步在消息组装前就中止完全没有assistant/message但客户端会合成可见的 interrupted assistant 节点按消息计数会悄悄丢掉常见的取消步少计。而step/end对每个进入的步在 agent 循环的finally中恰好追加一次因此完成、失败、取消、max-tokens 的步都恰好落一条同时计数在步结算时推进与窗口折叠推进的时机相同直播期间行为不发生可见变化。src/projection.ts的模块注释把step/end称为步生命周期权威事件step lifecycle authority。客户端接入useProjection优先窗口折叠兜底StatsLine的接入方式StatsLine.tsx体现了一条干净的降级链const projected useProjection(sessionStats) const stats useMemo(() projected ?? deriveStats(settledNodes), [projected, settledNodes])有投影全部数字读useProjection(sessionStats)翻页与压缩都无法改变它们无投影未组合该单元的装配如 TUI、headlessprojected为undefined整体回退到窗口折叠deriveStats——因为字段名刻意一一对应两者可整体互换wholesale swap并且回退只发生在没有投影值被提供的时候。展示层按组渲染N turns · M steps计数组、LLM/工具时长组、TTFT/吞吐速度组pipe 分隔没有任何数据的组整体消失。token 组仍走tokenUsage投影缓存命中率与 input/output token 据此展示。另外客户端 connection fixture 按镜像每个已组合键的既有纪律以sessionStatsOf平行实现了该折叠确保客户端侧的契约测试与 host 端投影保持一致。备选方案评估为什么最终选择投影单元原文档记录了六个备选方案及其否决理由理解它们有助于把握架构边界备选方案结论统计assistant/message事件否决多计 usage 宿主消息、少计被取消的步上述两个正确性缺陷统计step/start事件否决覆盖等价先于每条step/end但计数会在步开始而非结算时推进——一个没有收益的直播期可见行为变化把单元注册进core/agent-loop事件生产方否决循环是产品主干把 UI 读模型放进去会给每个装配强加 session-projection 依赖违反用插件而非改循环与默认组合不带可选项把单元注册进token-meter折叠同批事件的现有单元否决turn/step 计数不是 token 度量每个投影键应住在拥有其领域的包里在客户端折叠全量日志否决客户端按设计只持有分页窗口投影 RFC 的不在客户端折叠规则正是为了让数字在分页、压缩与冷读之间存活墙钟时间/TTFT/吞吐保持窗口口径否决同样的分页问题同样落在 LLM 时长上全量计数与窗口时间混在一条统计条里读起来是一套自相矛盾的数字最终结论投影携带完整数字集合窗口折叠降级为无单元时的回退。组合与部署在装配中挂载该插件只需在已有dsh-session与dsh-session-projection旁追加一行见 包 README- name: deepseek-ai/dsh-session - name: deepseek-ai/dsh-session-projection - name: deepseek-ai/dsh-session-statsWeb 端则通过 web-app 的 cordis.patch.yml 的 bundle 行组合- id: session-stats投影注册表本身是 base 层已有行。首次 token 谓词isTokenDelta被移至deepseek-ai/dsh-llm/message与其判别的StreamChunk类型同处host 折叠与客户端计时索引共用同一实现client-runtime 对其做转发导出——避免两处实现漂移。后果、边界语义与已知限制修复落地后的直接效果统计条从第一个尾页起就显示全日志数字翻页不再改变任何分组。每个 web 尾页与列表行多携带一个小键单元内部状态在步边界与首 token chunk 处变化因此变更流每步会多发几帧值相同的推送。原文档与包 README 明确记录了几条与旧窗口语义的已定义边缘差异它们不是缺陷而是新语义的必然结果计数的是尝试过的工作而非可见输出在产生可见内容之前就失败的步仍以step/end关闭并计入被崩溃打断的步在会话重新加载、恢复为其补写合成step/endinterruptedTurnClosers后计入。被取消的步被计数但不计时没有消息组装出来其部分流时间不进入任何时长数字——与窗口把中断节点渲染为未计时一致。max-tokens 的 usage 宿主消息会贡献 surface 上看不到的模型时间。计数是日志口径而非 surface 口径后来被压缩掉的步仍保持计数数字描述整个会话而非当前模型可见的 surface。只在组合了投影注册表的地方挂载TUI 与 headless 装配不提供sessionStats键其消费者回退到窗口口径计数。测试与验证该修复的验证覆盖三层单元测试projection.spec.ts验证了投影单元的完整事件折叠语义包括空日志返回零值、跨 turn 计数与变更流 seq 通知、拒绝/空 turn 不计数、取消步计数、max-tokens 宿主消息不额外加步、插件迟挂载时对日志已有步的惰性折叠、无插件时无键且卸载后键消失HMR 安全、受控时间戳下的模型/首 token/解码时长累计、步内重试保留首 token 边界、空 delta 与步外 chunk 忽略、工具墙钟按 callId 配对与turn/end清理、原型属性名 callId 防御、无效 usage 跳过 decode、无关事件零累计与负时钟钳制。加载器组合测试loader-composition.spec.ts验证真实 Loader 组合下 YAML 形状可加载并服务全会话计数以及函数插件命名空间无默认导出。端到端测试stats-paged-history.e2e.ts是本次修复的钉死场景冷种一份28 轮56 条 surface 消息超过一页 50 条的确定性日志通过真实持久化 API 种子写入零模型调用。断言要点在不完整尾页上首轮m1尚未加载统计条就已读出全量28 turns · 28 steps点击「加载更早」补全旧页后统计条 textContent逐字符不变全量加载后按已挂载的 turn-tail 页脚计数验证窗口口径data-chat-flow-key^9:turn-tail应为 28。测试注释明确点题这正是投影修复的 bug——修复前窗口折叠按加载页数重复计数加载更早会让计数器增长。另两个曾把统计条当作已加载窗口探针解析的 e2echat-scroll-contract、complex-history.perf也改为统计已挂载的消息流行数/turn-tail 页脚。延伸阅读会话投影子系统文档——驱动各单元、提供快照与变更流值的注册表能力缝session-projection 包——单元所注册的注册表契约session 包地图——相邻的持久化、投影、标题与遥测包本决策的完整记录英文 / 中文。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考