@easynet/agent-memory 1.0.57 → 1.0.58
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/dist/test/integration/real-memory-features.test.js +144 -146
- package/dist/test/integration/real-memory-features.test.js.map +1 -1
- package/dist/test/integration/vlm-llm-live.test.js +88 -90
- package/dist/test/integration/vlm-llm-live.test.js.map +1 -1
- package/dist/test/unit/core/agent-memory.test.js +139 -141
- package/dist/test/unit/core/agent-memory.test.js.map +1 -1
- package/dist/test/unit/core/config.test.js +162 -164
- package/dist/test/unit/core/config.test.js.map +1 -1
- package/dist/test/unit/core/router.test.js +102 -104
- package/dist/test/unit/core/router.test.js.map +1 -1
- package/dist/test/unit/create-agent-memory.test.js +296 -298
- package/dist/test/unit/create-agent-memory.test.js.map +1 -1
- package/dist/test/unit/ingest/url-ingest.test.js +116 -118
- package/dist/test/unit/ingest/url-ingest.test.js.map +1 -1
- package/dist/test/unit/providers/rag-provider.test.js +96 -98
- package/dist/test/unit/providers/rag-provider.test.js.map +1 -1
- package/dist/test/unit/providers/sqlite-store.test.js +94 -96
- package/dist/test/unit/providers/sqlite-store.test.js.map +1 -1
- package/package.json +4 -4
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Optional mem0 live test:
|
|
8
8
|
* RUN_REAL_MEMORY_INTEGRATION=1 RUN_REAL_MEM0_INTEGRATION=1 AGENT_MEMORY_MEM0_CONFIG_JSON='{"vectorStore":{"provider":"qdrant","config":{"host":"localhost","port":6333}}}' pnpm -C agent-memory test:integration
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { it } from "node:test";
|
|
11
11
|
import assert from "node:assert";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
@@ -38,159 +38,157 @@ async function createTempDir(prefix) {
|
|
|
38
38
|
await mkdir(dir, { recursive: true });
|
|
39
39
|
return dir;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: knowledgeDb } },
|
|
55
|
-
},
|
|
56
|
-
memory: {
|
|
57
|
-
thread: { storeId: "thread_sqlite" },
|
|
58
|
-
cross_thread: { storeId: "cross_sqlite" },
|
|
59
|
-
knowledge: { storeId: "knowledge_sqlite" },
|
|
60
|
-
},
|
|
41
|
+
it("sqlite: thread/cross_thread/knowledge persistence + key APIs", { skip: !HAS_WORKING_SQLITE }, async () => {
|
|
42
|
+
const dir = await createTempDir("agent-memory-real-sqlite");
|
|
43
|
+
const threadDb = join(dir, "thread.sqlite");
|
|
44
|
+
const crossDb = join(dir, "cross.sqlite");
|
|
45
|
+
const knowledgeDb = join(dir, "knowledge.sqlite");
|
|
46
|
+
const notePath = join(dir, "guide.txt");
|
|
47
|
+
await writeFile(notePath, "TypeScript coding standards. Keep APIs small and explicit.", "utf8");
|
|
48
|
+
const memoryRegistry = await createAgentMemoryRegistry({
|
|
49
|
+
config: {
|
|
50
|
+
stores: {
|
|
51
|
+
thread: { id: "thread_sqlite", type: "sqlite", options: { dbPath: threadDb } },
|
|
52
|
+
cross_thread: { id: "cross_sqlite", type: "sqlite", options: { dbPath: crossDb } },
|
|
53
|
+
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: knowledgeDb } },
|
|
61
54
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await memory.memorize("user:real", "cross_thread", "User prefers zh-CN output.");
|
|
67
|
-
await memory.memorize("user:real", "knowledge", "Project stack includes TypeScript.", {
|
|
68
|
-
key: "doc://stack",
|
|
69
|
-
source: "manual",
|
|
70
|
-
});
|
|
71
|
-
await memory.memorize("user:real", "knowledge", pathToFileURL(notePath), {
|
|
72
|
-
key: "doc://guide",
|
|
73
|
-
source: "file",
|
|
74
|
-
});
|
|
75
|
-
const all = await memory.recall({
|
|
76
|
-
namespace: "user:real",
|
|
77
|
-
query: "TypeScript and preference",
|
|
78
|
-
types: ["thread", "cross_thread", "knowledge"],
|
|
79
|
-
topK: 20,
|
|
80
|
-
});
|
|
81
|
-
assert.ok(all.items.length >= 3, "should recall entries from all memory types");
|
|
82
|
-
const byKey = await memory.getByKey("user:real", "doc://stack");
|
|
83
|
-
assert.ok(byKey, "getByKey should return persisted knowledge item");
|
|
84
|
-
assert.strictEqual(byKey?.type, "knowledge");
|
|
85
|
-
await memory.deleteByKey("user:real", "doc://stack");
|
|
86
|
-
const deleted = await memory.getByKey("user:real", "doc://stack");
|
|
87
|
-
assert.strictEqual(deleted, null);
|
|
88
|
-
const threadCount = (await openDb(threadDb))
|
|
89
|
-
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
90
|
-
.get("user:real").c;
|
|
91
|
-
const crossCount = (await openDb(crossDb))
|
|
92
|
-
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
93
|
-
.get("user:real").c;
|
|
94
|
-
const knowledgeCount = (await openDb(knowledgeDb))
|
|
95
|
-
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
96
|
-
.get("user:real").c;
|
|
97
|
-
assert.ok(threadCount >= 1, "thread sqlite DB should contain rows");
|
|
98
|
-
assert.ok(crossCount >= 1, "cross_thread sqlite DB should contain rows");
|
|
99
|
-
assert.ok(knowledgeCount >= 1, "knowledge sqlite DB should contain rows");
|
|
100
|
-
await memory.deleteByNamespace("user:real");
|
|
101
|
-
const afterDelete = await memory.recall({
|
|
102
|
-
namespace: "user:real",
|
|
103
|
-
query: "anything",
|
|
104
|
-
types: ["thread", "cross_thread", "knowledge"],
|
|
105
|
-
topK: 20,
|
|
106
|
-
});
|
|
107
|
-
assert.strictEqual(afterDelete.items.length, 0);
|
|
108
|
-
}
|
|
109
|
-
finally {
|
|
110
|
-
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
it("thread compaction: writes summary into real sqlite", { skip: !HAS_WORKING_SQLITE }, async () => {
|
|
114
|
-
const dir = await createTempDir("agent-memory-real-compaction");
|
|
115
|
-
const threadDb = join(dir, "thread.sqlite");
|
|
116
|
-
const memoryRegistry = await createAgentMemoryRegistry({
|
|
117
|
-
config: {
|
|
118
|
-
stores: {
|
|
119
|
-
thread: { id: "thread_sqlite", type: "sqlite", options: { dbPath: threadDb } },
|
|
120
|
-
cross_thread: { id: "cross_sqlite", type: "sqlite", options: { dbPath: join(dir, "cross.sqlite") } },
|
|
121
|
-
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: join(dir, "knowledge.sqlite") } },
|
|
122
|
-
},
|
|
123
|
-
memory: {
|
|
124
|
-
thread: {
|
|
125
|
-
storeId: "thread_sqlite",
|
|
126
|
-
sessionCompaction: {
|
|
127
|
-
enabled: true,
|
|
128
|
-
maxTokens: 8,
|
|
129
|
-
keepRecentTurns: 1,
|
|
130
|
-
summaryKey: "__summary__",
|
|
131
|
-
checkEveryTurns: 1,
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
cross_thread: { storeId: "cross_sqlite" },
|
|
135
|
-
knowledge: { storeId: "knowledge_sqlite" },
|
|
136
|
-
},
|
|
55
|
+
memory: {
|
|
56
|
+
thread: { storeId: "thread_sqlite" },
|
|
57
|
+
cross_thread: { storeId: "cross_sqlite" },
|
|
58
|
+
knowledge: { storeId: "knowledge_sqlite" },
|
|
137
59
|
},
|
|
138
|
-
|
|
139
|
-
});
|
|
140
|
-
const memory = await memoryRegistry.getAgentMemory();
|
|
141
|
-
try {
|
|
142
|
-
await memory.memorize("session:real", "thread", "A".repeat(40));
|
|
143
|
-
await memory.memorize("session:real", "thread", "B".repeat(40));
|
|
144
|
-
const summary = await memory.getByKey("session:real", "__summary__");
|
|
145
|
-
assert.ok(summary, "summary should be stored after compaction");
|
|
146
|
-
assert.strictEqual(summary?.type, "thread");
|
|
147
|
-
assert.strictEqual(summary?.content, "summary(1)");
|
|
148
|
-
const summaryCount = (await openDb(threadDb))
|
|
149
|
-
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ? AND path_key = ?")
|
|
150
|
-
.get("session:real", "__summary__").c;
|
|
151
|
-
assert.strictEqual(summaryCount, 1);
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
155
|
-
}
|
|
60
|
+
},
|
|
156
61
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
cross_thread: { id: "mem0", type: "mem0" },
|
|
165
|
-
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: ":memory:" } },
|
|
166
|
-
},
|
|
167
|
-
memory: {
|
|
168
|
-
thread: { storeId: "thread_sqlite" },
|
|
169
|
-
cross_thread: { storeId: "mem0" },
|
|
170
|
-
knowledge: { storeId: "knowledge_sqlite" },
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
overrides: { mem0 },
|
|
62
|
+
const memory = await memoryRegistry.getAgentMemory();
|
|
63
|
+
try {
|
|
64
|
+
await memory.memorize("user:real", "thread", "Current session asks for concise answers.");
|
|
65
|
+
await memory.memorize("user:real", "cross_thread", "User prefers zh-CN output.");
|
|
66
|
+
await memory.memorize("user:real", "knowledge", "Project stack includes TypeScript.", {
|
|
67
|
+
key: "doc://stack",
|
|
68
|
+
source: "manual",
|
|
174
69
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const recalled = await memory.recall({
|
|
179
|
-
namespace,
|
|
180
|
-
query: "What format does the user prefer?",
|
|
181
|
-
types: ["cross_thread"],
|
|
182
|
-
topK: 5,
|
|
70
|
+
await memory.memorize("user:real", "knowledge", pathToFileURL(notePath), {
|
|
71
|
+
key: "doc://guide",
|
|
72
|
+
source: "file",
|
|
183
73
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
74
|
+
const all = await memory.recall({
|
|
75
|
+
namespace: "user:real",
|
|
76
|
+
query: "TypeScript and preference",
|
|
77
|
+
types: ["thread", "cross_thread", "knowledge"],
|
|
78
|
+
topK: 20,
|
|
79
|
+
});
|
|
80
|
+
assert.ok(all.items.length >= 3, "should recall entries from all memory types");
|
|
81
|
+
const byKey = await memory.getByKey("user:real", "doc://stack");
|
|
82
|
+
assert.ok(byKey, "getByKey should return persisted knowledge item");
|
|
83
|
+
assert.strictEqual(byKey?.type, "knowledge");
|
|
84
|
+
await memory.deleteByKey("user:real", "doc://stack");
|
|
85
|
+
const deleted = await memory.getByKey("user:real", "doc://stack");
|
|
86
|
+
assert.strictEqual(deleted, null);
|
|
87
|
+
const threadCount = (await openDb(threadDb))
|
|
88
|
+
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
89
|
+
.get("user:real").c;
|
|
90
|
+
const crossCount = (await openDb(crossDb))
|
|
91
|
+
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
92
|
+
.get("user:real").c;
|
|
93
|
+
const knowledgeCount = (await openDb(knowledgeDb))
|
|
94
|
+
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ?")
|
|
95
|
+
.get("user:real").c;
|
|
96
|
+
assert.ok(threadCount >= 1, "thread sqlite DB should contain rows");
|
|
97
|
+
assert.ok(crossCount >= 1, "cross_thread sqlite DB should contain rows");
|
|
98
|
+
assert.ok(knowledgeCount >= 1, "knowledge sqlite DB should contain rows");
|
|
99
|
+
await memory.deleteByNamespace("user:real");
|
|
187
100
|
const afterDelete = await memory.recall({
|
|
188
|
-
namespace,
|
|
189
|
-
query: "
|
|
190
|
-
types: ["cross_thread"],
|
|
191
|
-
topK:
|
|
101
|
+
namespace: "user:real",
|
|
102
|
+
query: "anything",
|
|
103
|
+
types: ["thread", "cross_thread", "knowledge"],
|
|
104
|
+
topK: 20,
|
|
192
105
|
});
|
|
193
106
|
assert.strictEqual(afterDelete.items.length, 0);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
it("thread compaction: writes summary into real sqlite", { skip: !HAS_WORKING_SQLITE }, async () => {
|
|
113
|
+
const dir = await createTempDir("agent-memory-real-compaction");
|
|
114
|
+
const threadDb = join(dir, "thread.sqlite");
|
|
115
|
+
const memoryRegistry = await createAgentMemoryRegistry({
|
|
116
|
+
config: {
|
|
117
|
+
stores: {
|
|
118
|
+
thread: { id: "thread_sqlite", type: "sqlite", options: { dbPath: threadDb } },
|
|
119
|
+
cross_thread: { id: "cross_sqlite", type: "sqlite", options: { dbPath: join(dir, "cross.sqlite") } },
|
|
120
|
+
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: join(dir, "knowledge.sqlite") } },
|
|
121
|
+
},
|
|
122
|
+
memory: {
|
|
123
|
+
thread: {
|
|
124
|
+
storeId: "thread_sqlite",
|
|
125
|
+
sessionCompaction: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
maxTokens: 8,
|
|
128
|
+
keepRecentTurns: 1,
|
|
129
|
+
summaryKey: "__summary__",
|
|
130
|
+
checkEveryTurns: 1,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
cross_thread: { storeId: "cross_sqlite" },
|
|
134
|
+
knowledge: { storeId: "knowledge_sqlite" },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
summarizeSessionTurns: async ({ oldTurns }) => `summary(${oldTurns.length})`,
|
|
138
|
+
});
|
|
139
|
+
const memory = await memoryRegistry.getAgentMemory();
|
|
140
|
+
try {
|
|
141
|
+
await memory.memorize("session:real", "thread", "A".repeat(40));
|
|
142
|
+
await memory.memorize("session:real", "thread", "B".repeat(40));
|
|
143
|
+
const summary = await memory.getByKey("session:real", "__summary__");
|
|
144
|
+
assert.ok(summary, "summary should be stored after compaction");
|
|
145
|
+
assert.strictEqual(summary?.type, "thread");
|
|
146
|
+
assert.strictEqual(summary?.content, "summary(1)");
|
|
147
|
+
const summaryCount = (await openDb(threadDb))
|
|
148
|
+
.prepare("SELECT COUNT(*) as c FROM memory_items WHERE namespace = ? AND path_key = ?")
|
|
149
|
+
.get("session:real", "__summary__").c;
|
|
150
|
+
assert.strictEqual(summaryCount, 1);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
it("mem0 live: cross_thread write/query with real backend", { skip: !RUN_REAL_MEM0 || !MEM0_CONFIG_JSON }, async () => {
|
|
157
|
+
const { Memory } = (await import("mem0ai/oss"));
|
|
158
|
+
const mem0 = new Memory(JSON.parse(MEM0_CONFIG_JSON));
|
|
159
|
+
const memoryRegistry = await createAgentMemoryRegistry({
|
|
160
|
+
config: {
|
|
161
|
+
stores: {
|
|
162
|
+
thread: { id: "thread_sqlite", type: "sqlite", options: { dbPath: ":memory:" } },
|
|
163
|
+
cross_thread: { id: "mem0", type: "mem0" },
|
|
164
|
+
knowledge: { id: "knowledge_sqlite", type: "sqlite", options: { dbPath: ":memory:" } },
|
|
165
|
+
},
|
|
166
|
+
memory: {
|
|
167
|
+
thread: { storeId: "thread_sqlite" },
|
|
168
|
+
cross_thread: { storeId: "mem0" },
|
|
169
|
+
knowledge: { storeId: "knowledge_sqlite" },
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
overrides: { mem0 },
|
|
173
|
+
});
|
|
174
|
+
const memory = await memoryRegistry.getAgentMemory();
|
|
175
|
+
const namespace = `user:mem0:${randomUUID()}`;
|
|
176
|
+
await memory.memorize(namespace, "cross_thread", "User prefers concise answers and markdown lists.");
|
|
177
|
+
const recalled = await memory.recall({
|
|
178
|
+
namespace,
|
|
179
|
+
query: "What format does the user prefer?",
|
|
180
|
+
types: ["cross_thread"],
|
|
181
|
+
topK: 5,
|
|
182
|
+
});
|
|
183
|
+
assert.ok(recalled.items.length >= 1, "mem0 recall should return at least one item");
|
|
184
|
+
assert.ok(recalled.items.some((item) => item.content.toLowerCase().includes("concise")), "mem0 recall should include inserted preference");
|
|
185
|
+
await memory.deleteByNamespace(namespace);
|
|
186
|
+
const afterDelete = await memory.recall({
|
|
187
|
+
namespace,
|
|
188
|
+
query: "format",
|
|
189
|
+
types: ["cross_thread"],
|
|
190
|
+
topK: 5,
|
|
194
191
|
});
|
|
192
|
+
assert.strictEqual(afterDelete.items.length, 0);
|
|
195
193
|
});
|
|
196
194
|
//# sourceMappingURL=real-memory-features.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"real-memory-features.test.js","sourceRoot":"","sources":["../../../test/integration/real-memory-features.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"real-memory-features.test.js","sourceRoot":"","sources":["../../../test/integration/real-memory-features.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* VLM: AGENT_MEMORY_VLM_URL (default http://localhost:11434), AGENT_MEMORY_VLM_MODEL (default qwen3-vl:2b)
|
|
6
6
|
* LLM: AGENT_MEMORY_LLM_URL (default https://ollama-rtx-4070.easynet.world/), AGENT_MEMORY_LLM_MODEL (default gpt-oss-80k:latest)
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { it } from "node:test";
|
|
9
9
|
import assert from "node:assert";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { writeFile, mkdir, rm } from "node:fs/promises";
|
|
@@ -24,104 +24,102 @@ const MINIMAL_PNG = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAA
|
|
|
24
24
|
const baseConfig = {
|
|
25
25
|
stores: [{ id: "default", type: "in_memory" }],
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const captionFn = createOllamaCaptionFn(VLM_URL, VLM_MODEL);
|
|
35
|
-
const memoryRegistry = await createAgentMemoryRegistry({
|
|
36
|
-
config: baseConfig,
|
|
37
|
-
captionFn,
|
|
38
|
-
});
|
|
39
|
-
const memory = await memoryRegistry.getAgentMemory();
|
|
40
|
-
const item = await memory.memorize("test:vlm-live", "knowledge", pathToFileURL(imagePath));
|
|
41
|
-
assert.ok(item.id);
|
|
42
|
-
assert.strictEqual(item.type, "knowledge");
|
|
43
|
-
const result = await memory.recall({
|
|
44
|
-
namespace: "test:vlm-live",
|
|
45
|
-
query: "image",
|
|
46
|
-
topK: 5,
|
|
47
|
-
});
|
|
48
|
-
assert.ok(result.items.length >= 1, "recall should return at least one item");
|
|
49
|
-
const content = result.items[0].content;
|
|
50
|
-
assert.ok(content && !content.startsWith("(image:"), "stored content should be VLM caption, not placeholder");
|
|
51
|
-
assert.ok(content.length > 5, "caption should be non-trivial");
|
|
52
|
-
}
|
|
53
|
-
finally {
|
|
54
|
-
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
it("LLM: memorize without type uses classifyMemory and recall returns typed item", async () => {
|
|
58
|
-
const classifyMemory = createOllamaClassifyMemoryFn(LLM_URL, LLM_MODEL);
|
|
27
|
+
it("VLM: memorize image URL with captionFn and recall returns caption", async () => {
|
|
28
|
+
const dir = join(tmpdir(), `agent-memory-vlm-${randomUUID()}`);
|
|
29
|
+
await mkdir(dir, { recursive: true });
|
|
30
|
+
const imagePath = join(dir, "test.png");
|
|
31
|
+
await writeFile(imagePath, MINIMAL_PNG);
|
|
32
|
+
try {
|
|
33
|
+
const captionFn = createOllamaCaptionFn(VLM_URL, VLM_MODEL);
|
|
59
34
|
const memoryRegistry = await createAgentMemoryRegistry({
|
|
60
35
|
config: baseConfig,
|
|
61
|
-
|
|
36
|
+
captionFn,
|
|
62
37
|
});
|
|
63
38
|
const memory = await memoryRegistry.getAgentMemory();
|
|
64
|
-
const item = await memory.memorize("test:
|
|
39
|
+
const item = await memory.memorize("test:vlm-live", "knowledge", pathToFileURL(imagePath));
|
|
65
40
|
assert.ok(item.id);
|
|
66
|
-
assert.
|
|
41
|
+
assert.strictEqual(item.type, "knowledge");
|
|
67
42
|
const result = await memory.recall({
|
|
68
|
-
namespace: "test:
|
|
69
|
-
query: "
|
|
43
|
+
namespace: "test:vlm-live",
|
|
44
|
+
query: "image",
|
|
70
45
|
topK: 5,
|
|
71
46
|
});
|
|
72
|
-
assert.
|
|
73
|
-
|
|
47
|
+
assert.ok(result.items.length >= 1, "recall should return at least one item");
|
|
48
|
+
const content = result.items[0].content;
|
|
49
|
+
assert.ok(content && !content.startsWith("(image:"), "stored content should be VLM caption, not placeholder");
|
|
50
|
+
assert.ok(content.length > 5, "caption should be non-trivial");
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
it("LLM: memorize without type uses classifyMemory and recall returns typed item", async () => {
|
|
57
|
+
const classifyMemory = createOllamaClassifyMemoryFn(LLM_URL, LLM_MODEL);
|
|
58
|
+
const memoryRegistry = await createAgentMemoryRegistry({
|
|
59
|
+
config: baseConfig,
|
|
60
|
+
classifyMemory,
|
|
74
61
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
config: baseConfig,
|
|
84
|
-
captionFn,
|
|
85
|
-
classifyMemory,
|
|
86
|
-
});
|
|
87
|
-
const memory = await memoryRegistry.getAgentMemory();
|
|
88
|
-
try {
|
|
89
|
-
// 1) Memorize image — real VLM captions it
|
|
90
|
-
const imageItem = await memory.memorize("test:full", "knowledge", pathToFileURL(imagePath));
|
|
91
|
-
assert.ok(imageItem.id);
|
|
92
|
-
assert.strictEqual(imageItem.type, "knowledge");
|
|
93
|
-
assert.ok(imageItem.content && !imageItem.content.startsWith("(image:"), "VLM must have produced a real caption");
|
|
94
|
-
// 2) Memorize text without type — real LLM classifies it
|
|
95
|
-
const textItem = await memory.memorize("test:full", "cross_thread", "User preference: prefers dark theme and compact layout.");
|
|
96
|
-
assert.ok(textItem.id);
|
|
97
|
-
assert.ok(["thread", "cross_thread", "knowledge"].includes(textItem.type), "LLM must have classified type");
|
|
98
|
-
// 3) Recall — should return both items (real provider)
|
|
99
|
-
const recallResult = await memory.recall({
|
|
100
|
-
namespace: "test:full",
|
|
101
|
-
query: "preference and image",
|
|
102
|
-
topK: 10,
|
|
103
|
-
});
|
|
104
|
-
assert.ok(recallResult.items.length >= 2, "recall should return image + text");
|
|
105
|
-
assert.ok(recallResult.traceId);
|
|
106
|
-
const contents = recallResult.items.map((i) => i.content);
|
|
107
|
-
const hasCaption = contents.some((c) => c && !c.startsWith("(image:") && c.length > 5);
|
|
108
|
-
const hasPreference = contents.some((c) => c && c.includes("dark") && c.includes("preference"));
|
|
109
|
-
assert.ok(hasCaption, "recall should include VLM caption");
|
|
110
|
-
assert.ok(hasPreference, "recall should include user preference text");
|
|
111
|
-
// 4) Recall again — validate full stack formatting for prompt injection
|
|
112
|
-
const promptResult = await memory.recall({
|
|
113
|
-
namespace: "test:full",
|
|
114
|
-
query: "user preferences and any images",
|
|
115
|
-
topK: 10,
|
|
116
|
-
});
|
|
117
|
-
assert.ok(promptResult.traceId);
|
|
118
|
-
assert.ok(promptResult.injectedText.length > 0);
|
|
119
|
-
assert.ok(promptResult.injectedText.includes("knowledge") || promptResult.injectedText.includes("cross_thread") || promptResult.injectedText.includes("thread"), "injected text should include type context");
|
|
120
|
-
assert.ok(promptResult.items.length >= 2);
|
|
121
|
-
}
|
|
122
|
-
finally {
|
|
123
|
-
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
124
|
-
}
|
|
62
|
+
const memory = await memoryRegistry.getAgentMemory();
|
|
63
|
+
const item = await memory.memorize("test:llm-live", "cross_thread", "GET /users returns a list of users. POST /users creates a user. See API docs.");
|
|
64
|
+
assert.ok(item.id);
|
|
65
|
+
assert.ok(["thread", "cross_thread", "knowledge"].includes(item.type), "type should be one of thread/cross_thread/knowledge");
|
|
66
|
+
const result = await memory.recall({
|
|
67
|
+
namespace: "test:llm-live",
|
|
68
|
+
query: "API",
|
|
69
|
+
topK: 5,
|
|
125
70
|
});
|
|
71
|
+
assert.strictEqual(result.items.length, 1);
|
|
72
|
+
assert.strictEqual(result.items[0].content, "GET /users returns a list of users. POST /users creates a user. See API docs.");
|
|
73
|
+
});
|
|
74
|
+
it("full use case: VLM + LLM together — image caption, text classify, and recall", async () => {
|
|
75
|
+
const dir = join(tmpdir(), `agent-memory-full-${randomUUID()}`);
|
|
76
|
+
await mkdir(dir, { recursive: true });
|
|
77
|
+
const imagePath = join(dir, "doc.png");
|
|
78
|
+
await writeFile(imagePath, MINIMAL_PNG);
|
|
79
|
+
const captionFn = createOllamaCaptionFn(VLM_URL, VLM_MODEL);
|
|
80
|
+
const classifyMemory = createOllamaClassifyMemoryFn(LLM_URL, LLM_MODEL);
|
|
81
|
+
const memoryRegistry = await createAgentMemoryRegistry({
|
|
82
|
+
config: baseConfig,
|
|
83
|
+
captionFn,
|
|
84
|
+
classifyMemory,
|
|
85
|
+
});
|
|
86
|
+
const memory = await memoryRegistry.getAgentMemory();
|
|
87
|
+
try {
|
|
88
|
+
// 1) Memorize image — real VLM captions it
|
|
89
|
+
const imageItem = await memory.memorize("test:full", "knowledge", pathToFileURL(imagePath));
|
|
90
|
+
assert.ok(imageItem.id);
|
|
91
|
+
assert.strictEqual(imageItem.type, "knowledge");
|
|
92
|
+
assert.ok(imageItem.content && !imageItem.content.startsWith("(image:"), "VLM must have produced a real caption");
|
|
93
|
+
// 2) Memorize text without type — real LLM classifies it
|
|
94
|
+
const textItem = await memory.memorize("test:full", "cross_thread", "User preference: prefers dark theme and compact layout.");
|
|
95
|
+
assert.ok(textItem.id);
|
|
96
|
+
assert.ok(["thread", "cross_thread", "knowledge"].includes(textItem.type), "LLM must have classified type");
|
|
97
|
+
// 3) Recall — should return both items (real provider)
|
|
98
|
+
const recallResult = await memory.recall({
|
|
99
|
+
namespace: "test:full",
|
|
100
|
+
query: "preference and image",
|
|
101
|
+
topK: 10,
|
|
102
|
+
});
|
|
103
|
+
assert.ok(recallResult.items.length >= 2, "recall should return image + text");
|
|
104
|
+
assert.ok(recallResult.traceId);
|
|
105
|
+
const contents = recallResult.items.map((i) => i.content);
|
|
106
|
+
const hasCaption = contents.some((c) => c && !c.startsWith("(image:") && c.length > 5);
|
|
107
|
+
const hasPreference = contents.some((c) => c && c.includes("dark") && c.includes("preference"));
|
|
108
|
+
assert.ok(hasCaption, "recall should include VLM caption");
|
|
109
|
+
assert.ok(hasPreference, "recall should include user preference text");
|
|
110
|
+
// 4) Recall again — validate full stack formatting for prompt injection
|
|
111
|
+
const promptResult = await memory.recall({
|
|
112
|
+
namespace: "test:full",
|
|
113
|
+
query: "user preferences and any images",
|
|
114
|
+
topK: 10,
|
|
115
|
+
});
|
|
116
|
+
assert.ok(promptResult.traceId);
|
|
117
|
+
assert.ok(promptResult.injectedText.length > 0);
|
|
118
|
+
assert.ok(promptResult.injectedText.includes("knowledge") || promptResult.injectedText.includes("cross_thread") || promptResult.injectedText.includes("thread"), "injected text should include type context");
|
|
119
|
+
assert.ok(promptResult.items.length >= 2);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
123
|
+
}
|
|
126
124
|
});
|
|
127
125
|
//# sourceMappingURL=vlm-llm-live.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vlm-llm-live.test.js","sourceRoot":"","sources":["../../../test/integration/vlm-llm-live.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"vlm-llm-live.test.js","sourceRoot":"","sources":["../../../test/integration/vlm-llm-live.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAY,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EACL,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAElC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG,CAAC;AAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,wBAAwB,CAAC;AAC7E,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,aAAa,CAAC;AACtE,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,wCAAwC,CAAC;AAC/E,MAAM,SAAS,GACb,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,oBAAoB,CAAC;AAE7D,qDAAqD;AACrD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAC7B,kGAAkG,EAClG,QAAQ,CACT,CAAC;AAEF,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAoB,EAAE,CAAC;CACxD,CAAC;AAEA,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;IACjF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,UAAU,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACxC,MAAM,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC5D,MAAM,cAAc,GAAG,MAAM,yBAAyB,CAAC;YACrD,MAAM,EAAE,UAAU;YAClB,SAAS;SACV,CAAC,CAAC;QACL,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,EAAE,CAAC;QAEnD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAChC,eAAe,EACf,WAAW,EACX,aAAa,CAAC,SAAS,CAAC,CACzB,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACjC,SAAS,EAAE,eAAe;YAC1B,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,wCAAwC,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACxC,MAAM,CAAC,EAAE,CACP,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EACzC,uDAAuD,CACxD,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,+BAA+B,CAAC,CAAC;IACjE,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;IAC5F,MAAM,cAAc,GAAG,4BAA4B,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,MAAM,yBAAyB,CAAC;QACrD,MAAM,EAAE,UAAU;QAClB,cAAc;KACf,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,EAAE,CAAC;IAErD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAChC,eAAe,EACf,cAAc,EACd,+EAA+E,CAChF,CAAC;IACF,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnB,MAAM,CAAC,EAAE,CACP,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAC3D,qDAAqD,CACtD,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;QACjC,SAAS,EAAE,eAAe;QAC1B,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,CAAC;KACR,CAAC,CAAC;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3C,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EACvB,+EAA+E,CAChF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;IAC5F,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,UAAU,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,4BAA4B,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,MAAM,yBAAyB,CAAC;QACrD,MAAM,EAAE,UAAU;QAClB,SAAS;QACT,cAAc;KACf,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,cAAc,EAAE,CAAC;IAErD,IAAI,CAAC;QACH,2CAA2C;QAC3C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,QAAQ,CACrC,WAAW,EACX,WAAW,EACX,aAAa,CAAC,SAAS,CAAC,CACzB,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACxB,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CACP,SAAS,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAC7D,uCAAuC,CACxC,CAAC;QAEF,yDAAyD;QACzD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CACpC,WAAW,EACX,cAAc,EACd,yDAAyD,CAC1D,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvB,MAAM,CAAC,EAAE,CACP,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC/D,+BAA+B,CAChC,CAAC;QAEF,uDAAuD;QACvD,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACvC,SAAS,EAAE,WAAW;YACtB,KAAK,EAAE,sBAAsB;YAC7B,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,mCAAmC,CAAC,CAAC;QAC/E,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAEhC,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvF,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;QAChG,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,mCAAmC,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,4CAA4C,CAAC,CAAC;QAEvE,wEAAwE;QACxE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACvC,SAAS,EAAE,WAAW;YACtB,KAAK,EAAE,iCAAiC;YACxC,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,EAAE,CACP,YAAY,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,YAAY,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,YAAY,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACrJ,2CAA2C,CAC5C,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClE,CAAC;AACH,CAAC,CAAC,CAAC"}
|