@scotthuang/engram 0.6.8 → 0.7.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 +79 -2
- package/dist/src/__tests__/profile.test.js +66 -22
- package/dist/src/__tests__/profile.test.js.map +1 -1
- package/dist/src/index.js +76 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/profile.d.ts +22 -5
- package/dist/src/profile.js +106 -14
- package/dist/src/profile.js.map +1 -1
- package/dist/src/settle.js +16 -14
- package/dist/src/settle.js.map +1 -1
- package/package.json +1 -1
- package/dist/bm25.d.ts +0 -60
- package/dist/bm25.js +0 -271
- package/dist/bm25.js.map +0 -1
- package/dist/config.d.ts +0 -47
- package/dist/config.js +0 -83
- package/dist/config.js.map +0 -1
- package/dist/image-store.d.ts +0 -146
- package/dist/image-store.js +0 -418
- package/dist/image-store.js.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -1138
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -32
- package/dist/logger.js +0 -106
- package/dist/logger.js.map +0 -1
- package/dist/profile.d.ts +0 -37
- package/dist/profile.js +0 -107
- package/dist/profile.js.map +0 -1
- package/dist/recall.d.ts +0 -98
- package/dist/recall.js +0 -729
- package/dist/recall.js.map +0 -1
- package/dist/settle.d.ts +0 -83
- package/dist/settle.js +0 -675
- package/dist/settle.js.map +0 -1
- package/dist/vector.d.ts +0 -66
- package/dist/vector.js +0 -275
- package/dist/vector.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,83 @@
|
|
|
1
|
-
# Engram
|
|
1
|
+
# Engram - 分层语义记忆系统
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenClaw Plugin for hierarchical semantic memory.
|
|
4
|
+
|
|
5
|
+
## 🚀 快速启动
|
|
6
|
+
|
|
7
|
+
### 一键启动所有服务
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 首次运行会自动创建虚拟环境并安装依赖
|
|
11
|
+
./start.sh
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
启动后访问:
|
|
15
|
+
- 前端:http://localhost:3210
|
|
16
|
+
- 后端:http://localhost:8765
|
|
17
|
+
|
|
18
|
+
### 停止服务
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
./stop.sh
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 查看日志
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 实时查看后端日志
|
|
28
|
+
tail -f .logs/backend.log
|
|
29
|
+
|
|
30
|
+
# 实时查看前端日志
|
|
31
|
+
tail -f .logs/frontend.log
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 📁 项目结构
|
|
35
|
+
|
|
36
|
+
- `face/` - 人脸和声纹识别模块
|
|
37
|
+
- `service/` - 后端服务 (FastAPI + Python)
|
|
38
|
+
- `web/` - 前端界面 (React + Vite)
|
|
39
|
+
- `src/` - 核心记忆系统代码
|
|
40
|
+
- `scripts/` - 工具脚本
|
|
41
|
+
|
|
42
|
+
## 🔧 手动启动(可选)
|
|
43
|
+
|
|
44
|
+
如果需要单独启动前端或后端:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# 仅启动后端
|
|
48
|
+
cd face/service
|
|
49
|
+
./start.sh
|
|
50
|
+
|
|
51
|
+
# 仅启动前端
|
|
52
|
+
cd face/web
|
|
53
|
+
./start.sh
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 📋 环境要求
|
|
57
|
+
|
|
58
|
+
- Node.js (推荐 LTS 版本)
|
|
59
|
+
- Python 3.12
|
|
60
|
+
- macOS / Linux
|
|
61
|
+
|
|
62
|
+
## 📦 功能模块
|
|
63
|
+
|
|
64
|
+
### 人脸识别
|
|
65
|
+
- 注册人脸
|
|
66
|
+
- 识别照片中的人脸
|
|
67
|
+
- Gallery 展示和测试
|
|
68
|
+
|
|
69
|
+
### 声纹识别
|
|
70
|
+
- 注册声纹
|
|
71
|
+
- 识别语音中的说话人
|
|
72
|
+
- Gallery 语音测试
|
|
73
|
+
|
|
74
|
+
### 记忆系统
|
|
75
|
+
- 短期记忆
|
|
76
|
+
- 长期记忆
|
|
77
|
+
- 语义检索
|
|
78
|
+
- 自动浓缩
|
|
79
|
+
|
|
80
|
+
---
|
|
4
81
|
|
|
5
82
|
## 方案文档
|
|
6
83
|
|
|
@@ -23,7 +23,6 @@ describe("ProfileManager", () => {
|
|
|
23
23
|
await fs.mkdir(profileDir, { recursive: true });
|
|
24
24
|
const saved = { ...EMPTY_PROFILE, summary: "test summary", coreTags: ["tag1"] };
|
|
25
25
|
await fs.writeFile(join(profileDir, "semantic_profile.json"), JSON.stringify(saved));
|
|
26
|
-
// 创建新的 manager 实例测试从文件加载
|
|
27
26
|
const manager2 = new ProfileManager(tempDir);
|
|
28
27
|
const loaded = await manager2.load();
|
|
29
28
|
expect(loaded.summary).toBe("test summary");
|
|
@@ -31,22 +30,37 @@ describe("ProfileManager", () => {
|
|
|
31
30
|
});
|
|
32
31
|
});
|
|
33
32
|
describe("addTag", () => {
|
|
34
|
-
it("adds a new tag
|
|
33
|
+
it("adds a new tag with default layer (interest)", async () => {
|
|
35
34
|
const profile = { ...EMPTY_PROFILE };
|
|
36
35
|
const result = manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
37
36
|
expect(result.tags["口味偏好"]).toHaveLength(1);
|
|
38
37
|
expect(result.tags["口味偏好"][0].value).toBe("喜欢辣");
|
|
39
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");
|
|
40
46
|
});
|
|
41
47
|
it("increases confidence for existing tag", async () => {
|
|
42
48
|
const profile = {
|
|
43
49
|
...EMPTY_PROFILE,
|
|
44
|
-
tags: { 口味偏好: [{ value: "喜欢辣", confidence: 0.5, lastSeen: "2026-01-01" }] },
|
|
50
|
+
tags: { 口味偏好: [{ value: "喜欢辣", confidence: 0.5, lastSeen: "2026-01-01", layer: "pattern" }] },
|
|
45
51
|
};
|
|
46
52
|
const result = manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
47
53
|
expect(result.tags["口味偏好"]).toHaveLength(1);
|
|
48
54
|
expect(result.tags["口味偏好"][0].confidence).toBeCloseTo(0.6);
|
|
49
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
|
+
});
|
|
50
64
|
it("adds multiple tags to same dimension", async () => {
|
|
51
65
|
const profile = { ...EMPTY_PROFILE };
|
|
52
66
|
manager.addTag(profile, "口味偏好", "喜欢辣");
|
|
@@ -55,37 +69,72 @@ describe("ProfileManager", () => {
|
|
|
55
69
|
});
|
|
56
70
|
});
|
|
57
71
|
describe("decayTags", () => {
|
|
58
|
-
it("
|
|
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", () => {
|
|
59
83
|
const profile = {
|
|
60
84
|
...EMPTY_PROFILE,
|
|
61
85
|
tags: {
|
|
62
|
-
|
|
63
|
-
{ value: "喜欢辣", confidence: 0.9, lastSeen: "2026-03-17" },
|
|
64
|
-
],
|
|
86
|
+
习惯: [{ value: "晚睡", confidence: 0.9, lastSeen: "2026-03-17", layer: "pattern" }],
|
|
65
87
|
},
|
|
66
88
|
};
|
|
67
|
-
|
|
68
|
-
expect(
|
|
89
|
+
manager.decayTags(profile);
|
|
90
|
+
expect(profile.tags["习惯"][0].confidence).toBeCloseTo(0.9 * 0.995);
|
|
69
91
|
});
|
|
70
|
-
it("
|
|
92
|
+
it("quickly decays interest tags", () => {
|
|
71
93
|
const profile = {
|
|
72
94
|
...EMPTY_PROFILE,
|
|
73
95
|
tags: {
|
|
74
|
-
|
|
96
|
+
项目: [{ value: "声纹识别", confidence: 0.7, lastSeen: "2026-03-17", layer: "interest" }],
|
|
75
97
|
},
|
|
76
98
|
};
|
|
77
|
-
|
|
78
|
-
expect(
|
|
99
|
+
manager.decayTags(profile);
|
|
100
|
+
expect(profile.tags["项目"][0].confidence).toBeCloseTo(0.7 * 0.95);
|
|
79
101
|
});
|
|
80
|
-
it("removes
|
|
102
|
+
it("removes tags below their layer threshold", () => {
|
|
81
103
|
const profile = {
|
|
82
104
|
...EMPTY_PROFILE,
|
|
83
105
|
tags: {
|
|
84
|
-
|
|
106
|
+
过时: [{ value: "旧兴趣", confidence: 0.15, lastSeen: "2026-01-01", layer: "interest" }],
|
|
85
107
|
},
|
|
86
108
|
};
|
|
87
|
-
|
|
88
|
-
expect("
|
|
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 最后
|
|
89
138
|
});
|
|
90
139
|
});
|
|
91
140
|
describe("getRecallContext", () => {
|
|
@@ -100,11 +149,6 @@ describe("ProfileManager", () => {
|
|
|
100
149
|
expect(ctx).toContain("辣味中餐");
|
|
101
150
|
expect(ctx).toContain("天河");
|
|
102
151
|
});
|
|
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
152
|
});
|
|
109
153
|
describe("save", () => {
|
|
110
154
|
it("saves profile to file", async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.test.js","sourceRoot":"","sources":["../../../src/__tests__/profile.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,IAAI,OAAuB,CAAC;IAC5B,IAAI,OAAe,CAAC;IAEpB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;YACtD,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;YAChF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,uBAAuB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAErF,
|
|
1
|
+
{"version":3,"file":"profile.test.js","sourceRoot":"","sources":["../../../src/__tests__/profile.test.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.js
CHANGED
|
@@ -423,36 +423,47 @@ export default function register(api) {
|
|
|
423
423
|
api.registerTool({
|
|
424
424
|
name: "memory_system_profile",
|
|
425
425
|
label: "User Profile",
|
|
426
|
-
description: "
|
|
426
|
+
description: "查看或更新用户语义画像(三层:identity 核心身份 / pattern 行为模式 / interest 动态兴趣)。",
|
|
427
427
|
parameters: {
|
|
428
428
|
type: "object",
|
|
429
429
|
properties: {
|
|
430
430
|
action: { type: "string", enum: ["get", "update"], description: "操作类型" },
|
|
431
431
|
dimension: { type: "string", description: "标签维度(update 时使用)" },
|
|
432
432
|
value: { type: "string", description: "标签值(update 时使用)" },
|
|
433
|
+
layer: { type: "string", enum: ["identity", "pattern", "interest"], description: "标签层级(update 时使用,默认 interest)" },
|
|
433
434
|
},
|
|
434
435
|
required: ["action"],
|
|
435
436
|
},
|
|
436
437
|
async execute(_toolCallId, params) {
|
|
437
|
-
const { action, dimension, value } = params;
|
|
438
|
+
const { action, dimension, value, layer } = params;
|
|
438
439
|
const profile = await profileManager.load();
|
|
439
440
|
if (action === "get") {
|
|
440
441
|
const context = profileManager.getRecallContext(profile);
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
442
|
+
// 按层分组展示
|
|
443
|
+
const layerLabels = { identity: "🏠 核心身份", pattern: "📊 行为模式", interest: "🎯 动态兴趣" };
|
|
444
|
+
const layerGroups = { identity: [], pattern: [], interest: [] };
|
|
445
|
+
for (const [dim, tags] of Object.entries(profile.tags)) {
|
|
446
|
+
for (const t of tags) {
|
|
447
|
+
const l = t.layer || "interest";
|
|
448
|
+
layerGroups[l].push(`${t.value}(${(t.confidence * 100).toFixed(0)}%) [${dim}]`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const fullTags = Object.entries(layerGroups)
|
|
452
|
+
.filter(([, tags]) => tags.length > 0)
|
|
453
|
+
.map(([l, tags]) => `### ${layerLabels[l]}\n${tags.map(t => `- ${t}`).join("\n")}`)
|
|
454
|
+
.join("\n\n");
|
|
444
455
|
return {
|
|
445
456
|
content: [{
|
|
446
457
|
type: "text",
|
|
447
|
-
text: `${context}\n\n
|
|
458
|
+
text: `${context}\n\n${fullTags || "(暂无标签)"}`,
|
|
448
459
|
}],
|
|
449
460
|
};
|
|
450
461
|
}
|
|
451
462
|
if (action === "update" && dimension && value) {
|
|
452
|
-
profileManager.addTag(profile, dimension, value);
|
|
463
|
+
profileManager.addTag(profile, dimension, value, layer || "interest");
|
|
453
464
|
await profileManager.save(profile);
|
|
454
465
|
return {
|
|
455
|
-
content: [{ type: "text", text: `Added tag: [${dimension}] ${value}` }],
|
|
466
|
+
content: [{ type: "text", text: `Added tag: [${dimension}] ${value} (layer=${layer || "interest"})` }],
|
|
456
467
|
};
|
|
457
468
|
}
|
|
458
469
|
return { content: [{ type: "text", text: "Invalid action or missing params." }] };
|
|
@@ -1357,6 +1368,63 @@ export default function register(api) {
|
|
|
1357
1368
|
// Rebuild BM25 index after adding new entry
|
|
1358
1369
|
await recallEngine.rebuildBM25();
|
|
1359
1370
|
logger.info(`[engram] [async] BM25 index rebuilt after new entry added`);
|
|
1371
|
+
// ---- 实时 interest 更新:从对话中快速抽取动态兴趣标签 ----
|
|
1372
|
+
if (config.condense.apiKey && config.condense.baseUrl) {
|
|
1373
|
+
try {
|
|
1374
|
+
const interestSystemPrompt = `从以下对话中提取用户当前关注的话题/项目/兴趣,输出 JSON 数组。
|
|
1375
|
+
每条格式:{"dimension": "分类", "value": "标签"}
|
|
1376
|
+
只提取动态兴趣(当前在做的事、近期关注的话题),不要提取身份信息或长期习惯。
|
|
1377
|
+
最多 3 条,宁缺毋滥。如果没有有价值的兴趣标签,输出空数组 []。
|
|
1378
|
+
只输出 JSON。`;
|
|
1379
|
+
// 截取前 1500 字符避免 token 浪费
|
|
1380
|
+
const snippet = contentToSave.slice(0, 1500);
|
|
1381
|
+
const interestApiKey = config.condense.apiKey || "";
|
|
1382
|
+
const interestBaseUrl = config.condense.baseUrl || "";
|
|
1383
|
+
const interestModel = config.condense.model || config.settleModel || "MiniMax-M2.7-highspeed";
|
|
1384
|
+
// 直接用底层 LLM 调用
|
|
1385
|
+
const controller = new AbortController();
|
|
1386
|
+
const timeoutId = setTimeout(() => controller.abort(), 15_000);
|
|
1387
|
+
const response = await fetch(`${interestBaseUrl}/v1/messages`, {
|
|
1388
|
+
method: "POST",
|
|
1389
|
+
headers: {
|
|
1390
|
+
"Content-Type": "application/json",
|
|
1391
|
+
"x-api-key": interestApiKey,
|
|
1392
|
+
"anthropic-version": "2023-06-01",
|
|
1393
|
+
},
|
|
1394
|
+
body: JSON.stringify({
|
|
1395
|
+
model: interestModel,
|
|
1396
|
+
system: interestSystemPrompt,
|
|
1397
|
+
messages: [{ role: "user", content: snippet }],
|
|
1398
|
+
max_tokens: 512,
|
|
1399
|
+
temperature: 0.3,
|
|
1400
|
+
}),
|
|
1401
|
+
signal: controller.signal,
|
|
1402
|
+
});
|
|
1403
|
+
clearTimeout(timeoutId);
|
|
1404
|
+
if (response.ok) {
|
|
1405
|
+
const data = await response.json();
|
|
1406
|
+
const textBlock = data.content?.find((b) => b.type === "text");
|
|
1407
|
+
const interestResult = textBlock?.text?.trim() || "";
|
|
1408
|
+
if (interestResult) {
|
|
1409
|
+
const cleaned = interestResult.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?\s*```\s*$/, "");
|
|
1410
|
+
const interests = JSON.parse(cleaned);
|
|
1411
|
+
if (Array.isArray(interests) && interests.length > 0) {
|
|
1412
|
+
const profile = await profileManager.reload();
|
|
1413
|
+
for (const tag of interests) {
|
|
1414
|
+
if (tag.dimension && tag.value) {
|
|
1415
|
+
profileManager.addTag(profile, tag.dimension, tag.value, "interest");
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
await profileManager.save(profile);
|
|
1419
|
+
logger.info(`[engram] [async] Updated ${interests.length} interest tags from conversation`);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
catch (interestErr) {
|
|
1425
|
+
logger.error(`[engram] [async] Interest update failed (non-critical): ${interestErr}`);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1360
1428
|
}
|
|
1361
1429
|
catch (asyncErr) {
|
|
1362
1430
|
logger.error(`[engram] [async] Background processing failed: ${asyncErr}`);
|