@cogitator-ai/memory 0.10.0 → 0.11.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 +204 -88
- package/dist/adapters/postgres.d.ts +1 -0
- package/dist/adapters/postgres.d.ts.map +1 -1
- package/dist/adapters/postgres.js +78 -38
- package/dist/adapters/postgres.js.map +1 -1
- package/dist/adapters/qdrant.d.ts.map +1 -1
- package/dist/adapters/qdrant.js +16 -2
- package/dist/adapters/qdrant.js.map +1 -1
- package/dist/adapters/sqlite.d.ts +1 -0
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js +17 -16
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/compaction.d.ts +14 -1
- package/dist/compaction.d.ts.map +1 -1
- package/dist/compaction.js +4 -1
- package/dist/compaction.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge-graph/entity-extractor.d.ts +24 -11
- package/dist/knowledge-graph/entity-extractor.d.ts.map +1 -1
- package/dist/knowledge-graph/entity-extractor.js +15 -3
- package/dist/knowledge-graph/entity-extractor.js.map +1 -1
- package/dist/knowledge-graph/graph-adapter.d.ts +1 -0
- package/dist/knowledge-graph/graph-adapter.d.ts.map +1 -1
- package/dist/knowledge-graph/graph-adapter.js +470 -448
- package/dist/knowledge-graph/graph-adapter.js.map +1 -1
- package/dist/knowledge-graph/sqlite-graph-adapter.d.ts +1 -0
- package/dist/knowledge-graph/sqlite-graph-adapter.d.ts.map +1 -1
- package/dist/knowledge-graph/sqlite-graph-adapter.js +459 -435
- package/dist/knowledge-graph/sqlite-graph-adapter.js.map +1 -1
- package/dist/schema.d.ts +4 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
|
+
import { unwrap } from '../result.js';
|
|
2
3
|
export class SQLiteGraphAdapter {
|
|
3
4
|
db = null;
|
|
4
5
|
path;
|
|
@@ -87,6 +88,15 @@ export class SQLiteGraphAdapter {
|
|
|
87
88
|
failure(error) {
|
|
88
89
|
return { success: false, error };
|
|
89
90
|
}
|
|
91
|
+
async run(operation) {
|
|
92
|
+
try {
|
|
93
|
+
await this.initialize();
|
|
94
|
+
return await operation();
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
return this.failure(err instanceof Error ? err.message : String(err));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
90
100
|
generateId(prefix) {
|
|
91
101
|
return `${prefix}_${nanoid(12)}`;
|
|
92
102
|
}
|
|
@@ -164,300 +174,313 @@ export class SQLiteGraphAdapter {
|
|
|
164
174
|
return dot / denom;
|
|
165
175
|
}
|
|
166
176
|
async addNode(node) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
return this.run(async () => {
|
|
178
|
+
const db = this.ensureDb();
|
|
179
|
+
const id = this.generateId('node');
|
|
180
|
+
const now = new Date();
|
|
181
|
+
const nowStr = now.toISOString();
|
|
182
|
+
db.prepare(`INSERT INTO graph_nodes
|
|
183
|
+
(id, agent_id, type, name, aliases, description, properties, embedding, confidence, source, metadata, access_count, created_at, updated_at, last_accessed_at)
|
|
184
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`).run(id, node.agentId, node.type, node.name, JSON.stringify(node.aliases), node.description ?? null, JSON.stringify(node.properties), node.embedding ? JSON.stringify(node.embedding) : null, node.confidence, node.source, JSON.stringify(node.metadata ?? {}), nowStr, nowStr, nowStr);
|
|
185
|
+
return this.success({
|
|
186
|
+
...node,
|
|
187
|
+
id,
|
|
188
|
+
createdAt: now,
|
|
189
|
+
updatedAt: now,
|
|
190
|
+
lastAccessedAt: now,
|
|
191
|
+
accessCount: 0,
|
|
192
|
+
});
|
|
182
193
|
});
|
|
183
194
|
}
|
|
184
195
|
async getNode(nodeId) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
return this.run(async () => {
|
|
197
|
+
const db = this.ensureDb();
|
|
198
|
+
db.prepare(`UPDATE graph_nodes SET last_accessed_at = ?, access_count = access_count + 1 WHERE id = ?`).run(new Date().toISOString(), nodeId);
|
|
199
|
+
const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
|
|
200
|
+
if (!row)
|
|
201
|
+
return this.success(null);
|
|
202
|
+
return this.success(this.rowToNode(row));
|
|
203
|
+
});
|
|
192
204
|
}
|
|
193
205
|
async getNodeByName(agentId, name) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
return this.run(async () => {
|
|
207
|
+
const db = this.ensureDb();
|
|
208
|
+
const row = db
|
|
209
|
+
.prepare(`SELECT * FROM graph_nodes WHERE agent_id = ? AND name = ?`)
|
|
210
|
+
.get(agentId, name);
|
|
211
|
+
if (row)
|
|
212
|
+
return this.success(this.rowToNode(row));
|
|
213
|
+
const allRows = db
|
|
214
|
+
.prepare(`SELECT * FROM graph_nodes WHERE agent_id = ?`)
|
|
215
|
+
.all(agentId);
|
|
216
|
+
for (const r of allRows) {
|
|
217
|
+
const aliases = JSON.parse(r.aliases);
|
|
218
|
+
if (aliases.includes(name)) {
|
|
219
|
+
return this.success(this.rowToNode(r));
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
return this.success(null);
|
|
223
|
+
});
|
|
211
224
|
}
|
|
212
225
|
async updateNode(nodeId, updates) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
226
|
+
return this.run(async () => {
|
|
227
|
+
const db = this.ensureDb();
|
|
228
|
+
const setClauses = ['updated_at = ?'];
|
|
229
|
+
const params = [new Date().toISOString()];
|
|
230
|
+
if (updates.name !== undefined) {
|
|
231
|
+
setClauses.push('name = ?');
|
|
232
|
+
params.push(updates.name);
|
|
233
|
+
}
|
|
234
|
+
if (updates.aliases !== undefined) {
|
|
235
|
+
setClauses.push('aliases = ?');
|
|
236
|
+
params.push(JSON.stringify(updates.aliases));
|
|
237
|
+
}
|
|
238
|
+
if (updates.description !== undefined) {
|
|
239
|
+
setClauses.push('description = ?');
|
|
240
|
+
params.push(updates.description);
|
|
241
|
+
}
|
|
242
|
+
if (updates.properties !== undefined) {
|
|
243
|
+
setClauses.push('properties = ?');
|
|
244
|
+
params.push(JSON.stringify(updates.properties));
|
|
245
|
+
}
|
|
246
|
+
if (updates.confidence !== undefined) {
|
|
247
|
+
setClauses.push('confidence = ?');
|
|
248
|
+
params.push(updates.confidence);
|
|
249
|
+
}
|
|
250
|
+
if (updates.metadata !== undefined) {
|
|
251
|
+
setClauses.push('metadata = ?');
|
|
252
|
+
params.push(JSON.stringify(updates.metadata));
|
|
253
|
+
}
|
|
254
|
+
if (updates.embedding !== undefined) {
|
|
255
|
+
setClauses.push('embedding = ?');
|
|
256
|
+
params.push(JSON.stringify(updates.embedding));
|
|
257
|
+
}
|
|
258
|
+
params.push(nodeId);
|
|
259
|
+
const result = db
|
|
260
|
+
.prepare(`UPDATE graph_nodes SET ${setClauses.join(', ')} WHERE id = ?`)
|
|
261
|
+
.run(...params);
|
|
262
|
+
if (result.changes === 0) {
|
|
263
|
+
return this.failure(`Node not found: ${nodeId}`);
|
|
264
|
+
}
|
|
265
|
+
const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
|
|
266
|
+
return this.success(this.rowToNode(row));
|
|
267
|
+
});
|
|
254
268
|
}
|
|
255
269
|
async deleteNode(nodeId) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
270
|
+
return this.run(async () => {
|
|
271
|
+
const db = this.ensureDb();
|
|
272
|
+
db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(nodeId);
|
|
273
|
+
return this.success(undefined);
|
|
274
|
+
});
|
|
260
275
|
}
|
|
261
276
|
async queryNodes(query) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
277
|
+
return this.run(async () => {
|
|
278
|
+
const db = this.ensureDb();
|
|
279
|
+
let sql = `SELECT * FROM graph_nodes WHERE agent_id = ?`;
|
|
280
|
+
const params = [query.agentId];
|
|
281
|
+
if (query.types && query.types.length > 0) {
|
|
282
|
+
const placeholders = query.types.map(() => '?').join(', ');
|
|
283
|
+
sql += ` AND type IN (${placeholders})`;
|
|
284
|
+
params.push(...query.types);
|
|
285
|
+
}
|
|
286
|
+
if (query.namePattern) {
|
|
287
|
+
sql += ` AND name LIKE ? ESCAPE '\\'`;
|
|
288
|
+
params.push(`%${query.namePattern.replace(/[%_\\]/g, '\\$&')}%`);
|
|
289
|
+
}
|
|
290
|
+
if (query.minConfidence !== undefined) {
|
|
291
|
+
sql += ` AND confidence >= ?`;
|
|
292
|
+
params.push(query.minConfidence);
|
|
293
|
+
}
|
|
294
|
+
sql += ' ORDER BY access_count DESC, updated_at DESC';
|
|
295
|
+
if (query.limit) {
|
|
296
|
+
sql += ` LIMIT ?`;
|
|
297
|
+
params.push(query.limit);
|
|
298
|
+
}
|
|
299
|
+
const rows = db.prepare(sql).all(...params);
|
|
300
|
+
return this.success(rows.map((row) => this.rowToNode(row, query.includeEmbedding)));
|
|
301
|
+
});
|
|
286
302
|
}
|
|
287
303
|
async searchNodesSemantic(options) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
const limit = options.limit ?? 10;
|
|
294
|
-
const threshold = options.threshold ?? 0.7;
|
|
295
|
-
let sql = `SELECT * FROM graph_nodes WHERE agent_id = ? AND embedding IS NOT NULL`;
|
|
296
|
-
const params = [options.agentId];
|
|
297
|
-
if (options.entityTypes && options.entityTypes.length > 0) {
|
|
298
|
-
const placeholders = options.entityTypes.map(() => '?').join(', ');
|
|
299
|
-
sql += ` AND type IN (${placeholders})`;
|
|
300
|
-
params.push(...options.entityTypes);
|
|
301
|
-
}
|
|
302
|
-
const rows = db.prepare(sql).all(...params);
|
|
303
|
-
const scored = [];
|
|
304
|
-
for (const row of rows) {
|
|
305
|
-
if (!row.embedding)
|
|
306
|
-
continue;
|
|
307
|
-
const emb = JSON.parse(row.embedding);
|
|
308
|
-
const score = this.cosineSimilarity(options.vector, emb);
|
|
309
|
-
if (score >= threshold) {
|
|
310
|
-
scored.push({ ...this.rowToNode(row), score });
|
|
304
|
+
return this.run(async () => {
|
|
305
|
+
const db = this.ensureDb();
|
|
306
|
+
if (!options.vector) {
|
|
307
|
+
return this.failure('searchNodesSemantic requires vector');
|
|
311
308
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
const limit = options.limit ?? 10;
|
|
310
|
+
const threshold = options.threshold ?? 0.7;
|
|
311
|
+
let sql = `SELECT * FROM graph_nodes WHERE agent_id = ? AND embedding IS NOT NULL`;
|
|
312
|
+
const params = [options.agentId];
|
|
313
|
+
if (options.entityTypes && options.entityTypes.length > 0) {
|
|
314
|
+
const placeholders = options.entityTypes.map(() => '?').join(', ');
|
|
315
|
+
sql += ` AND type IN (${placeholders})`;
|
|
316
|
+
params.push(...options.entityTypes);
|
|
317
|
+
}
|
|
318
|
+
const rows = db.prepare(sql).all(...params);
|
|
319
|
+
const scored = [];
|
|
320
|
+
for (const row of rows) {
|
|
321
|
+
if (!row.embedding)
|
|
322
|
+
continue;
|
|
323
|
+
const emb = JSON.parse(row.embedding);
|
|
324
|
+
const score = this.cosineSimilarity(options.vector, emb);
|
|
325
|
+
if (score >= threshold) {
|
|
326
|
+
scored.push({ ...this.rowToNode(row), score });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
scored.sort((a, b) => b.score - a.score);
|
|
330
|
+
return this.success(scored.slice(0, limit));
|
|
331
|
+
});
|
|
315
332
|
}
|
|
316
333
|
async addEdge(edge) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
334
|
+
return this.run(async () => {
|
|
335
|
+
const db = this.ensureDb();
|
|
336
|
+
const id = this.generateId('edge');
|
|
337
|
+
const now = new Date();
|
|
338
|
+
const nowStr = now.toISOString();
|
|
339
|
+
db.prepare(`INSERT INTO graph_edges
|
|
340
|
+
(id, agent_id, source_node_id, target_node_id, type, label, weight, bidirectional, properties, confidence, source, valid_from, valid_until, metadata, created_at, updated_at)
|
|
341
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, edge.agentId, edge.sourceNodeId, edge.targetNodeId, edge.type, edge.label ?? null, edge.weight, edge.bidirectional ? 1 : 0, JSON.stringify(edge.properties), edge.confidence, edge.source, edge.validFrom?.toISOString() ?? null, edge.validUntil?.toISOString() ?? null, JSON.stringify(edge.metadata ?? {}), nowStr, nowStr);
|
|
342
|
+
return this.success({
|
|
343
|
+
...edge,
|
|
344
|
+
id,
|
|
345
|
+
createdAt: now,
|
|
346
|
+
updatedAt: now,
|
|
347
|
+
});
|
|
330
348
|
});
|
|
331
349
|
}
|
|
332
350
|
async getEdge(edgeId) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
351
|
+
return this.run(async () => {
|
|
352
|
+
const db = this.ensureDb();
|
|
353
|
+
const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
|
|
354
|
+
if (!row)
|
|
355
|
+
return this.success(null);
|
|
356
|
+
return this.success(this.rowToEdge(row));
|
|
357
|
+
});
|
|
339
358
|
}
|
|
340
359
|
async getEdgesBetween(sourceNodeId, targetNodeId) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
360
|
+
return this.run(async () => {
|
|
361
|
+
const db = this.ensureDb();
|
|
362
|
+
const rows = db
|
|
363
|
+
.prepare(`SELECT * FROM graph_edges
|
|
364
|
+
WHERE (source_node_id = ? AND target_node_id = ?)
|
|
365
|
+
OR (bidirectional = 1 AND source_node_id = ? AND target_node_id = ?)`)
|
|
366
|
+
.all(sourceNodeId, targetNodeId, targetNodeId, sourceNodeId);
|
|
367
|
+
return this.success(rows.map((row) => this.rowToEdge(row)));
|
|
368
|
+
});
|
|
349
369
|
}
|
|
350
370
|
async updateEdge(edgeId, updates) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
371
|
+
return this.run(async () => {
|
|
372
|
+
const db = this.ensureDb();
|
|
373
|
+
const setClauses = ['updated_at = ?'];
|
|
374
|
+
const params = [new Date().toISOString()];
|
|
375
|
+
if (updates.weight !== undefined) {
|
|
376
|
+
setClauses.push('weight = ?');
|
|
377
|
+
params.push(updates.weight);
|
|
378
|
+
}
|
|
379
|
+
if (updates.label !== undefined) {
|
|
380
|
+
setClauses.push('label = ?');
|
|
381
|
+
params.push(updates.label);
|
|
382
|
+
}
|
|
383
|
+
if (updates.properties !== undefined) {
|
|
384
|
+
setClauses.push('properties = ?');
|
|
385
|
+
params.push(JSON.stringify(updates.properties));
|
|
386
|
+
}
|
|
387
|
+
if (updates.confidence !== undefined) {
|
|
388
|
+
setClauses.push('confidence = ?');
|
|
389
|
+
params.push(updates.confidence);
|
|
390
|
+
}
|
|
391
|
+
if (updates.validFrom !== undefined) {
|
|
392
|
+
setClauses.push('valid_from = ?');
|
|
393
|
+
params.push(updates.validFrom.toISOString());
|
|
394
|
+
}
|
|
395
|
+
if (updates.validUntil !== undefined) {
|
|
396
|
+
setClauses.push('valid_until = ?');
|
|
397
|
+
params.push(updates.validUntil.toISOString());
|
|
398
|
+
}
|
|
399
|
+
if (updates.metadata !== undefined) {
|
|
400
|
+
setClauses.push('metadata = ?');
|
|
401
|
+
params.push(JSON.stringify(updates.metadata));
|
|
402
|
+
}
|
|
403
|
+
params.push(edgeId);
|
|
404
|
+
const result = db
|
|
405
|
+
.prepare(`UPDATE graph_edges SET ${setClauses.join(', ')} WHERE id = ?`)
|
|
406
|
+
.run(...params);
|
|
407
|
+
if (result.changes === 0) {
|
|
408
|
+
return this.failure(`Edge not found: ${edgeId}`);
|
|
409
|
+
}
|
|
410
|
+
const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
|
|
411
|
+
return this.success(this.rowToEdge(row));
|
|
412
|
+
});
|
|
392
413
|
}
|
|
393
414
|
async deleteEdge(edgeId) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
415
|
+
return this.run(async () => {
|
|
416
|
+
const db = this.ensureDb();
|
|
417
|
+
db.prepare(`DELETE FROM graph_edges WHERE id = ?`).run(edgeId);
|
|
418
|
+
return this.success(undefined);
|
|
419
|
+
});
|
|
398
420
|
}
|
|
399
421
|
async queryEdges(query) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
422
|
+
return this.run(async () => {
|
|
423
|
+
const db = this.ensureDb();
|
|
424
|
+
let sql = `SELECT * FROM graph_edges WHERE agent_id = ?`;
|
|
425
|
+
const params = [query.agentId];
|
|
426
|
+
if (query.sourceNodeId) {
|
|
427
|
+
sql += ` AND source_node_id = ?`;
|
|
428
|
+
params.push(query.sourceNodeId);
|
|
429
|
+
}
|
|
430
|
+
if (query.targetNodeId) {
|
|
431
|
+
sql += ` AND target_node_id = ?`;
|
|
432
|
+
params.push(query.targetNodeId);
|
|
433
|
+
}
|
|
434
|
+
if (query.types && query.types.length > 0) {
|
|
435
|
+
const placeholders = query.types.map(() => '?').join(', ');
|
|
436
|
+
sql += ` AND type IN (${placeholders})`;
|
|
437
|
+
params.push(...query.types);
|
|
438
|
+
}
|
|
439
|
+
if (query.minWeight !== undefined) {
|
|
440
|
+
sql += ` AND weight >= ?`;
|
|
441
|
+
params.push(query.minWeight);
|
|
442
|
+
}
|
|
443
|
+
if (query.minConfidence !== undefined) {
|
|
444
|
+
sql += ` AND confidence >= ?`;
|
|
445
|
+
params.push(query.minConfidence);
|
|
446
|
+
}
|
|
447
|
+
if (query.bidirectionalOnly) {
|
|
448
|
+
sql += ' AND bidirectional = 1';
|
|
449
|
+
}
|
|
450
|
+
sql += ' ORDER BY weight DESC, confidence DESC';
|
|
451
|
+
if (query.limit) {
|
|
452
|
+
sql += ` LIMIT ?`;
|
|
453
|
+
params.push(query.limit);
|
|
454
|
+
}
|
|
455
|
+
const rows = db.prepare(sql).all(...params);
|
|
456
|
+
return this.success(rows.map((row) => this.rowToEdge(row)));
|
|
457
|
+
});
|
|
435
458
|
}
|
|
436
459
|
async traverse(options) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
460
|
+
return this.run(async () => {
|
|
461
|
+
const visited = new Set();
|
|
462
|
+
const visitedEdges = new Set();
|
|
463
|
+
const paths = [];
|
|
464
|
+
const allNodes = [];
|
|
465
|
+
const allEdges = [];
|
|
466
|
+
const startNode = unwrap(await this.getNode(options.startNodeId));
|
|
467
|
+
if (!startNode) {
|
|
468
|
+
return this.failure(`Start node not found: ${options.startNodeId}`);
|
|
469
|
+
}
|
|
470
|
+
visited.add(startNode.id);
|
|
471
|
+
allNodes.push(startNode);
|
|
472
|
+
await this.traverseRecursive(startNode, [], [], 0, options, visited, visitedEdges, paths, allNodes, allEdges);
|
|
473
|
+
let actualMaxDepth = 0;
|
|
474
|
+
for (const p of paths) {
|
|
475
|
+
if (p.length > actualMaxDepth)
|
|
476
|
+
actualMaxDepth = p.length;
|
|
477
|
+
}
|
|
478
|
+
return this.success({
|
|
479
|
+
paths,
|
|
480
|
+
visitedNodes: allNodes,
|
|
481
|
+
visitedEdges: allEdges,
|
|
482
|
+
depth: actualMaxDepth,
|
|
483
|
+
});
|
|
461
484
|
});
|
|
462
485
|
}
|
|
463
486
|
async traverseRecursive(currentNode, pathNodes, pathEdges, currentDepth, options, visited, visitedEdges, paths, allNodes, allEdges) {
|
|
@@ -474,11 +497,9 @@ export class SQLiteGraphAdapter {
|
|
|
474
497
|
}
|
|
475
498
|
if (options.limit && paths.length >= options.limit)
|
|
476
499
|
return;
|
|
477
|
-
const
|
|
478
|
-
if (!neighborsResult.success)
|
|
479
|
-
return;
|
|
500
|
+
const neighbors = unwrap(await this.getNeighbors(currentNode.id, options.direction));
|
|
480
501
|
let hasUnvisitedNeighbors = false;
|
|
481
|
-
for (const { node, edge } of
|
|
502
|
+
for (const { node, edge } of neighbors) {
|
|
482
503
|
if (options.edgeTypes && !options.edgeTypes.includes(edge.type))
|
|
483
504
|
continue;
|
|
484
505
|
if (options.minEdgeWeight !== undefined && edge.weight < options.minEdgeWeight)
|
|
@@ -506,188 +527,191 @@ export class SQLiteGraphAdapter {
|
|
|
506
527
|
}
|
|
507
528
|
}
|
|
508
529
|
async findShortestPath(_agentId, startNodeId, endNodeId, maxDepth = 5) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
continue;
|
|
527
|
-
const neighborsResult = await this.getNeighbors(current.nodeId, 'both');
|
|
528
|
-
if (!neighborsResult.success)
|
|
529
|
-
continue;
|
|
530
|
-
for (const { node, edge } of neighborsResult.data) {
|
|
531
|
-
if (visited.has(node.id))
|
|
530
|
+
return this.run(async () => {
|
|
531
|
+
if (startNodeId === endNodeId) {
|
|
532
|
+
const node = unwrap(await this.getNode(startNodeId));
|
|
533
|
+
if (!node)
|
|
534
|
+
return this.success(null);
|
|
535
|
+
return this.success({
|
|
536
|
+
nodes: [node],
|
|
537
|
+
edges: [],
|
|
538
|
+
totalWeight: 0,
|
|
539
|
+
length: 0,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
const queue = [{ nodeId: startNodeId, pathNodeIds: [startNodeId], pathEdgeIds: [], totalWeight: 0 }];
|
|
543
|
+
const visited = new Set([startNodeId]);
|
|
544
|
+
while (queue.length > 0) {
|
|
545
|
+
const current = queue.shift();
|
|
546
|
+
if (current.pathEdgeIds.length >= maxDepth)
|
|
532
547
|
continue;
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
548
|
+
const neighbors = unwrap(await this.getNeighbors(current.nodeId, 'both'));
|
|
549
|
+
for (const { node, edge } of neighbors) {
|
|
550
|
+
if (visited.has(node.id))
|
|
551
|
+
continue;
|
|
552
|
+
const newPath = {
|
|
553
|
+
nodeId: node.id,
|
|
554
|
+
pathNodeIds: [...current.pathNodeIds, node.id],
|
|
555
|
+
pathEdgeIds: [...current.pathEdgeIds, edge.id],
|
|
556
|
+
totalWeight: current.totalWeight + edge.weight,
|
|
557
|
+
};
|
|
558
|
+
if (node.id === endNodeId) {
|
|
559
|
+
const nodes = [];
|
|
560
|
+
for (const nid of newPath.pathNodeIds) {
|
|
561
|
+
const pathNode = unwrap(await this.getNode(nid));
|
|
562
|
+
if (pathNode)
|
|
563
|
+
nodes.push(pathNode);
|
|
564
|
+
}
|
|
565
|
+
const edges = [];
|
|
566
|
+
for (const eid of newPath.pathEdgeIds) {
|
|
567
|
+
const pathEdge = unwrap(await this.getEdge(eid));
|
|
568
|
+
if (pathEdge)
|
|
569
|
+
edges.push(pathEdge);
|
|
570
|
+
}
|
|
571
|
+
return this.success({
|
|
572
|
+
nodes,
|
|
573
|
+
edges,
|
|
574
|
+
totalWeight: newPath.totalWeight,
|
|
575
|
+
length: edges.length,
|
|
576
|
+
});
|
|
545
577
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const er = await this.getEdge(eid);
|
|
549
|
-
if (er.success && er.data)
|
|
550
|
-
edges.push(er.data);
|
|
551
|
-
}
|
|
552
|
-
return this.success({
|
|
553
|
-
nodes,
|
|
554
|
-
edges,
|
|
555
|
-
totalWeight: newPath.totalWeight,
|
|
556
|
-
length: edges.length,
|
|
557
|
-
});
|
|
578
|
+
visited.add(node.id);
|
|
579
|
+
queue.push(newPath);
|
|
558
580
|
}
|
|
559
|
-
visited.add(node.id);
|
|
560
|
-
queue.push(newPath);
|
|
561
581
|
}
|
|
562
|
-
|
|
563
|
-
|
|
582
|
+
return this.success(null);
|
|
583
|
+
});
|
|
564
584
|
}
|
|
565
585
|
async getNeighbors(nodeId, direction = 'both') {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
`;
|
|
579
|
-
let sql;
|
|
580
|
-
if (direction === 'outgoing') {
|
|
581
|
-
sql = `
|
|
582
|
-
SELECT ${selectCols}
|
|
583
|
-
FROM graph_edges e
|
|
584
|
-
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
585
|
-
WHERE e.source_node_id = ?
|
|
586
|
-
UNION ALL
|
|
587
|
-
SELECT ${selectCols}
|
|
588
|
-
FROM graph_edges e
|
|
589
|
-
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
590
|
-
WHERE e.target_node_id = ? AND e.bidirectional = 1
|
|
591
|
-
`;
|
|
592
|
-
}
|
|
593
|
-
else if (direction === 'incoming') {
|
|
594
|
-
sql = `
|
|
595
|
-
SELECT ${selectCols}
|
|
596
|
-
FROM graph_edges e
|
|
597
|
-
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
598
|
-
WHERE e.target_node_id = ?
|
|
599
|
-
UNION ALL
|
|
600
|
-
SELECT ${selectCols}
|
|
601
|
-
FROM graph_edges e
|
|
602
|
-
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
603
|
-
WHERE e.source_node_id = ? AND e.bidirectional = 1
|
|
604
|
-
`;
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
sql = `
|
|
608
|
-
SELECT ${selectCols}
|
|
609
|
-
FROM graph_edges e
|
|
610
|
-
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
611
|
-
WHERE e.source_node_id = ?
|
|
612
|
-
UNION ALL
|
|
613
|
-
SELECT ${selectCols}
|
|
614
|
-
FROM graph_edges e
|
|
615
|
-
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
616
|
-
WHERE e.target_node_id = ?
|
|
586
|
+
return this.run(async () => {
|
|
587
|
+
const db = this.ensureDb();
|
|
588
|
+
const selectCols = `
|
|
589
|
+
n.id, n.agent_id, n.type, n.name, n.aliases, n.description,
|
|
590
|
+
n.properties, n.embedding, n.confidence, n.source, n.metadata,
|
|
591
|
+
n.created_at, n.updated_at, n.last_accessed_at, n.access_count,
|
|
592
|
+
e.id as edge_id, e.agent_id as edge_agent_id, e.type as edge_type,
|
|
593
|
+
e.source_node_id as edge_source_node_id, e.target_node_id as edge_target_node_id,
|
|
594
|
+
e.label as edge_label, e.weight as edge_weight, e.bidirectional as edge_bidirectional,
|
|
595
|
+
e.properties as edge_properties, e.confidence as edge_confidence,
|
|
596
|
+
e.source as edge_source, e.valid_from as edge_valid_from, e.valid_until as edge_valid_until,
|
|
597
|
+
e.metadata as edge_metadata, e.created_at as edge_created_at, e.updated_at as edge_updated_at
|
|
617
598
|
`;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
599
|
+
let sql;
|
|
600
|
+
if (direction === 'outgoing') {
|
|
601
|
+
sql = `
|
|
602
|
+
SELECT ${selectCols}
|
|
603
|
+
FROM graph_edges e
|
|
604
|
+
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
605
|
+
WHERE e.source_node_id = ?
|
|
606
|
+
UNION ALL
|
|
607
|
+
SELECT ${selectCols}
|
|
608
|
+
FROM graph_edges e
|
|
609
|
+
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
610
|
+
WHERE e.target_node_id = ? AND e.bidirectional = 1
|
|
611
|
+
`;
|
|
612
|
+
}
|
|
613
|
+
else if (direction === 'incoming') {
|
|
614
|
+
sql = `
|
|
615
|
+
SELECT ${selectCols}
|
|
616
|
+
FROM graph_edges e
|
|
617
|
+
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
618
|
+
WHERE e.target_node_id = ?
|
|
619
|
+
UNION ALL
|
|
620
|
+
SELECT ${selectCols}
|
|
621
|
+
FROM graph_edges e
|
|
622
|
+
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
623
|
+
WHERE e.source_node_id = ? AND e.bidirectional = 1
|
|
624
|
+
`;
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
sql = `
|
|
628
|
+
SELECT ${selectCols}
|
|
629
|
+
FROM graph_edges e
|
|
630
|
+
JOIN graph_nodes n ON n.id = e.target_node_id
|
|
631
|
+
WHERE e.source_node_id = ?
|
|
632
|
+
UNION ALL
|
|
633
|
+
SELECT ${selectCols}
|
|
634
|
+
FROM graph_edges e
|
|
635
|
+
JOIN graph_nodes n ON n.id = e.source_node_id
|
|
636
|
+
WHERE e.target_node_id = ?
|
|
637
|
+
`;
|
|
638
|
+
}
|
|
639
|
+
const rows = db.prepare(sql).all(nodeId, nodeId);
|
|
640
|
+
return this.success(rows.map((row) => ({
|
|
641
|
+
node: this.rowToNode(row),
|
|
642
|
+
edge: this.neighborRowToEdge(row),
|
|
643
|
+
})));
|
|
644
|
+
});
|
|
624
645
|
}
|
|
625
646
|
async mergeNodes(targetNodeId, sourceNodeIds) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
647
|
+
return this.run(async () => {
|
|
648
|
+
const db = this.ensureDb();
|
|
649
|
+
for (const sourceId of sourceNodeIds) {
|
|
650
|
+
const sourceNode = unwrap(await this.getNode(sourceId));
|
|
651
|
+
db.prepare(`UPDATE graph_edges SET source_node_id = ? WHERE source_node_id = ?`).run(targetNodeId, sourceId);
|
|
652
|
+
db.prepare(`UPDATE graph_edges SET target_node_id = ? WHERE target_node_id = ?`).run(targetNodeId, sourceId);
|
|
653
|
+
db.prepare(`DELETE FROM graph_edges WHERE source_node_id = ? AND target_node_id = ?`).run(targetNodeId, targetNodeId);
|
|
654
|
+
if (sourceNode) {
|
|
655
|
+
const targetRow = db
|
|
656
|
+
.prepare(`SELECT aliases FROM graph_nodes WHERE id = ?`)
|
|
657
|
+
.get(targetNodeId);
|
|
658
|
+
if (targetRow) {
|
|
659
|
+
const currentAliases = JSON.parse(targetRow.aliases);
|
|
660
|
+
const newAliases = [...currentAliases, sourceNode.name, ...sourceNode.aliases];
|
|
661
|
+
const uniqueAliases = [...new Set(newAliases)];
|
|
662
|
+
db.prepare(`UPDATE graph_nodes SET aliases = ? WHERE id = ?`).run(JSON.stringify(uniqueAliases), targetNodeId);
|
|
663
|
+
}
|
|
642
664
|
}
|
|
665
|
+
db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(sourceId);
|
|
643
666
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
return this.
|
|
649
|
-
}
|
|
650
|
-
return this.success(mergedNode.data);
|
|
667
|
+
const mergedNode = unwrap(await this.getNode(targetNodeId));
|
|
668
|
+
if (!mergedNode) {
|
|
669
|
+
return this.failure(`Target node not found: ${targetNodeId}`);
|
|
670
|
+
}
|
|
671
|
+
return this.success(mergedNode);
|
|
672
|
+
});
|
|
651
673
|
}
|
|
652
674
|
async clearGraph(agentId) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
675
|
+
return this.run(async () => {
|
|
676
|
+
const db = this.ensureDb();
|
|
677
|
+
db.prepare(`DELETE FROM graph_edges WHERE agent_id = ?`).run(agentId);
|
|
678
|
+
db.prepare(`DELETE FROM graph_nodes WHERE agent_id = ?`).run(agentId);
|
|
679
|
+
return this.success(undefined);
|
|
680
|
+
});
|
|
658
681
|
}
|
|
659
682
|
async getGraphStats(agentId) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
683
|
+
return this.run(async () => {
|
|
684
|
+
const db = this.ensureDb();
|
|
685
|
+
const nodeCountRow = db
|
|
686
|
+
.prepare(`SELECT COUNT(*) as count FROM graph_nodes WHERE agent_id = ?`)
|
|
687
|
+
.get(agentId);
|
|
688
|
+
const edgeCountRow = db
|
|
689
|
+
.prepare(`SELECT COUNT(*) as count FROM graph_edges WHERE agent_id = ?`)
|
|
690
|
+
.get(agentId);
|
|
691
|
+
const nodesByTypeRows = db
|
|
692
|
+
.prepare(`SELECT type, COUNT(*) as count FROM graph_nodes WHERE agent_id = ? GROUP BY type`)
|
|
693
|
+
.all(agentId);
|
|
694
|
+
const edgesByTypeRows = db
|
|
695
|
+
.prepare(`SELECT type, COUNT(*) as count FROM graph_edges WHERE agent_id = ? GROUP BY type`)
|
|
696
|
+
.all(agentId);
|
|
697
|
+
const nodeCount = nodeCountRow.count;
|
|
698
|
+
const edgeCount = edgeCountRow.count;
|
|
699
|
+
const nodesByType = {};
|
|
700
|
+
for (const row of nodesByTypeRows) {
|
|
701
|
+
nodesByType[row.type] = row.count;
|
|
702
|
+
}
|
|
703
|
+
const edgesByType = {};
|
|
704
|
+
for (const row of edgesByTypeRows) {
|
|
705
|
+
edgesByType[row.type] = row.count;
|
|
706
|
+
}
|
|
707
|
+
return this.success({
|
|
708
|
+
nodeCount,
|
|
709
|
+
edgeCount,
|
|
710
|
+
nodesByType: nodesByType,
|
|
711
|
+
edgesByType: edgesByType,
|
|
712
|
+
averageEdgesPerNode: nodeCount > 0 ? edgeCount / nodeCount : 0,
|
|
713
|
+
maxDepth: 0,
|
|
714
|
+
});
|
|
691
715
|
});
|
|
692
716
|
}
|
|
693
717
|
}
|