dsh-layered-memory 0.8.5 → 0.8.6
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 +15 -2
- package/README.md +4 -2
- package/dist/config.d.ts +7 -0
- package/dist/config.js +3 -0
- package/dist/embedding-worker.cjs +176 -0
- package/dist/hooks/recall.d.ts +10 -6
- package/dist/hooks/recall.js +40 -8
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -2
- package/dist/pipeline/runner.d.ts +5 -0
- package/dist/pipeline/runner.js +23 -4
- package/dist/pipeline/trigger.d.ts +2 -0
- package/dist/pipeline/trigger.js +11 -0
- package/dist/store/embedding-source.d.ts +2 -1
- package/dist/store/embedding-source.js +7 -2
- package/dist/store/embedding.d.ts +2 -1
- package/dist/store/l0.js +10 -4
- package/dist/store/l1.d.ts +11 -1
- package/dist/store/l1.js +26 -6
- package/dist/store/local-embedding.d.ts +69 -46
- package/dist/store/local-embedding.js +179 -75
- package/dist/store/recall-dedupe.d.ts +26 -0
- package/dist/store/recall-dedupe.js +138 -0
- package/dist/store/runtime-installer.d.ts +0 -2
- package/dist/store/runtime-installer.js +0 -6
- package/dist/store/search-utils.d.ts +17 -0
- package/dist/store/search-utils.js +29 -0
- package/dist/store/sqlite.js +23 -9
- package/dist/util/recall-budget.d.ts +2 -2
- package/dist/util/recall-budget.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 召回去重存储:sessionId → 已注入 L1 记录 id 集合的持久化映射。
|
|
3
|
+
*
|
|
4
|
+
* 语义(2026-08-24 设计共识):
|
|
5
|
+
* - 同会话内已注入过的记忆不再重复注入(模型上下文已持有,重复注入浪费 token);
|
|
6
|
+
* - 压制粒度 = 记录 id——去重合并更新会换新 id,新内容天然解除压制重新注入;
|
|
7
|
+
* - compact/clear 事件重置(上下文被压缩/清空,注入内容已丢失);resume 不重置;
|
|
8
|
+
* - 热路径(召回 pre-step)同步内存读取,mark/reset 写穿持久化(session-modes 同款:
|
|
9
|
+
* 串行化原子写 + 失败降级内存态),任何 I/O 失败绝不抛进召回路径。
|
|
10
|
+
*/
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { errDetail } from '../util/filelog.js';
|
|
13
|
+
import { atomicWriteJson, ensureDir, readJsonIfExists } from './io.js';
|
|
14
|
+
/** 会话条目上限(按 updatedAt 淘汰最旧;防文件无限增长)。 */
|
|
15
|
+
export const RECALL_DEDUPE_SESSION_CAP = 200;
|
|
16
|
+
/** 单会话记录 id 上限(按插入序淘汰最旧;Set 迭代序即插入序)。 */
|
|
17
|
+
export const RECALL_DEDUPE_IDS_CAP = 512;
|
|
18
|
+
/** 条目过期清理(90 天未更新即丢弃,与 session-modes 同款量级)。 */
|
|
19
|
+
const PRUNE_MS = 90 * 24 * 3600_000;
|
|
20
|
+
export class RecallDedupeStore {
|
|
21
|
+
logger;
|
|
22
|
+
file;
|
|
23
|
+
entries = new Map();
|
|
24
|
+
persistFailed = false;
|
|
25
|
+
/** 串行化持久化写(避免并发原子写撞临时文件名);init 链最前(先载入再落盘,防丢更新)。 */
|
|
26
|
+
writeChain;
|
|
27
|
+
constructor(dataDir, logger) {
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.file = path.join(dataDir, 'recall-dedupe.json');
|
|
30
|
+
this.writeChain = this.init();
|
|
31
|
+
}
|
|
32
|
+
/** 载入持久化映射(合并进内存——构造与载入之间发生的 mark 不丢);失败降级内存态。 */
|
|
33
|
+
async init() {
|
|
34
|
+
const data = await readJsonIfExists(this.file);
|
|
35
|
+
if (!data?.sessions || typeof data.sessions !== 'object')
|
|
36
|
+
return;
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (const [sid, entry] of Object.entries(data.sessions)) {
|
|
40
|
+
if (!Array.isArray(entry?.recordIds))
|
|
41
|
+
continue;
|
|
42
|
+
if (now - (entry.updatedAt ?? 0) > PRUNE_MS)
|
|
43
|
+
continue;
|
|
44
|
+
const existing = this.entries.get(sid);
|
|
45
|
+
if (existing) {
|
|
46
|
+
// 合并:构造后、载入完成前已发生的 mark(保留较大 updatedAt)
|
|
47
|
+
for (const id of entry.recordIds)
|
|
48
|
+
existing.ids.add(id);
|
|
49
|
+
existing.updatedAt = Math.max(existing.updatedAt, entry.updatedAt ?? 0);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.entries.set(sid, { ids: new Set(entry.recordIds), updatedAt: entry.updatedAt ?? now });
|
|
53
|
+
}
|
|
54
|
+
count++;
|
|
55
|
+
}
|
|
56
|
+
if (count > 0)
|
|
57
|
+
this.logger?.info(`[memory] 召回去重记录载入 ${count} 个会话`);
|
|
58
|
+
}
|
|
59
|
+
/** 该会话的已注入集合(热路径同步读;未出现过的会话返回空集合,惰性建条)。 */
|
|
60
|
+
seen(sessionId) {
|
|
61
|
+
let entry = this.entries.get(sessionId);
|
|
62
|
+
if (!entry) {
|
|
63
|
+
entry = { ids: new Set(), updatedAt: 0 };
|
|
64
|
+
this.entries.set(sessionId, entry);
|
|
65
|
+
}
|
|
66
|
+
return entry.ids;
|
|
67
|
+
}
|
|
68
|
+
/** 标记本轮实际注入的记录 id(写穿;调用方保证只传模型真实看到的条目)。 */
|
|
69
|
+
mark(sessionId, recordIds) {
|
|
70
|
+
if (recordIds.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
const ids = this.seen(sessionId);
|
|
73
|
+
for (const id of recordIds)
|
|
74
|
+
ids.add(id);
|
|
75
|
+
// 插入序淘汰最旧(Set 迭代序 = 插入序)
|
|
76
|
+
while (ids.size > RECALL_DEDUPE_IDS_CAP) {
|
|
77
|
+
const oldest = ids.values().next().value;
|
|
78
|
+
if (oldest === undefined)
|
|
79
|
+
break;
|
|
80
|
+
ids.delete(oldest);
|
|
81
|
+
}
|
|
82
|
+
const entry = this.entries.get(sessionId);
|
|
83
|
+
entry.updatedAt = Date.now();
|
|
84
|
+
this.writeChain = this.writeChain.then(() => this.persist());
|
|
85
|
+
}
|
|
86
|
+
/** 清空该会话的记录(compact/clear 后上下文已丢失,记忆需可重新注入)。 */
|
|
87
|
+
reset(sessionId) {
|
|
88
|
+
if (!this.entries.has(sessionId))
|
|
89
|
+
return;
|
|
90
|
+
this.entries.delete(sessionId);
|
|
91
|
+
this.writeChain = this.writeChain.then(() => this.persist());
|
|
92
|
+
}
|
|
93
|
+
/** 等待在途持久化写完成(测试/停机用)。 */
|
|
94
|
+
flush() {
|
|
95
|
+
return this.writeChain;
|
|
96
|
+
}
|
|
97
|
+
async persist() {
|
|
98
|
+
try {
|
|
99
|
+
await ensureDir(path.dirname(this.file));
|
|
100
|
+
await atomicWriteJson(this.file, this.serialize());
|
|
101
|
+
this.persistFailed = false;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (!this.persistFailed) {
|
|
105
|
+
this.persistFailed = true;
|
|
106
|
+
this.logger?.warn(`[memory] 召回去重持久化失败(降级内存态): ${errDetail(err)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
serialize() {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
// 超期清理 + 条数上限(按 updatedAt 淘汰最旧)
|
|
113
|
+
for (const [sid, e] of this.entries) {
|
|
114
|
+
if (now - e.updatedAt > PRUNE_MS && e.updatedAt > 0)
|
|
115
|
+
this.entries.delete(sid);
|
|
116
|
+
}
|
|
117
|
+
while (this.entries.size > RECALL_DEDUPE_SESSION_CAP) {
|
|
118
|
+
let oldest;
|
|
119
|
+
let oldestAt = Infinity;
|
|
120
|
+
for (const [sid, e] of this.entries) {
|
|
121
|
+
if (e.updatedAt > 0 && e.updatedAt < oldestAt) {
|
|
122
|
+
oldest = sid;
|
|
123
|
+
oldestAt = e.updatedAt;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (oldest === undefined)
|
|
127
|
+
break; // 只剩惰性空条目(updatedAt=0),不占文件体积可留待过期清理
|
|
128
|
+
this.entries.delete(oldest);
|
|
129
|
+
}
|
|
130
|
+
const sessions = {};
|
|
131
|
+
for (const [sid, e] of this.entries) {
|
|
132
|
+
if (e.ids.size === 0)
|
|
133
|
+
continue; // 空集合不落盘
|
|
134
|
+
sessions[sid] = { recordIds: [...e.ids], updatedAt: e.updatedAt };
|
|
135
|
+
}
|
|
136
|
+
return { version: 1, sessions };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -60,8 +60,6 @@ export declare class RuntimeInstaller {
|
|
|
60
60
|
* runNpm 起跑前复查即不再起新进程(否则回退的 npm 会跑到自然结束且无法再取消)。
|
|
61
61
|
*/
|
|
62
62
|
cancel(): boolean;
|
|
63
|
-
/** 从 runtime 目录解析已安装的 transformers 模块(LocalEmbeddingService 用)。 */
|
|
64
|
-
resolveModule(): unknown;
|
|
65
63
|
private pushLine;
|
|
66
64
|
/** 跑一次 npm 子进程(采集尾行 + 超时 kill),返回退出码(null = 被杀死/启动失败)。 */
|
|
67
65
|
private runNpm;
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* - 幂等:已装版本 == 目标版本直接就绪;版本漂移(插件升级换了钉死版本)重装覆盖。
|
|
15
15
|
*/
|
|
16
16
|
import { spawn } from 'node:child_process';
|
|
17
|
-
import { createRequire } from 'node:module';
|
|
18
17
|
import { promises as fs } from 'node:fs';
|
|
19
18
|
import * as path from 'node:path';
|
|
20
19
|
import { fileURLToPath } from 'node:url';
|
|
@@ -139,11 +138,6 @@ export class RuntimeInstaller {
|
|
|
139
138
|
this.child?.kill();
|
|
140
139
|
return true;
|
|
141
140
|
}
|
|
142
|
-
/** 从 runtime 目录解析已安装的 transformers 模块(LocalEmbeddingService 用)。 */
|
|
143
|
-
resolveModule() {
|
|
144
|
-
const req = createRequire(path.join(this.runtimeDir, 'package.json'));
|
|
145
|
-
return req(RuntimeInstaller.packageName);
|
|
146
|
-
}
|
|
147
141
|
pushLine(line) {
|
|
148
142
|
const lines = this.progress.lastLines;
|
|
149
143
|
lines.push(line.length > 300 ? line.slice(0, 300) + '…' : line);
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
/** 标准 RRF 常数(原论文值);k 越大越偏向低排名项(分布更平滑)。 */
|
|
2
2
|
export declare const RRF_K = 60;
|
|
3
|
+
/** 衰减地板(#29 时效加权的安全边界):老记忆最多损失一半排序分,永不沉底。
|
|
4
|
+
* 内部常量不进配置——它是安全机制不是调参旋钮。 */
|
|
5
|
+
export declare const DECAY_FLOOR = 0.5;
|
|
6
|
+
/**
|
|
7
|
+
* 时效衰减加权(#29,读路径专用):score × max(FLOOR, 0.5^(Δ天/半衰期)) 后重排序。
|
|
8
|
+
*
|
|
9
|
+
* - Δ 按 updated_at(内容版本时间)起算,缺失/非法按最老 → 地板接管(零特判分支);
|
|
10
|
+
* - 乘法保相关性主导:只在相关度相近的候选之间轮转名次(名额新鲜度),不淘汰不
|
|
11
|
+
* 硬过滤——score≈0 的新记忆乘什么都是 ≈0;hit 的原 score 字段不被改写(排序用
|
|
12
|
+
* 加权分,展示仍反映检索相关度);
|
|
13
|
+
* - halfLifeDays ≤ 0 直接原样返回(开关关闭);
|
|
14
|
+
* - 仅用于召回/工具检索;searchCandidates(去重候选)不得应用——写路径找同语义
|
|
15
|
+
* 旧记录要无视新旧,衰减会让去重漏检(同事实双记录)。
|
|
16
|
+
*/
|
|
17
|
+
export declare function applyDecayWeight<T extends {
|
|
18
|
+
score: number;
|
|
19
|
+
}>(hits: T[], halfLifeDays: number, updatedAtOf: (hit: T) => number | undefined, now?: number): T[];
|
|
3
20
|
/**
|
|
4
21
|
* RRF 融合多个已排序列表:每项得分 = 各列表 1/(k + rank + 1) 之和。
|
|
5
22
|
* 出现在多个列表的项得分累加,按得分降序返回(附 rrfScore)。
|
|
@@ -10,6 +10,35 @@
|
|
|
10
10
|
import { tokenize } from '../util/text.js';
|
|
11
11
|
/** 标准 RRF 常数(原论文值);k 越大越偏向低排名项(分布更平滑)。 */
|
|
12
12
|
export const RRF_K = 60;
|
|
13
|
+
/** 衰减地板(#29 时效加权的安全边界):老记忆最多损失一半排序分,永不沉底。
|
|
14
|
+
* 内部常量不进配置——它是安全机制不是调参旋钮。 */
|
|
15
|
+
export const DECAY_FLOOR = 0.5;
|
|
16
|
+
/**
|
|
17
|
+
* 时效衰减加权(#29,读路径专用):score × max(FLOOR, 0.5^(Δ天/半衰期)) 后重排序。
|
|
18
|
+
*
|
|
19
|
+
* - Δ 按 updated_at(内容版本时间)起算,缺失/非法按最老 → 地板接管(零特判分支);
|
|
20
|
+
* - 乘法保相关性主导:只在相关度相近的候选之间轮转名次(名额新鲜度),不淘汰不
|
|
21
|
+
* 硬过滤——score≈0 的新记忆乘什么都是 ≈0;hit 的原 score 字段不被改写(排序用
|
|
22
|
+
* 加权分,展示仍反映检索相关度);
|
|
23
|
+
* - halfLifeDays ≤ 0 直接原样返回(开关关闭);
|
|
24
|
+
* - 仅用于召回/工具检索;searchCandidates(去重候选)不得应用——写路径找同语义
|
|
25
|
+
* 旧记录要无视新旧,衰减会让去重漏检(同事实双记录)。
|
|
26
|
+
*/
|
|
27
|
+
export function applyDecayWeight(hits, halfLifeDays, updatedAtOf, now = Date.now()) {
|
|
28
|
+
if (!(halfLifeDays > 0) || hits.length === 0)
|
|
29
|
+
return hits;
|
|
30
|
+
const weight = (h) => {
|
|
31
|
+
const t = updatedAtOf(h);
|
|
32
|
+
if (t == null || !Number.isFinite(t))
|
|
33
|
+
return DECAY_FLOOR;
|
|
34
|
+
const days = Math.max(0, (now - t) / 86_400_000);
|
|
35
|
+
return Math.max(DECAY_FLOOR, 0.5 ** (days / halfLifeDays));
|
|
36
|
+
};
|
|
37
|
+
return hits
|
|
38
|
+
.map((h) => ({ h, weighted: h.score * weight(h) }))
|
|
39
|
+
.sort((a, b) => b.weighted - a.weighted)
|
|
40
|
+
.map((x) => x.h);
|
|
41
|
+
}
|
|
13
42
|
/**
|
|
14
43
|
* RRF 融合多个已排序列表:每项得分 = 各列表 1/(k + rank + 1) 之和。
|
|
15
44
|
* 出现在多个列表的项得分累加,按得分降序返回(附 rrfScore)。
|
package/dist/store/sqlite.js
CHANGED
|
@@ -670,12 +670,19 @@ export class MemoryDb {
|
|
|
670
670
|
/** 事务内的单条写入体(upsertL1 / upsertL1Batch 共用;调用方负责 BEGIN/COMMIT)。 */
|
|
671
671
|
upsertL1InTx(record, embedding) {
|
|
672
672
|
const ts = timestampsToDb(record.timestamps);
|
|
673
|
+
// 绑定层字段兜底(取 schema 列默认):旧版 JSONL 等外部数据缺字段时 undefined
|
|
674
|
+
// 无法绑定(node:sqlite 拒绝绑定),曾致旧版导入逐条全挂、每次启动无限重试(#28)。
|
|
675
|
+
// 主表与 FTS 两条语句共用同源归一化值;type 归一化后 familyForType 也不再收到 undefined。
|
|
676
|
+
const type = record.type ?? '';
|
|
677
|
+
const priority = record.priority ?? 50;
|
|
678
|
+
const sceneName = record.scene_name ?? '';
|
|
679
|
+
const family = record.family ?? familyForType(type);
|
|
673
680
|
// 防御性 FTS 删除的前置点查(主键索引,微秒级):record_id 在 FTS 表是 UNINDEXED,
|
|
674
681
|
// 按 id DELETE 是 O(N) 全表扫描——导入/重建/重嵌等"全新增"路径曾为每条记录白付一次
|
|
675
682
|
// 全扫(批量写整体 O(N²))。只有主表已有该行(覆盖/合并)才可能有旧 FTS 行需要删。
|
|
676
683
|
// 同批重复 id 也能正确处理:首条插入后,第二条的点查在同一事务内已见新行。
|
|
677
684
|
const ftsExisted = this.ftsAvailable ? this.stmtL1Exists.get(record.id) !== undefined : false;
|
|
678
|
-
this.stmtUpsertL1.run(record.id, record.content,
|
|
685
|
+
this.stmtUpsertL1.run(record.id, record.content, type, priority, sceneName, record.sessionId ?? 'default', record.version ?? 0, ts.str, ts.start, ts.end, toIso(record.createdAt), toIso(record.updatedAt), JSON.stringify(record.metadata ?? {}), family);
|
|
679
686
|
// vec0 不支持 ON CONFLICT → 先删后插;零向量跳过(cosine 未定义)
|
|
680
687
|
if (this.stmtDeleteL1Vec && this.stmtInsertL1Vec) {
|
|
681
688
|
this.stmtDeleteL1Vec.run(record.id);
|
|
@@ -688,7 +695,7 @@ export class MemoryDb {
|
|
|
688
695
|
if (this.ftsAvailable) {
|
|
689
696
|
if (ftsExisted)
|
|
690
697
|
this.stmtL1FtsDelete.run(record.id);
|
|
691
|
-
this.stmtL1FtsInsert.run(tokenizeForFts(record.content), record.content, record.id,
|
|
698
|
+
this.stmtL1FtsInsert.run(tokenizeForFts(record.content), record.content, record.id, type, priority, sceneName, record.sessionId ?? 'default', record.version ?? 0, ts.str, ts.start, ts.end, JSON.stringify(record.metadata ?? {}), family);
|
|
692
699
|
}
|
|
693
700
|
}
|
|
694
701
|
/** 批量删除 L1(元数据 + 向量 + FTS),返回删除条数。IN 按 ≤900 分块(避变量数上限)。 */
|
|
@@ -932,22 +939,29 @@ export class MemoryDb {
|
|
|
932
939
|
try {
|
|
933
940
|
this.db.exec('BEGIN');
|
|
934
941
|
for (let i = 0; i < records.length; i++) {
|
|
935
|
-
const
|
|
942
|
+
const rec = records[i];
|
|
943
|
+
// 绑定层字段兜底(取 schema 列默认):同 upsertL1InTx——外部数据缺字段时
|
|
944
|
+
// undefined 无法绑定(#28);主表/向量/FTS 共用同源归一化值
|
|
945
|
+
const sessionId = rec.sessionId ?? 'default';
|
|
946
|
+
const role = rec.role ?? '';
|
|
947
|
+
const content = rec.content ?? '';
|
|
948
|
+
const recordedAt = rec.recordedAt ?? '';
|
|
949
|
+
const timestamp = rec.timestamp ?? 0;
|
|
936
950
|
// 同 upsertL1 的点查预判:全新增路径跳过 UNINDEXED 列的 FTS 全扫删除
|
|
937
|
-
const ftsExisted = this.ftsAvailable ? this.stmtL0Exists.get(
|
|
938
|
-
this.stmtUpsertL0.run(
|
|
951
|
+
const ftsExisted = this.ftsAvailable ? this.stmtL0Exists.get(rec.id) !== undefined : false;
|
|
952
|
+
this.stmtUpsertL0.run(rec.id, sessionId, role, content, recordedAt, timestamp);
|
|
939
953
|
if (this.stmtDeleteL0Vec && this.stmtInsertL0Vec) {
|
|
940
|
-
this.stmtDeleteL0Vec.run(
|
|
954
|
+
this.stmtDeleteL0Vec.run(rec.id);
|
|
941
955
|
const vec = embeddings?.[i];
|
|
942
956
|
if (vec && !isZeroVector(vec)) {
|
|
943
|
-
this.stmtInsertL0Vec.run(
|
|
957
|
+
this.stmtInsertL0Vec.run(rec.id, vecToBuffer(vec), recordedAt);
|
|
944
958
|
}
|
|
945
959
|
}
|
|
946
960
|
if (this.ftsAvailable) {
|
|
947
961
|
// 同 upsertL1:FTS 失败冒泡触发整批回滚,禁止"删了没补"的索引空洞。
|
|
948
962
|
if (ftsExisted)
|
|
949
|
-
this.stmtL0FtsDelete.run(
|
|
950
|
-
this.stmtL0FtsInsert.run(tokenizeForFts(
|
|
963
|
+
this.stmtL0FtsDelete.run(rec.id);
|
|
964
|
+
this.stmtL0FtsInsert.run(tokenizeForFts(content), content, rec.id, sessionId, role, recordedAt, timestamp);
|
|
951
965
|
}
|
|
952
966
|
}
|
|
953
967
|
this.db.exec('COMMIT');
|
|
@@ -27,6 +27,6 @@ export declare function applyRecallBudget(lines: string[], limits: RecallBudgetL
|
|
|
27
27
|
* resolve 为空结果(空数组)与超时(undefined)语义不同,调用方据此区分日志。
|
|
28
28
|
*/
|
|
29
29
|
export declare function raceRecallTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | undefined>;
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
30
|
+
/** 召回路径嵌入调用的内层钳制(固定值):给 FTS 降级留出总预算内的时间。
|
|
31
|
+
* 远程作用于 HTTP fetch;本地作用于 worker 代理的等待(race 放弃、迟到回复丢弃)。 */
|
|
32
32
|
export declare const RECALL_EMBED_CAP_MS = 3000;
|
|
@@ -80,6 +80,6 @@ export async function raceRecallTimeout(promise, timeoutMs) {
|
|
|
80
80
|
clearTimeout(timer);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
/**
|
|
84
|
-
*
|
|
83
|
+
/** 召回路径嵌入调用的内层钳制(固定值):给 FTS 降级留出总预算内的时间。
|
|
84
|
+
* 远程作用于 HTTP fetch;本地作用于 worker 代理的等待(race 放弃、迟到回复丢弃)。 */
|
|
85
85
|
export const RECALL_EMBED_CAP_MS = 3_000;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-layered-memory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "L0~L3 分层蒸馏记忆插件 for DeepSeek Harness:自动捕获对话(L0)、抽取原子记忆(L1)、整合场景块(L2)、蒸馏核心画像/团队方法论(L3),并在模型步骤前自动召回注入。移植自 MemoryCore (TencentDB Agent Memory) 的管线设计。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|