cozo-memory 1.1.4 → 1.1.6
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 +161 -1159
- package/dist/adaptive-query-fusion.js +397 -0
- package/dist/dynamic-fusion.js +63 -8
- package/dist/explainable-retrieval.js +552 -0
- package/dist/hierarchical-memory.js +358 -0
- package/dist/proactive-suggestions.js +382 -0
- package/dist/temporal-conflict-resolution.js +386 -0
- package/dist/temporal-pattern-detection-backup.js +358 -0
- package/dist/temporal-pattern-detection.js +482 -0
- package/dist/test-adaptive-query-fusion.js +208 -0
- package/dist/test-explainable-retrieval.js +408 -0
- package/dist/test-hierarchical-and-patterns.js +17 -0
- package/dist/test-hierarchical-memory.js +205 -0
- package/dist/test-proactive-suggestions.js +240 -0
- package/dist/test-temporal-conflict-resolution.js +228 -0
- package/dist/test-temporal-patterns.js +317 -0
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProactiveSuggestionsService = exports.ConfidenceLevel = exports.SuggestionSource = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Suggestion source type
|
|
6
|
+
*/
|
|
7
|
+
var SuggestionSource;
|
|
8
|
+
(function (SuggestionSource) {
|
|
9
|
+
SuggestionSource["VECTOR_SIMILARITY"] = "vector_similarity";
|
|
10
|
+
SuggestionSource["COMMON_NEIGHBORS"] = "common_neighbors";
|
|
11
|
+
SuggestionSource["INFERENCE"] = "inference";
|
|
12
|
+
SuggestionSource["GRAPH_PROXIMITY"] = "graph_proximity";
|
|
13
|
+
})(SuggestionSource || (exports.SuggestionSource = SuggestionSource = {}));
|
|
14
|
+
/**
|
|
15
|
+
* Suggestion confidence level
|
|
16
|
+
*/
|
|
17
|
+
var ConfidenceLevel;
|
|
18
|
+
(function (ConfidenceLevel) {
|
|
19
|
+
ConfidenceLevel["HIGH"] = "high";
|
|
20
|
+
ConfidenceLevel["MEDIUM"] = "medium";
|
|
21
|
+
ConfidenceLevel["LOW"] = "low";
|
|
22
|
+
})(ConfidenceLevel || (exports.ConfidenceLevel = ConfidenceLevel = {}));
|
|
23
|
+
/**
|
|
24
|
+
* ProactiveSuggestionsService
|
|
25
|
+
*
|
|
26
|
+
* Provides intelligent relationship discovery and connection suggestions
|
|
27
|
+
* using multiple strategies:
|
|
28
|
+
* 1. Vector Similarity - Find semantically similar entities
|
|
29
|
+
* 2. Common Neighbors - Find entities sharing connections
|
|
30
|
+
* 3. Inference Engine - Discover implicit relationships
|
|
31
|
+
* 4. Graph Proximity - Find nearby entities in the knowledge graph
|
|
32
|
+
*/
|
|
33
|
+
class ProactiveSuggestionsService {
|
|
34
|
+
db;
|
|
35
|
+
embeddings;
|
|
36
|
+
config;
|
|
37
|
+
constructor(db, embeddings, config = {}) {
|
|
38
|
+
this.db = db;
|
|
39
|
+
this.embeddings = embeddings;
|
|
40
|
+
this.config = {
|
|
41
|
+
maxSuggestions: config.maxSuggestions ?? 10,
|
|
42
|
+
minConfidence: config.minConfidence ?? 0.5,
|
|
43
|
+
enableVectorSimilarity: config.enableVectorSimilarity ?? true,
|
|
44
|
+
enableCommonNeighbors: config.enableCommonNeighbors ?? true,
|
|
45
|
+
enableInference: config.enableInference ?? true,
|
|
46
|
+
enableGraphProximity: config.enableGraphProximity ?? true,
|
|
47
|
+
vectorSimilarityWeight: config.vectorSimilarityWeight ?? 0.35,
|
|
48
|
+
commonNeighborsWeight: config.commonNeighborsWeight ?? 0.25,
|
|
49
|
+
inferenceWeight: config.inferenceWeight ?? 0.25,
|
|
50
|
+
graphProximityWeight: config.graphProximityWeight ?? 0.15,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Suggest connections for an entity
|
|
55
|
+
* Combines multiple discovery strategies with weighted scoring
|
|
56
|
+
*/
|
|
57
|
+
async suggestConnections(entityId) {
|
|
58
|
+
try {
|
|
59
|
+
const entity = await this.db.getEntity(entityId);
|
|
60
|
+
if (!entity) {
|
|
61
|
+
console.error(`[ProactiveSuggestions] Entity not found: ${entityId}`);
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const suggestions = new Map();
|
|
65
|
+
// Strategy 1: Vector Similarity
|
|
66
|
+
if (this.config.enableVectorSimilarity) {
|
|
67
|
+
const vectorSuggestions = await this.findVectorSimilarities(entity);
|
|
68
|
+
vectorSuggestions.forEach(s => {
|
|
69
|
+
const key = s.entity_id;
|
|
70
|
+
if (!suggestions.has(key)) {
|
|
71
|
+
suggestions.set(key, s);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const existing = suggestions.get(key);
|
|
75
|
+
existing.confidence = Math.max(existing.confidence, s.confidence * this.config.vectorSimilarityWeight);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Strategy 2: Common Neighbors
|
|
80
|
+
if (this.config.enableCommonNeighbors) {
|
|
81
|
+
const commonNeighborSuggestions = await this.findCommonNeighbors(entityId);
|
|
82
|
+
commonNeighborSuggestions.forEach(s => {
|
|
83
|
+
const key = s.entity_id;
|
|
84
|
+
if (!suggestions.has(key)) {
|
|
85
|
+
suggestions.set(key, s);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const existing = suggestions.get(key);
|
|
89
|
+
existing.confidence = Math.max(existing.confidence, s.confidence * this.config.commonNeighborsWeight);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Strategy 3: Graph Proximity
|
|
94
|
+
if (this.config.enableGraphProximity) {
|
|
95
|
+
const graphProximitySuggestions = await this.findGraphProximity(entityId);
|
|
96
|
+
graphProximitySuggestions.forEach(s => {
|
|
97
|
+
const key = s.entity_id;
|
|
98
|
+
if (!suggestions.has(key)) {
|
|
99
|
+
suggestions.set(key, s);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const existing = suggestions.get(key);
|
|
103
|
+
existing.confidence = Math.max(existing.confidence, s.confidence * this.config.graphProximityWeight);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Strategy 4: Inference Engine
|
|
108
|
+
if (this.config.enableInference) {
|
|
109
|
+
const inferenceSuggestions = await this.findInferredRelations(entityId);
|
|
110
|
+
inferenceSuggestions.forEach(s => {
|
|
111
|
+
const key = s.entity_id;
|
|
112
|
+
if (!suggestions.has(key)) {
|
|
113
|
+
suggestions.set(key, s);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const existing = suggestions.get(key);
|
|
117
|
+
existing.confidence = Math.max(existing.confidence, s.confidence * this.config.inferenceWeight);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Filter by minimum confidence and sort
|
|
122
|
+
const results = Array.from(suggestions.values())
|
|
123
|
+
.filter(s => s.confidence >= this.config.minConfidence)
|
|
124
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
125
|
+
.slice(0, this.config.maxSuggestions)
|
|
126
|
+
.map(s => ({
|
|
127
|
+
...s,
|
|
128
|
+
confidence_level: this.getConfidenceLevel(s.confidence),
|
|
129
|
+
}));
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error('[ProactiveSuggestions] Error suggesting connections:', error);
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Find semantically similar entities using vector search
|
|
139
|
+
*/
|
|
140
|
+
async findVectorSimilarities(entity) {
|
|
141
|
+
try {
|
|
142
|
+
const results = await this.db.vectorSearchEntity(entity.embedding, this.config.maxSuggestions * 2);
|
|
143
|
+
return results
|
|
144
|
+
.filter((row) => row[0] !== entity.id) // Exclude self
|
|
145
|
+
.slice(0, this.config.maxSuggestions)
|
|
146
|
+
.map((row, index) => ({
|
|
147
|
+
entity_id: row[0],
|
|
148
|
+
entity_name: row[1],
|
|
149
|
+
entity_type: row[2],
|
|
150
|
+
source: SuggestionSource.VECTOR_SIMILARITY,
|
|
151
|
+
confidence: Math.max(0.5, 1 - (index * 0.1)), // Decay confidence by rank
|
|
152
|
+
confidence_level: ConfidenceLevel.MEDIUM,
|
|
153
|
+
reason: `Semantically similar to "${entity.name}" based on vector embeddings`,
|
|
154
|
+
metadata: {
|
|
155
|
+
similarity_rank: index + 1,
|
|
156
|
+
},
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error('[ProactiveSuggestions] Error in vector similarity search:', error);
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Find entities that share common neighbors (connections)
|
|
166
|
+
* Entities with many shared neighbors are likely related
|
|
167
|
+
*/
|
|
168
|
+
async findCommonNeighbors(entityId) {
|
|
169
|
+
try {
|
|
170
|
+
// Get all neighbors of the entity
|
|
171
|
+
const directRelations = await this.db.getRelations(entityId);
|
|
172
|
+
const neighborIds = new Set(directRelations.map(r => r.to_id));
|
|
173
|
+
if (neighborIds.size === 0) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
// For each neighbor, find their other connections
|
|
177
|
+
const commonNeighborCounts = new Map();
|
|
178
|
+
for (const neighborId of neighborIds) {
|
|
179
|
+
const neighborRelations = await this.db.getRelations(neighborId);
|
|
180
|
+
for (const rel of neighborRelations) {
|
|
181
|
+
if (rel.to_id !== entityId && !neighborIds.has(rel.to_id)) {
|
|
182
|
+
const key = rel.to_id;
|
|
183
|
+
if (!commonNeighborCounts.has(key)) {
|
|
184
|
+
commonNeighborCounts.set(key, { count: 0, relationTypes: new Set() });
|
|
185
|
+
}
|
|
186
|
+
const entry = commonNeighborCounts.get(key);
|
|
187
|
+
entry.count++;
|
|
188
|
+
entry.relationTypes.add(rel.relation_type);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Convert to suggestions, sorted by common neighbor count
|
|
193
|
+
const suggestions = [];
|
|
194
|
+
for (const [targetId, data] of commonNeighborCounts.entries()) {
|
|
195
|
+
const targetEntity = await this.db.getEntity(targetId);
|
|
196
|
+
if (targetEntity) {
|
|
197
|
+
const confidence = Math.min(0.95, 0.5 + (data.count * 0.15)); // Confidence increases with shared neighbors
|
|
198
|
+
suggestions.push({
|
|
199
|
+
entity_id: targetId,
|
|
200
|
+
entity_name: targetEntity.name,
|
|
201
|
+
entity_type: targetEntity.type,
|
|
202
|
+
source: SuggestionSource.COMMON_NEIGHBORS,
|
|
203
|
+
confidence: confidence,
|
|
204
|
+
confidence_level: ConfidenceLevel.MEDIUM,
|
|
205
|
+
reason: `Shares ${data.count} common connection(s) with "${(await this.db.getEntity(entityId))?.name || 'entity'}"`,
|
|
206
|
+
metadata: {
|
|
207
|
+
common_neighbors_count: data.count,
|
|
208
|
+
relation_types: Array.from(data.relationTypes),
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return suggestions.sort((a, b) => b.confidence - a.confidence).slice(0, this.config.maxSuggestions);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.error('[ProactiveSuggestions] Error finding common neighbors:', error);
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Find entities within graph proximity (N hops away)
|
|
222
|
+
* Closer entities get higher confidence scores
|
|
223
|
+
*/
|
|
224
|
+
async findGraphProximity(entityId, maxHops = 2) {
|
|
225
|
+
try {
|
|
226
|
+
const visited = new Set();
|
|
227
|
+
const suggestions = new Map();
|
|
228
|
+
const queue = [{ id: entityId, hops: 0 }];
|
|
229
|
+
visited.add(entityId);
|
|
230
|
+
while (queue.length > 0) {
|
|
231
|
+
const { id, hops } = queue.shift();
|
|
232
|
+
if (hops > maxHops)
|
|
233
|
+
continue;
|
|
234
|
+
const relations = await this.db.getRelations(id);
|
|
235
|
+
for (const rel of relations) {
|
|
236
|
+
if (!visited.has(rel.to_id)) {
|
|
237
|
+
visited.add(rel.to_id);
|
|
238
|
+
const targetEntity = await this.db.getEntity(rel.to_id);
|
|
239
|
+
if (targetEntity && rel.to_id !== entityId) {
|
|
240
|
+
const hopDistance = hops + 1;
|
|
241
|
+
if (!suggestions.has(rel.to_id) || suggestions.get(rel.to_id).hops > hopDistance) {
|
|
242
|
+
suggestions.set(rel.to_id, { entity: targetEntity, hops: hopDistance });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (hops < maxHops) {
|
|
246
|
+
queue.push({ id: rel.to_id, hops: hops + 1 });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Convert to suggestions, with confidence inversely proportional to hops
|
|
252
|
+
const results = [];
|
|
253
|
+
for (const [targetId, data] of suggestions.entries()) {
|
|
254
|
+
const confidence = Math.max(0.5, 1 - (data.hops * 0.25)); // Decay by hops
|
|
255
|
+
results.push({
|
|
256
|
+
entity_id: targetId,
|
|
257
|
+
entity_name: data.entity.name,
|
|
258
|
+
entity_type: data.entity.type,
|
|
259
|
+
source: SuggestionSource.GRAPH_PROXIMITY,
|
|
260
|
+
confidence: confidence,
|
|
261
|
+
confidence_level: ConfidenceLevel.MEDIUM,
|
|
262
|
+
reason: `Located ${data.hops} hop(s) away in the knowledge graph`,
|
|
263
|
+
metadata: {
|
|
264
|
+
hops: data.hops,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return results.sort((a, b) => b.confidence - a.confidence).slice(0, this.config.maxSuggestions);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
console.error('[ProactiveSuggestions] Error finding graph proximity:', error);
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Find inferred relationships using the inference engine
|
|
277
|
+
* Applies logical rules to discover implicit connections
|
|
278
|
+
*/
|
|
279
|
+
async findInferredRelations(entityId) {
|
|
280
|
+
try {
|
|
281
|
+
// Get direct relations
|
|
282
|
+
const directRelations = await this.db.getRelations(entityId);
|
|
283
|
+
const suggestions = [];
|
|
284
|
+
// Rule 1: Transitive relations (A -> B -> C implies A -> C)
|
|
285
|
+
for (const rel of directRelations) {
|
|
286
|
+
const secondHopRelations = await this.db.getRelations(rel.to_id);
|
|
287
|
+
for (const secondRel of secondHopRelations) {
|
|
288
|
+
if (secondRel.to_id !== entityId) {
|
|
289
|
+
const targetEntity = await this.db.getEntity(secondRel.to_id);
|
|
290
|
+
if (targetEntity) {
|
|
291
|
+
suggestions.push({
|
|
292
|
+
entity_id: secondRel.to_id,
|
|
293
|
+
entity_name: targetEntity.name,
|
|
294
|
+
entity_type: targetEntity.type,
|
|
295
|
+
relation_type: `${rel.relation_type}_via_${secondRel.relation_type}`,
|
|
296
|
+
source: SuggestionSource.INFERENCE,
|
|
297
|
+
confidence: 0.7 * rel.strength * secondRel.strength, // Confidence based on relation strengths
|
|
298
|
+
confidence_level: ConfidenceLevel.MEDIUM,
|
|
299
|
+
reason: `Inferred transitive relationship: ${rel.relation_type} -> ${secondRel.relation_type}`,
|
|
300
|
+
metadata: {
|
|
301
|
+
inference_type: 'transitive',
|
|
302
|
+
intermediate_entity: rel.to_id,
|
|
303
|
+
combined_strength: rel.strength * secondRel.strength,
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Rule 2: Symmetric relations (if A -> B with "colleague", suggest B -> A)
|
|
311
|
+
for (const rel of directRelations) {
|
|
312
|
+
if (['colleague_of', 'related_to', 'similar_to', 'connected_to'].includes(rel.relation_type)) {
|
|
313
|
+
const targetEntity = await this.db.getEntity(rel.to_id);
|
|
314
|
+
if (targetEntity) {
|
|
315
|
+
suggestions.push({
|
|
316
|
+
entity_id: rel.to_id,
|
|
317
|
+
entity_name: targetEntity.name,
|
|
318
|
+
entity_type: targetEntity.type,
|
|
319
|
+
relation_type: rel.relation_type,
|
|
320
|
+
source: SuggestionSource.INFERENCE,
|
|
321
|
+
confidence: 0.8 * rel.strength,
|
|
322
|
+
confidence_level: ConfidenceLevel.MEDIUM,
|
|
323
|
+
reason: `Inferred symmetric relationship: ${rel.relation_type} (bidirectional)`,
|
|
324
|
+
metadata: {
|
|
325
|
+
inference_type: 'symmetric',
|
|
326
|
+
original_strength: rel.strength,
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Deduplicate and sort
|
|
333
|
+
const deduped = new Map();
|
|
334
|
+
for (const s of suggestions) {
|
|
335
|
+
const key = `${s.entity_id}_${s.relation_type}`;
|
|
336
|
+
if (!deduped.has(key) || deduped.get(key).confidence < s.confidence) {
|
|
337
|
+
deduped.set(key, s);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return Array.from(deduped.values())
|
|
341
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
342
|
+
.slice(0, this.config.maxSuggestions);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.error('[ProactiveSuggestions] Error finding inferred relations:', error);
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Convert numeric confidence to confidence level
|
|
351
|
+
*/
|
|
352
|
+
getConfidenceLevel(confidence) {
|
|
353
|
+
if (confidence >= 0.75)
|
|
354
|
+
return ConfidenceLevel.HIGH;
|
|
355
|
+
if (confidence >= 0.5)
|
|
356
|
+
return ConfidenceLevel.MEDIUM;
|
|
357
|
+
return ConfidenceLevel.LOW;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get suggestions for multiple entities
|
|
361
|
+
*/
|
|
362
|
+
async suggestConnectionsBatch(entityIds) {
|
|
363
|
+
const results = new Map();
|
|
364
|
+
for (const entityId of entityIds) {
|
|
365
|
+
results.set(entityId, await this.suggestConnections(entityId));
|
|
366
|
+
}
|
|
367
|
+
return results;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Update configuration
|
|
371
|
+
*/
|
|
372
|
+
updateConfig(config) {
|
|
373
|
+
this.config = { ...this.config, ...config };
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Get current configuration
|
|
377
|
+
*/
|
|
378
|
+
getConfig() {
|
|
379
|
+
return { ...this.config };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
exports.ProactiveSuggestionsService = ProactiveSuggestionsService;
|