@scotthuang/engram 0.9.3 → 0.9.5

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.
Files changed (63) hide show
  1. package/dist/{face/src/face-api.js → face-api.js} +1 -1
  2. package/dist/{face/src/face-store.js → face-store.js} +1 -1
  3. package/dist/{src/index.js → index.js} +47 -4
  4. package/dist/{src/index.js.map → index.js.map} +1 -1
  5. package/dist/{src/recall.js → recall.js} +3 -3
  6. package/dist/recall.js.map +1 -0
  7. package/dist/{face/src/voice-identify.js → voice-identify.js} +1 -1
  8. package/dist/{face/src/voice-store.js → voice-store.js} +1 -1
  9. package/openclaw.plugin.json +1 -1
  10. package/package.json +4 -4
  11. package/dist/src/__tests__/bm25.test.d.ts +0 -1
  12. package/dist/src/__tests__/bm25.test.js +0 -198
  13. package/dist/src/__tests__/bm25.test.js.map +0 -1
  14. package/dist/src/__tests__/config.test.d.ts +0 -1
  15. package/dist/src/__tests__/config.test.js +0 -30
  16. package/dist/src/__tests__/config.test.js.map +0 -1
  17. package/dist/src/__tests__/face-auto-dryrun.d.ts +0 -12
  18. package/dist/src/__tests__/face-auto-dryrun.js +0 -166
  19. package/dist/src/__tests__/face-auto-dryrun.js.map +0 -1
  20. package/dist/src/__tests__/profile.test.d.ts +0 -1
  21. package/dist/src/__tests__/profile.test.js +0 -174
  22. package/dist/src/__tests__/profile.test.js.map +0 -1
  23. package/dist/src/__tests__/promotion-dryrun.d.ts +0 -6
  24. package/dist/src/__tests__/promotion-dryrun.js +0 -105
  25. package/dist/src/__tests__/promotion-dryrun.js.map +0 -1
  26. package/dist/src/__tests__/recall.test.d.ts +0 -1
  27. package/dist/src/__tests__/recall.test.js +0 -162
  28. package/dist/src/__tests__/recall.test.js.map +0 -1
  29. package/dist/src/recall.js.map +0 -1
  30. /package/dist/{src/bm25.d.ts → bm25.d.ts} +0 -0
  31. /package/dist/{src/bm25.js → bm25.js} +0 -0
  32. /package/dist/{src/bm25.js.map → bm25.js.map} +0 -0
  33. /package/dist/{src/config.d.ts → config.d.ts} +0 -0
  34. /package/dist/{src/config.js → config.js} +0 -0
  35. /package/dist/{src/config.js.map → config.js.map} +0 -0
  36. /package/dist/{face/src/face-api.d.ts → face-api.d.ts} +0 -0
  37. /package/dist/{face/src/face-api.js.map → face-api.js.map} +0 -0
  38. /package/dist/{face/src/face-store.d.ts → face-store.d.ts} +0 -0
  39. /package/dist/{face/src/face-store.js.map → face-store.js.map} +0 -0
  40. /package/dist/{src/image-store.d.ts → image-store.d.ts} +0 -0
  41. /package/dist/{src/image-store.js → image-store.js} +0 -0
  42. /package/dist/{src/image-store.js.map → image-store.js.map} +0 -0
  43. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  44. /package/dist/{src/logger.d.ts → logger.d.ts} +0 -0
  45. /package/dist/{src/logger.js → logger.js} +0 -0
  46. /package/dist/{src/logger.js.map → logger.js.map} +0 -0
  47. /package/dist/{src/profile.d.ts → profile.d.ts} +0 -0
  48. /package/dist/{src/profile.js → profile.js} +0 -0
  49. /package/dist/{src/profile.js.map → profile.js.map} +0 -0
  50. /package/dist/{src/promotion.d.ts → promotion.d.ts} +0 -0
  51. /package/dist/{src/promotion.js → promotion.js} +0 -0
  52. /package/dist/{src/promotion.js.map → promotion.js.map} +0 -0
  53. /package/dist/{src/recall.d.ts → recall.d.ts} +0 -0
  54. /package/dist/{src/settle.d.ts → settle.d.ts} +0 -0
  55. /package/dist/{src/settle.js → settle.js} +0 -0
  56. /package/dist/{src/settle.js.map → settle.js.map} +0 -0
  57. /package/dist/{src/vector.d.ts → vector.d.ts} +0 -0
  58. /package/dist/{src/vector.js → vector.js} +0 -0
  59. /package/dist/{src/vector.js.map → vector.js.map} +0 -0
  60. /package/dist/{face/src/voice-identify.d.ts → voice-identify.d.ts} +0 -0
  61. /package/dist/{face/src/voice-identify.js.map → voice-identify.js.map} +0 -0
  62. /package/dist/{face/src/voice-store.d.ts → voice-store.d.ts} +0 -0
  63. /package/dist/{face/src/voice-store.js.map → voice-store.js.map} +0 -0
@@ -1,198 +0,0 @@
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"bm25.test.js","sourceRoot":"","sources":["../../../src/__tests__/bm25.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,30 +0,0 @@
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.vectorWeight).toBe(DEFAULTS.vectorWeight);
10
- expect(config.textWeight).toBe(DEFAULTS.textWeight);
11
- });
12
- it("returns defaults when empty object provided", () => {
13
- const config = parseConfig({});
14
- expect(config.shortTermDays).toBe(7);
15
- expect(config.halfLifeDays).toBe(30);
16
- });
17
- it("overrides defaults with provided values", () => {
18
- const config = parseConfig({ shortTermDays: 14, vectorWeight: 0.8 });
19
- expect(config.shortTermDays).toBe(14);
20
- expect(config.vectorWeight).toBe(0.8);
21
- // 其他值保持默认
22
- expect(config.halfLifeDays).toBe(DEFAULTS.halfLifeDays);
23
- });
24
- it("ignores invalid types and uses defaults", () => {
25
- const config = parseConfig({ shortTermDays: "abc", halfLifeDays: null });
26
- expect(config.shortTermDays).toBe(DEFAULTS.shortTermDays);
27
- expect(config.halfLifeDays).toBe(DEFAULTS.halfLifeDays);
28
- });
29
- });
30
- //# sourceMappingURL=config.test.js.map
@@ -1 +0,0 @@
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,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,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,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"}
@@ -1,12 +0,0 @@
1
- /**
2
- * 图片自动人脸识别注入 — 本地模拟测试
3
- *
4
- * 用 memory-engram/images/ 中已存储的图片模拟微信发图场景,
5
- * 验证 faceStore.detectAndMatch() + 上下文构建逻辑。
6
- *
7
- * 不依赖 OpenClaw,不修改任何数据,只做检测和打印。
8
- * 需要 Python 人脸服务已启动(localhost:8765)。
9
- *
10
- * 用法: npx tsx src/__tests__/face-auto-dryrun.ts
11
- */
12
- export {};
@@ -1,166 +0,0 @@
1
- /**
2
- * 图片自动人脸识别注入 — 本地模拟测试
3
- *
4
- * 用 memory-engram/images/ 中已存储的图片模拟微信发图场景,
5
- * 验证 faceStore.detectAndMatch() + 上下文构建逻辑。
6
- *
7
- * 不依赖 OpenClaw,不修改任何数据,只做检测和打印。
8
- * 需要 Python 人脸服务已启动(localhost:8765)。
9
- *
10
- * 用法: npx tsx src/__tests__/face-auto-dryrun.ts
11
- */
12
- import { FaceStore } from "../../face/src/face-store.js";
13
- const WORKSPACE = process.env.WORKSPACE_DIR || `${process.env.HOME}/.openclaw/workspace`;
14
- const faceStore = new FaceStore(WORKSPACE);
15
- // ---- 测试用例 ----
16
- const TEST_CASES = [
17
- // Scott + Wandy 缆车合影 (faces=2)
18
- {
19
- label: "Scott + Wandy 白云山缆车",
20
- imagePath: `${WORKSPACE}/memory-engram/images/2026-03/29/20260329_024148_83b84e.jpg`,
21
- },
22
- // Scott + Wandy 地铁(Wandy 睡着)(faces=2)
23
- {
24
- label: "Scott + Wandy 地铁",
25
- imagePath: `${WORKSPACE}/memory-engram/images/2026-03/29/20260329_024151_ae582b.jpg`,
26
- },
27
- // Wandy 单人 九龙泉 (faces=1)
28
- {
29
- label: "Wandy 九龙泉",
30
- imagePath: `${WORKSPACE}/memory-engram/images/2026-03/29/20260329_024144_70213a.jpg`,
31
- },
32
- // 一家三口火锅 (faces=6,含多人)
33
- {
34
- label: "一家三口火锅聚餐",
35
- imagePath: `${WORKSPACE}/memory-engram/images/2026-03/29/20260329_192738_963e15.jpg`,
36
- },
37
- // Season 冰淇淋 (faces=0 in metadata, 但应该能检测到)
38
- {
39
- label: "Season 吃冰淇淋",
40
- imagePath: `${WORKSPACE}/memory-engram/images/2026-04/10/20260410_204737_955aa8.jpg`,
41
- },
42
- // Wandy 海边沙滩 (faces=0 in metadata)
43
- {
44
- label: "Wandy 海边玩沙",
45
- imagePath: `${WORKSPACE}/memory-engram/images/2026-04/06/20260406_165533_7e65e2.jpg`,
46
- },
47
- // 暴雨预警截图 — 应该无人脸
48
- {
49
- label: "暴雨预警截图(无人脸)",
50
- imagePath: `${WORKSPACE}/memory-engram/images/2026-03/29/20260329_191026_b9b9fb.jpg`,
51
- },
52
- ];
53
- // ---- 模拟 before_prompt_build 中的人脸上下文构建 ----
54
- async function buildFaceContext(imagePaths) {
55
- const registry = await faceStore.loadRegistry();
56
- if (registry.persons.length === 0)
57
- return "";
58
- const relationMap = new Map();
59
- for (const p of registry.persons) {
60
- if (p.relation)
61
- relationMap.set(p.id, p.relation);
62
- }
63
- const allFaceLines = [];
64
- let unknownCount = 0;
65
- for (const imgPath of imagePaths) {
66
- const matches = await faceStore.detectAndMatch(imgPath);
67
- for (const m of matches) {
68
- if (m.status === "matched") {
69
- const rel = m.personId ? relationMap.get(m.personId) : "";
70
- const relStr = rel ? `(${rel},` : "(";
71
- allFaceLines.push(`- ${m.name}${relStr}${(m.confidence * 100).toFixed(0)}% 置信度)`);
72
- }
73
- else if (m.status === "uncertain") {
74
- allFaceLines.push(`- 疑似 ${m.name}(${(m.confidence * 100).toFixed(0)}%,不确定)`);
75
- }
76
- else {
77
- unknownCount++;
78
- }
79
- }
80
- }
81
- if (unknownCount > 0) {
82
- allFaceLines.push(`- ${unknownCount} 张未知人脸`);
83
- }
84
- if (allFaceLines.length === 0)
85
- return "";
86
- return [
87
- `<face-recognition>`,
88
- `图片中识别到以下人物:`,
89
- ...allFaceLines,
90
- `请结合以上信息理解图片内容。如用户问"这是谁",优先使用识别结果回答。`,
91
- `</face-recognition>`,
92
- ].join("\n");
93
- }
94
- // ---- 主流程 ----
95
- async function main() {
96
- console.log("═══════════════════════════════════════════════════════════════");
97
- console.log(" 图片自动人脸识别注入 — 本地模拟测试");
98
- console.log("═══════════════════════════════════════════════════════════════");
99
- // 1. 检查人脸服务
100
- const available = await faceStore.isServiceAvailable();
101
- console.log(`\n人脸服务状态: ${available ? "✅ 在线" : "❌ 离线"}`);
102
- if (!available) {
103
- console.log("请先启动 Python 人脸服务: cd face/service && python app.py");
104
- process.exit(1);
105
- }
106
- // 2. 检查注册人物
107
- const registry = await faceStore.loadRegistry();
108
- console.log(`已注册人物: ${registry.persons.length}`);
109
- for (const p of registry.persons) {
110
- console.log(` ${p.id}: ${p.name} (relation=${p.relation}, samples=${p.sampleCount})`);
111
- }
112
- // 3. 逐个测试
113
- console.log("\n───────────────────────────────────────────────────────────────\n");
114
- for (const tc of TEST_CASES) {
115
- console.log(`📸 ${tc.label}`);
116
- console.log(` ${tc.imagePath}`);
117
- const startMs = Date.now();
118
- try {
119
- const context = await buildFaceContext([tc.imagePath]);
120
- const elapsed = Date.now() - startMs;
121
- if (context) {
122
- console.log(` ⏱️ ${elapsed}ms`);
123
- console.log(` ${context.split("\n").map(l => ` ${l}`).join("\n")}`);
124
- }
125
- else {
126
- console.log(` ⏱️ ${elapsed}ms — 无人脸检测到`);
127
- }
128
- }
129
- catch (err) {
130
- console.log(` ❌ 检测失败: ${err}`);
131
- }
132
- console.log();
133
- }
134
- // 4. 模拟微信发图的完整 prompt
135
- console.log("───────────────────────────────────────────────────────────────");
136
- console.log("📱 模拟微信发图场景(Scott+Wandy 缆车合影)\n");
137
- const mockPrompt = `[media attached: ${TEST_CASES[0].imagePath} (image/jpeg)]
138
-
139
- To send an image back to the user, use this format: ![alt](openclaw://image/absolute-path-to-file.png)
140
- Keep caption in the text body.
141
-
142
- Conversation info (untrusted metadata):
143
- channel: weixin
144
-
145
- Sender (untrusted metadata):
146
- {"name":"Scott","id":"wxid_xxx"}`;
147
- // 提取图片路径
148
- const imagePathMatches = [...mockPrompt.matchAll(/\[media attached:\s*([^\s\]]+)\s*\(image\//gi)];
149
- const imagePaths = imagePathMatches.map(m => m[1]);
150
- console.log(` 提取到 ${imagePaths.length} 张图片: ${imagePaths.join(", ")}`);
151
- const startMs = Date.now();
152
- const faceContext = await buildFaceContext(imagePaths);
153
- console.log(` 检测耗时: ${Date.now() - startMs}ms`);
154
- if (faceContext) {
155
- console.log(`\n === 将注入给 LLM 的 prependContext ===\n`);
156
- console.log(faceContext);
157
- }
158
- else {
159
- console.log(`\n (无人脸信息注入)`);
160
- }
161
- console.log("\n═══════════════════════════════════════════════════════════════");
162
- console.log(" 模拟完成(未修改任何数据)");
163
- console.log("═══════════════════════════════════════════════════════════════\n");
164
- }
165
- main().catch(console.error);
166
- //# sourceMappingURL=face-auto-dryrun.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"face-auto-dryrun.js","sourceRoot":"","sources":["../../../src/__tests__/face-auto-dryrun.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,174 +0,0 @@
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
- const manager2 = new ProfileManager(tempDir);
27
- const loaded = await manager2.load();
28
- expect(loaded.summary).toBe("test summary");
29
- expect(loaded.coreTags).toEqual(["tag1"]);
30
- });
31
- });
32
- describe("addTag", () => {
33
- it("adds a new tag with default layer (interest)", async () => {
34
- const profile = { ...EMPTY_PROFILE };
35
- const result = manager.addTag(profile, "口味偏好", "喜欢辣");
36
- expect(result.tags["口味偏好"]).toHaveLength(1);
37
- expect(result.tags["口味偏好"][0].value).toBe("喜欢辣");
38
- expect(result.tags["口味偏好"][0].confidence).toBe(0.7);
39
- expect(result.tags["口味偏好"][0].layer).toBe("interest");
40
- });
41
- it("adds identity tag with high confidence", async () => {
42
- const profile = { ...EMPTY_PROFILE };
43
- manager.addTag(profile, "居住地", "广州天河", "identity");
44
- expect(profile.tags["居住地"][0].confidence).toBe(0.95);
45
- expect(profile.tags["居住地"][0].layer).toBe("identity");
46
- });
47
- it("increases confidence for existing tag", async () => {
48
- const profile = {
49
- ...EMPTY_PROFILE,
50
- tags: { 口味偏好: [{ value: "喜欢辣", confidence: 0.5, lastSeen: "2026-01-01", layer: "pattern" }] },
51
- };
52
- const result = manager.addTag(profile, "口味偏好", "喜欢辣");
53
- expect(result.tags["口味偏好"]).toHaveLength(1);
54
- expect(result.tags["口味偏好"][0].confidence).toBeCloseTo(0.6);
55
- });
56
- it("promotes tag layer when higher layer is specified", () => {
57
- const profile = {
58
- ...EMPTY_PROFILE,
59
- tags: { 居住地: [{ value: "广州", confidence: 0.7, lastSeen: "2026-01-01", layer: "interest" }] },
60
- };
61
- manager.addTag(profile, "居住地", "广州", "identity");
62
- expect(profile.tags["居住地"][0].layer).toBe("identity");
63
- });
64
- it("adds multiple tags to same dimension", async () => {
65
- const profile = { ...EMPTY_PROFILE };
66
- manager.addTag(profile, "口味偏好", "喜欢辣");
67
- manager.addTag(profile, "口味偏好", "不吃香菜");
68
- expect(profile.tags["口味偏好"]).toHaveLength(2);
69
- });
70
- });
71
- describe("decayTags", () => {
72
- it("does not decay identity tags", () => {
73
- const profile = {
74
- ...EMPTY_PROFILE,
75
- tags: {
76
- 身份: [{ value: "Scott", confidence: 0.95, lastSeen: "2026-03-17", layer: "identity" }],
77
- },
78
- };
79
- manager.decayTags(profile);
80
- expect(profile.tags["身份"][0].confidence).toBeCloseTo(0.95);
81
- });
82
- it("slowly decays pattern tags", () => {
83
- const profile = {
84
- ...EMPTY_PROFILE,
85
- tags: {
86
- 习惯: [{ value: "晚睡", confidence: 0.9, lastSeen: "2026-03-17", layer: "pattern" }],
87
- },
88
- };
89
- manager.decayTags(profile);
90
- expect(profile.tags["习惯"][0].confidence).toBeCloseTo(0.9 * 0.995);
91
- });
92
- it("quickly decays interest tags", () => {
93
- const profile = {
94
- ...EMPTY_PROFILE,
95
- tags: {
96
- 项目: [{ value: "声纹识别", confidence: 0.7, lastSeen: "2026-03-17", layer: "interest" }],
97
- },
98
- };
99
- manager.decayTags(profile);
100
- expect(profile.tags["项目"][0].confidence).toBeCloseTo(0.7 * 0.95);
101
- });
102
- it("removes tags below their layer threshold", () => {
103
- const profile = {
104
- ...EMPTY_PROFILE,
105
- tags: {
106
- 过时: [{ value: "旧兴趣", confidence: 0.15, lastSeen: "2026-01-01", layer: "interest" }],
107
- },
108
- };
109
- manager.decayTags(profile);
110
- expect(profile.tags["过时"]).toBeUndefined();
111
- });
112
- it("treats tags without layer as interest (backward compat)", () => {
113
- const profile = {
114
- ...EMPTY_PROFILE,
115
- tags: {
116
- 旧数据: [{ value: "旧标签", confidence: 0.9, lastSeen: "2026-03-17" }],
117
- },
118
- };
119
- manager.decayTags(profile);
120
- // 旧数据没有 layer → 视为 interest → factor=0.95
121
- expect(profile.tags["旧数据"][0].confidence).toBeCloseTo(0.9 * 0.95);
122
- });
123
- });
124
- describe("generateCoreTags", () => {
125
- it("prioritizes identity over pattern over interest", () => {
126
- const profile = {
127
- ...EMPTY_PROFILE,
128
- tags: {
129
- 兴趣: [{ value: "声纹项目", confidence: 0.9, layer: "interest" }],
130
- 身份: [{ value: "广州天河", confidence: 0.8, layer: "identity" }],
131
- 习惯: [{ value: "晚睡", confidence: 0.85, layer: "pattern" }],
132
- },
133
- };
134
- const tags = manager.generateCoreTags(profile);
135
- expect(tags[0]).toBe("广州天河"); // identity 优先
136
- expect(tags[1]).toBe("晚睡"); // pattern 次之
137
- expect(tags[2]).toBe("声纹项目"); // interest 最后
138
- });
139
- });
140
- describe("getRecallContext", () => {
141
- it("returns empty string for empty profile", () => {
142
- const ctx = manager.getRecallContext(EMPTY_PROFILE);
143
- expect(ctx).toBe("");
144
- });
145
- it("returns summary and core tags", () => {
146
- const profile = { ...EMPTY_PROFILE, summary: "辣味中餐爱好者", coreTags: ["辣味中餐", "天河"] };
147
- const ctx = manager.getRecallContext(profile);
148
- expect(ctx).toContain("辣味中餐爱好者");
149
- expect(ctx).toContain("辣味中餐");
150
- expect(ctx).toContain("天河");
151
- });
152
- });
153
- describe("save", () => {
154
- it("saves profile to file", async () => {
155
- const profile = { ...EMPTY_PROFILE, summary: "test" };
156
- await manager.save(profile);
157
- const raw = await fs.readFile(join(tempDir, "memory", "profile", "semantic_profile.json"), "utf-8");
158
- const loaded = JSON.parse(raw);
159
- expect(loaded.summary).toBe("test");
160
- });
161
- it("updates updatedAt on save", async () => {
162
- const profile = { ...EMPTY_PROFILE };
163
- const before = new Date();
164
- await manager.save(profile);
165
- const after = new Date();
166
- const raw = await fs.readFile(join(tempDir, "memory", "profile", "semantic_profile.json"), "utf-8");
167
- const loaded = JSON.parse(raw);
168
- const updated = new Date(loaded.updatedAt);
169
- expect(updated.getTime()).toBeGreaterThanOrEqual(before.getTime());
170
- expect(updated.getTime()).toBeLessThanOrEqual(after.getTime());
171
- });
172
- });
173
- });
174
- //# sourceMappingURL=profile.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"profile.test.js","sourceRoot":"","sources":["../../../src/__tests__/profile.test.ts"],"names":[],"mappings":"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"}