cozo-memory 1.1.3 → 1.1.4
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 +330 -4
- package/dist/adaptive-retrieval.js +520 -0
- package/dist/dynamic-fusion.js +602 -0
- package/dist/index.js +386 -6
- package/dist/logical-edges-service.js +316 -0
- package/dist/multi-hop-vector-pivot.js +390 -0
- package/dist/temporal-embedding-service.js +313 -0
- package/dist/test-adaptive-integration.js +84 -0
- package/dist/test-adaptive-retrieval.js +135 -0
- package/dist/test-dynamic-fusion.js +231 -0
- package/dist/test-logical-edges.js +282 -0
- package/dist/test-multi-hop-vector-pivot-v2.js +239 -0
- package/dist/test-multi-hop-vector-pivot.js +240 -0
- package/dist/test-temporal-embeddings.js +123 -0
- package/package.json +1 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogicalEdgesService = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Logical Edges Service (v1.0)
|
|
6
|
+
*
|
|
7
|
+
* Discovers implicit relationships from entity metadata using logical inference rules.
|
|
8
|
+
*
|
|
9
|
+
* Research Foundation:
|
|
10
|
+
* - SAGE (ICLR 2026): Implicit graph exploration with on-demand edge discovery
|
|
11
|
+
* - Metadata Knowledge Graphs (Atlan 2026): Metadata-driven relationship inference
|
|
12
|
+
* - Knowledge Graph Completion (Frontiers 2025): Predicting implicit relationships
|
|
13
|
+
*
|
|
14
|
+
* Patterns:
|
|
15
|
+
* 1. **Metadata-Based Edges**: Same category, type, domain, etc.
|
|
16
|
+
* 2. **Semantic Edges**: Entities with similar metadata patterns
|
|
17
|
+
* 3. **Hierarchical Edges**: Parent-child relationships from metadata
|
|
18
|
+
* 4. **Contextual Edges**: Time-based, location-based, or domain-based grouping
|
|
19
|
+
* 5. **Transitive Edges**: Derived from existing relationships + metadata
|
|
20
|
+
*/
|
|
21
|
+
class LogicalEdgesService {
|
|
22
|
+
db;
|
|
23
|
+
constructor(db) {
|
|
24
|
+
this.db = db;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Discover all logical edges for an entity based on metadata patterns
|
|
28
|
+
*/
|
|
29
|
+
async discoverLogicalEdges(entityId) {
|
|
30
|
+
const edges = [];
|
|
31
|
+
// 1. Same Category Edges
|
|
32
|
+
const categoryEdges = await this.findSameCategoryEdges(entityId);
|
|
33
|
+
edges.push(...categoryEdges);
|
|
34
|
+
// 2. Same Type Edges
|
|
35
|
+
const typeEdges = await this.findSameTypeEdges(entityId);
|
|
36
|
+
edges.push(...typeEdges);
|
|
37
|
+
// 3. Hierarchical Edges (parent-child from metadata)
|
|
38
|
+
const hierarchicalEdges = await this.findHierarchicalEdges(entityId);
|
|
39
|
+
edges.push(...hierarchicalEdges);
|
|
40
|
+
// 4. Contextual Edges (domain, time period, location)
|
|
41
|
+
const contextualEdges = await this.findContextualEdges(entityId);
|
|
42
|
+
edges.push(...contextualEdges);
|
|
43
|
+
// 5. Transitive Edges (derived from relationships + metadata)
|
|
44
|
+
const transitiveEdges = await this.findTransitiveLogicalEdges(entityId);
|
|
45
|
+
edges.push(...transitiveEdges);
|
|
46
|
+
// Deduplicate and return
|
|
47
|
+
return this.deduplicateEdges(edges);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pattern 1: Same Category Edges
|
|
51
|
+
*
|
|
52
|
+
* Entities with the same category metadata are logically related
|
|
53
|
+
* Example: All "Machine Learning" papers are related
|
|
54
|
+
*/
|
|
55
|
+
async findSameCategoryEdges(entityId) {
|
|
56
|
+
try {
|
|
57
|
+
const query = `
|
|
58
|
+
# Get the category of the target entity
|
|
59
|
+
source_category[category] :=
|
|
60
|
+
*entity{id: $entity_id, metadata, @ "NOW"},
|
|
61
|
+
category = get(metadata, 'category')
|
|
62
|
+
|
|
63
|
+
# Find all entities with the same category
|
|
64
|
+
?[other_id, other_name, other_type, confidence, reason] :=
|
|
65
|
+
source_category[category],
|
|
66
|
+
category != null,
|
|
67
|
+
*entity{id: other_id, name: other_name, type: other_type, metadata, @ "NOW"},
|
|
68
|
+
other_id != $entity_id,
|
|
69
|
+
get(metadata, 'category') == category,
|
|
70
|
+
confidence = 0.8,
|
|
71
|
+
reason = concat('Same category: ', category)
|
|
72
|
+
`;
|
|
73
|
+
const result = await this.db.run(query, { entity_id: entityId });
|
|
74
|
+
return result.rows.map((r) => ({
|
|
75
|
+
from_id: entityId,
|
|
76
|
+
to_id: r[0],
|
|
77
|
+
relation_type: "same_category",
|
|
78
|
+
confidence: r[3],
|
|
79
|
+
reason: r[4],
|
|
80
|
+
pattern: "metadata_category"
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error("[LogicalEdges] Same category error:", error.message);
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Pattern 2: Same Type Edges
|
|
90
|
+
*
|
|
91
|
+
* Entities of the same type are logically related
|
|
92
|
+
* Example: All "Person" entities, all "Project" entities
|
|
93
|
+
*/
|
|
94
|
+
async findSameTypeEdges(entityId) {
|
|
95
|
+
try {
|
|
96
|
+
const query = `
|
|
97
|
+
# Get the type of the target entity
|
|
98
|
+
source_type[entity_type] :=
|
|
99
|
+
*entity{id: $entity_id, type: entity_type, @ "NOW"}
|
|
100
|
+
|
|
101
|
+
# Find all entities with the same type
|
|
102
|
+
?[other_id, other_name, confidence, reason] :=
|
|
103
|
+
source_type[entity_type],
|
|
104
|
+
*entity{id: other_id, name: other_name, type: entity_type, @ "NOW"},
|
|
105
|
+
other_id != $entity_id,
|
|
106
|
+
confidence = 0.7,
|
|
107
|
+
reason = concat('Same type: ', entity_type)
|
|
108
|
+
`;
|
|
109
|
+
const result = await this.db.run(query, { entity_id: entityId });
|
|
110
|
+
return result.rows.map((r) => ({
|
|
111
|
+
from_id: entityId,
|
|
112
|
+
to_id: r[0],
|
|
113
|
+
relation_type: "same_type",
|
|
114
|
+
confidence: r[2],
|
|
115
|
+
reason: r[3],
|
|
116
|
+
pattern: "metadata_type"
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error("[LogicalEdges] Same type error:", error.message);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Pattern 3: Hierarchical Edges
|
|
126
|
+
*
|
|
127
|
+
* Parent-child relationships derived from metadata hierarchy
|
|
128
|
+
* Example: "parent_id" in metadata indicates parent entity
|
|
129
|
+
*/
|
|
130
|
+
async findHierarchicalEdges(entityId) {
|
|
131
|
+
try {
|
|
132
|
+
const query = `
|
|
133
|
+
# Get parent_id from metadata
|
|
134
|
+
source_parent[parent_id] :=
|
|
135
|
+
*entity{id: $entity_id, metadata, @ "NOW"},
|
|
136
|
+
parent_id = get(metadata, 'parent_id'),
|
|
137
|
+
parent_id != null
|
|
138
|
+
|
|
139
|
+
# Find parent entity
|
|
140
|
+
?[parent_id, parent_name, confidence, reason] :=
|
|
141
|
+
source_parent[parent_id],
|
|
142
|
+
*entity{id: parent_id, name: parent_name, @ "NOW"},
|
|
143
|
+
confidence = 0.9,
|
|
144
|
+
reason = 'Parent relationship from metadata'
|
|
145
|
+
|
|
146
|
+
# Also find children (reverse direction)
|
|
147
|
+
?[child_id, child_name, confidence, reason] :=
|
|
148
|
+
*entity{id: child_id, metadata, @ "NOW"},
|
|
149
|
+
get(metadata, 'parent_id') == $entity_id,
|
|
150
|
+
*entity{id: child_id, name: child_name, @ "NOW"},
|
|
151
|
+
confidence = 0.9,
|
|
152
|
+
reason = 'Child relationship from metadata'
|
|
153
|
+
`;
|
|
154
|
+
const result = await this.db.run(query, { entity_id: entityId });
|
|
155
|
+
return result.rows.map((r) => ({
|
|
156
|
+
from_id: entityId,
|
|
157
|
+
to_id: r[0],
|
|
158
|
+
relation_type: "hierarchical",
|
|
159
|
+
confidence: r[2],
|
|
160
|
+
reason: r[3],
|
|
161
|
+
pattern: "metadata_hierarchy"
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error("[LogicalEdges] Hierarchical error:", error.message);
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Pattern 4: Contextual Edges
|
|
171
|
+
*
|
|
172
|
+
* Entities sharing context (domain, time period, location, organization)
|
|
173
|
+
* Example: All papers from 2025, all entities in "AI" domain
|
|
174
|
+
*/
|
|
175
|
+
async findContextualEdges(entityId) {
|
|
176
|
+
try {
|
|
177
|
+
// Simplified contextual edge discovery
|
|
178
|
+
// Find entities with same domain
|
|
179
|
+
const query = `
|
|
180
|
+
# Get domain from metadata
|
|
181
|
+
source_domain[domain] :=
|
|
182
|
+
*entity{id: $entity_id, metadata, @ "NOW"},
|
|
183
|
+
domain = get(metadata, 'domain'),
|
|
184
|
+
domain != null
|
|
185
|
+
|
|
186
|
+
# Find entities with matching domain
|
|
187
|
+
?[other_id, other_name, confidence, reason] :=
|
|
188
|
+
source_domain[domain],
|
|
189
|
+
*entity{id: other_id, name: other_name, metadata, @ "NOW"},
|
|
190
|
+
other_id != $entity_id,
|
|
191
|
+
get(metadata, 'domain') == domain,
|
|
192
|
+
confidence = 0.75,
|
|
193
|
+
reason = concat('Same domain: ', domain)
|
|
194
|
+
`;
|
|
195
|
+
const result = await this.db.run(query, { entity_id: entityId });
|
|
196
|
+
return result.rows.map((r) => ({
|
|
197
|
+
from_id: entityId,
|
|
198
|
+
to_id: r[0],
|
|
199
|
+
relation_type: "contextual",
|
|
200
|
+
confidence: r[2],
|
|
201
|
+
reason: r[3],
|
|
202
|
+
pattern: "metadata_context"
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error("[LogicalEdges] Contextual error:", error.message);
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Pattern 5: Transitive Logical Edges
|
|
212
|
+
*
|
|
213
|
+
* Derived from existing relationships combined with metadata patterns
|
|
214
|
+
* Example: If A -> B (explicit) and B has same category as C, then A -> C (transitive)
|
|
215
|
+
*/
|
|
216
|
+
async findTransitiveLogicalEdges(entityId) {
|
|
217
|
+
try {
|
|
218
|
+
const query = `
|
|
219
|
+
# Get entities connected via explicit relationships
|
|
220
|
+
connected[mid_id] :=
|
|
221
|
+
*relationship{from_id: $entity_id, to_id: mid_id, @ "NOW"}
|
|
222
|
+
|
|
223
|
+
# Get metadata of connected entities
|
|
224
|
+
connected_metadata[mid_id, mid_category, mid_type] :=
|
|
225
|
+
connected[mid_id],
|
|
226
|
+
*entity{id: mid_id, type: mid_type, metadata, @ "NOW"},
|
|
227
|
+
mid_category = get(metadata, 'category')
|
|
228
|
+
|
|
229
|
+
# Find entities with same category as connected entities
|
|
230
|
+
?[other_id, other_name, confidence, reason] :=
|
|
231
|
+
connected_metadata[mid_id, category, _],
|
|
232
|
+
category != null,
|
|
233
|
+
*entity{id: other_id, name: other_name, metadata, @ "NOW"},
|
|
234
|
+
other_id != $entity_id,
|
|
235
|
+
other_id != mid_id,
|
|
236
|
+
get(metadata, 'category') == category,
|
|
237
|
+
confidence = 0.6,
|
|
238
|
+
reason = concat('Transitive via category match through ', mid_id)
|
|
239
|
+
|
|
240
|
+
# Find entities with same type as connected entities
|
|
241
|
+
?[other_id, other_name, confidence, reason] :=
|
|
242
|
+
connected_metadata[mid_id, _, entity_type],
|
|
243
|
+
*entity{id: other_id, name: other_name, type: entity_type, @ "NOW"},
|
|
244
|
+
other_id != $entity_id,
|
|
245
|
+
other_id != mid_id,
|
|
246
|
+
confidence = 0.55,
|
|
247
|
+
reason = concat('Transitive via type match through ', mid_id)
|
|
248
|
+
`;
|
|
249
|
+
const result = await this.db.run(query, { entity_id: entityId });
|
|
250
|
+
return result.rows.map((r) => ({
|
|
251
|
+
from_id: entityId,
|
|
252
|
+
to_id: r[0],
|
|
253
|
+
relation_type: "transitive_logical",
|
|
254
|
+
confidence: r[2],
|
|
255
|
+
reason: r[3],
|
|
256
|
+
pattern: "metadata_transitive"
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.error("[LogicalEdges] Transitive error:", error.message);
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Deduplicate edges by (from_id, to_id, relation_type)
|
|
266
|
+
* Keep the one with highest confidence
|
|
267
|
+
*/
|
|
268
|
+
deduplicateEdges(edges) {
|
|
269
|
+
const map = new Map();
|
|
270
|
+
for (const edge of edges) {
|
|
271
|
+
const key = `${edge.from_id}|${edge.to_id}|${edge.relation_type}`;
|
|
272
|
+
const existing = map.get(key);
|
|
273
|
+
if (!existing || edge.confidence > existing.confidence) {
|
|
274
|
+
map.set(key, edge);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return Array.from(map.values());
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Create logical edges as explicit relationships in the database
|
|
281
|
+
* (Optional: for performance optimization)
|
|
282
|
+
*/
|
|
283
|
+
async materializeLogicalEdges(entityId) {
|
|
284
|
+
try {
|
|
285
|
+
const edges = await this.discoverLogicalEdges(entityId);
|
|
286
|
+
let created = 0;
|
|
287
|
+
for (const edge of edges) {
|
|
288
|
+
try {
|
|
289
|
+
const now = Date.now() * 1000;
|
|
290
|
+
await this.db.run(`?[from_id, to_id, relation_type, created_at, strength, metadata] <- [
|
|
291
|
+
[$from_id, $to_id, $rel_type, [${now}, true], $strength, $metadata]
|
|
292
|
+
] :insert relationship {from_id, to_id, relation_type, created_at => strength, metadata}`, {
|
|
293
|
+
from_id: edge.from_id,
|
|
294
|
+
to_id: edge.to_id,
|
|
295
|
+
rel_type: edge.relation_type,
|
|
296
|
+
strength: edge.confidence,
|
|
297
|
+
metadata: { logical_edge: true, pattern: edge.pattern, reason: edge.reason }
|
|
298
|
+
});
|
|
299
|
+
created++;
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
// Ignore duplicate key errors
|
|
303
|
+
if (!e.message?.includes("duplicate")) {
|
|
304
|
+
console.error("[LogicalEdges] Materialization error:", e.message);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return created;
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
console.error("[LogicalEdges] Materialization failed:", error.message);
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
exports.LogicalEdgesService = LogicalEdgesService;
|