n8n-nodes-engram 0.2.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/LICENSE +661 -0
- package/README.md +334 -0
- package/dist/community/CommunityDetector.d.ts +11 -0
- package/dist/community/CommunityDetector.js +126 -0
- package/dist/community/CommunityDetector.js.map +1 -0
- package/dist/community/CommunitySummarizer.d.ts +8 -0
- package/dist/community/CommunitySummarizer.js +56 -0
- package/dist/community/CommunitySummarizer.js.map +1 -0
- package/dist/community/index.d.ts +2 -0
- package/dist/community/index.js +8 -0
- package/dist/community/index.js.map +1 -0
- package/dist/credentials/EngramExtractionApi.credentials.d.ts +8 -0
- package/dist/credentials/EngramExtractionApi.credentials.js +41 -0
- package/dist/credentials/EngramExtractionApi.credentials.js.map +1 -0
- package/dist/credentials/EngramNeo4jApi.credentials.d.ts +8 -0
- package/dist/credentials/EngramNeo4jApi.credentials.js +59 -0
- package/dist/credentials/EngramNeo4jApi.credentials.js.map +1 -0
- package/dist/descriptions.d.ts +4 -0
- package/dist/descriptions.js +41 -0
- package/dist/descriptions.js.map +1 -0
- package/dist/embeddings/EmbeddingService.d.ts +24 -0
- package/dist/embeddings/EmbeddingService.js +64 -0
- package/dist/embeddings/EmbeddingService.js.map +1 -0
- package/dist/embeddings/cosine.d.ts +1 -0
- package/dist/embeddings/cosine.js +21 -0
- package/dist/embeddings/cosine.js.map +1 -0
- package/dist/embeddings/index.d.ts +2 -0
- package/dist/embeddings/index.js +8 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/extraction/ContradictionDetector.d.ts +11 -0
- package/dist/extraction/ContradictionDetector.js +35 -0
- package/dist/extraction/ContradictionDetector.js.map +1 -0
- package/dist/extraction/EntityDeduplicator.d.ts +17 -0
- package/dist/extraction/EntityDeduplicator.js +39 -0
- package/dist/extraction/EntityDeduplicator.js.map +1 -0
- package/dist/extraction/EntityExtractor.d.ts +11 -0
- package/dist/extraction/EntityExtractor.js +33 -0
- package/dist/extraction/EntityExtractor.js.map +1 -0
- package/dist/extraction/ExtractionPipeline.d.ts +24 -0
- package/dist/extraction/ExtractionPipeline.js +126 -0
- package/dist/extraction/ExtractionPipeline.js.map +1 -0
- package/dist/extraction/LlmClient.d.ts +36 -0
- package/dist/extraction/LlmClient.js +73 -0
- package/dist/extraction/LlmClient.js.map +1 -0
- package/dist/extraction/RelationshipExtractor.d.ts +12 -0
- package/dist/extraction/RelationshipExtractor.js +38 -0
- package/dist/extraction/RelationshipExtractor.js.map +1 -0
- package/dist/extraction/index.d.ts +6 -0
- package/dist/extraction/index.js +16 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/prompts.d.ts +16 -0
- package/dist/extraction/prompts.js +101 -0
- package/dist/extraction/prompts.js.map +1 -0
- package/dist/memory/EngramChatMemory.d.ts +42 -0
- package/dist/memory/EngramChatMemory.js +162 -0
- package/dist/memory/EngramChatMemory.js.map +1 -0
- package/dist/memory/EngramChatMessageHistory.d.ts +22 -0
- package/dist/memory/EngramChatMessageHistory.js +72 -0
- package/dist/memory/EngramChatMessageHistory.js.map +1 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.js +8 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/nodes/EngramAdmin/EngramAdmin.node.d.ts +5 -0
- package/dist/nodes/EngramAdmin/EngramAdmin.node.js +798 -0
- package/dist/nodes/EngramAdmin/EngramAdmin.node.js.map +1 -0
- package/dist/nodes/EngramAdmin/engram-admin.png +0 -0
- package/dist/nodes/EngramExplorer/EngramExplorer.node.d.ts +5 -0
- package/dist/nodes/EngramExplorer/EngramExplorer.node.js +932 -0
- package/dist/nodes/EngramExplorer/EngramExplorer.node.js.map +1 -0
- package/dist/nodes/EngramExplorer/engram-explorer.png +0 -0
- package/dist/nodes/EngramMemory/EngramMemory.node.d.ts +10 -0
- package/dist/nodes/EngramMemory/EngramMemory.node.js +462 -0
- package/dist/nodes/EngramMemory/EngramMemory.node.js.map +1 -0
- package/dist/nodes/EngramMemory/engram.png +0 -0
- package/dist/nodes/EngramTrigger/EngramTrigger.node.d.ts +5 -0
- package/dist/nodes/EngramTrigger/EngramTrigger.node.js +146 -0
- package/dist/nodes/EngramTrigger/EngramTrigger.node.js.map +1 -0
- package/dist/nodes/EngramTrigger/engram-trigger.png +0 -0
- package/dist/schemas/Community.schema.d.ts +656 -0
- package/dist/schemas/Community.schema.js +26 -0
- package/dist/schemas/Community.schema.js.map +1 -0
- package/dist/schemas/EntityEdge.schema.d.ts +86 -0
- package/dist/schemas/EntityEdge.schema.js +34 -0
- package/dist/schemas/EntityEdge.schema.js.map +1 -0
- package/dist/schemas/EntityNode.schema.d.ts +56 -0
- package/dist/schemas/EntityNode.schema.js +24 -0
- package/dist/schemas/EntityNode.schema.js.map +1 -0
- package/dist/schemas/EpisodicNode.schema.d.ts +53 -0
- package/dist/schemas/EpisodicNode.schema.js +23 -0
- package/dist/schemas/EpisodicNode.schema.js.map +1 -0
- package/dist/schemas/GraphData.schema.d.ts +220 -0
- package/dist/schemas/GraphData.schema.js +25 -0
- package/dist/schemas/GraphData.schema.js.map +1 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.js +20 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/search/HybridSearchEngine.d.ts +31 -0
- package/dist/search/HybridSearchEngine.js +140 -0
- package/dist/search/HybridSearchEngine.js.map +1 -0
- package/dist/search/MinisearchProvider.d.ts +20 -0
- package/dist/search/MinisearchProvider.js +77 -0
- package/dist/search/MinisearchProvider.js.map +1 -0
- package/dist/search/TextSearchProvider.d.ts +20 -0
- package/dist/search/TextSearchProvider.js +3 -0
- package/dist/search/TextSearchProvider.js.map +1 -0
- package/dist/search/index.d.ts +3 -0
- package/dist/search/index.js +8 -0
- package/dist/search/index.js.map +1 -0
- package/dist/storage/GraphologyStorage.d.ts +42 -0
- package/dist/storage/GraphologyStorage.js +665 -0
- package/dist/storage/GraphologyStorage.js.map +1 -0
- package/dist/storage/IGraphStorage.d.ts +64 -0
- package/dist/storage/IGraphStorage.js +3 -0
- package/dist/storage/IGraphStorage.js.map +1 -0
- package/dist/storage/Neo4jStorage.d.ts +45 -0
- package/dist/storage/Neo4jStorage.js +949 -0
- package/dist/storage/Neo4jStorage.js.map +1 -0
- package/dist/storage/StorageFactory.d.ts +14 -0
- package/dist/storage/StorageFactory.js +26 -0
- package/dist/storage/StorageFactory.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.js +8 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/traversal/EpisodeTraverser.d.ts +10 -0
- package/dist/traversal/EpisodeTraverser.js +46 -0
- package/dist/traversal/EpisodeTraverser.js.map +1 -0
- package/dist/traversal/GraphTraverser.d.ts +24 -0
- package/dist/traversal/GraphTraverser.js +89 -0
- package/dist/traversal/GraphTraverser.js.map +1 -0
- package/dist/traversal/index.d.ts +2 -0
- package/dist/traversal/index.js +8 -0
- package/dist/traversal/index.js.map +1 -0
- package/dist/utils/descriptions.d.ts +6 -0
- package/dist/utils/descriptions.js +95 -0
- package/dist/utils/descriptions.js.map +1 -0
- package/dist/utils/helpers.d.ts +23 -0
- package/dist/utils/helpers.js +146 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/logWrapper.d.ts +26 -0
- package/dist/utils/logWrapper.js +300 -0
- package/dist/utils/logWrapper.js.map +1 -0
- package/dist/utils/sharedFields.d.ts +6 -0
- package/dist/utils/sharedFields.js +121 -0
- package/dist/utils/sharedFields.js.map +1 -0
- package/dist/utils/temporal.d.ts +22 -0
- package/dist/utils/temporal.js +44 -0
- package/dist/utils/temporal.js.map +1 -0
- package/dist/utils/tracing.d.ts +7 -0
- package/dist/utils/tracing.js +20 -0
- package/dist/utils/tracing.js.map +1 -0
- package/dist/utils/uuid.d.ts +2 -0
- package/dist/utils/uuid.js +13 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +108 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.GraphologyStorage = void 0;
|
|
27
|
+
const graphology_1 = require("graphology");
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const uuid_1 = require("../utils/uuid");
|
|
31
|
+
const temporal_1 = require("../utils/temporal");
|
|
32
|
+
const MinisearchProvider_1 = require("../search/MinisearchProvider");
|
|
33
|
+
const cosine_1 = require("../embeddings/cosine");
|
|
34
|
+
class GraphologyStorage {
|
|
35
|
+
constructor(persistPath) {
|
|
36
|
+
this.initialized = false;
|
|
37
|
+
this.episodeOrder = new Map();
|
|
38
|
+
this.nextOrder = 0;
|
|
39
|
+
this.graph = new graphology_1.MultiDirectedGraph();
|
|
40
|
+
this.searchProvider = new MinisearchProvider_1.MinisearchProvider();
|
|
41
|
+
if (persistPath) {
|
|
42
|
+
const resolved = path.resolve(persistPath);
|
|
43
|
+
if (resolved.includes('..') || resolved.startsWith('/etc') || resolved.startsWith('/dev')) {
|
|
44
|
+
throw new Error(`Invalid persist path: ${persistPath}`);
|
|
45
|
+
}
|
|
46
|
+
this.persistPath = resolved;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async initialize() {
|
|
50
|
+
if (this.initialized)
|
|
51
|
+
return;
|
|
52
|
+
if (this.persistPath) {
|
|
53
|
+
await this.loadFromDisk();
|
|
54
|
+
}
|
|
55
|
+
this.initialized = true;
|
|
56
|
+
}
|
|
57
|
+
async close() {
|
|
58
|
+
if (this.persistPath) {
|
|
59
|
+
await this.saveToDisk();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async addEntity(input) {
|
|
63
|
+
var _a, _b, _c, _d;
|
|
64
|
+
const now = (0, temporal_1.nowIso)();
|
|
65
|
+
const entity = {
|
|
66
|
+
uuid: (0, uuid_1.generateUuid)(),
|
|
67
|
+
name: input.name,
|
|
68
|
+
group_id: input.group_id,
|
|
69
|
+
summary: (_a = input.summary) !== null && _a !== void 0 ? _a : '',
|
|
70
|
+
entity_type: (_b = input.entity_type) !== null && _b !== void 0 ? _b : 'unknown',
|
|
71
|
+
name_embedding: (_c = input.name_embedding) !== null && _c !== void 0 ? _c : null,
|
|
72
|
+
attributes: (_d = input.attributes) !== null && _d !== void 0 ? _d : {},
|
|
73
|
+
created_at: now,
|
|
74
|
+
updated_at: now,
|
|
75
|
+
};
|
|
76
|
+
this.graph.addNode(entity.uuid, { type: 'entity', data: entity });
|
|
77
|
+
this.searchProvider.indexEntity(entity.uuid, {
|
|
78
|
+
name: entity.name,
|
|
79
|
+
summary: entity.summary,
|
|
80
|
+
entity_type: entity.entity_type,
|
|
81
|
+
});
|
|
82
|
+
await this.persistIfConfigured();
|
|
83
|
+
return entity;
|
|
84
|
+
}
|
|
85
|
+
async getEntity(uuid) {
|
|
86
|
+
if (!this.graph.hasNode(uuid))
|
|
87
|
+
return null;
|
|
88
|
+
const attrs = this.graph.getNodeAttributes(uuid);
|
|
89
|
+
if (attrs.type !== 'entity')
|
|
90
|
+
return null;
|
|
91
|
+
return attrs.data;
|
|
92
|
+
}
|
|
93
|
+
async getEntityByName(name, groupId) {
|
|
94
|
+
const normalized = name.toLowerCase().trim();
|
|
95
|
+
let found = null;
|
|
96
|
+
this.graph.forEachNode((key, attrs) => {
|
|
97
|
+
if (found)
|
|
98
|
+
return;
|
|
99
|
+
if (attrs.type !== 'entity')
|
|
100
|
+
return;
|
|
101
|
+
const entity = attrs.data;
|
|
102
|
+
if (entity.group_id === groupId && entity.name.toLowerCase().trim() === normalized) {
|
|
103
|
+
found = entity;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return found;
|
|
107
|
+
}
|
|
108
|
+
async updateEntity(uuid, updates) {
|
|
109
|
+
const existing = await this.getEntity(uuid);
|
|
110
|
+
if (!existing)
|
|
111
|
+
throw new Error(`Entity not found: ${uuid}`);
|
|
112
|
+
const updated = {
|
|
113
|
+
...existing,
|
|
114
|
+
...updates,
|
|
115
|
+
uuid: existing.uuid,
|
|
116
|
+
created_at: existing.created_at,
|
|
117
|
+
updated_at: (0, temporal_1.nowIso)(),
|
|
118
|
+
};
|
|
119
|
+
this.graph.setNodeAttribute(uuid, 'data', updated);
|
|
120
|
+
this.searchProvider.indexEntity(updated.uuid, {
|
|
121
|
+
name: updated.name,
|
|
122
|
+
summary: updated.summary,
|
|
123
|
+
entity_type: updated.entity_type,
|
|
124
|
+
});
|
|
125
|
+
await this.persistIfConfigured();
|
|
126
|
+
return updated;
|
|
127
|
+
}
|
|
128
|
+
async deleteEntity(uuid) {
|
|
129
|
+
if (!this.graph.hasNode(uuid))
|
|
130
|
+
return;
|
|
131
|
+
this.graph.forEachEdge(uuid, (edgeKey, attrs) => {
|
|
132
|
+
if (attrs.type === 'entity_edge') {
|
|
133
|
+
this.searchProvider.removeEdge(edgeKey);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.searchProvider.removeEntity(uuid);
|
|
137
|
+
this.graph.dropNode(uuid);
|
|
138
|
+
await this.persistIfConfigured();
|
|
139
|
+
}
|
|
140
|
+
async listEntities(groupId, options) {
|
|
141
|
+
var _a, _b;
|
|
142
|
+
const entities = [];
|
|
143
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
144
|
+
if (attrs.type !== 'entity')
|
|
145
|
+
return;
|
|
146
|
+
const entity = attrs.data;
|
|
147
|
+
if (entity.group_id !== groupId)
|
|
148
|
+
return;
|
|
149
|
+
if ((options === null || options === void 0 ? void 0 : options.entity_type) && entity.entity_type !== options.entity_type)
|
|
150
|
+
return;
|
|
151
|
+
entities.push(entity);
|
|
152
|
+
});
|
|
153
|
+
entities.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
154
|
+
const offset = (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : 0;
|
|
155
|
+
const limit = (_b = options === null || options === void 0 ? void 0 : options.limit) !== null && _b !== void 0 ? _b : entities.length;
|
|
156
|
+
return entities.slice(offset, offset + limit);
|
|
157
|
+
}
|
|
158
|
+
async addEdge(input) {
|
|
159
|
+
var _a, _b, _c, _d, _e, _f;
|
|
160
|
+
const now = (0, temporal_1.nowIso)();
|
|
161
|
+
const edge = {
|
|
162
|
+
uuid: (0, uuid_1.generateUuid)(),
|
|
163
|
+
group_id: input.group_id,
|
|
164
|
+
source_node_uuid: input.source_node_uuid,
|
|
165
|
+
target_node_uuid: input.target_node_uuid,
|
|
166
|
+
name: input.name,
|
|
167
|
+
fact: input.fact,
|
|
168
|
+
fact_embedding: (_a = input.fact_embedding) !== null && _a !== void 0 ? _a : null,
|
|
169
|
+
episodes: (_b = input.episodes) !== null && _b !== void 0 ? _b : [],
|
|
170
|
+
valid_at: (_c = input.valid_at) !== null && _c !== void 0 ? _c : null,
|
|
171
|
+
invalid_at: (_d = input.invalid_at) !== null && _d !== void 0 ? _d : null,
|
|
172
|
+
expired_at: (_e = input.expired_at) !== null && _e !== void 0 ? _e : null,
|
|
173
|
+
attributes: (_f = input.attributes) !== null && _f !== void 0 ? _f : {},
|
|
174
|
+
created_at: now,
|
|
175
|
+
updated_at: now,
|
|
176
|
+
};
|
|
177
|
+
if (!this.graph.hasNode(edge.source_node_uuid) || !this.graph.hasNode(edge.target_node_uuid)) {
|
|
178
|
+
throw new Error(`Cannot create edge: source (${edge.source_node_uuid}) or target (${edge.target_node_uuid}) node not found`);
|
|
179
|
+
}
|
|
180
|
+
this.graph.addEdgeWithKey(edge.uuid, edge.source_node_uuid, edge.target_node_uuid, {
|
|
181
|
+
type: 'entity_edge',
|
|
182
|
+
data: edge,
|
|
183
|
+
});
|
|
184
|
+
this.searchProvider.indexEdge(edge.uuid, {
|
|
185
|
+
name: edge.name,
|
|
186
|
+
fact: edge.fact,
|
|
187
|
+
});
|
|
188
|
+
await this.persistIfConfigured();
|
|
189
|
+
return edge;
|
|
190
|
+
}
|
|
191
|
+
async getEdge(uuid) {
|
|
192
|
+
if (!this.graph.hasEdge(uuid))
|
|
193
|
+
return null;
|
|
194
|
+
const attrs = this.graph.getEdgeAttributes(uuid);
|
|
195
|
+
if (attrs.type !== 'entity_edge')
|
|
196
|
+
return null;
|
|
197
|
+
return attrs.data;
|
|
198
|
+
}
|
|
199
|
+
async getEdgesBetween(sourceUuid, targetUuid) {
|
|
200
|
+
const edges = [];
|
|
201
|
+
if (!this.graph.hasNode(sourceUuid) || !this.graph.hasNode(targetUuid))
|
|
202
|
+
return edges;
|
|
203
|
+
this.graph.forEachEdge(sourceUuid, (edgeKey, attrs, source, target) => {
|
|
204
|
+
if (attrs.type !== 'entity_edge' || !attrs.data)
|
|
205
|
+
return;
|
|
206
|
+
if ((source === sourceUuid && target === targetUuid) ||
|
|
207
|
+
(source === targetUuid && target === sourceUuid)) {
|
|
208
|
+
edges.push(attrs.data);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return edges;
|
|
212
|
+
}
|
|
213
|
+
async getEdgesForEntity(entityUuid) {
|
|
214
|
+
const edges = [];
|
|
215
|
+
if (!this.graph.hasNode(entityUuid))
|
|
216
|
+
return edges;
|
|
217
|
+
this.graph.forEachEdge(entityUuid, (_edgeKey, attrs) => {
|
|
218
|
+
if (attrs.type !== 'entity_edge' || !attrs.data)
|
|
219
|
+
return;
|
|
220
|
+
edges.push(attrs.data);
|
|
221
|
+
});
|
|
222
|
+
return edges;
|
|
223
|
+
}
|
|
224
|
+
async updateEdge(uuid, updates) {
|
|
225
|
+
const existing = await this.getEdge(uuid);
|
|
226
|
+
if (!existing)
|
|
227
|
+
throw new Error(`Edge not found: ${uuid}`);
|
|
228
|
+
const updated = {
|
|
229
|
+
...existing,
|
|
230
|
+
...updates,
|
|
231
|
+
uuid: existing.uuid,
|
|
232
|
+
source_node_uuid: existing.source_node_uuid,
|
|
233
|
+
target_node_uuid: existing.target_node_uuid,
|
|
234
|
+
created_at: existing.created_at,
|
|
235
|
+
updated_at: (0, temporal_1.nowIso)(),
|
|
236
|
+
};
|
|
237
|
+
this.graph.setEdgeAttribute(uuid, 'data', updated);
|
|
238
|
+
this.searchProvider.indexEdge(updated.uuid, {
|
|
239
|
+
name: updated.name,
|
|
240
|
+
fact: updated.fact,
|
|
241
|
+
});
|
|
242
|
+
await this.persistIfConfigured();
|
|
243
|
+
return updated;
|
|
244
|
+
}
|
|
245
|
+
async deleteEdge(uuid) {
|
|
246
|
+
if (!this.graph.hasEdge(uuid))
|
|
247
|
+
return;
|
|
248
|
+
this.searchProvider.removeEdge(uuid);
|
|
249
|
+
this.graph.dropEdge(uuid);
|
|
250
|
+
await this.persistIfConfigured();
|
|
251
|
+
}
|
|
252
|
+
async addEpisode(input) {
|
|
253
|
+
var _a, _b;
|
|
254
|
+
const episode = {
|
|
255
|
+
uuid: (0, uuid_1.generateUuid)(),
|
|
256
|
+
group_id: input.group_id,
|
|
257
|
+
content: input.content,
|
|
258
|
+
role: input.role,
|
|
259
|
+
source_type: (_a = input.source_type) !== null && _a !== void 0 ? _a : 'message',
|
|
260
|
+
reference_time: input.reference_time,
|
|
261
|
+
previous_episode_uuid: (_b = input.previous_episode_uuid) !== null && _b !== void 0 ? _b : null,
|
|
262
|
+
created_at: (0, temporal_1.nowIso)(),
|
|
263
|
+
};
|
|
264
|
+
this.graph.addNode(episode.uuid, { type: 'episode', data: episode });
|
|
265
|
+
this.episodeOrder.set(episode.uuid, this.nextOrder++);
|
|
266
|
+
if (episode.previous_episode_uuid && this.graph.hasNode(episode.previous_episode_uuid)) {
|
|
267
|
+
const chainKey = `next_${episode.previous_episode_uuid}_${episode.uuid}`;
|
|
268
|
+
this.graph.addEdgeWithKey(chainKey, episode.previous_episode_uuid, episode.uuid, {
|
|
269
|
+
type: 'next_episode',
|
|
270
|
+
data: null,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
await this.persistIfConfigured();
|
|
274
|
+
return episode;
|
|
275
|
+
}
|
|
276
|
+
async getEpisode(uuid) {
|
|
277
|
+
if (!this.graph.hasNode(uuid))
|
|
278
|
+
return null;
|
|
279
|
+
const attrs = this.graph.getNodeAttributes(uuid);
|
|
280
|
+
if (attrs.type !== 'episode')
|
|
281
|
+
return null;
|
|
282
|
+
return attrs.data;
|
|
283
|
+
}
|
|
284
|
+
async getRecentEpisodes(groupId, limit) {
|
|
285
|
+
const episodes = [];
|
|
286
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
287
|
+
if (attrs.type !== 'episode')
|
|
288
|
+
return;
|
|
289
|
+
const episode = attrs.data;
|
|
290
|
+
if (episode.group_id !== groupId)
|
|
291
|
+
return;
|
|
292
|
+
episodes.push(episode);
|
|
293
|
+
});
|
|
294
|
+
episodes.sort((a, b) => {
|
|
295
|
+
var _a, _b;
|
|
296
|
+
const timeDiff = new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
|
|
297
|
+
if (timeDiff !== 0)
|
|
298
|
+
return timeDiff;
|
|
299
|
+
return ((_a = this.episodeOrder.get(b.uuid)) !== null && _a !== void 0 ? _a : 0) - ((_b = this.episodeOrder.get(a.uuid)) !== null && _b !== void 0 ? _b : 0);
|
|
300
|
+
});
|
|
301
|
+
return episodes.slice(0, limit).reverse();
|
|
302
|
+
}
|
|
303
|
+
async getEpisodeCount(groupId) {
|
|
304
|
+
let count = 0;
|
|
305
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
306
|
+
if (attrs.type !== 'episode')
|
|
307
|
+
return;
|
|
308
|
+
const episode = attrs.data;
|
|
309
|
+
if (episode.group_id === groupId)
|
|
310
|
+
count++;
|
|
311
|
+
});
|
|
312
|
+
return count;
|
|
313
|
+
}
|
|
314
|
+
async searchEntities(query, groupId, options) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 10;
|
|
317
|
+
const minScore = (_b = options === null || options === void 0 ? void 0 : options.min_score) !== null && _b !== void 0 ? _b : 0;
|
|
318
|
+
const textResults = this.searchProvider.searchEntities(query, limit * 3);
|
|
319
|
+
const results = [];
|
|
320
|
+
for (const tr of textResults) {
|
|
321
|
+
if (tr.score < minScore)
|
|
322
|
+
continue;
|
|
323
|
+
const entity = await this.getEntity(tr.id);
|
|
324
|
+
if (!entity || entity.group_id !== groupId)
|
|
325
|
+
continue;
|
|
326
|
+
if ((options === null || options === void 0 ? void 0 : options.entity_type) && entity.entity_type !== options.entity_type)
|
|
327
|
+
continue;
|
|
328
|
+
results.push({ entity, score: tr.score });
|
|
329
|
+
}
|
|
330
|
+
return results.slice(0, limit);
|
|
331
|
+
}
|
|
332
|
+
async searchEdges(query, groupId, options) {
|
|
333
|
+
var _a, _b, _c;
|
|
334
|
+
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 10;
|
|
335
|
+
const minScore = (_b = options === null || options === void 0 ? void 0 : options.min_score) !== null && _b !== void 0 ? _b : 0;
|
|
336
|
+
const includeExpired = (_c = options === null || options === void 0 ? void 0 : options.include_expired) !== null && _c !== void 0 ? _c : false;
|
|
337
|
+
const textResults = this.searchProvider.searchEdges(query, limit * 3);
|
|
338
|
+
const results = [];
|
|
339
|
+
for (const tr of textResults) {
|
|
340
|
+
if (tr.score < minScore)
|
|
341
|
+
continue;
|
|
342
|
+
const edge = await this.getEdge(tr.id);
|
|
343
|
+
if (!edge || edge.group_id !== groupId)
|
|
344
|
+
continue;
|
|
345
|
+
if (!includeExpired && edge.expired_at !== null)
|
|
346
|
+
continue;
|
|
347
|
+
const sourceEntity = await this.getEntity(edge.source_node_uuid);
|
|
348
|
+
const targetEntity = await this.getEntity(edge.target_node_uuid);
|
|
349
|
+
if (!sourceEntity || !targetEntity)
|
|
350
|
+
continue;
|
|
351
|
+
results.push({ edge, sourceEntity, targetEntity, score: tr.score });
|
|
352
|
+
}
|
|
353
|
+
return results.slice(0, limit);
|
|
354
|
+
}
|
|
355
|
+
async searchEntitiesByVector(vector, groupId, options) {
|
|
356
|
+
var _a, _b;
|
|
357
|
+
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 10;
|
|
358
|
+
const minScore = (_b = options === null || options === void 0 ? void 0 : options.min_score) !== null && _b !== void 0 ? _b : 0;
|
|
359
|
+
const results = [];
|
|
360
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
361
|
+
if (attrs.type !== 'entity')
|
|
362
|
+
return;
|
|
363
|
+
const entity = attrs.data;
|
|
364
|
+
if (entity.group_id !== groupId)
|
|
365
|
+
return;
|
|
366
|
+
if (!entity.name_embedding || entity.name_embedding.length !== vector.length)
|
|
367
|
+
return;
|
|
368
|
+
try {
|
|
369
|
+
const score = (0, cosine_1.cosineSimilarity)(vector, entity.name_embedding);
|
|
370
|
+
if (score >= minScore) {
|
|
371
|
+
results.push({ entity, score });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
results.sort((a, b) => b.score - a.score);
|
|
378
|
+
return results.slice(0, limit);
|
|
379
|
+
}
|
|
380
|
+
async searchEdgesByVector(vector, groupId, options) {
|
|
381
|
+
var _a, _b;
|
|
382
|
+
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 10;
|
|
383
|
+
const minScore = (_b = options === null || options === void 0 ? void 0 : options.min_score) !== null && _b !== void 0 ? _b : 0;
|
|
384
|
+
const results = [];
|
|
385
|
+
this.graph.forEachEdge((_edgeKey, attrs) => {
|
|
386
|
+
if (attrs.type !== 'entity_edge' || !attrs.data)
|
|
387
|
+
return;
|
|
388
|
+
const edge = attrs.data;
|
|
389
|
+
if (edge.group_id !== groupId)
|
|
390
|
+
return;
|
|
391
|
+
if (!edge.fact_embedding || edge.fact_embedding.length !== vector.length)
|
|
392
|
+
return;
|
|
393
|
+
try {
|
|
394
|
+
const score = (0, cosine_1.cosineSimilarity)(vector, edge.fact_embedding);
|
|
395
|
+
if (score < minScore)
|
|
396
|
+
return;
|
|
397
|
+
const sourceNode = this.graph.hasNode(edge.source_node_uuid)
|
|
398
|
+
? this.graph.getNodeAttributes(edge.source_node_uuid)
|
|
399
|
+
: null;
|
|
400
|
+
const targetNode = this.graph.hasNode(edge.target_node_uuid)
|
|
401
|
+
? this.graph.getNodeAttributes(edge.target_node_uuid)
|
|
402
|
+
: null;
|
|
403
|
+
if (!sourceNode || !targetNode)
|
|
404
|
+
return;
|
|
405
|
+
if (sourceNode.type !== 'entity' || targetNode.type !== 'entity')
|
|
406
|
+
return;
|
|
407
|
+
results.push({
|
|
408
|
+
edge,
|
|
409
|
+
sourceEntity: sourceNode.data,
|
|
410
|
+
targetEntity: targetNode.data,
|
|
411
|
+
score,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
results.sort((a, b) => b.score - a.score);
|
|
418
|
+
return results.slice(0, limit);
|
|
419
|
+
}
|
|
420
|
+
async clearGroup(groupId) {
|
|
421
|
+
const nodesToRemove = [];
|
|
422
|
+
this.graph.forEachNode((key, attrs) => {
|
|
423
|
+
const data = attrs.data;
|
|
424
|
+
if ('group_id' in data && data.group_id === groupId) {
|
|
425
|
+
nodesToRemove.push(key);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
const edgesToRemove = [];
|
|
429
|
+
this.graph.forEachEdge((edgeKey, attrs) => {
|
|
430
|
+
if (attrs.type === 'entity_edge' && attrs.data) {
|
|
431
|
+
const edge = attrs.data;
|
|
432
|
+
if (edge.group_id === groupId) {
|
|
433
|
+
edgesToRemove.push(edgeKey);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
for (const edgeKey of edgesToRemove) {
|
|
438
|
+
this.searchProvider.removeEdge(edgeKey);
|
|
439
|
+
if (this.graph.hasEdge(edgeKey)) {
|
|
440
|
+
this.graph.dropEdge(edgeKey);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
for (const key of nodesToRemove) {
|
|
444
|
+
this.searchProvider.removeEntity(key);
|
|
445
|
+
if (this.graph.hasNode(key)) {
|
|
446
|
+
this.graph.dropNode(key);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
await this.persistIfConfigured();
|
|
450
|
+
}
|
|
451
|
+
async clearAll() {
|
|
452
|
+
this.graph.clear();
|
|
453
|
+
this.searchProvider.clear();
|
|
454
|
+
this.episodeOrder.clear();
|
|
455
|
+
this.nextOrder = 0;
|
|
456
|
+
await this.persistIfConfigured();
|
|
457
|
+
}
|
|
458
|
+
async exportGraph(groupId) {
|
|
459
|
+
const entities = [];
|
|
460
|
+
const edges = [];
|
|
461
|
+
const episodes = [];
|
|
462
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
463
|
+
if (attrs.type === 'entity') {
|
|
464
|
+
const entity = attrs.data;
|
|
465
|
+
if (!groupId || entity.group_id === groupId)
|
|
466
|
+
entities.push(entity);
|
|
467
|
+
}
|
|
468
|
+
else if (attrs.type === 'episode') {
|
|
469
|
+
const episode = attrs.data;
|
|
470
|
+
if (!groupId || episode.group_id === groupId)
|
|
471
|
+
episodes.push(episode);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
this.graph.forEachEdge((_edgeKey, attrs) => {
|
|
475
|
+
if (attrs.type === 'entity_edge' && attrs.data) {
|
|
476
|
+
const edge = attrs.data;
|
|
477
|
+
if (!groupId || edge.group_id === groupId)
|
|
478
|
+
edges.push(edge);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
version: '1.0',
|
|
483
|
+
exported_at: (0, temporal_1.nowIso)(),
|
|
484
|
+
group_id: groupId,
|
|
485
|
+
entities,
|
|
486
|
+
edges,
|
|
487
|
+
episodes,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
async importGraph(data) {
|
|
491
|
+
for (const entity of data.entities) {
|
|
492
|
+
if (!this.graph.hasNode(entity.uuid)) {
|
|
493
|
+
this.graph.addNode(entity.uuid, { type: 'entity', data: entity });
|
|
494
|
+
this.searchProvider.indexEntity(entity.uuid, {
|
|
495
|
+
name: entity.name,
|
|
496
|
+
summary: entity.summary,
|
|
497
|
+
entity_type: entity.entity_type,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const sortedEpisodes = [...data.episodes].sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
502
|
+
for (const episode of sortedEpisodes) {
|
|
503
|
+
if (!this.graph.hasNode(episode.uuid)) {
|
|
504
|
+
this.graph.addNode(episode.uuid, { type: 'episode', data: episode });
|
|
505
|
+
this.episodeOrder.set(episode.uuid, this.nextOrder++);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
for (const edge of data.edges) {
|
|
509
|
+
if (!this.graph.hasEdge(edge.uuid) &&
|
|
510
|
+
this.graph.hasNode(edge.source_node_uuid) &&
|
|
511
|
+
this.graph.hasNode(edge.target_node_uuid)) {
|
|
512
|
+
this.graph.addEdgeWithKey(edge.uuid, edge.source_node_uuid, edge.target_node_uuid, {
|
|
513
|
+
type: 'entity_edge',
|
|
514
|
+
data: edge,
|
|
515
|
+
});
|
|
516
|
+
this.searchProvider.indexEdge(edge.uuid, {
|
|
517
|
+
name: edge.name,
|
|
518
|
+
fact: edge.fact,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
for (const episode of data.episodes) {
|
|
523
|
+
if (episode.previous_episode_uuid &&
|
|
524
|
+
this.graph.hasNode(episode.previous_episode_uuid) &&
|
|
525
|
+
this.graph.hasNode(episode.uuid)) {
|
|
526
|
+
const chainKey = `next_${episode.previous_episode_uuid}_${episode.uuid}`;
|
|
527
|
+
if (!this.graph.hasEdge(chainKey)) {
|
|
528
|
+
this.graph.addEdgeWithKey(chainKey, episode.previous_episode_uuid, episode.uuid, {
|
|
529
|
+
type: 'next_episode',
|
|
530
|
+
data: null,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
await this.persistIfConfigured();
|
|
536
|
+
}
|
|
537
|
+
async getStats(groupId) {
|
|
538
|
+
let entityCount = 0;
|
|
539
|
+
let edgeCount = 0;
|
|
540
|
+
let episodeCount = 0;
|
|
541
|
+
const groupIds = new Set();
|
|
542
|
+
const entityTypes = {};
|
|
543
|
+
let oldestEpisode = null;
|
|
544
|
+
let newestEpisode = null;
|
|
545
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
546
|
+
var _a;
|
|
547
|
+
if (attrs.type === 'entity') {
|
|
548
|
+
const entity = attrs.data;
|
|
549
|
+
if (groupId && entity.group_id !== groupId)
|
|
550
|
+
return;
|
|
551
|
+
entityCount++;
|
|
552
|
+
groupIds.add(entity.group_id);
|
|
553
|
+
entityTypes[entity.entity_type] = ((_a = entityTypes[entity.entity_type]) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
554
|
+
}
|
|
555
|
+
else if (attrs.type === 'episode') {
|
|
556
|
+
const episode = attrs.data;
|
|
557
|
+
if (groupId && episode.group_id !== groupId)
|
|
558
|
+
return;
|
|
559
|
+
episodeCount++;
|
|
560
|
+
groupIds.add(episode.group_id);
|
|
561
|
+
if (!oldestEpisode || episode.created_at < oldestEpisode) {
|
|
562
|
+
oldestEpisode = episode.created_at;
|
|
563
|
+
}
|
|
564
|
+
if (!newestEpisode || episode.created_at > newestEpisode) {
|
|
565
|
+
newestEpisode = episode.created_at;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
this.graph.forEachEdge((_edgeKey, attrs) => {
|
|
570
|
+
if (attrs.type === 'entity_edge' && attrs.data) {
|
|
571
|
+
const edge = attrs.data;
|
|
572
|
+
if (groupId && edge.group_id !== groupId)
|
|
573
|
+
return;
|
|
574
|
+
edgeCount++;
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
return {
|
|
578
|
+
entity_count: entityCount,
|
|
579
|
+
edge_count: edgeCount,
|
|
580
|
+
episode_count: episodeCount,
|
|
581
|
+
group_ids: [...groupIds],
|
|
582
|
+
entity_types: entityTypes,
|
|
583
|
+
oldest_episode: oldestEpisode,
|
|
584
|
+
newest_episode: newestEpisode,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
async applyRetention(groupId, policy) {
|
|
588
|
+
if (policy.type === 'forever')
|
|
589
|
+
return 0;
|
|
590
|
+
let removed = 0;
|
|
591
|
+
if (policy.type === 'days' && policy.value) {
|
|
592
|
+
const days = policy.value;
|
|
593
|
+
const episodesToRemove = [];
|
|
594
|
+
this.graph.forEachNode((key, attrs) => {
|
|
595
|
+
if (attrs.type !== 'episode')
|
|
596
|
+
return;
|
|
597
|
+
const episode = attrs.data;
|
|
598
|
+
if (episode.group_id !== groupId)
|
|
599
|
+
return;
|
|
600
|
+
if ((0, temporal_1.isOlderThanDays)(episode.created_at, days)) {
|
|
601
|
+
episodesToRemove.push(key);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
for (const key of episodesToRemove) {
|
|
605
|
+
if (this.graph.hasNode(key)) {
|
|
606
|
+
this.graph.dropNode(key);
|
|
607
|
+
removed++;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (policy.type === 'max_episodes' && policy.value) {
|
|
612
|
+
const maxEpisodes = policy.value;
|
|
613
|
+
const episodes = [];
|
|
614
|
+
this.graph.forEachNode((_key, attrs) => {
|
|
615
|
+
if (attrs.type !== 'episode')
|
|
616
|
+
return;
|
|
617
|
+
const episode = attrs.data;
|
|
618
|
+
if (episode.group_id !== groupId)
|
|
619
|
+
return;
|
|
620
|
+
episodes.push({ uuid: episode.uuid, created_at: episode.created_at });
|
|
621
|
+
});
|
|
622
|
+
episodes.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
623
|
+
const toRemove = episodes.slice(maxEpisodes);
|
|
624
|
+
for (const ep of toRemove) {
|
|
625
|
+
if (this.graph.hasNode(ep.uuid)) {
|
|
626
|
+
this.graph.dropNode(ep.uuid);
|
|
627
|
+
removed++;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (removed > 0) {
|
|
632
|
+
await this.persistIfConfigured();
|
|
633
|
+
}
|
|
634
|
+
return removed;
|
|
635
|
+
}
|
|
636
|
+
async persistIfConfigured() {
|
|
637
|
+
if (this.persistPath) {
|
|
638
|
+
await this.saveToDisk();
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async saveToDisk() {
|
|
642
|
+
if (!this.persistPath)
|
|
643
|
+
return;
|
|
644
|
+
const data = await this.exportGraph();
|
|
645
|
+
const dir = path.dirname(this.persistPath);
|
|
646
|
+
if (!fs.existsSync(dir)) {
|
|
647
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
648
|
+
}
|
|
649
|
+
fs.writeFileSync(this.persistPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
650
|
+
}
|
|
651
|
+
async loadFromDisk() {
|
|
652
|
+
if (!this.persistPath || !fs.existsSync(this.persistPath))
|
|
653
|
+
return;
|
|
654
|
+
try {
|
|
655
|
+
const raw = fs.readFileSync(this.persistPath, 'utf-8');
|
|
656
|
+
const data = JSON.parse(raw);
|
|
657
|
+
await this.importGraph(data);
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
console.error(`Engram: Failed to load graph from ${this.persistPath}, starting fresh`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
exports.GraphologyStorage = GraphologyStorage;
|
|
665
|
+
//# sourceMappingURL=GraphologyStorage.js.map
|