dsh-gokifu-compact 1.11.13 → 1.11.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/engine.mjs +37 -4
- package/src/rooms/ledger.mjs +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 1.11.14 — 2026-10-31
|
|
4
|
+
|
|
5
|
+
### S3 · 降级要看得见:规则摘要会在热清单里明说「这次没有模型润色」
|
|
6
|
+
|
|
7
|
+
**问题**:模型调用失败时 `summarize` 会退到**规则摘要**(纯模板拼装)。这一步**静默发生** ——
|
|
8
|
+
用户只看到「压缩过了」,不知道这次的摘要**没有经过模型**,遇到摘要质量差时会以为插件坏了。
|
|
9
|
+
|
|
10
|
+
**改法**
|
|
11
|
+
|
|
12
|
+
- `summarize` 的**四条路径**(含 auto 与 manual)都写两个字段到图上:
|
|
13
|
+
**`lastFoldMode`**(`"rules"` / `"hybrid"` / `"llm"`)与 **`lastFoldDegraded`**(同一事实的布尔别名,
|
|
14
|
+
热清单/诊断读哪个都行)。**写失败绝不影响摘要本身**。
|
|
15
|
+
- 热清单在 `mode === "rules"` 时固定显示一行:**`⚠ 上次摘要是规则生成(无模型润色)`**;
|
|
16
|
+
`hybrid` 与旧图(没有这个字段)**不显示** —— 不制造假警报。
|
|
17
|
+
- 这一行**计入 `prePathChars`**,**500 token 预算仍然成立**。
|
|
18
|
+
|
|
19
|
+
### S4 · `minFold` 按窗口缩放:小窗口不再被 8000 卡住
|
|
20
|
+
|
|
21
|
+
**问题**:`minFoldTokens` 原来是固定 **8000**。窗口小的模型(例如 32K)永远够不到这个门槛 ⇒
|
|
22
|
+
**永远不 fold**,历史一直堆到溢出为止。
|
|
23
|
+
|
|
24
|
+
- `resolveSpec` 返回 **`minFoldTokens = max(8000, 窗口 × 0.08)`**,`selectRange` 用它;
|
|
25
|
+
**overflow 分支回落配置值**(溢出自救不该被这个下限挡住)
|
|
26
|
+
- **显式配置优先**:`8000` 只作为**默认哨兵**(注释里写明了这点),用户写了值就按用户的来
|
|
27
|
+
|
|
28
|
+
**测试 +9(533 → 542)**:`ledger` S3-1/S3-2(提示行出现与不出现)、
|
|
29
|
+
`engine-harness` S3-3…S3-5(四条路径写标记 / 旧图不显示 / 预算仍成立)+ **S4-1…S4-4**
|
|
30
|
+
(缩放公式 / 显式配置优先 / overflow 回落 / 边界)、`manual-compact` MC-4 补 manual 标记。
|
|
31
|
+
变异:删提示行 / rules 不写标记 / 取消缩放 —— 三条逐条红。
|
|
32
|
+
|
|
33
|
+
### 验证
|
|
34
|
+
|
|
35
|
+
- 全量:**542 passed / 0 skipped / 0 failed**(**45 个测试文件**;533 + 9)
|
|
36
|
+
- `engine-harness` **39/0** · `ledger` **31/0** · `release-hardening` 22/0
|
|
37
|
+
- `verify-install` **16/16**
|
|
38
|
+
- `bench 2000`:**351 tokens**(与基线一致)
|
|
39
|
+
- `npm pack --dry-run --json`:**75 文件**(数量不变)
|
|
40
|
+
|
|
41
|
+
**未验证**
|
|
42
|
+
|
|
43
|
+
- S3 的提示行只在**夹具与引擎级**验证过「rules 才出现」;**没有在真机上制造一次真实的模型失败**去确认它在真实热清单里的位置与可读性。
|
|
44
|
+
- S4 的缩放只经单测覆盖;**没有在 32K 这类小窗口的真实会话里观察过 fold 频次变化**。
|
|
45
|
+
- 「500 预算仍成立」是**按 token 估算**的(夹具上的 prePathChars 统计),不是真机计费对账。
|
|
46
|
+
|
|
3
47
|
## 1.11.13 — 2026-10-31
|
|
4
48
|
|
|
5
49
|
### ① 手动压缩失败**说人话**(新增房间 R16 `manual-compact`)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-GOkifu-compact
|
|
2
2
|
|
|
3
|
-
> 当前版本:**`1.11.
|
|
3
|
+
> 当前版本:**`1.11.14`** · npm [`dsh-gokifu-compact`](https://www.npmjs.com/package/dsh-gokifu-compact) · [GitHub Releases](https://github.com/MYCF711/dsh-GOkifu-compact/releases) · 更新记录见 [CHANGELOG.md](CHANGELOG.md)
|
|
4
4
|
|
|
5
5
|
**DSH 的棋谱式上下文压缩后端 —— 把「历史 → 一段摘要」换成「历史 → 可复盘的棋谱 DAG + 每步只有 351 tokens 的热清单」。**
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
热清单 351 tokens (2000 边合成夹具 · L5 降级档 · 2026-10-30/10-31 两次实测同值;旧版全量 28,800)
|
|
9
9
|
失败保留 8 / 8 (历史口径 †;官方 8 段模板 4.67 / 8)
|
|
10
10
|
清单开销 未复现/待测 (原载 0.2% = 历史单会话 v1.3.1 后实测;无复跑命令、无对照组;源 evidence/真实计费核对.md)
|
|
11
|
-
验证
|
|
11
|
+
验证 542 项单测(0 skipped) · 真实包 8/8 † · 真实进程 fold 3~6/6 †
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
GO = 围棋,kifu = 棋谱,compact = 压缩。一局棋下完,棋谱上不只有落子顺序,还有每一手的评语。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gokifu-compact",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.14",
|
|
4
4
|
"description": "DSH context-compression backend with a go game record (kifu) DAG: node = position, edge = attempted move + result. Incremental fold (each span summarized once), failure memory that survives compaction, transposition detection (chess-engine style), and a tiny hot ledger rebuilt every step.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
package/src/engine.mjs
CHANGED
|
@@ -331,6 +331,21 @@ export default class ChessCompactionEngine extends BasicCompactionEngine {
|
|
|
331
331
|
}
|
|
332
332
|
/* ────────────────────────── 摘要(唯一官方钩子) ────────────────────────── */
|
|
333
333
|
|
|
334
|
+
/**
|
|
335
|
+
* ★ S3(task-186):把「本次 fold 的摘要来源」写进图(auto 与 manual 都经过 summarize)。
|
|
336
|
+
* `lastFoldMode` = "rules" | "hybrid" | "llm";`lastFoldDegraded` 是同一个事实的布尔别名
|
|
337
|
+
* (热清单/诊断读哪个都行)。写失败绝不影响摘要本身。
|
|
338
|
+
*/
|
|
339
|
+
#markFoldMode(g, mode) {
|
|
340
|
+
try {
|
|
341
|
+
if (g === null || typeof g !== "object") return;
|
|
342
|
+
g.lastFoldMode = mode;
|
|
343
|
+
g.lastFoldDegraded = mode === "rules";
|
|
344
|
+
} catch {
|
|
345
|
+
/* 图不可写时不打断摘要 */
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
334
349
|
async summarize(input, agent, signal) {
|
|
335
350
|
const session = agent.session;
|
|
336
351
|
const g = this.#graph(session);
|
|
@@ -352,11 +367,17 @@ export default class ChessCompactionEngine extends BasicCompactionEngine {
|
|
|
352
367
|
model: routed?.model ?? "rules",
|
|
353
368
|
});
|
|
354
369
|
|
|
355
|
-
if (mode === "rules")
|
|
370
|
+
if (mode === "rules") {
|
|
371
|
+
this.#markFoldMode(g, "rules");
|
|
372
|
+
return ruleResult();
|
|
373
|
+
}
|
|
356
374
|
|
|
357
375
|
const target = summarizeTarget(this.chess, session, agent);
|
|
358
376
|
if (target === undefined) {
|
|
359
|
-
if (mode === "hybrid")
|
|
377
|
+
if (mode === "hybrid") {
|
|
378
|
+
this.#markFoldMode(g, "rules");
|
|
379
|
+
return ruleResult();
|
|
380
|
+
}
|
|
360
381
|
throw new Error("chess-context: no provider/model available for summarization");
|
|
361
382
|
}
|
|
362
383
|
|
|
@@ -431,6 +452,7 @@ export default class ChessCompactionEngine extends BasicCompactionEngine {
|
|
|
431
452
|
this.ctx.logger?.warn?.(
|
|
432
453
|
`chess-context: 摘要模型全部不可用(${String(error?.message ?? error)})——退回规则摘要(零模型调用),保证 fold 能落地`,
|
|
433
454
|
);
|
|
455
|
+
this.#markFoldMode(g, "rules");
|
|
434
456
|
return {
|
|
435
457
|
summary: [{ type: "text", text: ruleSummary(g, this.#activeRange()) }],
|
|
436
458
|
rawOutput: undefined,
|
|
@@ -462,6 +484,7 @@ ${constraintsBlock.split("\n").map((l) => `- ${l}`).join("\n")}` });
|
|
|
462
484
|
this._lastSummaryProvider = used.provider;
|
|
463
485
|
this._lastSummaryModel = used.model;
|
|
464
486
|
}
|
|
487
|
+
this.#markFoldMode(g, mode);
|
|
465
488
|
return {
|
|
466
489
|
summary,
|
|
467
490
|
rawOutput: raw,
|
|
@@ -660,7 +683,8 @@ ${constraintsBlock.split("\n").map((l) => `- ${l}`).join("\n")}` });
|
|
|
660
683
|
pricedNodes: measurement.nodes,
|
|
661
684
|
eventAt: (seq) => session.eventAt(seq),
|
|
662
685
|
retainTokens: spec.retainTokens,
|
|
663
|
-
|
|
686
|
+
// ★ S4:用 resolveSpec 算出的窗口缩放值;overflow 路径没有 spec.minFoldTokens ⇒ 回落配置值
|
|
687
|
+
minFoldTokens: spec.minFoldTokens ?? this.chess.minFoldTokens,
|
|
664
688
|
overflow,
|
|
665
689
|
balancedBefore: (seq) => toolPairingBalancedBefore(session, seq),
|
|
666
690
|
balancedAfter: (seq) => toolPairingBalancedAfter(session, seq),
|
|
@@ -748,6 +772,7 @@ function resolvePolicy(chess, target) {
|
|
|
748
772
|
headroomTokens: chess.headroomTokens,
|
|
749
773
|
retainRatio: chess.retainRatio,
|
|
750
774
|
retainTokens: chess.retainTokens,
|
|
775
|
+
minFoldTokens: chess.minFoldTokens,
|
|
751
776
|
};
|
|
752
777
|
}
|
|
753
778
|
|
|
@@ -778,7 +803,15 @@ function resolveSpec(policy, contextWindow, reservedCompletionTokens, firstFold
|
|
|
778
803
|
if (retainTokens >= thresholdTokens) {
|
|
779
804
|
throw new Error(`chess-context: retainTokens (${retainTokens}) must be less than threshold (${thresholdTokens})`);
|
|
780
805
|
}
|
|
781
|
-
|
|
806
|
+
// ★ S4(task-186):minFoldTokens 默认按窗口缩放 —— `max(8000, 窗口×0.08)`。
|
|
807
|
+
// 旧默认恒 8000:小窗口下「1.2 步就够 fold」的夹具会被反复触发(每步都在压)。
|
|
808
|
+
// 显式配置优先:`minFoldTokens` 只要不是默认哨兵值 8000(preset 模板总写 8000 ⇒ 无法区分
|
|
809
|
+
// 「没配」与「配了默认值」),就按用户给的值走。
|
|
810
|
+
const DEFAULT_MIN_FOLD = 8000;
|
|
811
|
+
const configuredMinFold = Number(policy.minFoldTokens);
|
|
812
|
+
const scaledMinFold = Math.max(DEFAULT_MIN_FOLD, Math.floor(contextWindow * 0.08));
|
|
813
|
+
const minFoldTokens = Number.isFinite(configuredMinFold) && configuredMinFold !== DEFAULT_MIN_FOLD ? configuredMinFold : scaledMinFold;
|
|
814
|
+
return { thresholdTokens, retainTokens, contextWindow, minFoldTokens };
|
|
782
815
|
}
|
|
783
816
|
|
|
784
817
|
function summarizeTarget(chess, session, agent) {
|
package/src/rooms/ledger.mjs
CHANGED
|
@@ -599,6 +599,10 @@ export function renderLedger(g, { maxTokens = 500, tailFailures = 20, markers =
|
|
|
599
599
|
if (level === 4 && doomed(4) && doomed(5)) continue;
|
|
600
600
|
const lines = [];
|
|
601
601
|
lines.push("<chess-ledger>");
|
|
602
|
+
// ★ S3 降级可见(task-186):上一次 fold 的摘要是**规则生成**时固定打一行提示 ——
|
|
603
|
+
// 否则模型会把没有润色的规则事实当成正常摘要读。旧图没有这个字段 ⇒ 不显示(向后兼容)。
|
|
604
|
+
// 两个字段都认:`lastFoldMode`(规范字段)与 `lastFoldDegraded`(同时写的布尔别名)。
|
|
605
|
+
if (g.lastFoldMode === "rules" || g.lastFoldDegraded === true) lines.push("⚠ 上次摘要是规则生成(无模型润色)");
|
|
602
606
|
// ★ C-17 rev6:L3+ 的 root 裁剪从 180 收到 168 —— 给「最小路径行」预留 12 字符。
|
|
603
607
|
// 不这么做时,1.9.1 在 L3 恰好用满预算的会话(如 5b38b355 HEAD=497/1490 字符)
|
|
604
608
|
// 只要多一行 `# path: n185` 就 501,成为唯一一个「新增超预算」。路径信息优先于
|