dsh-prime-memory 0.26.6 → 0.26.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.
@@ -0,0 +1,115 @@
1
+ /**
2
+ * 蒸馏路由健康学习(R9):按 `provider/model` 记「自适应退避后实测跑通的最小块长」,
3
+ * 跨进程留存,令下次 perChunk 初值直接取跑通过的值、不必每进程重付退避风暴;
4
+ * 面板可回显「实测最小可用块长 / 最后退避长度」。
5
+ *
6
+ * 运行时学习值、非磁盘契约:
7
+ * - 读 **fail-open**(缺文件/损坏/未知版本 → 空表起步,绝不抛、不阻蒸馏——学习值丢了最多少一次优化);
8
+ * - 写走 `atomicWriteJson`(原子 + 覆盖保护);
9
+ * - **只降不升**:更小的跑通块长才覆盖 lastGood(换 provider/过期由读侧决定重探,见 R5 接线)。
10
+ *
11
+ * 本模块 Phase 1 仅落地存储与纯逻辑,尚未接线到 runExtraction(接线在 Phase 4 R5/R9)。
12
+ */
13
+ import * as path from 'node:path';
14
+ import { atomicWriteJson, readJsonStrict } from '../util/io.js';
15
+ import { ROUTE_HEALTH_FILE_VERSION } from './file-versions.js';
16
+ function keyOf(provider, model) {
17
+ return `${provider}/${model}`;
18
+ }
19
+ /** 学习值默认过期(ms):超此龄视为陈旧、读侧应重探(换模型/网关不至于被历史最小值锁死)。 */
20
+ export const ROUTE_HEALTH_STALE_MS = 7 * 24 * 60 * 60_000;
21
+ export function routeHealthPathFor(dataDir) {
22
+ return path.join(dataDir, 'distill-route-health.json');
23
+ }
24
+ export class RouteHealthStore {
25
+ file;
26
+ routes = new Map();
27
+ dirty = false;
28
+ constructor(file) {
29
+ this.file = file;
30
+ }
31
+ /** 读盘 fail-open:缺/坏/未知版本 → 空表、不抛。 */
32
+ async load() {
33
+ const r = await readJsonStrict(this.file, { expectedVersion: ROUTE_HEALTH_FILE_VERSION });
34
+ this.routes = new Map();
35
+ if (!r.ok)
36
+ return; // missing / corrupt / unreadable / unknown_version 一律空起步
37
+ const raw = r.value?.routes;
38
+ if (!raw || typeof raw !== 'object')
39
+ return;
40
+ for (const [k, v] of Object.entries(raw)) {
41
+ if (!v || typeof v !== 'object')
42
+ continue;
43
+ const e = v;
44
+ if (typeof e.lastGoodChunkChars !== 'number' || !Number.isFinite(e.lastGoodChunkChars))
45
+ continue;
46
+ this.routes.set(k, {
47
+ lastGoodChunkChars: Math.max(0, Math.floor(e.lastGoodChunkChars)),
48
+ latencyEmaMs: typeof e.latencyEmaMs === 'number' && Number.isFinite(e.latencyEmaMs) ? e.latencyEmaMs : 0,
49
+ updatedAt: typeof e.updatedAt === 'string' ? e.updatedAt : '',
50
+ });
51
+ }
52
+ }
53
+ /** 该路由已学到、且未过期的跑通块长;0 = 无可用学习值(未学到或已陈旧)。 */
54
+ lastGoodChunkChars(provider, model, now = Date.now(), staleMs = ROUTE_HEALTH_STALE_MS) {
55
+ const e = this.routes.get(keyOf(provider, model));
56
+ if (!e || e.lastGoodChunkChars <= 0)
57
+ return 0;
58
+ const t = Date.parse(e.updatedAt);
59
+ if (Number.isFinite(t) && now - t > staleMs)
60
+ return 0; // 陈旧 → 交读侧重探
61
+ return e.lastGoodChunkChars;
62
+ }
63
+ /** 该路由延迟 EMA(ms);0 = 未记。 */
64
+ latencyEma(provider, model) {
65
+ return this.routes.get(keyOf(provider, model))?.latencyEmaMs ?? 0;
66
+ }
67
+ /**
68
+ * 记一次「跑通」的块长:只降不升(更小块长才覆盖 lastGood),并更新延迟 EMA。
69
+ * @param chunkChars 本次成功的块长(字符)
70
+ * @param latencyMs 本次成功调用耗时(可选,用于 EMA)
71
+ */
72
+ recordGood(provider, model, chunkChars, latencyMs) {
73
+ if (!(chunkChars > 0) || !Number.isFinite(chunkChars))
74
+ return;
75
+ const k = keyOf(provider, model);
76
+ const cur = this.routes.get(k);
77
+ const next = {
78
+ lastGoodChunkChars: cur && cur.lastGoodChunkChars > 0
79
+ ? Math.min(cur.lastGoodChunkChars, Math.floor(chunkChars))
80
+ : Math.floor(chunkChars),
81
+ latencyEmaMs: latencyMs !== undefined && Number.isFinite(latencyMs) && latencyMs >= 0
82
+ ? cur && cur.latencyEmaMs > 0
83
+ ? cur.latencyEmaMs * 0.7 + latencyMs * 0.3
84
+ : latencyMs
85
+ : (cur?.latencyEmaMs ?? 0),
86
+ updatedAt: new Date().toISOString(),
87
+ };
88
+ this.routes.set(k, next);
89
+ this.dirty = true;
90
+ }
91
+ /** 清除学习值:传 provider+model 清单条,否则清全部(面板「重置学习值」一键)。 */
92
+ reset(provider, model) {
93
+ if (provider && model)
94
+ this.routes.delete(keyOf(provider, model));
95
+ else
96
+ this.routes.clear();
97
+ this.dirty = true;
98
+ }
99
+ /** 全量快照(面板回显用)。 */
100
+ snapshot() {
101
+ return [...this.routes.entries()].map(([key, v]) => ({ key, ...v }));
102
+ }
103
+ /** 有变更才落盘;失败由调用方兜底(不抛穿蒸馏)。@returns 是否写了盘 */
104
+ async save() {
105
+ if (!this.dirty)
106
+ return false;
107
+ const routes = {};
108
+ for (const [k, v] of this.routes)
109
+ routes[k] = v;
110
+ const payload = { version: ROUTE_HEALTH_FILE_VERSION, routes };
111
+ await atomicWriteJson(this.file, payload);
112
+ this.dirty = false;
113
+ return true;
114
+ }
115
+ }
package/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "dsh-prime-memory",
3
3
  "name": "dsh-prime-memory",
4
- "version": "0.26.6",
4
+ "version": "0.26.10",
5
5
  "description": {
6
6
  "en": "Layered distilled memory plugin (L0 capture → L1 atomic memory → L2 scenes → L3 persona) with automatic recall injection before each model step.",
7
7
  "zh": "L0~L3 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "license": "MIT",
15
15
  "engines": {
16
- "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2"
16
+ "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
17
17
  },
18
18
  "components": {
19
19
  "host": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.26.6",
3
+ "version": "0.26.10",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "engines": {
66
66
  "node": ">=22.16.0",
67
- "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2"
67
+ "dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
68
68
  },
69
69
  "keywords": [
70
70
  "dsh",
@@ -88,15 +88,15 @@
88
88
  },
89
89
  "peerDependencies": {
90
90
  "@deepseek-ai/cordis": "^4.0.1",
91
- "@deepseek-ai/dsh-agent": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
92
- "@deepseek-ai/dsh-client-connection": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
93
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
94
- "@deepseek-ai/dsh-home-paths": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
95
- "@deepseek-ai/dsh-llm": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
96
- "@deepseek-ai/dsh-session": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
97
- "@deepseek-ai/dsh-settings": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
98
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2",
99
- "@deepseek-ai/dsh-tools": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2"
91
+ "@deepseek-ai/dsh-agent": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
92
+ "@deepseek-ai/dsh-client-connection": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
93
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
94
+ "@deepseek-ai/dsh-home-paths": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
95
+ "@deepseek-ai/dsh-llm": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
96
+ "@deepseek-ai/dsh-session": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
97
+ "@deepseek-ai/dsh-settings": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
98
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
99
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
100
100
  },
101
101
  "peerDependenciesMeta": {
102
102
  "@deepseek-ai/dsh-agent": {