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.
@@ -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;