@scotthuang/engram 0.5.9 → 0.6.0
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 +42 -0
- package/dist/face/src/face-api.d.ts +36 -0
- package/dist/face/src/face-api.js +90 -0
- package/dist/face/src/face-api.js.map +1 -0
- package/dist/face/src/face-store.d.ts +101 -0
- package/dist/face/src/face-store.js +361 -0
- package/dist/face/src/face-store.js.map +1 -0
- package/dist/src/__tests__/bm25.test.d.ts +1 -0
- package/dist/src/__tests__/bm25.test.js +198 -0
- package/dist/src/__tests__/bm25.test.js.map +1 -0
- package/dist/src/__tests__/config.test.d.ts +1 -0
- package/dist/src/__tests__/config.test.js +31 -0
- package/dist/src/__tests__/config.test.js.map +1 -0
- package/dist/src/__tests__/profile.test.d.ts +1 -0
- package/dist/src/__tests__/profile.test.js +130 -0
- package/dist/src/__tests__/profile.test.js.map +1 -0
- package/dist/src/__tests__/recall.test.d.ts +1 -0
- package/dist/src/__tests__/recall.test.js +162 -0
- package/dist/src/__tests__/recall.test.js.map +1 -0
- package/dist/src/bm25.d.ts +60 -0
- package/dist/src/bm25.js +271 -0
- package/dist/src/bm25.js.map +1 -0
- package/dist/src/config.d.ts +47 -0
- package/dist/src/config.js +83 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/image-store.d.ts +146 -0
- package/dist/src/image-store.js +418 -0
- package/dist/src/image-store.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +1236 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger.d.ts +32 -0
- package/dist/src/logger.js +106 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/profile.d.ts +37 -0
- package/dist/src/profile.js +107 -0
- package/dist/src/profile.js.map +1 -0
- package/dist/src/recall.d.ts +98 -0
- package/dist/src/recall.js +729 -0
- package/dist/src/recall.js.map +1 -0
- package/dist/src/settle.d.ts +83 -0
- package/dist/src/settle.js +687 -0
- package/dist/src/settle.js.map +1 -0
- package/dist/src/vector.d.ts +67 -0
- package/dist/src/vector.js +284 -0
- package/dist/src/vector.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { BM25Index } from "../bm25.js";
|
|
3
|
+
describe("BM25Index", () => {
|
|
4
|
+
describe("tokenize (via search)", () => {
|
|
5
|
+
const index = new BM25Index();
|
|
6
|
+
it("finds Chinese bigram matches", async () => {
|
|
7
|
+
await index.addEntry({ text: "在体育西吃了潮汕牛肉火锅", date: "2026-03-15", category: "饮食", filePath: "/test" });
|
|
8
|
+
await index.addEntry({ text: "讨论了记忆系统方案", date: "2026-03-15", category: "技术", filePath: "/test" });
|
|
9
|
+
const results = await index.search("牛肉火锅", 2);
|
|
10
|
+
expect(results).toHaveLength(1);
|
|
11
|
+
expect(results[0].entry.category).toBe("饮食");
|
|
12
|
+
});
|
|
13
|
+
it("finds English word matches", async () => {
|
|
14
|
+
await index.addEntry({ text: "installed node modules successfully", date: "2026-03-15", category: "技术", filePath: "/test" });
|
|
15
|
+
await index.addEntry({ text: "天气很好适合出门", date: "2026-03-15", category: "随聊", filePath: "/test" });
|
|
16
|
+
const results = await index.search("node modules", 2);
|
|
17
|
+
expect(results).toHaveLength(1);
|
|
18
|
+
expect(results[0].entry.category).toBe("技术");
|
|
19
|
+
});
|
|
20
|
+
it("returns empty for no matches", async () => {
|
|
21
|
+
await index.addEntry({ text: "今天天气很好", date: "2026-03-15", category: "随聊", filePath: "/test" });
|
|
22
|
+
const results = await index.search("量子力学", 3);
|
|
23
|
+
expect(results).toHaveLength(0);
|
|
24
|
+
});
|
|
25
|
+
it("tokenizes stock query correctly", async () => {
|
|
26
|
+
const index = new BM25Index();
|
|
27
|
+
// Add the daily memory entry that has the stock preference
|
|
28
|
+
await index.addEntry({
|
|
29
|
+
text: "查股票或画K线图时必须默认使用 akshare-stock skill(~/.openclaw/workspace/skills/akshare-stock/scripts/stock_kline.py),不知道股票代码时用百度搜索查询。",
|
|
30
|
+
date: "2026-03-17",
|
|
31
|
+
category: "偏好",
|
|
32
|
+
filePath: "/test/2026-03-17.md"
|
|
33
|
+
});
|
|
34
|
+
await index.addEntry({
|
|
35
|
+
text: "腾讯股票代码是 00700",
|
|
36
|
+
date: "2026-03-17",
|
|
37
|
+
category: "技术",
|
|
38
|
+
filePath: "/test"
|
|
39
|
+
});
|
|
40
|
+
// Search for the user's query "你去画一条腾讯的K线图"
|
|
41
|
+
const query = "你去画一条腾讯的K线图";
|
|
42
|
+
const results = await index.search(query, 5);
|
|
43
|
+
// Log for debugging
|
|
44
|
+
console.log("\n----- BM25 检索日志 -----");
|
|
45
|
+
console.log("Query:", query);
|
|
46
|
+
// Log tokens and results - follow the same pattern as in bm25.ts
|
|
47
|
+
const mod = await import("jieba-wasm");
|
|
48
|
+
const jiebaInstance = await mod.default;
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
console.log("Jieba 分词结果:", jiebaInstance.cut(query));
|
|
51
|
+
console.log("召回结果(按得分降序):");
|
|
52
|
+
results.forEach((r, i) => {
|
|
53
|
+
console.log(`${i + 1}. [${r.entry.category}] score=${r.score.toFixed(4)} → ${r.entry.text}`);
|
|
54
|
+
});
|
|
55
|
+
console.log("------------------------\n");
|
|
56
|
+
// Both entries should match
|
|
57
|
+
expect(results.length).toBe(2);
|
|
58
|
+
// The "腾讯股票代码" entry has exact "腾讯" match in a shorter doc → higher BM25 score
|
|
59
|
+
// This is actually the correct behavior, since we're looking for a stock about Tencent
|
|
60
|
+
expect(results[0].entry.category).toBe("技术");
|
|
61
|
+
expect(results[1].entry.category).toBe("偏好");
|
|
62
|
+
// Both entries contain matching tokens:
|
|
63
|
+
// Query tokens: 你 去 画 一条 腾讯 的 K线 图 → jieba tokenizes correctly
|
|
64
|
+
// Preference entry matches: 画, K线, 股票 → relevant to the query intent
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("search", () => {
|
|
68
|
+
it("returns results sorted by score", async () => {
|
|
69
|
+
const index = new BM25Index();
|
|
70
|
+
await index.addEntry({ text: "牛肉火锅牛肉火锅牛肉火锅", date: "2026-03-15", category: "饮食", filePath: "/test" });
|
|
71
|
+
await index.addEntry({ text: "吃过一次牛肉火锅", date: "2026-03-15", category: "饮食", filePath: "/test" });
|
|
72
|
+
await index.addEntry({ text: "今天去跑步了", date: "2026-03-15", category: "健康", filePath: "/test" });
|
|
73
|
+
const results = await index.search("牛肉火锅", 3);
|
|
74
|
+
expect(results.length).toBeGreaterThanOrEqual(2);
|
|
75
|
+
// 更高频率的应排在前面
|
|
76
|
+
expect(results[0].score).toBeGreaterThanOrEqual(results[1].score);
|
|
77
|
+
});
|
|
78
|
+
it("respects topK limit", async () => {
|
|
79
|
+
const index = new BM25Index();
|
|
80
|
+
for (let i = 0; i < 10; i++) {
|
|
81
|
+
await index.addEntry({ text: `测试条目${i} 包含关键词`, date: "2026-03-15", category: "测试", filePath: "/test" });
|
|
82
|
+
}
|
|
83
|
+
const results = await index.search("关键词", 3);
|
|
84
|
+
expect(results.length).toBeLessThanOrEqual(3);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("addEntry", () => {
|
|
88
|
+
it("updates index size", async () => {
|
|
89
|
+
const index = new BM25Index();
|
|
90
|
+
expect(index.size).toBe(0);
|
|
91
|
+
await index.addEntry({ text: "test entry", date: "2026-03-15", category: "测试", filePath: "/test" });
|
|
92
|
+
expect(index.size).toBe(1);
|
|
93
|
+
await index.addEntry({ text: "another entry", date: "2026-03-15", category: "测试", filePath: "/test" });
|
|
94
|
+
expect(index.size).toBe(2);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe("parseEntries", () => {
|
|
98
|
+
it("parses structured format correctly", async () => {
|
|
99
|
+
const index = new BM25Index();
|
|
100
|
+
// buildFromDirectory 会调用 parseEntries
|
|
101
|
+
// 直接测试 search 验证解析结果
|
|
102
|
+
const mockContent = `# 2026-03-15
|
|
103
|
+
|
|
104
|
+
### 19:30 [饮食]
|
|
105
|
+
在体育西吃了潮汕牛肉火锅,胸口捞很好吃
|
|
106
|
+
|
|
107
|
+
### 14:00 [技术]
|
|
108
|
+
讨论了记忆系统方案,确定三层架构
|
|
109
|
+
|
|
110
|
+
### 10:00 [随聊]
|
|
111
|
+
聊了股票
|
|
112
|
+
`;
|
|
113
|
+
// 使用 addEntry 模拟解析后的结果
|
|
114
|
+
await index.addEntry({ text: "在体育西吃了潮汕牛肉火锅,胸口捞很好吃", date: "2026-03-15", category: "饮食", filePath: "/test" });
|
|
115
|
+
await index.addEntry({ text: "讨论了记忆系统方案,确定三层架构", date: "2026-03-15", category: "技术", filePath: "/test" });
|
|
116
|
+
// 搜饮食
|
|
117
|
+
const foodResults = await index.search("牛肉火锅", 1);
|
|
118
|
+
expect(foodResults).toHaveLength(1);
|
|
119
|
+
expect(foodResults[0].entry.category).toBe("饮食");
|
|
120
|
+
// 搜技术
|
|
121
|
+
const techResults = await index.search("记忆系统", 1);
|
|
122
|
+
expect(techResults).toHaveLength(1);
|
|
123
|
+
expect(techResults[0].entry.category).toBe("技术");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe("buildFromTestFile", () => {
|
|
127
|
+
it("builds index from test/2026-03-17.md and tests search", async () => {
|
|
128
|
+
const index = new BM25Index();
|
|
129
|
+
// Read the test file
|
|
130
|
+
const fs = require('fs');
|
|
131
|
+
const path = require('path');
|
|
132
|
+
const filePath = path.join(__dirname, '../../test/2026-03-17.md');
|
|
133
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
134
|
+
// Parse all entries from the file
|
|
135
|
+
const lines = content.split('\n');
|
|
136
|
+
let currentEntry = null;
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
const headerMatch = line.match(/^###\s+\d{2}:\d{2}\s+\[([^\]]+)\]/);
|
|
139
|
+
if (headerMatch) {
|
|
140
|
+
// Save previous entry if exists
|
|
141
|
+
if (currentEntry && currentEntry.text.trim().length > 0) {
|
|
142
|
+
await index.addEntry({
|
|
143
|
+
text: currentEntry.text.trim(),
|
|
144
|
+
category: currentEntry.category,
|
|
145
|
+
date: "2026-03-17",
|
|
146
|
+
filePath,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Start new entry
|
|
150
|
+
currentEntry = {
|
|
151
|
+
category: headerMatch[1],
|
|
152
|
+
date: "2026-03-17",
|
|
153
|
+
text: "",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
else if (currentEntry) {
|
|
157
|
+
currentEntry.text += line + '\n';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Add the last entry
|
|
161
|
+
if (currentEntry && currentEntry.text.trim().length > 0) {
|
|
162
|
+
await index.addEntry({
|
|
163
|
+
text: currentEntry.text.trim(),
|
|
164
|
+
category: currentEntry.category,
|
|
165
|
+
date: "2026-03-17",
|
|
166
|
+
filePath,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
console.log("\n====== Test from file 2026-03-17.md ======");
|
|
170
|
+
console.log(`Total entries parsed: ${index.size}`);
|
|
171
|
+
console.log("==========================================\n");
|
|
172
|
+
// Test search for "bm25 检索"
|
|
173
|
+
const query1 = "bm25 检索 分词";
|
|
174
|
+
const results1 = await index.search(query1, 5);
|
|
175
|
+
console.log("\n----- Test search: 'bm25 检索 分词' -----");
|
|
176
|
+
console.log(`Found ${results1.length} results:`);
|
|
177
|
+
results1.forEach((r, i) => {
|
|
178
|
+
const preview = r.entry.text.length > 60 ? r.entry.text.substring(0, 60) + "..." : r.entry.text;
|
|
179
|
+
console.log(`${i + 1}. [${r.entry.category}] score=${r.score.toFixed(4)} → ${preview}`);
|
|
180
|
+
});
|
|
181
|
+
console.log("------------------------\n");
|
|
182
|
+
// Should find entries about BM25
|
|
183
|
+
expect(results1.length).toBeGreaterThan(0);
|
|
184
|
+
// Test search for "腾讯股票 K线"
|
|
185
|
+
const query2 = "腾讯股票 K线";
|
|
186
|
+
const results2 = await index.search(query2, 5);
|
|
187
|
+
console.log("\n----- Test search: '腾讯股票 K线' -----");
|
|
188
|
+
console.log(`Found ${results2.length} results:`);
|
|
189
|
+
results2.forEach((r, i) => {
|
|
190
|
+
const preview = r.entry.text.length > 60 ? r.entry.text.substring(0, 60) + "..." : r.entry.text;
|
|
191
|
+
console.log(`${i + 1}. [${r.entry.category}] score=${r.score.toFixed(4)} → ${preview}`);
|
|
192
|
+
});
|
|
193
|
+
console.log("------------------------\n");
|
|
194
|
+
expect(results2.length).toBeGreaterThan(0);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
//# sourceMappingURL=bm25.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bm25.test.js","sourceRoot":"","sources":["../../../src/__tests__/bm25.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseConfig, DEFAULTS } from "../config.js";
|
|
3
|
+
describe("parseConfig", () => {
|
|
4
|
+
it("returns defaults when no config provided", () => {
|
|
5
|
+
const config = parseConfig();
|
|
6
|
+
expect(config.shortTermDays).toBe(DEFAULTS.shortTermDays);
|
|
7
|
+
expect(config.halfLifeDays).toBe(DEFAULTS.halfLifeDays);
|
|
8
|
+
expect(config.recallTopK).toBe(DEFAULTS.recallTopK);
|
|
9
|
+
expect(config.minScore).toBe(DEFAULTS.minScore);
|
|
10
|
+
expect(config.vectorWeight).toBe(DEFAULTS.vectorWeight);
|
|
11
|
+
expect(config.textWeight).toBe(DEFAULTS.textWeight);
|
|
12
|
+
});
|
|
13
|
+
it("returns defaults when empty object provided", () => {
|
|
14
|
+
const config = parseConfig({});
|
|
15
|
+
expect(config.shortTermDays).toBe(7);
|
|
16
|
+
expect(config.halfLifeDays).toBe(30);
|
|
17
|
+
});
|
|
18
|
+
it("overrides defaults with provided values", () => {
|
|
19
|
+
const config = parseConfig({ shortTermDays: 14, minScore: 0.5 });
|
|
20
|
+
expect(config.shortTermDays).toBe(14);
|
|
21
|
+
expect(config.minScore).toBe(0.5);
|
|
22
|
+
// 其他值保持默认
|
|
23
|
+
expect(config.halfLifeDays).toBe(DEFAULTS.halfLifeDays);
|
|
24
|
+
});
|
|
25
|
+
it("ignores invalid types and uses defaults", () => {
|
|
26
|
+
const config = parseConfig({ shortTermDays: "abc", halfLifeDays: null });
|
|
27
|
+
expect(config.shortTermDays).toBe(DEFAULTS.shortTermDays);
|
|
28
|
+
expect(config.halfLifeDays).toBe(DEFAULTS.halfLifeDays);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAErD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,UAAU;QACV,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,aAAa,EAAE,KAAY,EAAE,YAAY,EAAE,IAAW,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { ProfileManager, EMPTY_PROFILE } from "../profile.js";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
describe("ProfileManager", () => {
|
|
7
|
+
let manager;
|
|
8
|
+
let tempDir;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
tempDir = join(tmpdir(), `engram-test-${Date.now()}`);
|
|
11
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
12
|
+
manager = new ProfileManager(tempDir);
|
|
13
|
+
});
|
|
14
|
+
describe("load", () => {
|
|
15
|
+
it("returns empty profile when no file exists", async () => {
|
|
16
|
+
const profile = await manager.load();
|
|
17
|
+
expect(profile.summary).toBe("");
|
|
18
|
+
expect(profile.coreTags).toEqual([]);
|
|
19
|
+
expect(profile.tags).toEqual({});
|
|
20
|
+
});
|
|
21
|
+
it("loads existing profile from file", async () => {
|
|
22
|
+
const profileDir = join(tempDir, "memory", "profile");
|
|
23
|
+
await fs.mkdir(profileDir, { recursive: true });
|
|
24
|
+
const saved = { ...EMPTY_PROFILE, summary: "test summary", coreTags: ["tag1"] };
|
|
25
|
+
await fs.writeFile(join(profileDir, "semantic_profile.json"), JSON.stringify(saved));
|
|
26
|
+
// 创建新的 manager 实例测试从文件加载
|
|
27
|
+
const manager2 = new ProfileManager(tempDir);
|
|
28
|
+
const loaded = await manager2.load();
|
|
29
|
+
expect(loaded.summary).toBe("test summary");
|
|
30
|
+
expect(loaded.coreTags).toEqual(["tag1"]);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("addTag", () => {
|
|
34
|
+
it("adds a new tag to a new dimension", async () => {
|
|
35
|
+
const profile = { ...EMPTY_PROFILE };
|
|
36
|
+
const result = manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
37
|
+
expect(result.tags["口味偏好"]).toHaveLength(1);
|
|
38
|
+
expect(result.tags["口味偏好"][0].value).toBe("喜欢辣");
|
|
39
|
+
expect(result.tags["口味偏好"][0].confidence).toBe(0.7);
|
|
40
|
+
});
|
|
41
|
+
it("increases confidence for existing tag", async () => {
|
|
42
|
+
const profile = {
|
|
43
|
+
...EMPTY_PROFILE,
|
|
44
|
+
tags: { 口味偏好: [{ value: "喜欢辣", confidence: 0.5, lastSeen: "2026-01-01" }] },
|
|
45
|
+
};
|
|
46
|
+
const result = manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
47
|
+
expect(result.tags["口味偏好"]).toHaveLength(1);
|
|
48
|
+
expect(result.tags["口味偏好"][0].confidence).toBeCloseTo(0.6);
|
|
49
|
+
});
|
|
50
|
+
it("adds multiple tags to same dimension", async () => {
|
|
51
|
+
const profile = { ...EMPTY_PROFILE };
|
|
52
|
+
manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
53
|
+
manager.addTag(profile, "口味偏好", "不吃香菜");
|
|
54
|
+
expect(profile.tags["口味偏好"]).toHaveLength(2);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("decayTags", () => {
|
|
58
|
+
it("reduces confidence of all tags", () => {
|
|
59
|
+
const profile = {
|
|
60
|
+
...EMPTY_PROFILE,
|
|
61
|
+
tags: {
|
|
62
|
+
口味偏好: [
|
|
63
|
+
{ value: "喜欢辣", confidence: 0.9, lastSeen: "2026-03-17" },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const result = manager.decayTags(profile, 0.5);
|
|
68
|
+
expect(result.tags["口味偏好"][0].confidence).toBeCloseTo(0.45);
|
|
69
|
+
});
|
|
70
|
+
it("removes tags below threshold", () => {
|
|
71
|
+
const profile = {
|
|
72
|
+
...EMPTY_PROFILE,
|
|
73
|
+
tags: {
|
|
74
|
+
过时: [{ value: "旧标签", confidence: 0.15, lastSeen: "2026-01-01" }],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const result = manager.decayTags(profile, 1.0);
|
|
78
|
+
expect(result.tags["过时"]).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
it("removes empty dimensions after filtering", () => {
|
|
81
|
+
const profile = {
|
|
82
|
+
...EMPTY_PROFILE,
|
|
83
|
+
tags: {
|
|
84
|
+
空维度: [{ value: "很低", confidence: 0.1, lastSeen: "2026-01-01" }],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const result = manager.decayTags(profile, 1.0);
|
|
88
|
+
expect("空维度" in result.tags).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("getRecallContext", () => {
|
|
92
|
+
it("returns empty string for empty profile", () => {
|
|
93
|
+
const ctx = manager.getRecallContext(EMPTY_PROFILE);
|
|
94
|
+
expect(ctx).toBe("");
|
|
95
|
+
});
|
|
96
|
+
it("returns summary and core tags", () => {
|
|
97
|
+
const profile = { ...EMPTY_PROFILE, summary: "辣味中餐爱好者", coreTags: ["辣味中餐", "天河"] };
|
|
98
|
+
const ctx = manager.getRecallContext(profile);
|
|
99
|
+
expect(ctx).toContain("辣味中餐爱好者");
|
|
100
|
+
expect(ctx).toContain("辣味中餐");
|
|
101
|
+
expect(ctx).toContain("天河");
|
|
102
|
+
});
|
|
103
|
+
it("works with only core tags", () => {
|
|
104
|
+
const profile = { ...EMPTY_PROFILE, summary: "", coreTags: ["标签1"] };
|
|
105
|
+
const ctx = manager.getRecallContext(profile);
|
|
106
|
+
expect(ctx).toContain("标签1");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe("save", () => {
|
|
110
|
+
it("saves profile to file", async () => {
|
|
111
|
+
const profile = { ...EMPTY_PROFILE, summary: "test" };
|
|
112
|
+
await manager.save(profile);
|
|
113
|
+
const raw = await fs.readFile(join(tempDir, "memory", "profile", "semantic_profile.json"), "utf-8");
|
|
114
|
+
const loaded = JSON.parse(raw);
|
|
115
|
+
expect(loaded.summary).toBe("test");
|
|
116
|
+
});
|
|
117
|
+
it("updates updatedAt on save", async () => {
|
|
118
|
+
const profile = { ...EMPTY_PROFILE };
|
|
119
|
+
const before = new Date();
|
|
120
|
+
await manager.save(profile);
|
|
121
|
+
const after = new Date();
|
|
122
|
+
const raw = await fs.readFile(join(tempDir, "memory", "profile", "semantic_profile.json"), "utf-8");
|
|
123
|
+
const loaded = JSON.parse(raw);
|
|
124
|
+
const updated = new Date(loaded.updatedAt);
|
|
125
|
+
expect(updated.getTime()).toBeGreaterThanOrEqual(before.getTime());
|
|
126
|
+
expect(updated.getTime()).toBeLessThanOrEqual(after.getTime());
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=profile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.test.js","sourceRoot":"","sources":["../../../src/__tests__/profile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
// 纯函数测试,不依赖外部模块
|
|
3
|
+
describe("Recall helpers", () => {
|
|
4
|
+
describe("temporalDecay", () => {
|
|
5
|
+
function temporalDecay(score, ageInDays, halfLifeDays) {
|
|
6
|
+
if (ageInDays <= 0)
|
|
7
|
+
return score;
|
|
8
|
+
const lambda = Math.log(2) / halfLifeDays;
|
|
9
|
+
return score * Math.exp(-lambda * ageInDays);
|
|
10
|
+
}
|
|
11
|
+
it("returns original score for 0 days", () => {
|
|
12
|
+
expect(temporalDecay(1.0, 0, 30)).toBeCloseTo(1.0);
|
|
13
|
+
});
|
|
14
|
+
it("returns ~50% at half-life", () => {
|
|
15
|
+
expect(temporalDecay(1.0, 30, 30)).toBeCloseTo(0.5, 1);
|
|
16
|
+
});
|
|
17
|
+
it("returns ~84% at 7 days with 30-day half-life", () => {
|
|
18
|
+
expect(temporalDecay(1.0, 7, 30)).toBeCloseTo(0.846, 2);
|
|
19
|
+
});
|
|
20
|
+
it("returns ~12.5% at 90 days with 30-day half-life", () => {
|
|
21
|
+
expect(temporalDecay(1.0, 90, 30)).toBeCloseTo(0.125, 2);
|
|
22
|
+
});
|
|
23
|
+
it("shorter half-life decays faster", () => {
|
|
24
|
+
const short = temporalDecay(1.0, 7, 7);
|
|
25
|
+
const long = temporalDecay(1.0, 7, 30);
|
|
26
|
+
expect(short).toBeLessThan(long);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("jaccard", () => {
|
|
30
|
+
function jaccard(a, b) {
|
|
31
|
+
const setA = new Set(a.split(""));
|
|
32
|
+
const setB = new Set(b.split(""));
|
|
33
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
34
|
+
const union = new Set([...setA, ...setB]);
|
|
35
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
36
|
+
}
|
|
37
|
+
it("returns 1.0 for identical strings", () => {
|
|
38
|
+
expect(jaccard("abc", "abc")).toBeCloseTo(1.0);
|
|
39
|
+
});
|
|
40
|
+
it("returns 0.0 for completely different strings", () => {
|
|
41
|
+
expect(jaccard("abc", "xyz")).toBeCloseTo(0.0);
|
|
42
|
+
});
|
|
43
|
+
it("returns partial similarity for overlapping strings", () => {
|
|
44
|
+
const sim = jaccard("abc", "abd");
|
|
45
|
+
expect(sim).toBeGreaterThan(0);
|
|
46
|
+
expect(sim).toBeLessThan(1);
|
|
47
|
+
});
|
|
48
|
+
it("handles empty strings", () => {
|
|
49
|
+
expect(jaccard("", "")).toBe(0);
|
|
50
|
+
expect(jaccard("abc", "")).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("MMR rerank", () => {
|
|
54
|
+
function mmrRerank(candidates, lambda = 0.7) {
|
|
55
|
+
if (candidates.length <= 1)
|
|
56
|
+
return candidates;
|
|
57
|
+
const jaccard = (a, b) => {
|
|
58
|
+
const setA = new Set(a.split(""));
|
|
59
|
+
const setB = new Set(b.split(""));
|
|
60
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
61
|
+
const union = new Set([...setA, ...setB]);
|
|
62
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
63
|
+
};
|
|
64
|
+
const selected = [];
|
|
65
|
+
const remaining = [...candidates];
|
|
66
|
+
remaining.sort((a, b) => b.finalScore - a.finalScore);
|
|
67
|
+
selected.push(remaining.shift());
|
|
68
|
+
while (remaining.length > 0) {
|
|
69
|
+
let bestIdx = -1;
|
|
70
|
+
let bestMmr = -Infinity;
|
|
71
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
72
|
+
const relevance = remaining[i].finalScore;
|
|
73
|
+
const maxSim = Math.max(...selected.map(s => jaccard(remaining[i].text, s.text)));
|
|
74
|
+
const mmrScore = lambda * relevance - (1 - lambda) * maxSim;
|
|
75
|
+
if (mmrScore > bestMmr) {
|
|
76
|
+
bestMmr = mmrScore;
|
|
77
|
+
bestIdx = i;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (bestIdx >= 0) {
|
|
81
|
+
selected.push(remaining.splice(bestIdx, 1)[0]);
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
return selected;
|
|
87
|
+
}
|
|
88
|
+
it("returns single item as-is", () => {
|
|
89
|
+
const candidates = [{ text: "hello", finalScore: 0.9 }];
|
|
90
|
+
expect(mmrRerank(candidates)).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
it("returns empty for empty input", () => {
|
|
93
|
+
expect(mmrRerank([])).toHaveLength(0);
|
|
94
|
+
});
|
|
95
|
+
it("promotes diverse results over duplicates", () => {
|
|
96
|
+
const candidates = [
|
|
97
|
+
{ text: "在体育西吃了潮汕牛肉火锅", finalScore: 0.92 },
|
|
98
|
+
{ text: "在天河吃了潮汕牛肉火锅", finalScore: 0.89 },
|
|
99
|
+
{ text: "在越秀吃了日本料理", finalScore: 0.70 },
|
|
100
|
+
];
|
|
101
|
+
const reranked = mmrRerank(candidates, 0.7);
|
|
102
|
+
expect(reranked).toHaveLength(3);
|
|
103
|
+
// 第一条应该还是分数最高的
|
|
104
|
+
expect(reranked[0].finalScore).toBe(0.92);
|
|
105
|
+
});
|
|
106
|
+
it("lambda=1.0 is pure relevance (no diversity)", () => {
|
|
107
|
+
const candidates = [
|
|
108
|
+
{ text: "aaa", finalScore: 0.9 },
|
|
109
|
+
{ text: "aaa", finalScore: 0.8 },
|
|
110
|
+
{ text: "bbb", finalScore: 0.5 },
|
|
111
|
+
];
|
|
112
|
+
const reranked = mmrRerank(candidates, 1.0);
|
|
113
|
+
expect(reranked.map(r => r.finalScore)).toEqual([0.9, 0.8, 0.5]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("dedup", () => {
|
|
117
|
+
function jaccard(a, b) {
|
|
118
|
+
const setA = new Set(a.split(""));
|
|
119
|
+
const setB = new Set(b.split(""));
|
|
120
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
121
|
+
const union = new Set([...setA, ...setB]);
|
|
122
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
123
|
+
}
|
|
124
|
+
function dedup(results) {
|
|
125
|
+
const sorted = [...results].sort((a, b) => b.score - a.score);
|
|
126
|
+
const unique = [];
|
|
127
|
+
for (const item of sorted) {
|
|
128
|
+
const isDup = unique.some(u => jaccard(item.text, u.text) > 0.7);
|
|
129
|
+
if (!isDup)
|
|
130
|
+
unique.push(item);
|
|
131
|
+
}
|
|
132
|
+
return unique;
|
|
133
|
+
}
|
|
134
|
+
it("removes near-duplicates", () => {
|
|
135
|
+
const results = [
|
|
136
|
+
{ text: "在体育西吃了潮汕牛肉火锅", score: 0.92 },
|
|
137
|
+
{ text: "在体育西吃潮汕牛肉火锅", score: 0.85 },
|
|
138
|
+
{ text: "在天河城看到特斯拉展厅", score: 0.70 },
|
|
139
|
+
];
|
|
140
|
+
const deduped = dedup(results);
|
|
141
|
+
expect(deduped.length).toBeLessThan(3);
|
|
142
|
+
});
|
|
143
|
+
it("keeps distinct results", () => {
|
|
144
|
+
const results = [
|
|
145
|
+
{ text: "今天天气很好", score: 0.9 },
|
|
146
|
+
{ text: "股票涨了", score: 0.8 },
|
|
147
|
+
{ text: "新开了家餐厅", score: 0.7 },
|
|
148
|
+
];
|
|
149
|
+
expect(dedup(results)).toHaveLength(3);
|
|
150
|
+
});
|
|
151
|
+
it("keeps higher-scored duplicate", () => {
|
|
152
|
+
const results = [
|
|
153
|
+
{ text: "abcde fghij", score: 0.9 },
|
|
154
|
+
{ text: "abcde fghij", score: 0.7 },
|
|
155
|
+
];
|
|
156
|
+
const deduped = dedup(results);
|
|
157
|
+
expect(deduped).toHaveLength(1);
|
|
158
|
+
expect(deduped[0].score).toBe(0.9);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=recall.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall.test.js","sourceRoot":"","sources":["../../../src/__tests__/recall.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory System Plugin - BM25 Short-term Memory Index
|
|
3
|
+
*
|
|
4
|
+
* 短期记忆的 BM25 索引构建与搜索
|
|
5
|
+
* 纯 TypeScript 实现,不依赖外部 BM25 库
|
|
6
|
+
*/
|
|
7
|
+
export type ShortTermEntry = {
|
|
8
|
+
text: string;
|
|
9
|
+
date: string;
|
|
10
|
+
category: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function initJieba(): Promise<void>;
|
|
14
|
+
export declare function tokenize(text: string): string[];
|
|
15
|
+
export declare class BM25Index {
|
|
16
|
+
private docs;
|
|
17
|
+
private idf;
|
|
18
|
+
private avgDl;
|
|
19
|
+
/**
|
|
20
|
+
* 从短期记忆文件构建索引
|
|
21
|
+
* v0.5: 全量索引所有 .md 文件(不再按 maxAgeDays 截断)
|
|
22
|
+
* maxAgeDays 参数保留但仅用于向下兼容(不再生效)
|
|
23
|
+
*/
|
|
24
|
+
buildFromDirectory(dir: string, _maxAgeDays?: number): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* 解析结构化的短期记忆文件
|
|
27
|
+
* 格式:### HH:MM [分类标签]\n摘要内容
|
|
28
|
+
*
|
|
29
|
+
* 特殊处理 [对话记录]: 按 Scott / Shadow 切分每一轮对话
|
|
30
|
+
* 支持两种格式:
|
|
31
|
+
* - 旧格式: Scott: / Shadow:
|
|
32
|
+
* - 新格式: Scott [MM-DD HH:MM]: / Shadow [MM-DD HH:MM]:
|
|
33
|
+
* 每一轮单独做一个 BM25 文档,精准召回不浪费
|
|
34
|
+
*/
|
|
35
|
+
private parseEntries;
|
|
36
|
+
/**
|
|
37
|
+
* 处理文本块,支持三种格式:
|
|
38
|
+
* 1. 精简格式(每行 | 分隔)
|
|
39
|
+
* 2. 对话记录(Scott/Shadow 切分)
|
|
40
|
+
* 3. 普通文本(整块索引)
|
|
41
|
+
*/
|
|
42
|
+
private processTextBlock;
|
|
43
|
+
/**
|
|
44
|
+
* 构建 IDF(逆文档频率)
|
|
45
|
+
*/
|
|
46
|
+
private buildIDF;
|
|
47
|
+
/**
|
|
48
|
+
* BM25 搜索
|
|
49
|
+
* @returns 带分数的结果列表
|
|
50
|
+
*/
|
|
51
|
+
search(query: string, topK?: number): Promise<Array<{
|
|
52
|
+
entry: ShortTermEntry;
|
|
53
|
+
score: number;
|
|
54
|
+
}>>;
|
|
55
|
+
/**
|
|
56
|
+
* 添加单条记录(用于实时写入后的即时索引)
|
|
57
|
+
*/
|
|
58
|
+
addEntry(entry: ShortTermEntry): Promise<void>;
|
|
59
|
+
get size(): number;
|
|
60
|
+
}
|