dsh-layered-memory 0.8.8 → 0.8.10

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 CHANGED
@@ -159,6 +159,16 @@ trajectory view):
159
159
  - Each session's choice is persisted by sessionId to `session-modes.json`, surviving
160
160
  restarts/session restore; stacks with the global switches (global is the master gate);
161
161
  L2/L3 are fully family-isolated — content never leaks across families.
162
+ - **Write-only sessions (#38)**: a three-state "injection" switch inside the popover
163
+ (follow global / on / off) — set to "off" for a **write-only session**: capture and
164
+ distillation continue as usual (conversation still settles into L0→L1→L2/L3), but
165
+ nothing is injected into this session (recall injection, the persona/navigation
166
+ stable section and the tools guide all stop; `memory_search` and the other read
167
+ tools return a write-only notice). The pill face changes to `Memory · Write-only`;
168
+ the override persists per session, and switching back to "follow global" clears it
169
+ to the settings-page recall toggle. Ideal for debug/eval/sensitive sessions that
170
+ should absorb without interference. Orthogonal to the off mode: off remains full
171
+ stealth (capture off too), while write-only keeps the "in" and gates the "out".
162
172
 
163
173
  ## UI Preview
164
174
 
@@ -274,6 +284,7 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
274
284
  | `embedding.proxy` | `''` | Three-state download proxy: `''` (default) = auto-detect proxy env vars (`HTTPS_PROXY`/`ALL_PROXY` etc., honoring `NO_PROXY`); `none` = disable, always direct; any other value = proxy URL (e.g. `http://127.0.0.1:7890`). Direct connections to the mirror are intermittently unreachable on some networks (connect timeouts and poisoned bytes have both been observed) — keep the default auto-detection on machines with a proxy |
275
285
  | `llm.provider/model` | empty | Static distillation route (deployment pin): when **both** fields are set the route is locked, outranking the settings-page runtime route chain and the default model (deployments can force distillation onto a specific route); when empty the route follows "settings-page route-chain primary → default model". At runtime, configure the primary route and fallback chain in the **route-chain editor** under Settings → Memory → Overview → distillation parameters (pick from **configured providers**, including custom ones added in dsh Settings → Models; the primary row may stay empty to follow the default model) — a non-empty chain takes over this static config wholesale, effective immediately with no restart |
276
286
  | `llm.fallbacks` | `[]` | Distillation fallback chain: an ordered list of backup routes tried one by one when the primary route fails (error / cut-off / network error / **empty output**); each entry is `{provider, model, reasoningEffort?}` (a non-empty effort overrides the global `llm.reasoningEffort`, still clamped by model capability); entries identical to the primary route are skipped; **each route gets the full `timeoutMs`**; when all routes fail, the existing per-session backoff takes over. Empty list (default) = single-route behavior unchanged (see [Distillation fallback chain & slow-TTFT models](#distillation-fallback-chain--slow-ttft-models) below); a non-empty settings-page runtime chain (`distillChain`) takes over **both** the primary route and the fallback chain (a single-row chain = explicitly no fallbacks), empty = follow this config |
287
+ | `llm.layerRoutes` | `{}` | **Per-layer distillation routing** (#34): keys `l1`/`l2`/`l3`, each holding a **complete chain** (entries like `llm.fallbacks`, **head row must have both provider+model explicitly**). A non-empty chain **fully replaces** that layer's resolution (its primary and fallbacks all come from the layer chain; the global chain no longer participates); empty/missing = the layer follows the global chain. `l1` covers both extraction and dedup call sites. Layers can also be edited at runtime in the segmented panel under distillation parameters on the settings page (takes priority over this static config); a deployment pin does not disable static layer chains (same deployer-owned config as the fallback-chain precedent). Orthogonal to and composable with the fallback chain — one complete chain per layer (ADR-0005) |
277
288
  | `llm.maxTokens` | `65536` | Fallback output cap for non-layered calls. Each distillation stage has its own budget (extraction 16k / dedup 8k / L2 32k / L3 16k; auto ×4 when the reasoning effort is high/xhigh/max, so thinking can't starve the text budget); the per-layer budgets are runtime-adjustable in Settings → Memory → Overview → distillation parameters (empty/0 = built-in defaults) |
278
289
  | `llm.reasoningEffort` | empty | Distillation reasoning effort: empty = **auto** (resolved from model capability: the model's default tier, else `high`); an explicit value (`off`/`none`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`) is only sent when the model declares support — effort vocabularies differ across providers (deepseek accepts `off`, OpenAI-style APIs use `none`, models that declare no tiers get nothing), and unsupported tiers degrade to not-sending with a one-time warning; output budgets auto-×4 at high/xhigh/max. At runtime, override the effort **per route** in the settings-page route-chain editor (per-row dropdown; the tier list follows each model's declared capability live, defaulting to this value) |
279
290
  | `llm.temperature` | `0.3` | Distillation temperature |
@@ -302,8 +313,27 @@ Free/slow tiers of some inference providers have **first-token latencies (TTFT)
302
313
  model: deepseek-v4-flash
303
314
  ```
304
315
 
316
+ 3. **Per-layer routing** (each layer on its own channel): distillation layers want different things from a model (L1 is high-frequency and wants cheap/fast/stable; L3 tolerates slow first packets but needs strong capability), so diverging layers can get their own chain — one **complete fallback chain per layer**, while unconfigured layers keep using the global chain:
317
+
318
+ ```yaml
319
+ llm:
320
+ layerRoutes: # per-layer routing (#34); the head row must set provider+model explicitly
321
+ l1: # l1 covers both extraction and dedup call sites: a cheap, fast, stable chain
322
+ - provider: opencode-go
323
+ model: deepseek-v4-flash
324
+ reasoningEffort: low
325
+ - provider: deepseek-official # in-layer fallback: L1 failures demote only here, never onto the global chain
326
+ model: deepseek-v4-flash
327
+ l3: # L3 persona distillation: low frequency, large inputs — a strong-capability chain
328
+ - provider: deepseek-official
329
+ model: deepseek-v4-flash
330
+ reasoningEffort: high
331
+ ```
332
+
333
+ Layers can also be edited at runtime in the **segmented panel** (global default / L1 / L2 / L3) under Settings → Memory → Overview → distillation parameters. In-layer priority: runtime layer chain > this static YAML layer chain > global default chain, falling back level by level.
334
+
305
335
  Failure = error / cut-off / network error / **empty output** (stream ends normally with 0 characters — worthless for distillation since parsing always fails, so it is treated as a route failure rather than an empty return); caller-initiated cancellation does not demote; each route gets the **full** `llm.timeoutMs` (a shared budget would give a slow-TTFT fallback route less time than its real first-packet needs, defeating the chain); token costs are recorded per attempt (failed attempts get a row too, with whatever tokens arrived before the stream broke), and successful calls are attributed to the route that actually served. The route chain can also be adjusted at runtime in the route-chain editor under Settings → Memory → Overview → distillation parameters (no config edit or restart needed); the YAML below suits deployments that want to pin the static chain.
306
- 3. **Raise the timeout**: `llm.timeoutMs` only helps when the route is genuinely slow but the gateway doesn't cut; if the gateway kills at 20s, raising the plugin timeout is futile — use the first two layers.
336
+ 4. **Raise the timeout**: `llm.timeoutMs` only helps when the route is genuinely slow but the gateway doesn't cut; if the gateway kills at 20s, raising the plugin timeout is futile — use the first two layers.
307
337
 
308
338
  ## Storage Layout
309
339
 
package/README.md CHANGED
@@ -124,6 +124,12 @@ L1/L2/L3 层级过滤)、层级 × 时间窗口表格(调用数 / 输出与
124
124
  打开期间自适应轮询(忙 2s / 静 5s),关闭即停;
125
125
  - 每会话的选择按 sessionId 持久化到 `session-modes.json`,重启/恢复会话不丢;
126
126
  与全局开关叠加(全局是总闸);L2/L3 完全分类,分类内容不渗透。
127
+ - **只写不读(#38)**:悬浮板内「注入」三态开关(跟随全局 / 开 / 关)——设为「关」
128
+ 即**只写会话**:捕获与蒸馏照常(对话照常沉淀为 L0→L1→L2/L3),但不向本会话注入
129
+ 任何记忆(召回注入、画像/导航稳定区、工具指南一并停止;`memory_search` 等读工具
130
+ 返回只写提示)。pill 面文换作 `记忆·只写` 提示状态;覆盖按会话持久化,切回
131
+ 「跟随全局」即清除、跟随设置页召回开关;适合调试/评测/敏感会话「只吸收不干扰」。
132
+ 与 off 档正交:off 仍是完全隐身(连捕获都关),只写保留「进」关「出」。
127
133
 
128
134
  ## 界面预览
129
135
 
@@ -277,6 +283,7 @@ ONNX 量化 **CPU 推理**——无需 API Key,数据不出本机)。本地
277
283
  | `embedding.proxy` | `''` | 模型下载代理三态:`''`(默认)= 自动探测代理环境变量(`HTTPS_PROXY`/`ALL_PROXY` 等,尊重 `NO_PROXY`);`none` = 禁用强制直连;其他值 = 代理 URL(如 `http://127.0.0.1:7890`)。镜像直连在国内网络间歇不可达(直连超时与污染字节交替出现过),开代理的机器建议保持默认自动探测 |
278
284
  | `llm.provider/model` | 空 | 蒸馏模型静态路由(部署 pin):provider 与 model **双字段齐**时锁定蒸馏路由,优先于设置页的运行时路由链与默认模型(部署可强制蒸馏走指定路由);留空则跟随"设置页路由链主路由 → 默认模型"。运行时可在设置页 → 记忆 → 概览 → 蒸馏参数的**蒸馏路由链编辑器**里配置主路由与回退链(从**已配置的供应商**(含 dsh 设置 → 模型里添加的自定义供应商)中选择,主路由行可留空跟随默认模型),非空即整体接管本静态配置,即时生效无需重启 |
279
285
  | `llm.fallbacks` | `[]` | 蒸馏回退链:主路由失败(报错/被掐断/网络异常/**空输出**)后按条目顺序逐个降级尝试的备用路由列表,条目 = `{provider, model, reasoningEffort?}`(档位非空覆盖全局 `llm.reasoningEffort`,仍按模型能力钳制);与主路由完全相同的条目自动跳过;**每条路由各享全额 `timeoutMs`**;全部失败交既有按会话退避重试。空数组(缺省)= 单路由行为不变(详见下方[蒸馏回退链与慢 TTFT 模型](#蒸馏回退链与慢-ttft-模型));设置页运行时路由链(`distillChain`)非空时**整体接管**主路由与回退链(单行链 = 显式无回退),空 = 跟随本配置 |
286
+ | `llm.layerRoutes` | `{}` | 蒸馏**按层路由**:层键 `l1`/`l2`/`l3` 各配一条**完整链**(条目同 `llm.fallbacks`,**头行必须 provider+model 双显式**),非空即**完整替换**该层解析(该层主路由与回退都归层链管,全局链对该层不参与),空/缺省 = 该层跟随全局;`l1` 同管抽取+去重两个调用点。运行时可在设置页「蒸馏参数」分段面板里按层编辑(优先于本静态配置);部署 pin 不废静态层链(同为部署配置,同回退链先例)。与回退链正交可组合——每层各自一条链(ADR-0005) |
280
287
  | `llm.maxTokens` | `65536` | 未分层调用的兜底输出总闸。各蒸馏层有独立预算(抽取 16k / 去重 8k / L2 32k / L3 16k;思考档 high/xhigh/max 时自动 ×4,防 reasoning 吃光预算),分层预算可在设置页 → 记忆 → 概览 → 蒸馏参数运行时调整(留空/0 = 跟随内置默认) |
281
288
  | `llm.reasoningEffort` | 空 | 蒸馏思考档位:空串 = **自动**(按模型能力解析:模型默认档 → `high`);显式值(`off`/`none`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`)仅在该模型声明支持时发送——跨供应商 effort 词汇表不同(deepseek 认 `off`,OpenAI 系是 `none`,未声明档位的模型不传),不支持的档位自动降级为不传并告警一次;思考档 high/xhigh/max 时输出预算自动 ×4。运行时可在设置页路由链编辑器里**逐路由**覆盖档位(行内下拉,词表按各模型声明的能力实时显示,缺省跟随本值) |
282
289
  | `llm.temperature` | `0.3` | 蒸馏温度 |
@@ -305,8 +312,31 @@ ONNX 量化 **CPU 推理**——无需 API Key,数据不出本机)。本地
305
312
  model: deepseek-v4-flash
306
313
  ```
307
314
 
315
+ 3. **按层路由**(各走各的通道):不同蒸馏层对模型诉求不同(L1 高频要便宜快稳、
316
+ L3 低频可容忍慢首包但要强能力),可给差异层单独配链——每层一条**完整回退链**,
317
+ 未配置的层照走全局链:
318
+
319
+ ```yaml
320
+ llm:
321
+ layerRoutes: # 按层独立路由(#34);头行必须显式 provider+model
322
+ l1: # l1 同管抽取 + 去重两个调用点:配条便宜快稳的链
323
+ - provider: opencode-go
324
+ model: deepseek-v4-flash
325
+ reasoningEffort: low
326
+ - provider: deepseek-official # 层内回退:L1 故障只降级到这里,不落全局链
327
+ model: deepseek-v4-flash
328
+ l3: # L3 画像蒸馏:低频大输入,配强能力链
329
+ - provider: deepseek-official
330
+ model: deepseek-v4-flash
331
+ reasoningEffort: high
332
+ ```
333
+
334
+ 也可在设置页 → 记忆 → 概览 → 蒸馏参数的**分段面板**(全局默认 / L1 / L2 / L3)
335
+ 里按层运行时编辑,层内优先级:运行时层链 > 本 YAML 静态层链 > 全局默认链,
336
+ 逐级兜底。
337
+
308
338
  失败 = 报错 / 被掐断 / 网络异常 / **空输出**(流正常结束但 0 字符——对蒸馏而言必然在解析阶段报废,改判为该路由失败而非返回空串);调用方主动取消不降级;每条路由各享**全额** `llm.timeoutMs`(共享预算会让慢 TTFT 的回退路由拿到的窗口小于它真实需要的首包时间,回退链形同虚设);token 成本逐次尝试记账(失败尝试也计一行,含流中断前已到的 token),成功调用归因到实际服务的路由。路由链也可在设置页 → 记忆 → 概览 → 蒸馏参数的「蒸馏路由链」编辑器里运行时调整(无需改配置重启);本 YAML 适合部署者固化静态链。
309
- 3. **调高超时**:`llm.timeoutMs` 只在路由确实慢但网关不掐时有用;网关 20s 掐断的场景调插件超时无效,请用前两层。
339
+ 4. **调高超时**:`llm.timeoutMs` 只在路由确实慢但网关不掐时有用;网关 20s 掐断的场景调插件超时无效,请用前两层。
310
340
 
311
341
  ## 日志与故障排查
312
342