dsh-token-panorama
v1.0.4
Published
Token panorama for DSH: totals, cache hit rate, peak/off-peak cost, calendar heatmap, session tree, workspaces, CSV/JSON export, plus context clear/compact actions, prompt optimization, and auto answers for ask_user_question batches.
Maintainers
Readme
Token Panorama for DSH(用量概览 · 中文说明)
一个 DeepSeek Harness 功能插件,看清全部会话的 token 去向:一个面板汇总累计总量、日历热力图、趋势曲线、
模型分布、逐会话用量明细,以及 CSV / JSON 导出。另带三组工具:上下文清理 / 压缩按钮、提示词优化按钮,
以及在你不在场时代答 ask_user_question 的问答功能。
数据全部来自平台自带的会话持久化服务,不解析日志文件,也不依赖任何第三方库。
功能
- 累计统计:总 token 消耗,输入 / 输出 / 缓存读取 / 缓存写入各多少,调用了多少次。
- 缓存命中率:缓存读取占输入侧 token 的比例,命中和未命中的绝对量都列出来。
- 成本折算:按 DeepSeek 分时段价格(高峰 / 空闲)估算花费,峰谷金额分开列。
- 日历热力图:GitHub 风格的周列视图,也可以切成每月一个小日历。
- 趋势曲线:按日 / 周 / 月 / 年聚合,悬停时间点看具体数值。
- 模型分布:按用量排序展示各模型的 token 与调用次数。点某个模型后,下方统计(热力图、趋势、 成本、峰谷、会话列表)只算它一个,再点一次取消。
- 工作区分布:按会话头记录的工作目录分组,一眼看出哪个项目最耗 token。
- 会话用量明细:默认只列主会话,点箭头展开名下的子代理,卡片高度不会跟着子代理数膨胀。 表头可排序,归档会话带标记、能用复选框隐藏;时间范围变化后,每个会话只统计落在范围内的部分。
- 分页:每页 40 组会话,量再大也不会一次渲染全部行。
- 统一时间范围:顶部过滤器控制全局(全部 / 今天 / 近 24 小时 / 本周 / 近 7 天 / 本月 / 近 30 天),
下方每张卡片(热力图、趋势、成本、峰谷、模型、会话、工作区)都按同一段范围重算,
也可以自选
起始/结束日期。 - 小时粒度:范围缩到「今天」或「近 24 小时」,趋势图横轴自动切成小时,没活动的小时补零画出来, 折线不会把两段活动连成一段。
- 数据导出:按天明细导出 CSV / JSON,也可以单独导出会话级 CSV(含工作区、父会话、成本列)。
- 增量刷新:按会话的 revision 判断要不要重读,刷新时只解码真正变化的会话。
- 节假日自动更新:任意年份运行时联网拉取并缓存,跨年不用等新版本。
- 上下文操作:输入框下方 dock 里的「清空上下文」(橙)和「压缩上下文」(绿),也提供
/clear-context、/compact-context与/compact命令。压缩可以在会话运行中用:先定好截断点 再让模型写摘要,压缩期间新产生的对话原样保留、接在摘要之后,检查点复用平台自带的 「上下文已压缩」标记展示条数与 token。/compact的一次压缩会把命令行与标记折叠成一张合并卡片, 跟平台自带压缩一致。清空替换整个表层,因此要求会话空闲。 - 提示词优化:常驻输入框工具栏的「优化提示词」按钮,把草稿交给可配置的优化模型改写,结果粘贴 回输入框,附件不动。可以让请求附带当前会话的上下文摘录,改写贴合正在进行的任务,指代「这个函数」 的提示词能改写成指名道姓的版本。指令、模型路由和上下文开关在「提示词优化」设置页配置。 模型直接返回提示词正文,不带代码块也不带块外建议,因此填入输入框的内容就是最终要发送的内容; 若你自己改写了指令并要求围栏,插件只会剥掉包住整段回答的那一层,块外文字会一并留下。
- 问答功能:代替你回答
ask_user_question的提问,让你不在时运行不会卡住。默认关闭;权限审批、 凭据密钥、不可逆操作、付费与发布类提问永不代答。指令、模型路由与等待上限在「问答功能」设置页配置。 - 中英双语:跟随平台语言设置。
环境要求
- DSH
>= 0.2.1-rc.3(在dsh.engines.dsh中声明)。 - 使用 Web UI 的 profile;插件注册到
sidebar.panellist与main两个插槽。
安装
在「插件」页面安装,或通过 plugin manager 指向本包目录(含 package.json 的文件夹):
plugin_manager install_bundle target="<本目录的绝对路径>"安装后侧边栏出现 Token 统计 入口,点击即可打开主面板。
卸载:在「插件」页面卸载 dsh-token-panorama 即可。
npm 包与下载量口径
本插件同时发布到公共 npm registry,包名 dsh-token-panorama。目录站(awesome-dsh-plugin)与
应用内市场可据此按包名安装并显示下载量;未发布到 npm 时才回退到 GitHub Release 的 tgz 附件。
- 发布新版本:
npm publish --access public。npm 要求开启两步验证,或使用勾选了 Bypass 2FA 的 granular access token。 - 目录站会读取仓库
package.json的name,在 registry 上确认同名包,并要求该 npm 包的repository字段回指本 GitHub 仓库。这层映射自动从 registry 抓取,条目里无需、也不允许 手写npm:字段。 - 市场显示的「下载量」是 npm 近 30 天(UTC 滚动窗口)下载数,来自
api.npmjs.org/downloads/point,由目录站每天夜间探测刷新。它不是「查看量 / 浏览量」: 目录与市场都不提供任何访问量字段。GitHub Release 附件的下载次数是另一套独立计数, 不会进入这个字段。 - 每次修改都会迭代
package.json的version,并发布新版本,使 registry 的latest与目录里的version保持一致。
使用
- 在侧边栏点击 Token 统计。
- 用两个日期输入框或快捷按钮调整时间范围。
- 热力图可在
周/月之间切换;趋势图可在日/周/月/年之间切换。 - 鼠标悬停趋势图任一数据点,查看该时间点的精确数值。
- 会话用量 表格默认只列主会话;点行首
▶展开该会话,展开后能看到它名下所有子代理会话。 点表头(会话 / 消耗 / 调用 / 创建时间)切换排序,取消勾选显示已归档可只看仍在使用的会话 (该勾选状态会记住,下次打开沿用上次的选择),导出会话 CSV落盘当前表格。 - 会话较多时用卡片底部的
上一页/下一页翻页,每页 40 组。 - 点击 刷新 重新扫描,或点击 导出 保存按天明细的 CSV / JSON。
- 点击 常规设置 打开设置窗口,修改时区、货币、峰谷时段、节假日与价目表,保存后立即重新计价。
面板打开时会自动刷新。首次扫描后只解码发生变化的会话,历史数据量很大时也能很快加载。
计费配置
插件的全部计费参数都在 profile 的配置里,在面板右上角点「常规设置」就能打开设置窗口在线修改, 保存后立即重新计价,不用重启 DSH。
设置窗口按结构编辑配置,不是按字段名逐条渲染:模型价格是一张表、峰谷是可复用的方案、 节假日是勾选清单,这几样通用的键值编辑器都表达不了。窗口分七个标签页:
| 标签页 | 内容 | | --- | --- | | 模型与价格 | 每个模型一行,未命中输入 / 命中缓存输入 / 缓存写入 / 输出四个独立价格框,峰谷开关与方案下拉,最后一列固定是删除 | | 峰谷方案 | 可复用的时段方案:名称、自动模式、若干「起止」窗口、空闲价比例 | | 节假日 | 系统预封装的日历,勾选即生效 | | 上下文 | 压缩指令、压缩模型路由、上下文占用、自动压缩与分段压缩开关 | | 提示词优化 | 优化指令、模型路由,以及请求是否附带当前会话的上下文摘录 | | 问答功能 | 代答指令、代答模型路由、等待上限,以及总开关 | | 通用 | 计费时区与货币单位 |
窗口底部还有导出配置和导入配置。导出的 JSON 只含计费设置,不含任何会话数据,可以直接分享给别人; 导入只填表,确认无误后点保存才写进 profile。
模型列表默认只列日志里实际出现过的模型(按用量降序),打开窗口就能给正在花钱的模型定价, 不用先知道模型叫什么。还没定价的模型显示「未配置」和「添加价格」按钮,点一下就建行。
每个模型独立定价。价目表里没有的模型走 defaultPrice,面板会在成本卡下面注明
「某模型无定价,按默认价计算」,不会闷头按 0 算。
价格拆成四项填,因为供应商对同一段输入的处理并不等价:
| 列 | 含义 | 不单独计费时 | | --- | --- | --- | | 未命中输入 | 首次送入、没命中前缀缓存的部分 | 填官方标价 | | 命中缓存输入 | 命中前缀缓存、按折扣价读取的部分 | 填 0 表示不收取 | | 缓存写入 | 把输入写入缓存的部分 | 填 0 表示不收取 | | 输出 | 模型生成的内容 | 填官方标价 |
缓存这两项必须分开:写入是存进缓存,读取是从缓存取,单价通常差十倍以上。合成一列的话,正在预热 缓存的会话会算得偏便宜,稳定复用缓存的反而偏贵。缓存写入也不算进命中率的分母,它是写入,不是读取。
峰谷是独立配置,一个方案可以给多个模型复用:模型行的开关打开后选方案名就行。开关关着的模型 全天按标价(高峰价)计费,归入「高峰时段」,它实际付的就是这个价。方案改名时引用它的模型跟着 改;删掉方案会留下悬空引用,这时该模型仍按高峰价计费,行内提示「方案已删除,请重新选择」, 可以重选。
空闲价按比例算。方案里的「空闲价比例」是标价的百分比,默认 0.5 就是半价,所以价格表只维护一份, 改比例会把高峰和空闲两个时段一起重新计价。
自动模式
「自动模式」决定一段时段的生效日期,参考闹钟响铃规则:
| 模式 | 含义 | | --- | --- | | 每天 | 不限星期与节假日 | | 法定工作日(智能跳过节假日) | 周一至周五,且不在已勾选的节假日里 | | 法定节假日(智能跳过工作日) | 只在已勾选的节假日生效 | | 周一至周五 | 只看星期,不管节假日 | | 自定义 | 七个星期勾选框自由组合 |
时段窗口
每个方案可以有多条「起始 – 结束」窗口,24 小时制。多条窗口不是摆设:DeepSeek 的高峰是两个 不连续的区间(09:00-12:00 与 14:00-18:00),午休走空闲价。写成一条 09:00-18:00 的话,午休 那段会被算成高峰,高峰 token 从约 4.0 亿虚增到约 4.5 亿。
end 小于 start 时按跨零点处理,22:00-02:00 不需要拆成两条。
节假日
节假日日期在运行时联网拉取,缓存在 profile 下,跨年不用等新版本。 只补没有数据的年份:已发布的年份不再变,还没发布的年份(比如下一年)每天重试一次。 三个免密数据源按顺序尝试,全挂了就保留已有缓存。
插件内置了 2026 年的日期当种子,第一次运行、还没发出网络请求之前计价就是对的。 设置里显示已加载的年数,也有一个强制重新拉取的刷新按钮。默认勾选法定节假日那一组。
内置清单只是种子数据,不是权威来源:某一年一旦拉取成功,以网络副本为准。
上下文操作
输入框下方的 dock 里有两个按钮,和上下文用量环在同一行:左边橙色清空上下文,右边绿色压缩上下文。
它们同时也是斜杠命令 /clear-context 与 /compact-context,不用鼠标也能触发。
两者的差别在替换范围。清空取整个表层,因此要求会话空闲:改写期间追加的步骤会跟着它依赖的 历史一起被丢掉。压缩只取调用摘要之前就选定的一段,终点落在最后一个不切断 tool-call/result 配对的位置,所以会话运行中也能用,压缩期间写入的内容会原样重放在检查点之后。两种情况下 会话和日志都原样保留,事件还在磁盘上,只是表层不再显示被替换的部分,上下文窗口也就腾出来了。
- 清空:写入一行占位说明。它不是摘要,下一条消息之前的内容都不再保留。
- 压缩:直接调用模型写出摘要,再用它替换那段历史。默认沿用会话的 provider 与模型, 可在「上下文」标签页覆盖。这段历史按原样的消息重放给模型(同样的系统提示词、同样的 工具定义,指令放在最后),provider 的前缀缓存能覆盖整段历史,这次调用按缓存命中计价, 不用把历史重发一遍。全程不派子代理,没有子会话也没有属于自己的工具,不会顺手改动任何东西。
两者完成后,对话里会出现一条命令卡片,按钮下方不再给一份重复的提示。两个按钮的反馈文本和
统计口径不一样:清空报告替换前后的条数与 token,压缩还会多报写入摘要的字符数。它们走平台
原生的 command/run / command/done 事件,卡片可见、可展开,但不进会话表层,不会污染模型上下文。
压缩用的提示词在设置窗口的「上下文」标签页里可编辑,同一页还能指定压缩模型的 provider 与模型,留空即沿用会话的。默认提示词是一份中文的「工作状态存档」指令,要求写清目标、 决策及理由、被否决的方案、原样路径与签名、报错原文、精确数值和未完成事项,按八个章节组织, 并明确禁止提及压缩本身。
内置的三段默认提示词(压缩、优化、问答)共用同一套结构:# 角色定义、## 核心能力、
## 核心红线、## 工作流程、## 交付格式。它们分属「上下文」「提示词优化」「问答功能」三个标签页,
但共用同一套骨架,改其中一段时其余两段照着对齐即可。默认值存放在 lib/contextops.js 的 DEFAULT_COMPACT_PROMPT / DEFAULT_OPTIMIZER_PROMPT
与 lib/ask.js 的 DEFAULT_ASK_PROMPT,test/prompts.test.mjs 会把这三条性质(同一套五标题结构、
自身只用半角标点、各自的专属条款)钉死,避免以后改写时漂移。
三者都遵守两条与调用点直接相关的约定:
- 优化指令不要求代码块。改写结果会被原样填入输入框,而接收端只会剥掉包住整段回答的首尾围栏; 若提示词要求「代码块 + 块外建议」,那些建议文字会一并进入待发送的草稿。
- 压缩指令里的标点规则只约束模型自己写的叙述。被逐字复现的原文(引号内内容、用户原话、 报错信息、代码、配置值)保持其原始标点,不得替换——这与「逐字保真」是同一件事。 分段压缩时,输入如带「第 N/M 段」或「合并」说明,一律以分段说明为准。
同一页还有三块与「窗口还剩多少」有关的内容:
- 上下文占用:显示当前会话按「系统提示词 + 工具定义 + 对话消息」三项算出的真实总占用, 以及所选模型的输入上下文上限、占用比例与三项分项。
- 自动压缩开关与触发阈值(默认 75%):决定本插件在每一步开始前自动压缩的时机。
同一会话两次压缩之间至少间隔一分钟,避免刚压完仍超阈值时每一步都再付一次摘要。
触发挂在与平台引擎同一个
agent/pre-step事件上,但由本插件执行,不写任何平台配置。 某个 profile 完全可能禁用平台引擎(dsh-compaction-basic可以被整行关掉,preset 内嵌 的行本来也改不到),那样开关存进去了也没人生效。判定用的是上面那三项 真实总和,不是平台面板顶部按 provider usage 锚定的那个数字,后者在长会话里可以显示 112K,而同一份请求实际带着 2.3M。 - 超出上限时分段压缩:决定历史大于摘要模型窗口时怎么办。关闭时丢弃最早的部分,只把 最近能装下的记录交给模型,并用显式标记写明丢了多少条。开启时按上限切成多段,并行各写 一份摘要,再由最后一次请求整合成一份替换整段历史,不丢历史,也不会有哪次请求越过窗口。 长对话压缩时模型无响应或返回空,原因就在这里。
- 压缩前会先量一次体积。历史若已超过摘要模型窗口的 2/3 且没开分段,就退回到「头尾摘录」: 开头保留目标与约束,结尾保留正在进行的事,中间以显式标记省略。被压缩的那一段伸进正在 运行的轮次也没关系,交给模型的就是这些消息本身,摘要正好覆盖检查点要替换的内容。
- 摘要被输出上限截断时是重试,不是接受。截断的检查点会用一份说到一半就停的摘要替换真实历史,所以
第一次尝试沿用会话当前发出的输出预算,之后每次重试把预算翻倍,上限是 64000 tokens(平台自己为压缩
预留的输出量),历史、提示词与模型都不动。只有
max-tokens会重试;适配器报错在第一次就抛出,不 为同一个错误付两次钱。所有档位都被截断时放弃这次压缩并点名截断原因,不会有半份摘要替换历史。 - 压缩锁在调用摘要之前就打开,不是之后。第二次请求会直接答「已有压缩在进行中」, 不会为同一份摘要付两次钱,平台自己的引擎也会让路。
对已经清空过的会话再清一次是空操作,按成功返回:拿一个占位换另一个同样的占位不算错误。
问答功能
模型需要人做决定时会调用 ask_user_question 并停下等待。本插件可以代你回答这批问题,让运行不会因为
你不在而卡住。它默认关闭:开关关着时插件对每一批问题都原样放行,提问照旧弹到你面前。
机制用的是平台自己的:提问经由 user-questions/request 上的 cordis waterfall 发出,任何监听者都可以
作答,谁先返回一批答案就由谁生效。插件以 prepend 注册这一个监听者,因此会排在人机卡片之前被询问
(排在它后面的话卡片先弹出来,代答永远没有机会)。作答是一次直接的 llm.stream 调用,只带问题与指令,
不传工具:模型除了文本什么也做不了。
除了一次确定的回答,其他所有结果都会把问题交回链上:开关关着、问题落在禁区、没有可路由的模型、答案解析 不出来、选项不是问题里给过的、调用失败、等待超时。问题随后照常到你手上,所以这里出问题最坏是多看一次 卡片,不会让问题消失。
无论如何都不会代答的:权限与审批、计划评审(任何带 intent 的批次)、凭据与密钥、API Key 与验证码、
删除与一切不可逆操作、付费与购买、发布与部署。检查是对问题正文和每个选项做关键词匹配,刻意宁滥勿缺:
误判的代价是多看一张卡片,漏判的代价是模型替你做了一个绝不该由它做的决定。
设置窗口的问答功能标签页里有开关、代答模型读到的指令、它的 provider 与模型(留空沿用会话的),以及
等待上限。上限按秒计,与 ask_user_question 同一口径:-1 表示不限时,其余必须是 1 到 2147483 的整数。
这个范围来自平台本身,工具会拒绝的值这里也存不进去。
答案在你看得到的地方有标注:模型给出的答案末尾带着是哪个模型答的(由 <provider>/<model> 代答),写在
答案正文里,因为问题服务只搬运 {id, selected, custom} 三项,其余字段会被丢掉。这次调用的花费记在工具账
本的 ask-answer 名下,并在用量概览里以「代答 N」显示,与压缩、优化并列。
答案也不会写进会话记录:问答卡片显示答了什么,下一轮的内容是模型自己的产物,因此代答做过的决定不会以 「你说过」的形式混进之后的摘要。
校验
所有值在保存前由平台 schema 与插件自身各校验一次:非法时区、非法 HH:MM、负数价格、
不在 0–1 的开区间内的空闲价比例、未知的自动模式都会被拒绝,并指出是哪一项不合法。
升级
1.0 的配置结构(peakWindows 单表、hitPeak/missPeak/outPeak 六字段价格、日期数组
形式的节假日)会在读取时自动迁移:高峰价成为新的标价,启用了空闲价的模型自动挂到
standard 方案上,日期数组折回对应的日历键。迁移是只读的,磁盘上的旧配置不被改写,
直到你在窗口里点保存。
窗口内所有文案都在 client.js 的 ZH / EN 字典里,两种语言各 285 个键,键集完全一致。
保存走平台的 configEditor 服务:先做 schema 校验,再把整份配置写进 profile 的
cordis.patch.yml 并触发热重载。插件在保存前也会自己校验一遍,把错误定位到具体字段,
而不是抛一个 Loader 级的失败。若当前 profile 没有该服务或对应条目,窗口会退化为只读并说明原因。
说明:DeepSeek 的高峰规则字面是「周一至周五」,所以调休补班(周末上班)的日子仍按空闲价计算, 面板不会把补班日算成高峰。价格单位是「每百万 tokens」,与官方公告一致。
数据统计口径
- 数据源:Host 的
sessionPersistence服务列出全部会话及其不透明版本号revision。版本号未变的会话直接 跳过;其余会话先只走一遍日志的 Zstandard 帧表(不解压),只解码上次扫描之后新增的帧——语料没变时 一个字节都不解压,需要重读时也只解码日志尾部而不是整段历史。 - 计数:每条带
data.usage的assistant/message事件计为一次调用。总量优先取usage.totalTokens, 缺失时回退为inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens。 - 范围:当前 profile 下的全部会话,包含子代理会话(
header.origin === 'subagent')与已归档会话; 页脚分别统计主会话、子代理和归档数量。 - 会话标题:从每个会话自己的
session/title事件中取最后一条,不依赖可变的元数据。 - 归档标记:读
workspaceRegistry.archivedSessionIds;该服务缺失时按「无归档」处理,不影响其他统计。 - 工作区:取会话头事件(
type: "session")里的cwd;没有 cwd 的会话归到「(无工作区)」。 - 父子关系:取会话头里的
parentSession。父会话不在本次统计内时,该子代理按顶层条目展示。 - 时间桶:按事件时间戳所处的本地日历日归类;计费额外按小时划分,小时桶用配置时区换算。
- 缓存:折叠结果写入
~/.dsh/storages/dsh-token-panorama/usage-cache.json(CACHE_VERSION = 5), 通过临时文件 + 重命名原子写入。每条记录同时保存已折叠到的字节偏移与最大序号,下一轮可直接续读。 - 投影种子:Host 挂载
sessionProjections时,本插件注册自己的折叠单元(tokenPanoramaUsage), 由 Host 在事件提交时持续维护。投影行已经追平日志末尾的会话完全不读日志,直接采用该行。只有在状态版本、 时区、水位线和日志身份全部吻合时才采信;任何一项不吻合就退回读日志,所以种子只会省事,不会改变结果。 没有该服务时行为与原先一致。读不出来的日志按revision记住十分钟,不再每轮重复解码。 - 准确性:总量严格等于底层
usage记录之和,面板不做任何估算;会话行跟随所选时间范围, 各会话在该范围内的用量之和与热力图对应日期完全一致;成本按小时计价后汇入日、模型与会话三个维度, 三者的求和恒等于总量。
HTTP 接口
Host 在 /token-stats/api 注册前缀路由。请求必须通过平台的 Host / Origin 校验
(ctx.connection.requestRejection),且为 POST + JSON 体。
| 方法 | 请求体 | 说明 |
| ------------ | ------------------- | ----------------------------------------------- |
| summary | {} | 返回当前快照,仅重新扫描发生变化的会话。 |
| summary | {"refresh": true} | 不信任本插件的折叠结果并重建,仍会采用能通过校验的 Host 投影行。 |
| refresh | {} | 等价于强制刷新。 |
| config | {} | 返回当前配置、字段描述与可编辑状态;models 附 Host 实时模型目录(含各自上下文上限)。 |
| saveConfig | {"value": {...}} | 校验并保存配置,返回保存后的配置与字段描述。自动压缩由本插件执行,不写任何平台配置,因此没有「是否同步成功」这一项。 |
| holidays | {} | 强制重新拉取节假日数据,返回是否变化与按年目录。|
| clearContext | {"sessionId": "..."} | 把该会话的整个表层换成一条占位说明。 |
| compactContext | {"sessionId": "..."} | 直接调用模型压缩该会话,并用摘要替换表层。 |
| contextPressure | {"sessionId": "..."} | 量一次该会话:总 token、系统提示词/工具定义/对话消息三项分项、路由模型的上下文上限与占用比例。 |
响应结构:
{
"ok": true,
"value": {
"generatedAt": 1793511000000,
"lastScanAt": 1793510999000,
"scan": null,
"sessions": { "total": 0, "subagent": 0, "main": 0, "archived": 0 },
"totals": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0,
"total": 0,
"calls": 0
},
"days": [["2026-11-01", 0, 0, 0, 0, 0, 0, 0]],
"models": [["deepseek-v4.1-flash", 0, 0, 0]],
"cache": { "hits": 0, "misses": 0, "hitRate": 0 },
"billing": {
"currency": "CNY",
"timeZone": "Asia/Shanghai",
"cost": 0,
"peakCost": 0,
"offPeakCost": 0,
"peakTokens": 0,
"offPeakTokens": 0,
"peakCalls": 0,
"offPeakCalls": 0,
"hitRate": 0,
"hits": 0,
"misses": 0,
"priced": ["deepseek-v4.1-flash"],
"unpriced": []
},
"sessionRows": [],
"firstDay": "2026-10-30",
"lastDay": "2026-11-01"
}
}days 每行是 [日期, 输入, 输出, 缓存读取, 缓存写入, 合计, 调用次数, 成本];
models 每行是 [模型名, 输入, 输出, 缓存读取, 缓存写入, 合计, 调用次数, 成本];
sessionRows 每行是
[会话 id, 标题, 创建时间, 是否子代理(0/1), 是否已归档(0/1), 合计, 调用次数, 父会话 id, 按天明细,
成本, 工作区, 按小时明细, 用过的模型名],其中按天明细为
[日期, 输入, 输出, 缓存读取, 缓存写入, 合计, 调用次数, 成本],按小时明细为
[小时, 模型名, 输入, 输出, 缓存读取, 缓存写入, 合计, 调用次数, 成本, 是否高峰(0/1)]。
归档状态取自 workspaceRegistry.archivedSessionIds,归档会话的日志保留在磁盘上,
因此照常出现在 sessionPersistence.list() 中。
按小时明细是会话级的最小事实单元,整张表的每个数字都能从它推出,前端按范围、按模型筛选会话时 不必回 Host 再问一次。高峰标志随行下发:只有 Host 知道峰谷时段与节假日日历,前端自行推算就得 复制同一套规则,两套规则迟早会漂移。
三个额外的按模型字段供前端做「只看某个模型」的联动,避免二次请求:
modelDays:[模型名, [按天明细…]],用于按时间范围重算该模型的每日曲线与热力图。modelHours:[模型名, [按小时明细…]],范围缩到一天以内时用它画小时曲线。modelBilling:[模型名, {cost, peakCost, offPeakCost, peakTokens, offPeakTokens, peakCalls, offPeakCalls, hits, misses}],用于重算该模型的成本、峰谷与命中率。
各类行统一为 14 列(带 key 的 days / hours / models 行为 15 列):
[输入, 输出, 缓存读取, 缓存写入, 合计, 调用次数, 成本, 高峰输入, 高峰输出, 高峰读取,
高峰写入, 高峰合计, 高峰调用次数, 高峰成本]。高峰块与全量块同宽同序,所以任何一张卡只要求出
当前范围的行就能同时得到成本与峰谷占比,两者不会描述不同的时间切片。
三者的求和都与全局 totals、billing 完全一致。
包结构
package.json manifest:exports、dsh.bundle.patch、dsh.client、engines
cordis.patch.yml 把本插件插入 profile patch
index.js Host 半入口:Config 校验、HTTP 路由、插件生命周期
lib/time.js 时区换算、小时分桶、数值归一
lib/scan.js 把会话事件折叠成「小时 × 模型」档位
lib/schema.js Config schema、默认价目表、字段描述
lib/billing.js 峰谷判定、计价、旧配置迁移
lib/holidays.js 节假日联网拉取、按年缓存、内置种子
lib/contextops.js 清空 / 压缩上下文的表面替换事务、真实三项测量、分段与整合、压缩与优化默认指令
lib/ask.js 问答代答:禁区筛查、答案校验、调用代答模型、默认代答指令
lib/store.js 增量缓存、帧级扫描与快照(逐日 / 逐模型聚合)
lib/frames.js 遍历日志的 Zstandard 帧表,只解码新增部分
lib/projection.js 交给 Host 持续维护的 tokenPanoramaUsage 投影单元
lib/http.js 请求体读取与 JSON 响应
client.js Client 半:面板、热力图、趋势图、折叠会话树、工作区、导出
locale/en.json 插件元数据(英文)
locale/zh.json 插件元数据(中文)
icon.svg 插件图标(亦可通过 "./icon" 导出)
icon-prompt-optimize.svg 「优化提示词」按钮图标(同款图形内联在 client.js)界面文案位于 client.js 的 ZH / EN 字典,通过 ctx.locale.register 注册;locale/*.json
只承载「插件」页面显示的标题与描述。
schemastery 由运行时从 profile 目录解析(见 schemaLibrary()),不写进 package.json
的 dependencies:本插件以 link: 方式安装,包自身的依赖不会被安装,声明了反而会导致加载失败。
说明与限制
- 热力图、趋势与计费按配置的
timeZone归类(默认Asia/Shanghai,与 DeepSeek 的计费口径一致)。 - 历史很长时热力图按周生成列,超出宽度横向滚动,而不是压缩到看不清。
- 用量严格按记录的模型归类;早于用量记录功能的会话不贡献数据。
- 价目表里没有的模型按
defaultPrice计价,并在「预估成本」卡片上点名提示,不会静默套价。 - 调休补班日(周末上班)按规则字面仍计空闲价;如需按补班计高峰,把峰谷方案改成「自定义」并勾上对应星期即可。
- 本插件只读取会话持久化与归档列表,注册一条 HTTP 前缀路由,并提供一个 Config schema。
- 上下文操作会改写会话表层(
compaction/*与一次表面替换),因此需要commands、tokenMeter、agents、llm四个平台服务;平台缺其中任何一个时,这两个按钮与命令在该 profile 里不可用, 统计功能不受影响。日志里被替换的历史事件不会被删除。 - 压缩必须先跑完摘要才能改写,因此一次压缩的耗时取决于会话长度与所选模型; 期间不会占住会话,可以继续对话,改写会在会话空闲时进行。
