dsh-memory-palace 1.4.0 → 1.4.1

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/README.md CHANGED
@@ -74,7 +74,7 @@ turn/end ──► 轻量兜底闸门
74
74
 
75
75
  ```bash
76
76
  dsh plugin --profile web add github:lovezi0/dsh-memory-palace
77
- # 锁定版本:dsh plugin --profile web add github:lovezi0/dsh-memory-palace#v1.4.0
77
+ # 锁定版本:dsh plugin --profile web add github:lovezi0/dsh-memory-palace#v1.4.1
78
78
  ```
79
79
 
80
80
  方式二:clone 后本地安装(开发 / 修改源码场景)
@@ -93,7 +93,7 @@ dsh plugin --profile web add . # 装入 web profile(profile 名按你的实
93
93
  # 直接由 dsh 从 npm 拉取并装入(本机若已配镜像会自动走镜像)
94
94
  dsh plugin --profile web add dsh-memory-palace
95
95
  # 锁定版本:
96
- dsh plugin --profile web add dsh-memory-palace@1.4.0
96
+ dsh plugin --profile web add dsh-memory-palace@1.4.1
97
97
 
98
98
  # 或先手动用 npm 安装(显式指定镜像),再装入:
99
99
  npm install dsh-memory-palace --registry=https://registry.npmmirror.com/
@@ -117,6 +117,9 @@ dsh plugin --profile web remove dsh-memory-palace
117
117
 
118
118
  ## 版本历史
119
119
 
120
+ - **1.4.1**
121
+ - 🐛修复输出预算使用错位的问题
122
+ - 🐛增加调试模式日志级别 *默认info 仅输出元数据日志,debug 输出完整LLM text*
120
123
  - **1.4.0**
121
124
  - 🔥智能模式增加最大输出Token限制
122
125
  - 🔥智能模式增加失败重试机制
package/lib/client.js CHANGED
@@ -64,14 +64,14 @@ window.__ModuleLoader__.load({
64
64
  notifyProjectDone: "项目记忆蒸馏完成",
65
65
  notifyFail: "记忆蒸馏失败",
66
66
  distillDebugLogLabel: "蒸馏调试日志",
67
- distillDebugLogHint: "开启后向 dsh 服务端控制台(stderr)输出蒸馏 LLM 调用诊断,仅排障用,平时关闭。",
67
+ distillDebugLogHint: "开启后向 dsh 服务端控制台(stderr)输出蒸馏 LLM 调用诊断。info 级别仅输出元数据(不含文本);配合 distillLogLevel=debug 会额外打印 LLM 原始响应(分隔符包裹),仅限受信本地排障。",
68
68
  dev: "开发",
69
69
  distillTimeoutLabel: "蒸馏超时(秒)",
70
70
  distillTimeoutHint: "单次蒸馏 LLM 调用的超时上限(秒);超时视为失败并降级。覆盖智能模式摘要与手动蒸馏,默认 60 秒。",
71
71
  summaryMaxTokensLabel: "会话摘要最大 Token(智能模式)",
72
- summaryMaxTokensHint: "智能模式会话摘要 LLM 的最大输出 Token(summary + durable 合计上限)。默认 2000,过小会截断 durable 事实,可上调到 4000+。",
73
- projectMaxTokensLabel: "项目蒸馏最大 Token",
74
- projectMaxTokensHint: "手动「蒸馏项目记忆」LLM 的最大输出 Token(蒸馏后 MEMORY.md 长度上限)。默认 8000,过小会截断浓缩结果,可上调。",
72
+ summaryMaxTokensHint: "智能模式会话摘要 LLM 的【最终输出软预算(prompt 约束,思考不受限;实际硬上限由模型自身 maxTokens 决定)】。默认 2000,可调大以容纳更多 durable 事实。",
73
+ projectMaxTokensLabel: "项目蒸馏输出软预算",
74
+ projectMaxTokensHint: "手动「蒸馏项目记忆」LLM 的【最终输出软预算(prompt 约束,思考不受限;实际硬上限由模型自身 maxTokens 决定)】。默认 8000。",
75
75
  feedbackEnabledLabel: "蒸馏回喂存量记忆",
76
76
  feedbackEnabledHint: "开启后,自动智能模式(turn/end)与手动蒸馏会话按钮均会把项目级 + 用户级 MEMORY.md 全文(逐行编号、无截断)回喂 LLM,使其能基于既有记忆做 add/replace/delete 增量维护。delete 仅手动蒸馏按钮开放,自动模式跳过(防误删)。默认关。"
77
77
  };
@@ -130,14 +130,14 @@ window.__ModuleLoader__.load({
130
130
  notifyProjectDone: "Project memory distilled",
131
131
  notifyFail: "Memory distillation failed",
132
132
  distillDebugLogLabel: "Distill debug log",
133
- distillDebugLogHint: "When on, prints distill LLM call diagnostics to the dsh server console (stderr). For troubleshooting only; keep off normally.",
133
+ distillDebugLogHint: "When on, prints distill LLM call diagnostics to the dsh server console (stderr). info level prints metadata only (no text); with distillLogLevel=debug it additionally prints the raw LLM response (wrapped in delimiters), for trusted local troubleshooting only.",
134
134
  dev: "Developer",
135
135
  distillTimeoutLabel: "Distill timeout (s)",
136
136
  distillTimeoutHint: "Timeout (in seconds) for a single distill LLM call; on timeout it fails and degrades. Covers both smart-mode summary and manual distillation. Default 60s.",
137
137
  summaryMaxTokensLabel: "Session summary max tokens (smart mode)",
138
- summaryMaxTokensHint: "Max output tokens for the smart-mode session summary LLM call (summary + durable combined cap). Default 2000; too low truncates durable facts, raise to 4000+ if needed.",
139
- projectMaxTokensLabel: "Project distill max tokens",
140
- projectMaxTokensHint: "Max output tokens for the manual 'distill project memory' LLM call (cap on the resulting MEMORY.md length). Default 8000; too low truncates the condensed result, raise if needed.",
138
+ summaryMaxTokensHint: "Smart-mode session summary LLM final output soft budget (prompt constraint, thinking not limited; the actual hard cap is the model's own maxTokens). Default 2000; raise to fit more durable facts.",
139
+ projectMaxTokensLabel: "Project distill output soft budget",
140
+ projectMaxTokensHint: "Manual 'distill project memory' LLM final output soft budget (prompt constraint, thinking not limited; actual hard cap is the model's own maxTokens). Default 8000.",
141
141
  feedbackEnabledLabel: "Feed existing memory into distillation",
142
142
  feedbackEnabledHint: "When on, both the auto smart-mode (turn/end) and the manual session distillation feed the full project-level + user-level MEMORY.md (line-numbered, no truncation) back to the LLM so it can maintain memory incrementally via add/replace/delete. delete is only allowed via the manual distill button; auto mode skips it (to avoid accidental memory loss). Off by default."
143
143
  };
@@ -9,7 +9,7 @@ export const SCENE_KEYWORDS = ["记住", "记一下", "remind", "偏好", "决
9
9
  // allowDelete=true(手动蒸馏按钮):开放 add/replace/delete;false(自动智能模式):禁 delete,仅 add/replace。
10
10
  // 注意:回喂是否实际发生由 distill.mjs 按 cfg().feedbackEnabled 决定(智能模式级,自动/手动均生效);
11
11
  // 此处仅声明能力边界——allowDelete 决定 prompt 是否允许 delete 指令(自动模式禁 delete,手动按钮放开)。
12
- export function SUMMARY_PROMPT({ allowDelete = false } = {}) {
12
+ export function SUMMARY_PROMPT({ allowDelete = false, outputBudget } = {}) {
13
13
  const feedbackInstr = allowDelete
14
14
  ? "【维护现有记忆】若对话中提供了「项目级记忆 / 用户级记忆(逐行编号)」,你可在输出 memoryOps 中对现有记忆做增量维护:\n" +
15
15
  '- add:新增一条事实(scope + fact),勿与现有重复;\n' +
@@ -35,6 +35,7 @@ export function SUMMARY_PROMPT({ allowDelete = false } = {}) {
35
35
  "2) durable 每条必须是**可独立成立的客观事实**;**严禁**把「助手说过的话 / 向用户提问 / 未确认的提议」本身当作事实记录。\n" +
36
36
  "3) 若对话中仅含未决的提问或提议、无确定结论,summary 写「本次对话为未决讨论,暂无确定结论」,durable 留空,绝不可照抄对话体文本。\n" +
37
37
  feedbackInstr +
38
+ (outputBudget != null ? "\n【输出长度约束】你的最终 JSON(summary + durable + memoryOps 合计)请控制在约 " + outputBudget + " token(≈" + Math.round(outputBudget * 1.8) + " 字)以内。思考/推理过程不受此限、可充分判断(含「该不该记」「是否一次性操作」「与旧记忆是否冲突需 update/delete」),但**最终成稿必须精炼、不超预算**;严禁为凑结构而填充无用内容。\n" : "") +
38
39
  "\n只输出 JSON,不要多余文字,格式:\n" +
39
40
  '{"summary":"...","durable":[{"scope":"project","fact":"一句话"}],"memoryOps":[{"op":"add","scope":"project","fact":"..."},{"op":"replace","line":12,"oldText":"...","newText":"..."},{"op":"delete","line":7,"oldText":"..."}]}\n' +
40
41
  "(没有需要维护的现有记忆时,memoryOps 留空数组 []。)"
@@ -43,8 +44,9 @@ export function SUMMARY_PROMPT({ allowDelete = false } = {}) {
43
44
 
44
45
  // v1.2.0 项目记忆蒸馏 prompt(固化自 upgrade plan/v1.2.0/记忆蒸馏Prompt.md,供按钮「蒸馏项目记忆」稳定调用)。
45
46
  // 用法:system = 本 prompt,user = 项目 MEMORY.md 全文,输出 = 精炼后的项目记忆(直接覆盖写回)。
46
- export const DISTILL_PROMPT =
47
- "你是一名「项目记忆蒸馏器」。我会给你一份项目的原始材料(可能是记忆文件、会话记录、零散笔记,或多份材料的拼合)。你的任务:把它提炼成一段精炼的项目记忆。\n" +
47
+ export function DISTILL_PROMPT({ outputBudget } = {}) {
48
+ return (
49
+ "你是一名「项目记忆蒸馏器」。我会给你一份项目的原始材料(可能是记忆文件、会话记录、零散笔记,或多份材料的拼合)。你的任务:把它提炼成一段精炼的项目记忆。\n" +
48
50
  "\n" +
49
51
  "【提炼总原则】\n" +
50
52
  "只保留「以后还会用到」的信息,删掉「一次性过程」。\n" +
@@ -93,4 +95,7 @@ export const DISTILL_PROMPT =
93
95
  "5. 每个坑是否「现象 → 根因 → 解法」三要素齐全?\n" +
94
96
  "6. 是否只输出了骨架要求的章节,没有额外发挥?\n" +
95
97
  "\n" +
96
- "现在开始处理我提供的材料。";
98
+ (outputBudget != null ? "【输出长度约束】你提炼后的项目记忆请控制在约 " + outputBudget + " token(≈" + Math.round(outputBudget * 1.8) + " 字)以内。思考不受限,但成稿须精炼、不超预算;只保留「以后还会用到」的信息,删掉「一次性过程」。\n" : "") +
99
+ "现在开始处理我提供的材料。"
100
+ );
101
+ }
package/lib/distill.mjs CHANGED
@@ -167,11 +167,11 @@ export function createDistill({ ctx, getConfig, paths, records, state }) {
167
167
  // (解析失败时抽取 {...} JSON 块兜底,仍失败则 summary 置空、记极简占位,绝不把 LLM 自由文本当 summary 落盘)。
168
168
  const timeoutMs = c.summaryTimeoutMs || 60000;
169
169
  const t0 = Date.now();
170
- dbg("session request", { provider, model, messages: messages.length, maxTokens: c.summaryMaxTokens, timeoutMs, maxRetries: RETRY_CONSTANTS.MAX_RETRIES });
170
+ dbg("session request", { provider, model, messages: messages.length, maxTokens: "(model native cap)", timeoutMs, maxRetries: RETRY_CONSTANTS.MAX_RETRIES });
171
171
  let result;
172
172
  try {
173
173
  result = await runWithRetry(
174
- () => streamOnce({ provider, model, system: SUMMARY_PROMPT({ allowDelete }), messages, maxTokens: c.summaryMaxTokens }, timeoutMs),
174
+ () => streamOnce({ provider, model, system: SUMMARY_PROMPT({ allowDelete, outputBudget: c.summaryMaxTokens }), messages }, timeoutMs),
175
175
  {
176
176
  maxRetries: RETRY_CONSTANTS.MAX_RETRIES,
177
177
  onAttempt: ({ attempt, maxRetries }) => dbg("session attempt", { attempt, maxRetries }),
@@ -185,6 +185,15 @@ export function createDistill({ ctx, getConfig, paths, records, state }) {
185
185
  const { finish, text, chunks, deltaChars, tFirstChunk } = result;
186
186
  dbg("session finish", { kind: finish?.kind, attempts: result.attempts });
187
187
  dbg("session stream done", { chunks, deltaChars, firstChunkMs: tFirstChunk });
188
+ // 本地调试(受 distillDebugLog + distillLogLevel==="debug" 双门控):把 LLM 原始响应 text 原样打到 stderr,
189
+ // 并用分隔符整段包裹,便于从 stderr 一眼框住起止、不与其它 dbg 行混淆。
190
+ if (cfg().distillDebugLog && cfg().distillLogLevel === "debug") {
191
+ console.error("[memory-palace][debug] session llm raw text begin >>>");
192
+ console.error("--------------------------->");
193
+ console.error(text);
194
+ console.error("<----------------------------");
195
+ console.error("[memory-palace][debug] session llm raw text end <<<");
196
+ }
188
197
  if (finish && finish.kind === "max-tokens") {
189
198
  console.error("[memory-palace] distill warn: llm finish max-tokens (attempting partial JSON)", JSON.stringify({}));
190
199
  }
@@ -304,7 +313,7 @@ export function createDistill({ ctx, getConfig, paths, records, state }) {
304
313
  return { ok: false, message: "无法确定蒸馏模型(summaryModel 未配置且会话无模型信息)。" };
305
314
  }
306
315
  ({ provider, model } = resolved);
307
- dbg("project request", { provider, model, inputChars: raw.length, maxTokens: c.projectMaxTokens, timeoutMs: c.summaryTimeoutMs || 60000 });
316
+ dbg("project request", { provider, model, inputChars: raw.length, maxTokens: "(model native cap)", timeoutMs: c.summaryTimeoutMs || 60000 });
308
317
 
309
318
  // LLM 蒸馏:system = 固化 DISTILL_PROMPT,user = MEMORY.md 全文。带失败重试(runWithRetry)。
310
319
  const timeoutMs = c.summaryTimeoutMs || 60000;
@@ -315,14 +324,13 @@ export function createDistill({ ctx, getConfig, paths, records, state }) {
315
324
  {
316
325
  provider,
317
326
  model,
318
- system: DISTILL_PROMPT,
327
+ system: DISTILL_PROMPT({ outputBudget: c.projectMaxTokens }),
319
328
  messages: [
320
329
  createUserMessage({
321
330
  content: [{ type: "text", text: raw }],
322
331
  source: { kind: "plugin", plugin: "memory-palace" },
323
332
  }),
324
333
  ],
325
- maxTokens: c.projectMaxTokens,
326
334
  },
327
335
  timeoutMs,
328
336
  ),
@@ -338,6 +346,14 @@ export function createDistill({ ctx, getConfig, paths, records, state }) {
338
346
  return { ok: false, message: `蒸馏${kindZh}(重试 ${e?.attempts || 0} 次后仍失败):${cls.message || ""}` };
339
347
  }
340
348
  const { finish, text, chunks, deltaChars, gotFirstChunk, tFirstChunk } = result;
349
+ // 本地调试(受 distillDebugLog + distillLogLevel==="debug" 双门控,与会话蒸馏路径一致):分隔符包裹整段原始响应。
350
+ if (cfg().distillDebugLog && cfg().distillLogLevel === "debug") {
351
+ console.error("[memory-palace][debug] project llm raw text begin >>>");
352
+ console.error("--------------------------->");
353
+ console.error(text);
354
+ console.error("<----------------------------");
355
+ console.error("[memory-palace][debug] project llm raw text end <<<");
356
+ }
341
357
  if (finish && finish.kind === "max-tokens") {
342
358
  dbg("project finish", { kind: "max-tokens", attempts: result.attempts });
343
359
  } else {
package/lib/index.js CHANGED
@@ -53,10 +53,12 @@ export const Config = Schema.object({
53
53
  memoryMode: Schema.union(["plugin", "smart"]).default("plugin").description("记忆模式:plugin=记忆公民指令+轮次轻量+错误捕获;smart=LLM 智能会话摘要(summary→每日日志 + durable→MEMORY.md,带 [smart] 标记)。切换需重启 dsh 生效。"),
54
54
  summaryModel: Schema.string().default("").description("智能模式的摘要模型,留空=复用当前会话 provider/model;也可填 provider/model(如 deepseek/deepseek-chat)固定廉价模型省 token。"),
55
55
  summaryTimeoutMs: Schema.number().default(60000).description("蒸馏 LLM 调用的超时(毫秒),超时视为失败并降级;覆盖智能模式摘要与手动蒸馏两条链路;默认 60000(60s)。"),
56
- distillDebugLog: Schema.boolean().default(false).description("调试开关:向 dsh 服务端 stderr 输出蒸馏 LLM 调用诊断日志(模型解析、请求参数、流进度计数、错误详情)。仅排障用,平时关闭;开启后也不打印任何对话内容或记忆文本。"),
57
- // ---- v1.4.0:智能模式最大输出 Token 限制(特性1,默认保守,可由设置页放大以防摘要/durable 被截断) ----
58
- summaryMaxTokens: Schema.number().default(2000).description("智能模式会话摘要 LLM 的最大输出 Token(summary + durable 合计上限)。默认 2000,过小会截断 durable 事实;可上调到 4000+。"),
59
- projectMaxTokens: Schema.number().default(8000).description("手动「蒸馏项目记忆」LLM 的最大输出 Token(蒸馏后 MEMORY.md 长度上限)。默认 8000,过小会截断浓缩结果;可上调。"),
56
+ distillDebugLog: Schema.boolean().default(false).description("调试开关:向 dsh 服务端 stderr 输出蒸馏 LLM 调用诊断。distillLogLevel=info 时仅输出元数据(模型解析/请求参数/流进度/错误详情,不含文本);distillLogLevel=debug 会额外打印 LLM 原始响应文本(分隔符包裹),仅限受信本地排障开启。"),
57
+ // ---- v1.4.1:蒸馏 stderr 日志级别(平铺键,不进 UI;默认 info;distillDebugLog=true 时生效) ----
58
+ distillLogLevel: Schema.string().default("info").description("蒸馏 stderr 日志级别:info=仅元数据诊断(默认,不打印 LLM 原始响应);debug=额外打印 LLM 原始响应文本(分隔符包裹),仅限受信本地排障开启。无需 UI 配置,经 profile/settings.yaml 设置 distillLogLevel 键。"),
59
+ // ---- v1.4.0:智能模式最终输出软预算(v1.4.1 起语义变更:prompt 软约束,思考不受限;实际硬上限由模型自身 maxTokens 决定) ----
60
+ summaryMaxTokens: Schema.number().default(2000).description("智能模式会话摘要 LLM 的【最终输出软预算(prompt 约束,思考不受限)】。默认 2000。实际硬上限由模型自身 maxTokens 决定;可调大以容纳更多 durable 事实。"),
61
+ projectMaxTokens: Schema.number().default(8000).description("手动「蒸馏项目记忆」LLM 的【最终输出软预算(prompt 约束,思考不受限)】。默认 8000。实际硬上限由模型自身 maxTokens 决定。"),
60
62
  // ---- v1.4.0:蒸馏时回喂存量记忆(特性3,默认关;开启后自动智能模式 turn/end 与手动蒸馏按钮均回喂,delete 仅手动放开) ----
61
63
  feedbackEnabled: Schema.boolean().default(false).description("蒸馏时把项目级 + 用户级 MEMORY.md 全文(逐行编号、无截断)回喂给 LLM,使其能基于既有记忆做 add/replace/delete 增量维护。默认关;开启后自动智能模式(turn/end)与手动蒸馏会话按钮均回喂存量记忆;delete 仍仅手动按钮开放,自动模式跳过(防误删)。"),
62
64
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-palace",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "把 WorkBuddy 的文件式记忆系统移植进 DeepSeek Harness:用户级 + 工作区的人类可读 Markdown 记忆(~/.deepseek-harness/MEMORY.md 与 <cwd>/.deepseek-harness/memory/)。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",