- ホーム
- プラグイン
- セッションとメッセージ
- dsh-compact-manager
dsh-compact-manager
tony92921/dsh-compact-manager
Take over context compaction on both the manual and the automatic path, each runnable by a compaction model (falling back to a second model, or used directly) or by a mechanical fold that builds the summary card locally with no model call; the fold goes through the same official compaction transaction, so the replaced events stay in the append-only session log under their sequence numbers. It adapts code from dsh-compact-saviour and takes the mechanical fold from dsh-ledger-compact (both MIT) — install it instead of dsh-compact-saviour, not alongside it.
インストール
dsh plugin --profile web add github:tony92921/dsh-compact-managerREADME
dsh-compact-manager
接管上下文压缩,同时支持手动压缩和自动压缩,两条路径都提供模型压缩与 机械折页压缩两种引擎。
- 模型压缩 — 由你配置的压缩模型产出摘要。支持备用模型兜底(官方摘要器优先、连续 失败后切换),也支持直接使用备用模型,并可再挂一个备用模型 2 做二重兜底。
- 机械折页压缩 — 把旧历史换成短卡,不发网络请求、不消耗 token,速度以毫秒计。 折页同样走官方压缩事务,日志里留下与模型路径完全相同的标记三元组与 surface op, 原始内容保留在只追加的会话日志里,按 seq 可溯洄。
- 折页算法取自
dsh-ledger-compact。
压缩策略
压缩引擎决定谁来做压缩,两个手动按钮与 /compact 都跟随它:
| 引擎 | 自动压力/溢出压缩 | 手动按钮、/compact、/fast-compact |
|---|---|---|
| 机械 | 机械折页 | 机械折页 |
| 模型 | 按压缩方式 | 备用模型 1,失败切备用模型 2 |
压缩方式只在模型引擎下有意义,它决定自动路径怎么用模型:
- 官方优先,失败后救援(
rescue)— 先由官方摘要器处理,连续失败rescueAfterFailures次后才交给备用模型 1。 - 直接使用备用模型(
direct)— 自动压缩直接走备用模型 1,官方摘要器不参与。
两条路径上,备用模型 1 失败都会自动换成备用模型 2(若已配置)。
引擎选择会一次写入两处配置(replaceDefault 与 engine),所以不会出现自相矛盾的状态。
命令行的 ledger / saviour 覆盖上述一切,包括 replaceDefault——否则一个卡住的
配置就没有出口。
双模型回退
mode: direct 时自动压缩直接走备用模型 1;mode: rescue 时先让官方摘要器尝试
rescueAfterFailures 次,仍失败才交给备用模型 1。两条路径上,备用模型 1 失败都会自动换成
备用模型 2(若已配置)。
回退发生在 summarize 回调内部,不是包在 compactSurfaceRegion 外面。这个位置
是刻意的:压缩事务每次尝试都会写一组标记和一个 surface op,包在外面会让备用模型 1 的失败
留下需要清理的标记对,而部分提交根本无法重试。放在回调内部,事务只看到一次摘要调用,
补上的是哪一个模型产出的文本。
三条边界:
- 用户取消不换模型。 取消是你要求的,静默再花一次请求是反的。
- 只配一半的备用模型不生效。 有 provider 没 model 会在请求时
resolveModelInfo失败, 而那时代码已经用掉了备用模型 1 的尝试。保存时直接拒绝。 - 与备用模型 1 相同的备用模型会被去重。 同一个模型再试一次只会得到同样的错误。
路由租约(routes.js)激活时禁用回退:租约的存在就是要把这次压缩锁定在一个
provider/model 上计价,换模型会破坏它。
连通性测试
模型 tab 底部有两个按钮,分别发一次真实压缩请求测试备用模型 1 和备用模型 2,显示耗时。
能解析模型、能列出 reasoning 档位,都不证明它真的会回话。配对了 provider/model 依然
可能失败于:余额不足、密钥被撤、限流、模型拒绝自己的 reasoning 参数、或返回的散文
汇编器用不了。所以这里发的是和真实压缩同形状的请求(同样的 purpose: "compaction"、
同样的系统提示、真正的 transcript),不是一句 “hello”——后者在真实压缩失败时会通过。
三条性质:
- 不改动任何东西。 不写会话日志、不碰缓存、不动 job 状态,可以在这条对话上随便按。
- 耗时由 Host 测量,不含页面自己的往返,所以跨浏览器可比。
- 长度可选:极小 200 / 小 2k / 中 100k / 大 300k / 全量 900k。小模型在小输入上通过、 大输入上失败,这个差别正是「先测试再托付」的意义。极小档是便宜的连通性冒烟测试, 只证明路由会回话,不证明它能压缩一段真实历史——那个答案在大档位里。
- 最大档不是 1M。 窗口在输入和输出之间共享,1M 窗口的模型也要给输出预算和系统提示 留位置。按满窗口发过去,任何模型都会在预检里被判「测不了」,这一档就永远不可能通过。 900k 留出约 100k 余量,1M 窗口的模型能真正跑起来。
超时随档位放大(1.5 分钟到 15 分钟):一次 900k 的 prefill 会先排队好几分钟,固定超时会把 健康的模型报成坏的。档位超出模型声明的上下文窗口时,在发请求之前就拒绝,说明窗口多大, 并指出哪一档还能用——上传几兆字符后才从 provider 拿到错误,既慢又费钱。
reasoning level 不受支持时会在发请求之前拦下,并列出该模型实际支持的档位——
不会花掉一次请求才发现配错了。
失败会显示阶段(resolve / validate / request)和原始错误信息,成功会显示
输入/输出 token、模型返回的摘要开头。两个结果各占一行,互不覆盖,方便对比。
两个手动按钮
聊天输入框上方的原生上下文弹层里有两个按钮:
[ 手动模型压缩 ] ← 模型路径(原有行为)
[ 手动机械压缩 ] ← 本地折页:不调模型、不花 token
第二个按钮在没有配置压缩模型时也能用,那正是本地折页唯一可行的场景。
「手动机械压缩」默认需要点两次:第一下把按钮变成「再点一次确认折页」(4 秒后自动还原), 第二下才真正折页。关掉设置里的「折页二次确认」就是单击立即执行。
输入栏左侧不再有闪电按钮。它原本是上下文压力表和折页快捷键,但同一个折页动作在两个 地方各有一个入口,而压力读数在上下文弹层里已经能看到。
圆环(上下文按钮)现在只做它本来的事:点开上下文明细。它曾经还有一个长按 650 ms 直接 压缩的手势,已经删掉——该控件可见的含义是「给我看明细」,却藏着一个会改写历史的动作, 而且它绕过了折页自己的二次确认。压缩只能从上面两个按钮发起。
折页不是「丢弃」
机械折页同样走 compactSurfaceRegion,所以日志里得到与模型路径完全相同的
compaction/start + compaction/summary + compaction/end 标记三元组、相同的
surface op 形状和相同的稳定性检查。区别只在摘要器:foldSummary 从重放的消息里
直接构造短卡,不发网络请求。
这保证了折过的会话对历史 UI、撤销和其他压缩路径仍然可读。
设置页
设置 → 压缩管理,四个 tab:
- 模型 — 压缩方式、救援前失败次数、备用模型 1 与备用模型 2
- 自动压缩 — 引擎选择、自动压缩的 80%、headroom、retain,以及算出的实际触发点
- 机械 — 工具结果定形阈值、密图预算、密图节省比例、折页二次确认
- 诊断 — 模型压缩 / 机械压缩两组计数,命令提示
改动即时保存
没有保存按钮,任何改动都会在 250ms 后自动写入。为什么不是每次击键都发请求:
- 合并连续改动。 数字框拖动或连点几个开关只发一次请求。
- 串行化写入。 Host 每次保存都要解析模型元数据,两个请求并行可能乱序落地, 留下旧配置。
- 不被回包覆盖。 Host 会回显存下的配置;如果用户在往返期间又改了别处, 直接采用回显会把他刚改的字段改回去。只有「发出后没再改过」才采纳回显。
- 失败可见。 被 Host 拒绝(比如模型只选了一半)时,界面上的控件已经显示新值, 所以必须用红色错误条说明「未保存:<原因>」,且不自动消失。成功提示 「已自动保存」会在 2 秒后淡出。
自动压缩 tab
三个输入共同决定自动压缩什么时候发生,单独看任何一个都没有意义:
消息预算 = 窗口 − 输出预留
触发点 = min( 窗口 × 触发线比例 , 消息预算 − headroom )
保留原文 = 消息预算 × retain
headroom 是绝对值(默认 65536),所以同一个 80% 在不同窗口上含义不同:
| 窗口 | 触发点 | 由谁决定 |
|---|---|---|
| 1,000,000 | 800,000(80%) | 触发线比例 |
| 200,000 | 101,696(50.8%) | headroom |
| 131,072 | 32,768(25%) | headroom |
页面不自己重算这个公式,而是向 Host 要结果(?threshold=1),保证「页面承诺的」
和「引擎执行的」是同一个函数。算出的触发点、两个候选值、由谁决定,都直接显示。
thresholdOverride 默认开启,但三个默认值与 compaction-basic 自带配置完全一致,
所以不修改就等同于没接管。关闭则完全沿用官方配置。
保留比例如果接近触发线,保留段会吞掉整个可压区域,selectCompactableRange
每次都找不到可压内容。这种情况页面会显示已自动下调,而不是静默地永不压缩。
诊断 tab
诊断页的计数全部是本进程内的累计值,宿主重启后归零,且不分会话。
按插件实际工作方式分两组,两组互斥——「非模型压缩」里没有任何一项花 token, 「模型压缩」里没有任何一项省 token:
模型压缩:压缩次数、失败、共省、最近一次(前 → 后),以及一行上次用的模型
「上次用的模型」显示的是实际产出摘要的那个模型,取自提交结果而非设置。回退发生时 它显示备用模型 2,并用警告色标出「模型 1(x/y)失败,已用备用模型」——把两个模型都写出来, 是因为只看幸存者会掩盖回退曾经发生过。
非模型压缩:手动折页、接管折页、钩住引擎、已定形、其中贴图、定形共省、 定形失败、状态、会话 API
手动折页和接管折页分开计数:合并成一个数会掩盖 replaceDefault 是否真的生效过。
命令
/fast-compact # 按 engine 设置折一次
/fast-compact ledger # 强制机械折页(不调模型)
/fast-compact saviour # 交给压缩模型
/fast-compact status # 当前设置与可用性
折页不可展开,但原文没丢
官方压缩核心 @deepseek-ai/dsh-compaction 只导出前向能力,没有 undo / revert,
所以折叠之后没有「还原」按钮,任何插件都给不了。
但原始内容没丢:会话日志是只追加的,compaction/summary 事件的 shadowedSeqs
记下了每条被替换的事件号,原始事件永远在日志里,只是 surface 不再引用它。
需要原文时直接读日志文件、按 seq 定位即可——不需要插件提供命令。
入境定形
只裁当前回合紧邻上一步、还没发送的大 tool_result。已经发出去的节点逐字节不动——
改写它们会让 provider 缓存前缀失效,代价是整段重新 prefill。
安装
dsh plugin add dsh-compact-manager
或从本地源码链接:
dsh plugin --profile <名称> add link:/path/to/dsh-compact-manager
装好后在 设置 → 压缩管理 配置。插件包裹 live 的
@deepseek-ai/dsh-compaction-basic 引擎实例,所以不要卸载 compaction-basic——
官方的事务标记、surface op 和稳定性校验仍然全部来自它。
开发
node tools/build-client.mjs # 重新生成 lib/client.js
npm test # 单元测试
npm run probe # 客户端/宿主/静态三项检查
lib/client.js 是生成物,CI 会重新构建并检查 diff,所以改前端请改
tools/client-app.js 与 tools/style.js。
目录
lib/
index.js Host 入口:命令、HTTP、远程服务、入境钩子
config.js Config schema
controller.js 策略选择 + 两条压缩路径
compact-core.js 压缩事务(vendored)
pressure.js token meter 装饰器(vendored)
routes.js 压缩路由租约(vendored)
typert.js 远程描述符
ledger/ 折页、入境、密图、字库
client-vendor/ 圆环/压力表/动画原语(vendored,MIT,见 NOTICE;长按手势已剔除)
tools/ 客户端构建、探针
test/ 单元测试
许可
MIT。包含的第三方代码及其原始版权声明见 NOTICE。