@modusensus/dsh-mneme 0.7.25 → 0.7.27
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.en.md +12 -6
- package/README.md +12 -6
- package/lib/api.js +99 -0
- package/lib/client.js +132 -4
- package/lib/config.js +6 -6
- package/lib/dream/sleep.js +3 -3
- package/lib/dream.js +56 -2
- package/lib/settings.js +4 -0
- package/package.json +1 -1
- package/src/api.js +99 -0
- package/src/config.js +6 -6
- package/src/dream/sleep.js +3 -3
- package/src/dream.js +56 -2
- package/src/settings.js +4 -0
- package/test/api.test.js +171 -6
- package/test/client.test.js +72 -1
- package/test/reasoning-effort.test.js +163 -0
package/README.en.md
CHANGED
|
@@ -26,13 +26,14 @@ English | [简体中文](README.md)
|
|
|
26
26
|
- Per-type `committed / failed / pending` receipts; the health endpoint distinguishes `ok / degraded / unknown`
|
|
27
27
|
- State write failures are never silent: sync failures are logged and leave debt behind, converging automatically on restart
|
|
28
28
|
|
|
29
|
-
### Model Tools (
|
|
29
|
+
### Model Tools (8)
|
|
30
30
|
|
|
31
31
|
| Tool | Function |
|
|
32
32
|
|------|------|
|
|
33
33
|
| `memory_save` | Save a memory (automatic dedup and merge by title) |
|
|
34
34
|
| `memory_search` | Full-text search (Chinese-substring friendly; vector semantic search can be enabled) |
|
|
35
35
|
| `memory_list` | Paginated listing by type (`include_archived=true` to view archived items) |
|
|
36
|
+
| `memory_get` | Read a single memory's full body by id (v0.7.25; read the full text after a search/list hit) |
|
|
36
37
|
| `memory_update` | Modify an existing memory |
|
|
37
38
|
| `memory_delete` | Delete a memory |
|
|
38
39
|
| `memory_forget` | Suppress injection (down-weighted rather than deleted; recoverable) |
|
|
@@ -66,14 +67,14 @@ The default `32768` reserves headroom for reasoning models, where reasoning alon
|
|
|
66
67
|
| Medium (10k–50k chars) | `65536` |
|
|
67
68
|
| Large (>50k chars) | `131072` (cap) |
|
|
68
69
|
|
|
69
|
-
> With **reasoning models** (e.g. DeepSeek-R1-like), the model may spend the entire budget on reasoning and return an empty body (the log shows `no json array in llm output`). Resolution order: ① set `dreamReasoningEffort` to `low` to suppress reasoning overhead (
|
|
70
|
+
> With **reasoning models** (e.g. DeepSeek-R1-like), the model may spend the entire budget on reasoning and return an empty body (the log shows `no json array in llm output`). Resolution order: ① set `dreamReasoningEffort` to `low` to suppress reasoning overhead (v0.7.26+: if the model doesn't support that tier, `resolveDreamEffort` auto-falls back to the model's default/first supported tier or omits the field — the rejection reason lands in `llm_audit`); ② if the retry still returns an empty body under the model's default reasoning behavior, raise `dreamMaxTokens` (reasoning and body share this budget) or route `dreamProvider`/`dreamModel` to a non-reasoning model. The sleep side has the corresponding `sleepReasoningEffort`.
|
|
70
71
|
|
|
71
72
|
**Consolidation model classification** (settings panel "consolidation model" = `dreamProvider`/`dreamModel`; sleep side: `sleepProvider`/`sleepModel`):
|
|
72
73
|
|
|
73
74
|
| Model kind | Examples | Notes |
|
|
74
75
|
|-----------|----------|-------|
|
|
75
76
|
| **Non-reasoning (recommended)** | glm-5-2-class | No reasoning declaration; even if an effort is configured and the harness rejects it, the fallback strips the field and the retry succeeds. Lowest risk of empty-body runs |
|
|
76
|
-
| **Reasoning (test first)** | deepseek-v4-flash-ga and other v4-flash-ga family | Reasons by default and may burn the whole token budget on an empty body; some SKUs (e.g. v4-flash-ga) are additionally declared by the harness as accepting **no reasoning effort at all**
|
|
77
|
+
| **Reasoning (test first)** | deepseek-v4-flash-ga and other v4-flash-ga family | Reasons by default and may burn the whole token budget on an empty body; some SKUs (e.g. v4-flash-ga) are additionally declared by the harness as accepting **no reasoning effort at all** (the no-effort retry gets rejected through the harness `defaultEffort`, `UNSUPPORTED_REASONING_EFFORT`). Fixed in v0.7.26+: `resolveDreamEffort` probes the model's supported effort tiers before streaming — an unsupported configured tier auto-falls back to the model's default/first supported tier, and the field is omitted for models that declare no reasoning capability. If you use one, set `dreamReasoningEffort` and test; switch to a non-reasoning model otherwise |
|
|
77
78
|
|
|
78
79
|
### Sleep Mode: System-Level Sleep 💤 (v0.4.0, opt-in)
|
|
79
80
|
|
|
@@ -173,6 +174,9 @@ Every **background LLM call** (autoDream consolidation + summary, autoSummarize
|
|
|
173
174
|
|
|
174
175
|
| Version | Highlights |
|
|
175
176
|
|------|------|
|
|
177
|
+
| **v0.7.27** | v0.7.26 endpoint backfill: the v0.7.26 release tree did not include the `fix/dream-effort-trap` branch, so the `GET /api/dsh-mneme/llm-providers` + `POST /api/dsh-mneme/test-model` endpoints announced in its CHANGELOG/Release were absent from the package (the panel's cascading dropdown + connectivity test would 404); this release rebases and merges the branch (PR #100), landing the endpoints — panel connectivity test now works; 733 tests green |
|
|
178
|
+
| **v0.7.26** | Consolidation `UNSUPPORTED_REASONING_EFFORT` root-cause fix + dream/sleep model connectivity test: root-caused the **defaultEffort trap** — the harness injects `reasoning.defaultEffort` when effort is omitted, so if that default tier itself is unsupported, retrying without effort never helps (whatever effort is sent gets rejected); fix: new `resolveDreamEffort` proactively queries `ctx.llm.resolveModelInfo()` for the model's supported effort tiers and sends a supported one — an unsupported configured tier auto-falls back to the model's default/first supported tier, and the field is omitted when the model declares no reasoning capability. Settings panel adds selection hints on 6 dream/sleep model fields (guiding toward non-thinking models so they don't burn the token budget on reasoning). New `GET /api/dsh-mneme/llm-providers` (host-side provider/model discovery; keys never touch the plugin side) + `POST /api/dsh-mneme/test-model` (connectivity test; empty body resolves via the consolidation route and returns `modelId`); 732 tests green |
|
|
179
|
+
| **v0.7.25** | Tool-compat hardening + `memory_get`/render content preview + consolidation-model guide: new `memory_get` tool (8th model tool, read a single memory's full body by id) + `memory_search`/`memory_list` render now embeds title/metadata/body preview (the model reads actual memory content even when the host only forwards render text) + consolidation-model selection guide (config comments/README classification, non-thinking vs thinking). Fixed `memory_get` execute nested inside output crashing with `userExecute is not a function` (previous tests only counted tool names, never executed it) + tool de-dup on live patch reload + Standalone API port-collision retry (EADDRINUSE) + client inject declaration alignment + better-sidebar hardening; 718 tests green |
|
|
176
180
|
| **v0.7.24** | Fixed DSH Desktop plugin-tree load crash (v0.7.23 regression): cordis 4's ctx is a Proxy — accessing a property not declared in `inject` throws `cannot get property "webServer" without inject` (not `undefined`), and removing webServer from inject meant cordis no longer waited for the host service, so Desktop crashed on restart; fix: restored webServer to inject (cordis applies the plugin only after the host service is ready) + apply/register guard switched to `ctx.reflect.get` (inject-free read, returns `undefined` when absent, never throws); verified with a real cordis + dsh-host-webserver plugin (API routes 200, unknown path 404, headless silently inactive); 714 tests green |
|
|
177
181
|
| **v0.7.23** | Root-caused "memory consolidation keeps failing": a legal empty decision array `[]` from consolidation is no longer treated as a failure (CONSOLIDATION_PROMPT explicitly allows "no output when nothing needs changing", so a model with a healthy, non-redundant memory legitimately returns `[]` — yet `validateDecisions` hard-rejected it as `decision list must be a non-empty array`, failing the whole run and flooding the audit with failures; **model-agnostic** — ChatGPT/Claude hit the same trap; fix: empty array short-circuits to `ok:true` no-op instead of tripping the implicit-keep coverage check). Plus: empty-body fix part 2 (`dreamMaxTokens` default 8192→32768 so thinking models don't burn the whole budget on reasoning) + skipInvalid splice residue bug (length equality ≠ content equality, skipped decisions leaked into apply/audit); 712 tests green |
|
|
178
182
|
| **v0.7.22** | Restored the v0.6.9 skipInvalid tolerant-validation path (issue #89 regression, lost in the v0.7.11 rewrite): `dreamSkipInvalid` (default true) skips individual invalid decisions, applies the valid subset, and marks the run degraded; `allowCrossTypeMerge` (default false) explicitly relaxes cross-type merging — weak models (e.g. qwen3.8-flash) with jittery schema compliance no longer fail the whole batch and burn LLM calls. Strict mode and the sleep path behave unchanged; global caps/coverage floors still reject the whole run (running over cap = broken model, not minor schema drift). New `dreamMinIntervalMinutes` (0–10080, default 0 = unlimited) minimum autoDream trigger interval — failed/degraded runs also consume the interval (throttling exists to stop back-to-back failing calls); feature_flags whitelist now 34 keys; 696 tests green |
|
|
@@ -230,6 +234,8 @@ Every **background LLM call** (autoDream consolidation + summary, autoSummarize
|
|
|
230
234
|
| v0.7.20 | ✅ Done | Heat restore + phase-two frontend + better-sidebar fix | Heat model fully restored (issue #87, backported from v0.7.10: power-law decay + TYPE_DECAY + sleep heat-combined dual protection + entity heat projection) + acceptance checklist landed (heatEnabled default OFF / feature_flags 31 keys / lightMode linkage / sleep demotion audit / updated_at⊥last_accessed_at contract) + phase-two frontend (/list heat projection, HeatBadge three-tier, order=heat page-local sort) + better-sidebar fix (issue #88: inner dynamic sub-plugin); 685 tests green |
|
|
231
235
|
| v0.7.21 | ✅ Done | effort-fallback stream fix | Catch-based effort fallback was dead code on the stream path (dsh-llm rc.1 turns adapter failures into a terminal error finish chunk instead of a throw) → `streamText` captures the finish-chunk cause (`describeStreamFailure`) + `withEffortFallback` gains a `getStreamError` accessor (retries without effort when rejected) + `runAuditedLlm` supports `spec.streamError` (real cause in audit); 688 tests green |
|
|
232
236
|
| v0.7.22 | ✅ Done | skipInvalid tolerant-validation restore (issue #89) + autoDream throttle | Restored v0.6.9 skipInvalid dual-track structure (lost in the v0.7.11 rewrite): `dreamSkipInvalid` skips individual invalid decisions + applies the valid subset + marks the run degraded; `allowCrossTypeMerge` explicitly relaxes cross-type merging; weak models (qwen3.8-flash) with jittery schema compliance no longer fail the whole batch. New `dreamMinIntervalMinutes` (0–10080, default 0) minimum trigger interval — failed/degraded runs also consume it. Strict mode / sleep path unchanged; feature_flags whitelist 34 keys; 696 tests green |
|
|
237
|
+
| v0.7.25 | ✅ Done | Tool-compat hardening + content preview + consolidation-model guide | New `memory_get` tool (8th model tool, full-body read by id) + `memory_search`/`memory_list` render embeds title/metadata/body preview + consolidation-model selection guide; fixed `memory_get` execute nested inside output (`userExecute is not a function`) + tool de-dup on live patch reload + Standalone API port-collision retry + client inject alignment + better-sidebar hardening; 718 tests green |
|
|
238
|
+
| **v0.7.26** | ✅ Done | Consolidation effort-trap root fix + LLM connectivity test | `UNSUPPORTED_REASONING_EFFORT` root cause (defaultEffort trap: harness injects `reasoning.defaultEffort` when effort is omitted, so an unsupported default can never be retried) → `resolveDreamEffort` proactively queries `ctx.llm.resolveModelInfo()` for the model's supported effort tiers (unsupported configured tier falls back to the model's default/first supported tier; field omitted when the model declares no reasoning capability); settings panel adds selection hints on 6 dream/sleep model fields; new `GET /api/dsh-mneme/llm-providers` + `POST /api/dsh-mneme/test-model` (connectivity test; empty body resolves via the consolidation route; keys never touch the plugin side); 732 tests green |
|
|
233
239
|
| **v0.8.0** | 🚧 Planned (late Sep) | Graph enhancement | Interest-drift visualization + scope isolation (issue #17) + cross-workspace sharing |
|
|
234
240
|
|
|
235
241
|
> All new capabilities ship as **toggleable features** (enabled/disabled via configuration), conservatively on by default and never breaking existing behavior. The `failure_memories` table and the autoDream decision engine have already paved the way for future reflective growth.
|
|
@@ -301,7 +307,7 @@ It works out of the box with the defaults. To adjust, override in `~/.dsh/profil
|
|
|
301
307
|
| `dreamDelayMs` | `2000` | Asynchronous consolidation delay (debounce) |
|
|
302
308
|
| `dreamProvider` / `dreamModel` | empty | Explicit dream LLM route — config wins over the agent's default model (config-first, v0.7.16); left empty, the agent's default model is used |
|
|
303
309
|
| `dreamMaxTokens` | `32768` | Maximum tokens per dream LLM call (cap 131072; reasoning and body share this budget on reasoning models — raise it when the body comes back empty, see the tuning guide below) |
|
|
304
|
-
| `dreamReasoningEffort` | `none` | Reasoning-effort passthrough for the dream LLM: `low` / `medium` / `high` / `none` (`none` = omit the field and use the model default; set `low` when a reasoning model exhausts its budget on reasoning and produces an empty body) |
|
|
310
|
+
| `dreamReasoningEffort` | `none` | Reasoning-effort passthrough for the dream LLM: `low` / `medium` / `high` / `none` (`none` = omit the field and use the model default; set `low` when a reasoning model exhausts its budget on reasoning and produces an empty body; v0.7.26+ auto-falls back to the model's default/first supported tier when the configured tier is unsupported, and omits the field for models without reasoning capability) |
|
|
305
311
|
| `apiToken` | empty | Optional API auth token; once set, write operations and key endpoints require `Authorization: Bearer <apiToken>` |
|
|
306
312
|
| `embedProvider` | `openai` | Semantic backend: `openai` (default, v0.1-compatible) / `local` (ONNX offline) / `ollama` |
|
|
307
313
|
| `localEmbedModel` | `Xenova/bge-small-zh-v1.5` | Local ONNX embedding model |
|
|
@@ -420,7 +426,7 @@ dsh-mneme config show # Show current config (toke
|
|
|
420
426
|
│ 服务层:saveWithDedupe / injectCandidates │
|
|
421
427
|
│ / mergeHumanEdits / onWrite 钩子 │
|
|
422
428
|
├─────────────────────────────────────────────────┤
|
|
423
|
-
│ 模型接口:
|
|
429
|
+
│ 模型接口:8 个工具 + 自动注入 + 会话摘要 │
|
|
424
430
|
├─────────────────────────────────────────────────┤
|
|
425
431
|
│ autoDream:阈值调度 → LLM 决策清单 │
|
|
426
432
|
│ → 校验(fail-safe)→ 应用 → 摘要 │
|
|
@@ -437,7 +443,7 @@ src/
|
|
|
437
443
|
├── mirror.js # Markdown 镜像(渲染/解析,人工优先)
|
|
438
444
|
├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子)
|
|
439
445
|
├── config.js # schemastery 配置 schema
|
|
440
|
-
├── tools.js #
|
|
446
|
+
├── tools.js # 8 个模型工具(defineTool)
|
|
441
447
|
├── inject.js # systemPrompt.context 动态注入
|
|
442
448
|
├── summarize.js # 会话结束 LLM 摘要
|
|
443
449
|
├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要)
|
package/README.md
CHANGED
|
@@ -49,13 +49,14 @@ dsh web
|
|
|
49
49
|
- 逐 type 记录 `committed / failed / pending` 回执,健康端点区分 `ok / degraded / unknown`
|
|
50
50
|
- 状态写失败不静默:同步失败落日志并留债务,重启自动收敛
|
|
51
51
|
|
|
52
|
-
### 模型工具(
|
|
52
|
+
### 模型工具(8 个)
|
|
53
53
|
|
|
54
54
|
| 工具 | 功能 |
|
|
55
55
|
|------|------|
|
|
56
56
|
| `memory_save` | 记录一条记忆(自动按标题去重合并) |
|
|
57
57
|
| `memory_search` | 全文搜索(中文子串友好,可启用向量语义搜索) |
|
|
58
58
|
| `memory_list` | 按类型分页列出(`include_archived=true` 可查看已归档) |
|
|
59
|
+
| `memory_get` | 读取单条记忆完整正文(v0.7.25,按 id;memory_search / memory_list 命中后读全文) |
|
|
59
60
|
| `memory_update` | 修改已有记忆 |
|
|
60
61
|
| `memory_delete` | 删除记忆(按记忆 ID 精确删除) |
|
|
61
62
|
| `memory_forget` | 抑制注入(降权不删除,可恢复) |
|
|
@@ -89,14 +90,14 @@ dsh web
|
|
|
89
90
|
| 中等(1 万-5 万字) | `65536` |
|
|
90
91
|
| 大型(5 万字以上) | `131072`(上限) |
|
|
91
92
|
|
|
92
|
-
> 若使用**思考型模型**(如 deepseek-v4-flash / DeepSeek-R1 类),模型可能把全部预算花在 reasoning 上导致正文为空(日志出现 `no json array in llm output`)。处理顺序:① 把 `dreamReasoningEffort` 设为 `low`
|
|
93
|
+
> 若使用**思考型模型**(如 deepseek-v4-flash / DeepSeek-R1 类),模型可能把全部预算花在 reasoning 上导致正文为空(日志出现 `no json array in llm output`)。处理顺序:① 把 `dreamReasoningEffort` 设为 `low` 显式压低思考(v0.7.26+ 若模型不支持该档位,`resolveDreamEffort` 会自动换用模型支持的默认/首个档位或省略字段,拒绝原因会记入 llm_audit);② 重试走模型默认思考行为后正文仍为空的,调大 `dreamMaxTokens`(reasoning 与正文共享该预算)或配置 `dreamProvider`/`dreamModel` 指向非思考模型。sleep 侧对应 `sleepReasoningEffort`。
|
|
93
94
|
|
|
94
95
|
**巩固模型分类声明**(settings panel「巩固模型」= `dreamProvider`/`dreamModel`,睡眠侧对应 `sleepProvider`/`sleepModel`):
|
|
95
96
|
|
|
96
97
|
| 模型类别 | 例子 | 说明 |
|
|
97
98
|
|---------|------|------|
|
|
98
99
|
| **非思考模型(推荐)** | glm-5-2 类等 | 无 reasoning 声明;即使配了 effort 被 harness 拒绝,fallback 去掉字段重试即成功。空体风险最低 |
|
|
99
|
-
| **思考模型(需实测)** | deepseek-v4-flash-ga 等 v4-flash-ga 系 | 默认开推理,可能烧光 token
|
|
100
|
+
| **思考模型(需实测)** | deepseek-v4-flash-ga 等 v4-flash-ga 系 | 默认开推理,可能烧光 token 预算返回空体;部分型号(如 v4-flash-ga)在 harness 侧被声明为**不接受任何 reasoning effort**(去掉 effort 时 harness 的 `defaultEffort` 会顶上来再次拒绝)。v0.7.26+ 已根治:`resolveDreamEffort` 发流前探测模型支持的档位,不支持的配置档位自动换用模型默认/首个支持档位,声明无 reasoning 能力的型号则省略字段。选用时建议配 `dreamReasoningEffort` 实测,不行就换非思考模型 |
|
|
100
101
|
|
|
101
102
|
### Sleep Mode 系统级睡眠 💤(v0.4.0,opt-in)
|
|
102
103
|
|
|
@@ -214,6 +215,9 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
|
|
|
214
215
|
|
|
215
216
|
| 版本 | 亮点 |
|
|
216
217
|
|------|------|
|
|
218
|
+
| **v0.7.27** | v0.7.26 端点遗漏补齐:v0.7.26 发版树未含 `fix/dream-effort-trap` 分支,CHANGELOG/Release 宣告的 `GET /api/dsh-mneme/llm-providers` + `POST /api/dsh-mneme/test-model` 两个端点实际不在包内(面板「级联下拉 + 测试连通性」会 404);本版将分支 rebase 后合入(PR #100),端点落地,面板连通性测试可用;733 测试全绿 |
|
|
219
|
+
| **v0.7.26** | 记忆巩固 `UNSUPPORTED_REASONING_EFFORT` 根治 + 巩固/睡眠模型连通性测试:根治 **defaultEffort 陷阱**——harness 在 effort 省略时注入 `reasoning.defaultEffort`,若该默认档位本身不被模型支持,则去 effort 重试也无济于事(换什么 effort 都会被拒绝);修复:新增 `resolveDreamEffort` 经 `ctx.llm.resolveModelInfo()` 主动探测模型支持的档位再发送——配置档位不被支持时自动换用模型支持的默认/首个档位、模型声明无 reasoning 能力则省略字段;设置面板巩固/睡眠 6 字段补选型提示(引导非思考模型,避免再踩思考模型烧光 token 预算);新增 `GET /api/dsh-mneme/llm-providers`(宿主侧 provider/model 发现,密钥不经插件侧)+ `POST /api/dsh-mneme/test-model`(模型连通性测试,空 body 按巩固路由解析返回 modelId);732 测试全绿 |
|
|
220
|
+
| **v0.7.25** | 工具兼容性加固 + `memory_get`/render 内容预览 + 巩固模型引导:新增 `memory_get` 工具(第 8 个模型工具,按 id 读单条记忆全文)+ `memory_search`/`memory_list` render 嵌入标题/元数据/正文预览(宿主只透传 render 文本时模型也能直接读到记忆内容)+ 巩固模型选型引导(config 注释/README 分类声明,非思考 vs 思考模型差异);修复 `memory_get` execute 嵌套进 output 的崩溃(`userExecute is not a function`,此前测试只数工具名从未执行该工具而掩盖)+ 工具重复注册去重(live patch reload)+ Standalone API 端口冲突重试(EADDRINUSE)+ client inject 声明对齐 + better-sidebar 集成加固;718 测试全绿 |
|
|
217
221
|
| **v0.7.24** | 修复 DSH Desktop 插件树加载崩溃(v0.7.23 回归):cordis 4 的 ctx 是 Proxy,访问未在 inject 声明的 `webServer` 会抛 `cannot get property without inject`(而非返回 undefined),且去掉 inject 后 cordis 不再等待宿主服务 → 桌面端重启即崩;修复:恢复 webServer 到 inject(cordis 等宿主就绪再 apply)+ apply/register 守卫改 `ctx.reflect.get`(免 inject 读取、未提供返回 undefined 不抛错);真实 cordis + dsh-host-webserver 插件实测;714 测试全绿 |
|
|
218
222
|
| **v0.7.23** | 记忆沉淀「反复失败」根治:consolidation 合法空数组 `[]` 不再误判 failed(CONSOLIDATION_PROMPT 允许「无问题无需输出」,模型无冗余时合法返回 `[]`——此前 `validateDecisions` 硬判 non-empty → 整单 failed、审计反复失败,且与模型无关,ChatGPT/Claude 同样踩中;修复:空数组显式短路 `ok:true` no-op)+ 空体修复第二段(`dreamMaxTokens` 默认 8192→32768,思考模型推理烧光预算的根治余量,设置面板可调)+ skipInvalid splice 残留 bug(长度相等≠内容一致,被跳决策残留);712 测试全绿 |
|
|
219
223
|
| **v0.7.22** | 恢复 v0.6.9 的 skipInvalid 宽容校验路径(issue #89 回归,v0.7.11 重写丢失):`dreamSkipInvalid`(默认 true)单条非法决策跳过 + 合法子集应用 + run 记 degraded,`allowCrossTypeMerge`(默认 false)显式放宽跨类型合并——弱模型(如 qwen3.8-flash)决策合规抖动不再整单拒绝白烧 LLM 调用;严格模式/sleep 路径行为不变,全局上限/覆盖率下限仍整单拒绝(刷爆上限=模型坏了,非轻微 schema 漂移);新增 `dreamMinIntervalMinutes`(0-10080,默认 0=不限)autoDream 最小触发间隔,失败/degraded run 也占用间隔(节流防失败调用连发);feature_flags 白名单 34 键;696 测试全绿 |
|
|
@@ -301,6 +305,8 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
|
|
|
301
305
|
| **v0.7.18** | ✅ 完成 | 生态第一步 + 查询收敛 | better-sidebar 软集成(inject 声明 + optional peer `dsh-better-sidebar` + registerTab 复用四视图,未装安全跳过;窄容器 `@container` 适配)+ `/list?deposited=only` 沉淀视图(receipt_chain ∪ source=dream)+ 记忆库沉淀/已归档筛选 chip + 状态页仪表盘化(统计 + 查看全部跳转预置筛选)+ 抽屉归档记忆「恢复」;667 测试全绿 |
|
|
302
306
|
| **v0.7.20** | ✅ 完成 | heat 回归 + 阶段二前端 + better-sidebar 修复 | heat 热度模型完整找回(issue #87,v0.7.10 移植:幂律衰减 + TYPE_DECAY + sleep 热联合双保护 + 实体热投影)+ 验收清单落地(heatEnabled 默认关 / feature_flags 31 键 / lightMode 联动 / sleep 降级审计暴露 / updated_at⊥last_accessed_at 契约)+ 阶段二前端(/list heat 投影、HeatBadge 三档、order=heat 页内排序)+ better-sidebar 修复(issue #88:内层动态子插件);685 测试全绿 |
|
|
303
307
|
| **v0.7.21** | ✅ 完成 | effort 回退流式修复 | autoDream/sleep 的 catch 式 effort 回退在流式路径是死代码(dsh-llm rc.1 把 adapter 异常转成终态 error finish chunk 不再抛出)→ `streamText` 捕获 finish-chunk 失败原因(`describeStreamFailure` 归一化)+ `withEffortFallback` 增加 `getStreamError` 访问器(effort 被拒去重试)+ `runAuditedLlm` 支持 `spec.streamError`(audit 记真实原因);688 测试全绿 |
|
|
308
|
+
| **v0.7.26** | ✅ 完成 | 巩固 effort 陷阱根治 + LLM 连通性测试 | 记忆巩固 `UNSUPPORTED_REASONING_EFFORT` 根治(defaultEffort 陷阱:harness 省略 effort 时注入 `reasoning.defaultEffort`,默认档位不被模型支持则任何重试无效)→ 新增 `resolveDreamEffort` 经 `ctx.llm.resolveModelInfo()` 探测模型支持的档位(不支持的配置档位自动换用模型支持的默认/首个档位,无 reasoning 能力则省略字段);设置面板巩固/睡眠 6 字段补选型提示(引导非思考模型);新增 `GET /api/dsh-mneme/llm-providers`(宿主侧 provider/model 发现)+ `POST /api/dsh-mneme/test-model`(模型连通性测试,空 body 按巩固路由解析,密钥不经插件侧);732 测试全绿 |
|
|
309
|
+
| **v0.7.25** | ✅ 完成 | 工具兼容性加固 + 内容预览 + 巩固模型引导 | 新增 `memory_get` 工具(第 8 个模型工具,按 id 读单条记忆全文)+ `memory_search`/`memory_list` render 嵌入标题/元数据/正文预览(宿主只透传 render 文本时模型也能读到内容)+ 巩固模型选型引导(config 注释/README 分类声明);修复 memory_get execute 嵌套 output 的崩溃(userExecute is not a function)+ 工具重复注册去重(live patch reload)+ Standalone API 端口冲突重试 + client inject 声明对齐 + better-sidebar 集成加固;718 测试全绿 |
|
|
304
310
|
| **v0.7.24** | ✅ 完成 | 桌面端崩溃紧急修复 | v0.7.23 把 webServer 移出 inject 致 cordis Proxy 抛 `cannot get property "webServer" without inject`(未注入属性直接访问抛错而非 undefined),桌面端重启插件树加载失败;修复:恢复 webServer 到 inject(cordis 等宿主就绪再 apply)+ apply/register 守卫改 `ctx.reflect.get`(免 inject 读取、未提供返回 undefined 不抛错,未来 headless 移出 inject 也安全);真实 cordis + dsh-host-webserver 实测 API 路由 200 / 未知路径 404 / headless 静默不激活;714 测试全绿 |
|
|
305
311
|
| **v0.7.23** | ✅ 完成 | 记忆沉淀「反复失败」根治 + 空体第二段 + skipInvalid splice 修复 | consolidation 合法空数组 `[]` no-op(CONSOLIDATION_PROMPT 允许无问题无需输出;此前 validateDecisions 硬判 non-empty → 整单 failed,模型无关、ChatGPT/Claude 同样踩中;修复:空数组显式短路 ok,不再触发隐式 keep 覆盖率误判);`dreamMaxTokens` 默认 8192→32768(思考模型推理烧光预算根治余量);skipInvalid splice 残留 bug(长度相等≠内容一致,被跳决策残留进 apply);712 测试全绿 |
|
|
306
312
|
| **v0.7.22** | ✅ 完成 | skipInvalid 宽容校验回归(issue #89)+ autoDream 节流 | 恢复 v0.6.9 的 skipInvalid 双轨结构(v0.7.11 重写丢失):`dreamSkipInvalid` 单条非法决策跳过 + 合法子集应用 + run 记 degraded,`allowCrossTypeMerge` 显式放宽跨类型合并;弱模型(qwen3.8-flash)决策合规抖动不再整单拒绝;新增 `dreamMinIntervalMinutes`(0-10080,默认 0=不限)最小触发间隔,失败/degraded run 也占用间隔;严格模式/sleep 路径行为不变;feature_flags 白名单 34 键;696 测试全绿 |
|
|
@@ -375,7 +381,7 @@ dsh web
|
|
|
375
381
|
| `dreamDelayMs` | `2000` | 整理异步延迟(去抖) |
|
|
376
382
|
| `dreamProvider` / `dreamModel` | 空 | dream 的 LLM 路由覆盖(显式配置优先于 agent 默认模型;留空则回退到 agent 默认模型) |
|
|
377
383
|
| `dreamMaxTokens` | `32768` | dream LLM 调用最大 token 数(上限 131072;思考型模型的 reasoning 与正文共享该预算,正文为空时优先调大,见下方调优指南) |
|
|
378
|
-
| `dreamReasoningEffort` | `none` | dream LLM 推理强度透传:`low` / `medium` / `high` / `none`(`none`=不传该字段,沿用模型默认;思考型模型(如 deepseek-v4-flash)想压低思考可设 `low
|
|
384
|
+
| `dreamReasoningEffort` | `none` | dream LLM 推理强度透传:`low` / `medium` / `high` / `none`(`none`=不传该字段,沿用模型默认;思考型模型(如 deepseek-v4-flash)想压低思考可设 `low`;v0.7.26+ 模型不支持配置档位时自动换用其支持的默认/首个档位,无 reasoning 能力的型号省略字段) |
|
|
379
385
|
| `apiToken` | 空 | 可选 API 鉴权 token;设置后写操作与密钥接口要求 `Authorization: Bearer <apiToken>` |
|
|
380
386
|
| `embedProvider` | `openai` | 语义后端:`openai`(默认,兼容 v0.1)/ `local`(ONNX 离线)/ `ollama` |
|
|
381
387
|
| `localEmbedModel` | `Xenova/bge-small-zh-v1.5` | 本地 ONNX embedding 模型 |
|
|
@@ -508,7 +514,7 @@ dsh-mneme config show # 查看当前配置(toke
|
|
|
508
514
|
│ 服务层:saveWithDedupe / injectCandidates │
|
|
509
515
|
│ / mergeHumanEdits / onWrite 钩子 │
|
|
510
516
|
├─────────────────────────────────────────────────┤
|
|
511
|
-
│ 模型接口:
|
|
517
|
+
│ 模型接口:8 个工具 + 自动注入 + 会话摘要 │
|
|
512
518
|
├─────────────────────────────────────────────────┤
|
|
513
519
|
│ autoDream:阈值调度 → LLM 决策清单 │
|
|
514
520
|
│ → 校验(fail-safe)→ 应用 → 摘要 │
|
|
@@ -525,7 +531,7 @@ src/
|
|
|
525
531
|
├── mirror.js # Markdown 镜像(渲染/解析,人工优先)
|
|
526
532
|
├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子)
|
|
527
533
|
├── config.js # schemastery 配置 schema
|
|
528
|
-
├── tools.js #
|
|
534
|
+
├── tools.js # 8 个模型工具(defineTool)
|
|
529
535
|
├── inject.js # systemPrompt.context 动态注入
|
|
530
536
|
├── summarize.js # 会话结束 LLM 摘要
|
|
531
537
|
├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要)
|
package/lib/api.js
CHANGED
|
@@ -4,6 +4,7 @@ import { timingSafeEqual, randomBytes } from "node:crypto";
|
|
|
4
4
|
import { FEATURE_FLAG_SPEC } from "./settings.js";
|
|
5
5
|
import { TYPE_FILE, renderMirrorText, parseHumanEdits } from "./mirror.js";
|
|
6
6
|
import { computeHeat } from "./heat.js";
|
|
7
|
+
import { describeStreamFailure, resolveRoute } from "./dream.js";
|
|
7
8
|
|
|
8
9
|
// headers:少数端点(/export 附件下载)需要追加 Content-Disposition 等响应头。
|
|
9
10
|
function sendJson(res, status, payload, headers = {}) {
|
|
@@ -277,6 +278,104 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
277
278
|
}
|
|
278
279
|
});
|
|
279
280
|
|
|
281
|
+
// --- dream/sleep 模型连通性 + 模型发现(settings 面板)----------------------
|
|
282
|
+
// Connectivity probe for a dream/sleep model route: runs one minimal LLM call
|
|
283
|
+
// through the same harness path the consolidation uses and reports success /
|
|
284
|
+
// failure + latency + error. Reuses describeStreamFailure so the panel shows
|
|
285
|
+
// the same error wording the audit rows carry. Optional reasoningEffort
|
|
286
|
+
// mirrors what the consolidation would send — the panel can verify a model
|
|
287
|
+
// actually accepts the configured effort without tripping a full run.
|
|
288
|
+
async function testLlmConnectivity(llm, provider, model, reasoningEffort) {
|
|
289
|
+
const startedAt = Date.now();
|
|
290
|
+
const elapsed = () => Date.now() - startedAt;
|
|
291
|
+
const modelId = `${provider}:${model}`;
|
|
292
|
+
try {
|
|
293
|
+
let reply = "";
|
|
294
|
+
let error = "";
|
|
295
|
+
for await (const chunk of llm.stream({
|
|
296
|
+
provider,
|
|
297
|
+
model,
|
|
298
|
+
purpose: "dsh-mneme-connectivity-test",
|
|
299
|
+
maxTokens: 16,
|
|
300
|
+
...(reasoningEffort && reasoningEffort !== "none" ? { reasoningEffort } : {}),
|
|
301
|
+
messages: [{ role: "system", content: [{ type: "text", text: "Reply with exactly one word: ok" }] }]
|
|
302
|
+
})) {
|
|
303
|
+
if (chunk.type === "text-delta" && typeof chunk.text === "string") reply += chunk.text;
|
|
304
|
+
if (chunk.type === "finish" && (chunk.reason?.kind === "error" || chunk.reason?.kind === "aborted")) {
|
|
305
|
+
error = describeStreamFailure(chunk.reason);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (error) return { ok: false, durationMs: elapsed(), error, modelId };
|
|
310
|
+
return { ok: true, durationMs: elapsed(), modelId, reply: reply.trim().slice(0, 100) };
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return { ok: false, durationMs: elapsed(), error: String(error?.message ?? error), modelId };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// List every provider + model the host has registered, so the panel can offer
|
|
317
|
+
// a dropdown over REAL models instead of free-text guesses. Read-only, no auth
|
|
318
|
+
// (same as list/search). Best-effort per provider: one whose model list can't
|
|
319
|
+
// be resolved yields an empty models array instead of failing the whole call.
|
|
320
|
+
register({
|
|
321
|
+
kind: "exact",
|
|
322
|
+
path: "/api/dsh-mneme/llm-providers",
|
|
323
|
+
handler(req, res) {
|
|
324
|
+
try {
|
|
325
|
+
const llm = ctx.llm;
|
|
326
|
+
if (typeof llm?.listProviders !== "function" || typeof llm?.listModels !== "function") {
|
|
327
|
+
return sendJson(res, 501, { error: "llm-unavailable" });
|
|
328
|
+
}
|
|
329
|
+
let providers;
|
|
330
|
+
try { providers = llm.listProviders() ?? []; } catch { providers = []; }
|
|
331
|
+
return Promise.all(providers.map(async (p) => {
|
|
332
|
+
let models = [];
|
|
333
|
+
try { models = (await llm.listModels(p.id)) ?? []; } catch { models = []; }
|
|
334
|
+
return {
|
|
335
|
+
provider: p.id,
|
|
336
|
+
models: models.map((m) => ({ id: m.id, name: m.name ?? m.id }))
|
|
337
|
+
};
|
|
338
|
+
})).then((rows) => sendJson(res, 200, { providers: rows }));
|
|
339
|
+
} catch {
|
|
340
|
+
return sendJson(res, 500, { error: "internal" });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Connectivity test for a dream/sleep model route. Runs one tiny LLM call and
|
|
346
|
+
// reports ok / failure + latency + error. Auth-gated: it spends the user's
|
|
347
|
+
// API quota, so a stray local page must not be able to drain it via loopback.
|
|
348
|
+
register({
|
|
349
|
+
kind: "exact",
|
|
350
|
+
path: "/api/dsh-mneme/test-model",
|
|
351
|
+
handler(req, res) {
|
|
352
|
+
try {
|
|
353
|
+
if (req.method !== "POST") return sendJson(res, 404, { error: "not-found" });
|
|
354
|
+
if (!requireAuth(req, res, apiToken)) return;
|
|
355
|
+
return readBody(req).then(async (text) => {
|
|
356
|
+
const body = parseBody(text);
|
|
357
|
+
let provider = typeof body.provider === "string" ? body.provider.trim() : "";
|
|
358
|
+
let model = typeof body.model === "string" ? body.model.trim() : "";
|
|
359
|
+
if ((!provider && model) || (provider && !model)) return sendJson(res, 400, { error: "missing-provider-or-model" });
|
|
360
|
+
if (!provider && !model) {
|
|
361
|
+
// 空 = 按巩固路由解析(dreamProvider/dreamModel > agent 默认模型),
|
|
362
|
+
// 让面板一键测"当前巩固模型"而无需手填。
|
|
363
|
+
const route = resolveRoute(ctx, config ?? {}, ctx.logger);
|
|
364
|
+
if (!route) return sendJson(res, 400, { error: "no-route" });
|
|
365
|
+
provider = route.provider;
|
|
366
|
+
model = route.model;
|
|
367
|
+
}
|
|
368
|
+
const llm = ctx.llm;
|
|
369
|
+
if (typeof llm?.stream !== "function") return sendJson(res, 501, { error: "llm-unavailable" });
|
|
370
|
+
const result = await testLlmConnectivity(llm, provider, model, body.reasoningEffort);
|
|
371
|
+
return sendJson(res, result.ok ? 200 : 502, result);
|
|
372
|
+
});
|
|
373
|
+
} catch {
|
|
374
|
+
return sendJson(res, 500, { error: "internal" });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
280
379
|
// --- delete one memory by id ---
|
|
281
380
|
// Mutation route: apiToken-gated like the profile/rules writes above. POST
|
|
282
381
|
// body is JSON { id }. store.remove deletes silently, so existence is checked
|
package/lib/client.js
CHANGED
|
@@ -326,6 +326,15 @@ window.__ModuleLoader__.load({
|
|
|
326
326
|
"memory.features.dreamProvider": "巩固模型 Provider",
|
|
327
327
|
"memory.features.dreamModel": "巩固用模型名",
|
|
328
328
|
"memory.features.dreamModelHint": "留空 = 跟随主对话模型;只影响记忆巩固(autoDream)用的模型",
|
|
329
|
+
"memory.features.sleepProvider": "睡眠 Provider",
|
|
330
|
+
"memory.features.sleepModel": "睡眠模型",
|
|
331
|
+
"memory.features.sleepModelHint": "留空 = 用巩固模型或当前模型;建议选非思考模型",
|
|
332
|
+
"memory.features.routeFollowDefault": "跟随默认路由",
|
|
333
|
+
"memory.features.modelTest": "测试连通性",
|
|
334
|
+
"memory.features.modelTesting": "测试中…",
|
|
335
|
+
"memory.features.modelTestOk": "连通正常",
|
|
336
|
+
"memory.features.modelTestFail": "测试失败",
|
|
337
|
+
"memory.features.modelTestHint": "真实发起一次最小巩固调用,验证 Provider/模型连通与 effort 支持",
|
|
329
338
|
"memory.explorer.viewCards": "卡片",
|
|
330
339
|
"memory.explorer.viewTimeline": "时间线",
|
|
331
340
|
"memory.explorer.viewAria": "视图切换",
|
|
@@ -606,6 +615,15 @@ window.__ModuleLoader__.load({
|
|
|
606
615
|
"memory.features.dreamProvider": "Consolidation provider",
|
|
607
616
|
"memory.features.dreamModel": "Consolidation model",
|
|
608
617
|
"memory.features.dreamModelHint": "Leave empty to follow the main conversation model; only affects autoDream consolidation",
|
|
618
|
+
"memory.features.sleepProvider": "Sleep provider",
|
|
619
|
+
"memory.features.sleepModel": "Sleep model",
|
|
620
|
+
"memory.features.sleepModelHint": "Leave empty to reuse the consolidation model; a non-reasoning model is recommended",
|
|
621
|
+
"memory.features.routeFollowDefault": "Follow default route",
|
|
622
|
+
"memory.features.modelTest": "Test connectivity",
|
|
623
|
+
"memory.features.modelTesting": "Testing…",
|
|
624
|
+
"memory.features.modelTestOk": "Connected",
|
|
625
|
+
"memory.features.modelTestFail": "Test failed",
|
|
626
|
+
"memory.features.modelTestHint": "Fires one minimal consolidation call to verify provider/model connectivity and effort support",
|
|
609
627
|
"memory.explorer.viewCards": "Cards",
|
|
610
628
|
"memory.explorer.viewTimeline": "Timeline",
|
|
611
629
|
"memory.explorer.viewAria": "Switch view",
|
|
@@ -750,6 +768,7 @@ window.__ModuleLoader__.load({
|
|
|
750
768
|
".mneme-chip:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
751
769
|
".mneme-chip.mneme-active{color:var(--dsw-alias-state-business-primary);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent)}",
|
|
752
770
|
".mneme-select{box-sizing:border-box;height:30px;padding:0 8px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base,transparent);color:var(--dsw-alias-label-primary);font-family:inherit;font-size:13px;outline:none}",
|
|
771
|
+
".mneme-routeselect{width:240px;max-width:60%}",
|
|
753
772
|
".mneme-footbtn{border:none;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;font-size:12px;line-height:16px;padding:3px 8px;border-radius:6px}",
|
|
754
773
|
".mneme-footbtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
755
774
|
".mneme-hint{color:var(--dsw-alias-label-tertiary);padding:24px 0;text-align:center;font-size:13px}",
|
|
@@ -1601,6 +1620,11 @@ window.__ModuleLoader__.load({
|
|
|
1601
1620
|
const [savedTick, setSavedTick] = useState(false);
|
|
1602
1621
|
const [showAdv, setShowAdv] = useState(false);
|
|
1603
1622
|
const [strs, setStrs] = useState({}); // 字符串输入的本地草稿:key -> string
|
|
1623
|
+
// 巩固/睡眠模型路由下拉的数据源:GET /llm-providers 探测(云端 v0.7.26+
|
|
1624
|
+
// 的插件侧端点)。null = 端点不可用(404/失败)→ 回退纯文本输入,不挡旧后端。
|
|
1625
|
+
const [routes, setRoutes] = useState(null);
|
|
1626
|
+
// 连通性测试结果:{ running: true } | { ok, ms, detail }
|
|
1627
|
+
const [testState, setTestState] = useState(null);
|
|
1604
1628
|
|
|
1605
1629
|
useEffect(() => {
|
|
1606
1630
|
let cancelled = false;
|
|
@@ -1618,6 +1642,16 @@ window.__ModuleLoader__.load({
|
|
|
1618
1642
|
return () => { cancelled = true; };
|
|
1619
1643
|
}, [t]);
|
|
1620
1644
|
|
|
1645
|
+
// 插件侧枚举端点探测:不可用时静默保持 routes=null(文本框回退)。
|
|
1646
|
+
useEffect(() => {
|
|
1647
|
+
let cancelled = false;
|
|
1648
|
+
apiFetch("/api/dsh-mneme/llm-providers")
|
|
1649
|
+
.then((res) => { if (!res.ok) throw new Error("HTTP " + res.status); return res.json(); })
|
|
1650
|
+
.then((j) => { if (!cancelled) setRoutes(Array.isArray(j && j.providers) ? j.providers : []); })
|
|
1651
|
+
.catch(() => {});
|
|
1652
|
+
return () => { cancelled = true; };
|
|
1653
|
+
}, [t]);
|
|
1654
|
+
|
|
1621
1655
|
const eff = (state && state.effective) || {};
|
|
1622
1656
|
|
|
1623
1657
|
const put = async (patch) => {
|
|
@@ -1659,6 +1693,91 @@ window.__ModuleLoader__.load({
|
|
|
1659
1693
|
onToggle: () => put({ [k]: !eff[k] })
|
|
1660
1694
|
});
|
|
1661
1695
|
|
|
1696
|
+
// 模型枚举项的统一形状:string 或 {id, name?}(/llm-providers 契约)。
|
|
1697
|
+
const modelLabel = (m) => (typeof m === "string" ? m : String((m && (m.name || m.id)) ?? ""));
|
|
1698
|
+
const modelValue = (m) => (typeof m === "string" ? m : String((m && m.id) ?? ""));
|
|
1699
|
+
|
|
1700
|
+
// 连通性测试:POST /test-model,空 provider/model = 按巩固路由解析
|
|
1701
|
+
// (agent 默认)。durationMs 优先后端值,缺失时客户端兜底计时;成功行
|
|
1702
|
+
// 附模型的实际回复(reply,后端截 100 字符)——「真的答了 ok」而非只报通。
|
|
1703
|
+
const runModelTest = async (provider, model) => {
|
|
1704
|
+
setTestState({ running: true });
|
|
1705
|
+
const started = Date.now();
|
|
1706
|
+
try {
|
|
1707
|
+
const res = await apiFetch("/api/dsh-mneme/test-model", {
|
|
1708
|
+
method: "POST",
|
|
1709
|
+
headers: { "Content-Type": "application/json" },
|
|
1710
|
+
body: JSON.stringify({ provider, model })
|
|
1711
|
+
});
|
|
1712
|
+
const j = await res.json().catch(() => ({}));
|
|
1713
|
+
const ms = typeof j.durationMs === "number" ? j.durationMs : Date.now() - started;
|
|
1714
|
+
if (!res.ok || j.ok === false) {
|
|
1715
|
+
setTestState({
|
|
1716
|
+
ok: false,
|
|
1717
|
+
ms,
|
|
1718
|
+
detail: [j.modelId, j.error || "HTTP " + res.status].filter(Boolean).join(" · ")
|
|
1719
|
+
});
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
setTestState({
|
|
1723
|
+
ok: true,
|
|
1724
|
+
ms,
|
|
1725
|
+
detail: [j.modelId, j.reply ? "「" + j.reply + "」" : ""].filter(Boolean).join(" · ")
|
|
1726
|
+
});
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
setTestState({ ok: false, ms: Date.now() - started, detail: String((err && err.message) ?? err) });
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
// 巩固/睡眠路由行:provider/model 级联下拉(数据来自宿主侧已注册的
|
|
1733
|
+
// 适配器,用户不会选到不存在的模型)+ 连通性测试。空值 = 跟随默认
|
|
1734
|
+
// 路由;改动即提交(与 embedProvider 下拉一致)。当前值不在枚举里时
|
|
1735
|
+
// 保留为额外选项,避免静默改值。
|
|
1736
|
+
const routeSelects = (providerKey, modelKey) => {
|
|
1737
|
+
const curP = strs[providerKey] ?? "";
|
|
1738
|
+
const curM = strs[modelKey] ?? "";
|
|
1739
|
+
const entries = Array.isArray(routes) ? routes : [];
|
|
1740
|
+
const entry = entries.find((p) => p && p.provider === curP);
|
|
1741
|
+
const models = (entry && Array.isArray(entry.models)) ? entry.models : [];
|
|
1742
|
+
const mVals = models.map(modelValue);
|
|
1743
|
+
const pList = entries.map((p) => p.provider).concat(
|
|
1744
|
+
curP && !entries.some((p) => p.provider === curP) ? [curP] : []);
|
|
1745
|
+
const putKey = (key) => (e) => {
|
|
1746
|
+
const v = e.target.value;
|
|
1747
|
+
setStrs((c) => ({ ...c, [key]: v }));
|
|
1748
|
+
put({ [key]: v });
|
|
1749
|
+
};
|
|
1750
|
+
return h(react.Fragment, null,
|
|
1751
|
+
h("div", { className: "mneme-featnum" },
|
|
1752
|
+
h("span", { className: "mneme-featnumlabel" }, t(`memory.features.${providerKey}`)),
|
|
1753
|
+
h("select", {
|
|
1754
|
+
className: "mneme-select mneme-routeselect", value: curP, disabled: busy,
|
|
1755
|
+
"aria-label": t(`memory.features.${providerKey}`), onChange: putKey(providerKey)
|
|
1756
|
+
},
|
|
1757
|
+
h("option", { value: "" }, t("memory.features.routeFollowDefault")),
|
|
1758
|
+
pList.map((p) => h("option", { key: p, value: p }, p)))),
|
|
1759
|
+
h("div", { className: "mneme-featnum" },
|
|
1760
|
+
h("span", { className: "mneme-featnumlabel" }, t(`memory.features.${modelKey}`)),
|
|
1761
|
+
h("select", {
|
|
1762
|
+
className: "mneme-select mneme-routeselect", value: curM, disabled: busy,
|
|
1763
|
+
"aria-label": t(`memory.features.${modelKey}`), onChange: putKey(modelKey)
|
|
1764
|
+
},
|
|
1765
|
+
h("option", { value: "" }, t("memory.features.routeFollowDefault")),
|
|
1766
|
+
models.map((m, i) => h("option", { key: modelValue(m) + "|" + i, value: modelValue(m) }, modelLabel(m))),
|
|
1767
|
+
curM && !mVals.includes(curM) ? h("option", { key: "current", value: curM }, curM) : null)),
|
|
1768
|
+
h("div", { className: "mneme-featnum" },
|
|
1769
|
+
h("button", {
|
|
1770
|
+
type: "button", className: "mneme-btn",
|
|
1771
|
+
disabled: busy || (testState && testState.running),
|
|
1772
|
+
onClick: () => runModelTest(curP, curM)
|
|
1773
|
+
}, t((testState && testState.running) ? "memory.features.modelTesting" : "memory.features.modelTest")),
|
|
1774
|
+
testState && !testState.running && h("div", { className: "mneme-featsubhint" },
|
|
1775
|
+
(testState.ok ? "✓ " + t("memory.features.modelTestOk") : "✗ " + t("memory.features.modelTestFail"))
|
|
1776
|
+
+ (typeof testState.ms === "number" ? " · " + (testState.ms / 1000).toFixed(1) + "s" : "")
|
|
1777
|
+
+ (testState.detail ? " · " + testState.detail : "")))
|
|
1778
|
+
);
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1662
1781
|
const strRow = (key) => h("div", { className: "mneme-featnum", key },
|
|
1663
1782
|
h("span", { className: "mneme-featnumlabel" }, t(`memory.features.${key}`)),
|
|
1664
1783
|
h("input", {
|
|
@@ -1689,13 +1808,22 @@ window.__ModuleLoader__.load({
|
|
|
1689
1808
|
eff.embedProvider === "ollama" && strRow("ollamaModel")
|
|
1690
1809
|
);
|
|
1691
1810
|
|
|
1692
|
-
// 巩固模型:autoDream
|
|
1811
|
+
// 巩固模型:autoDream 开着才展开,避免闲置配置占版面。/llm-providers
|
|
1812
|
+
// 可用时用级联下拉 + 连通性测试;旧后端(端点 404)回退纯文本输入。
|
|
1693
1813
|
const dreamSub = eff.autoDream && h("div", { className: "mneme-featsub" },
|
|
1694
|
-
|
|
1695
|
-
|
|
1814
|
+
Array.isArray(routes)
|
|
1815
|
+
? routeSelects("dreamProvider", "dreamModel")
|
|
1816
|
+
: h(react.Fragment, null, strRow("dreamProvider"), strRow("dreamModel")),
|
|
1696
1817
|
h("div", { className: "mneme-featsubhint" }, t("memory.features.dreamModelHint"))
|
|
1697
1818
|
);
|
|
1698
1819
|
|
|
1820
|
+
// 睡眠模型:sleepModeEnabled 开着才展开(sleepProvider/sleepModel 随本版
|
|
1821
|
+
// 进白名单;下拉与测试复用同一 /llm-providers 数据源)。
|
|
1822
|
+
const sleepSub = eff.sleepModeEnabled && Array.isArray(routes) && h("div", { className: "mneme-featsub" },
|
|
1823
|
+
routeSelects("sleepProvider", "sleepModel"),
|
|
1824
|
+
h("div", { className: "mneme-featsubhint" }, t("memory.features.sleepModelHint"))
|
|
1825
|
+
);
|
|
1826
|
+
|
|
1699
1827
|
if (error && !state) return h("section", { className: "mneme-set-card" },
|
|
1700
1828
|
h("div", { className: "mneme-set-title" }, t("memory.features.title")),
|
|
1701
1829
|
h("div", { className: "mneme-set-hint" }, error));
|
|
@@ -1713,7 +1841,7 @@ window.__ModuleLoader__.load({
|
|
|
1713
1841
|
h("div", { className: "mneme-featgroup" }, t(`memory.features.${g.key}`)),
|
|
1714
1842
|
g.items.map(boolRow),
|
|
1715
1843
|
g.key === "group.enhance" && embedSub,
|
|
1716
|
-
g.key === "group.dream" && dreamSub
|
|
1844
|
+
g.key === "group.dream" && h(react.Fragment, null, dreamSub, sleepSub)
|
|
1717
1845
|
)),
|
|
1718
1846
|
h("div", { className: "mneme-featgroup" },
|
|
1719
1847
|
h("button", {
|
package/lib/config.js
CHANGED
|
@@ -62,8 +62,8 @@ export const Config = z.object({
|
|
|
62
62
|
// reasoning effort —— 即使去掉 effort 重试,harness 的 defaultEffort 也会顶上来
|
|
63
63
|
// 再次拒绝(UNSUPPORTED_REASONING_EFFORT),插件 fallback 无法绕开。
|
|
64
64
|
// 选用时建议配 dreamReasoningEffort 并实测;不行就换非思考模型。
|
|
65
|
-
dreamProvider: z.string(),
|
|
66
|
-
dreamModel: z.string(),
|
|
65
|
+
dreamProvider: z.string().description("记忆巩固专用模型的服务商(settings「巩固模型」)。巩固反复失败时,优先改用官方非思考模型的服务商(如 deepseek / glm)。"),
|
|
66
|
+
dreamModel: z.string().description("记忆巩固专用模型。建议选非思考模型(如 deepseek-chat、glm-5-2 类):思考模型可能烧光 token 预算返回空体,导致巩固失败(UNSUPPORTED_REASONING_EFFORT)。"),
|
|
67
67
|
dreamMaxTokens: z.natural().min(256).max(131072).default(32768),
|
|
68
68
|
// Pass-through reasoning effort for dream's LLM calls. 'none' (default)
|
|
69
69
|
// omits the field so the provider's own default applies; low/medium/high
|
|
@@ -78,7 +78,7 @@ export const Config = z.object({
|
|
|
78
78
|
z.const("medium"),
|
|
79
79
|
z.const("high"),
|
|
80
80
|
z.const("none")
|
|
81
|
-
]).default("none"),
|
|
81
|
+
]).default("none").description("巩固模型的推理档位:'none'(默认)用服务商自带默认;low/medium/high 原样传递。模型不支持的值会自动换用其支持的档位(v0.7.26+)。"),
|
|
82
82
|
// 滑动窗口上限(v0.4.4):autoDream 每次只对最近 dreamMaxSnapshotSize 条
|
|
83
83
|
// 记忆做 consolidation。大记忆量下全量快照会把 LLM 输入撑爆(636 记忆 →
|
|
84
84
|
// 677 "missing" errors、applied=0),窗口外的旧记忆不进 snapshot。
|
|
@@ -251,8 +251,8 @@ export const Config = z.object({
|
|
|
251
251
|
sleepMaxPatternPerRun: z.natural().min(0).max(10).default(3),
|
|
252
252
|
// Optional LLM route override for sleep's bulk passes (empty = use dream
|
|
253
253
|
// route / agent default model).
|
|
254
|
-
sleepProvider: z.string().default(""),
|
|
255
|
-
sleepModel: z.string().default(""),
|
|
254
|
+
sleepProvider: z.string().default("").description("sleep 深维护专用模型服务商,留空用巩固模型或当前模型。"),
|
|
255
|
+
sleepModel: z.string().default("").description("sleep 深维护专用模型,留空用巩固模型或当前模型;建议同巩固模型选非思考模型。"),
|
|
256
256
|
// Pass-through reasoning effort for sleep's LLM passes, same semantics as
|
|
257
257
|
// dreamReasoningEffort: 'none' (default) omits the field; low/medium/high
|
|
258
258
|
// are forwarded verbatim.
|
|
@@ -261,7 +261,7 @@ export const Config = z.object({
|
|
|
261
261
|
z.const("medium"),
|
|
262
262
|
z.const("high"),
|
|
263
263
|
z.const("none")
|
|
264
|
-
]).default("none"),
|
|
264
|
+
]).default("none").description("同 dreamReasoningEffort:sleep 各阶段 LLM 的推理档位,'none' 用服务商默认。"),
|
|
265
265
|
|
|
266
266
|
// --- epistemic trust: memory source credibility (v0.4.5) -----------------
|
|
267
267
|
// Distinguish memories by source: observation (measured / witnessed),
|
package/lib/dream/sleep.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { randomUUID, createHash } from "node:crypto";
|
|
18
18
|
import { validateDecisions, applyDecisions } from "./decisions.js";
|
|
19
19
|
import { findPotentialConflicts } from "./clustering.js";
|
|
20
|
-
import { buildReceipt, describeStreamFailure, withEffortFallback } from "../dream.js";
|
|
20
|
+
import { buildReceipt, describeStreamFailure, resolveDreamEffort, withEffortFallback } from "../dream.js";
|
|
21
21
|
import { computeHeat } from "../heat.js";
|
|
22
22
|
|
|
23
23
|
const SUMMARY_MAX = 120;
|
|
@@ -190,7 +190,7 @@ async function phaseConflicts(ctx, service, config, logger, runId, semantic = nu
|
|
|
190
190
|
const listText = selected.map((p) =>
|
|
191
191
|
`候选冲突:\nid=${p.a.id} | type=${p.a.type} | title=${p.a.title}\n${p.a.content}\n---\nid=${p.b.id} | type=${p.b.type} | title=${p.b.title}\n${p.b.content}\n(相似度 ${p.similarity.toFixed(2)})`
|
|
192
192
|
).join("\n\n");
|
|
193
|
-
const sleepEffort =
|
|
193
|
+
const sleepEffort = await resolveDreamEffort(ctx, route, config.sleepReasoningEffort, logger);
|
|
194
194
|
let conflictStreamFailure = "";
|
|
195
195
|
const runConflict = (withEffort) => {
|
|
196
196
|
conflictStreamFailure = "";
|
|
@@ -309,7 +309,7 @@ async function phasePatterns(ctx, service, config, logger, runId, signal = null)
|
|
|
309
309
|
.map((m) => `id=${m.id} | type=${m.type} | importance=${m.importance} | updated=${m.updated_at} | title=${m.title} | content=${m.content}`)
|
|
310
310
|
.join("\n");
|
|
311
311
|
const maxPatterns = config.sleepMaxPatternPerRun ?? 3;
|
|
312
|
-
const sleepEffort =
|
|
312
|
+
const sleepEffort = await resolveDreamEffort(ctx, route, config.sleepReasoningEffort, logger);
|
|
313
313
|
let patternStreamFailure = "";
|
|
314
314
|
const runPattern = (withEffort) => {
|
|
315
315
|
patternStreamFailure = "";
|