cozo-memory 1.2.0 → 1.2.2

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.
@@ -18,22 +18,27 @@ class MemoryActivationService {
18
18
  /**
19
19
  * Calculate activation score for an observation
20
20
  * Formula: R = e^(-t/S)
21
+ * With emotional salience: decay rate is reduced by salienceBoostDecay
21
22
  */
22
- calculateActivation(timeSinceAccess, strength) {
23
+ calculateActivation(timeSinceAccess, strength, salienceBoostDecay = 0.0) {
23
24
  if (timeSinceAccess === 0)
24
25
  return 1.0;
25
- const exponent = -timeSinceAccess / strength;
26
+ // Apply salience decay reduction (slower decay for emotionally salient memories)
27
+ const effectiveTime = timeSinceAccess * (1.0 - salienceBoostDecay);
28
+ const exponent = -effectiveTime / strength;
26
29
  const activation = Math.pow(this.config.decayBase, exponent);
27
30
  return Math.max(0, Math.min(1, activation));
28
31
  }
29
32
  /**
30
33
  * Get current memory strength for an observation
31
34
  * S = initialStrength + (accessCount * strengthIncrement)
35
+ * With emotional salience boost: S = S * salienceBoostStrength
32
36
  */
33
- calculateStrength(accessCount) {
34
- const strength = this.config.initialStrength +
37
+ calculateStrength(accessCount, salienceBoostStrength = 1.0) {
38
+ const baseStrength = this.config.initialStrength +
35
39
  (accessCount * this.config.strengthIncrement);
36
- return Math.min(strength, this.config.maxStrength);
40
+ const boostedStrength = baseStrength * salienceBoostStrength;
41
+ return Math.min(boostedStrength, this.config.maxStrength);
37
42
  }
38
43
  /**
39
44
  * Convert timestamp to time units since last access
@@ -57,12 +62,12 @@ class MemoryActivationService {
57
62
  const query = entityId
58
63
  ? `
59
64
  ?[id, entity_id, text, metadata, created_at] :=
60
- *observation{id, entity_id, text, metadata, created_at},
65
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"},
61
66
  entity_id == $entity_id
62
67
  `
63
68
  : `
64
69
  ?[id, entity_id, text, metadata, created_at] :=
65
- *observation{id, entity_id, text, metadata, created_at}
70
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"}
66
71
  `;
67
72
  const result = await this.db.run(query, entityId ? { entity_id: entityId } : {});
68
73
  const observations = result.rows;
@@ -71,15 +76,22 @@ class MemoryActivationService {
71
76
  // Extract access metadata
72
77
  const accessCount = (metadata?.access_count || 0);
73
78
  const lastAccessTime = (metadata?.last_access_time || created_at);
74
- // Calculate activation
79
+ // Extract emotional salience boosts (if present)
80
+ const salienceBoostStrength = (metadata?.salience_boost_strength || 1.0);
81
+ const salienceBoostDecay = (metadata?.salience_boost_decay || 0.0);
82
+ const salienceScore = (metadata?.emotional_salience || 0.0);
83
+ // Calculate activation with salience boosts
75
84
  const timeSinceAccess = this.getTimeSinceAccess(lastAccessTime);
76
- const strength = this.calculateStrength(accessCount);
77
- const activation = this.calculateActivation(timeSinceAccess, strength);
85
+ const strength = this.calculateStrength(accessCount, salienceBoostStrength);
86
+ const activation = this.calculateActivation(timeSinceAccess, strength, salienceBoostDecay);
78
87
  // Determine retention
79
88
  const shouldRetain = activation >= this.config.retentionThreshold;
89
+ const salienceInfo = salienceScore > 0
90
+ ? ` [Salience: ${salienceScore.toFixed(2)}, Boost: ×${salienceBoostStrength.toFixed(2)}]`
91
+ : '';
80
92
  const reason = shouldRetain
81
- ? `Active memory (activation: ${activation.toFixed(3)}, strength: ${strength.toFixed(1)})`
82
- : `Below threshold (activation: ${activation.toFixed(3)} < ${this.config.retentionThreshold})`;
93
+ ? `Active memory (activation: ${activation.toFixed(3)}, strength: ${strength.toFixed(1)})${salienceInfo}`
94
+ : `Below threshold (activation: ${activation.toFixed(3)} < ${this.config.retentionThreshold})${salienceInfo}`;
83
95
  scores.push({
84
96
  observationId: id,
85
97
  entityId: entity_id,
@@ -105,15 +117,15 @@ class MemoryActivationService {
105
117
  try {
106
118
  // Get current observation
107
119
  const result = await this.db.run(`
108
- ?[id, entity_id, text, metadata, created_at] :=
109
- *observation{id, entity_id, text, metadata, created_at},
120
+ ?[id, entity_id, session_id, task_id, text, metadata, created_at] :=
121
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"},
110
122
  id == $id
111
123
  `, { id: observationId });
112
124
  if (result.rows.length === 0) {
113
125
  console.warn(`[MemoryActivation] Observation ${observationId} not found`);
114
126
  return;
115
127
  }
116
- const [id, entity_id, text, metadata, created_at] = result.rows[0];
128
+ const [id, entity_id, session_id, task_id, text, metadata, created_at] = result.rows[0];
117
129
  const currentMetadata = (metadata || {});
118
130
  // Update access metadata
119
131
  const accessCount = (currentMetadata.access_count || 0) + 1;
@@ -123,16 +135,28 @@ class MemoryActivationService {
123
135
  access_count: accessCount,
124
136
  last_access_time: lastAccessTime,
125
137
  };
138
+ // Get embedding (we need it for the update)
139
+ const embResult = await this.db.run(`
140
+ ?[embedding] := *observation{id: $id, embedding, @ "NOW"}
141
+ `, { id: observationId });
142
+ if (embResult.rows.length === 0) {
143
+ console.warn(`[MemoryActivation] Could not get embedding for observation ${observationId}`);
144
+ return;
145
+ }
146
+ const embedding = embResult.rows[0][0];
126
147
  // Update observation with new metadata
127
148
  await this.db.run(`
128
- ?[id, entity_id, text, metadata, created_at] <- [
129
- [$id, $entity_id, $text, $metadata, $created_at]
149
+ ?[id, entity_id, session_id, task_id, text, embedding, metadata, created_at] <- [
150
+ [$id, $entity_id, $session_id, $task_id, $text, $embedding, $metadata, $created_at]
130
151
  ]
131
- :put observation {id, entity_id, text => metadata, created_at}
152
+ :put observation {id, created_at => entity_id, session_id, task_id, text, embedding, metadata}
132
153
  `, {
133
154
  id,
134
155
  entity_id,
156
+ session_id,
157
+ task_id,
135
158
  text,
159
+ embedding,
136
160
  metadata: updatedMetadata,
137
161
  created_at
138
162
  });
@@ -159,12 +183,12 @@ class MemoryActivationService {
159
183
  // Actually delete weak memories
160
184
  let pruned = 0;
161
185
  for (const candidate of candidates) {
162
- // Delete the observation
186
+ // Delete the observation using :rm
163
187
  await this.db.run(`
164
- ?[id, entity_id, text, metadata, created_at] :=
165
- *observation{id, entity_id, text, metadata, created_at},
188
+ ?[id, entity_id, session_id, task_id, text, metadata, created_at] :=
189
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"},
166
190
  id == $id
167
- :rm observation {id, entity_id, text => metadata, created_at}
191
+ :rm observation {id, created_at => entity_id, session_id, task_id, text, metadata}
168
192
  `, { id: candidate.observationId });
169
193
  pruned++;
170
194
  }
@@ -244,8 +268,8 @@ class MemoryActivationService {
244
268
  try {
245
269
  // Get the observation
246
270
  const result = await this.db.run(`
247
- ?[id, entity_id, text, metadata, created_at] :=
248
- *observation{id, entity_id, text, metadata, created_at},
271
+ ?[id, entity_id, session_id, task_id, text, metadata, created_at] :=
272
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"},
249
273
  id == $id
250
274
  `, { id: observationId });
251
275
  if (result.rows.length === 0) {
@@ -255,13 +279,13 @@ class MemoryActivationService {
255
279
  const [id, entity_id] = result.rows[0];
256
280
  // Get all observations for the same entity (excluding the current one)
257
281
  const relatedResult = await this.db.run(`
258
- ?[id, entity_id, text, metadata, created_at] :=
259
- *observation{id, entity_id, text, metadata, created_at},
282
+ ?[id, entity_id, session_id, task_id, text, metadata, created_at] :=
283
+ *observation{id, entity_id, session_id, task_id, text, metadata, created_at, @ "NOW"},
260
284
  entity_id == $entity_id,
261
285
  id != $id
262
286
  `, { entity_id, id: observationId });
263
287
  let boosted = 0;
264
- for (const [relId, relEntityId, relText, relMetadata, relCreatedAt] of relatedResult.rows) {
288
+ for (const [relId, relEntityId, relSessionId, relTaskId, relText, relMetadata, relCreatedAt] of relatedResult.rows) {
265
289
  const currentMetadata = (relMetadata || {});
266
290
  // Simulate a partial access (priming effect)
267
291
  const accessCount = (currentMetadata.access_count || 0) + boostFactor;
@@ -269,16 +293,26 @@ class MemoryActivationService {
269
293
  ...currentMetadata,
270
294
  access_count: accessCount,
271
295
  };
296
+ // Get embedding
297
+ const embResult = await this.db.run(`
298
+ ?[embedding] := *observation{id: $id, embedding, @ "NOW"}
299
+ `, { id: relId });
300
+ if (embResult.rows.length === 0)
301
+ continue;
302
+ const relEmbedding = embResult.rows[0][0];
272
303
  // Update the related observation
273
304
  await this.db.run(`
274
- ?[id, entity_id, text, metadata, created_at] <- [
275
- [$id, $entity_id, $text, $metadata, $created_at]
305
+ ?[id, entity_id, session_id, task_id, text, embedding, metadata, created_at] <- [
306
+ [$id, $entity_id, $session_id, $task_id, $text, $embedding, $metadata, $created_at]
276
307
  ]
277
- :put observation {id, entity_id, text => metadata, created_at}
308
+ :put observation {id, created_at => entity_id, session_id, task_id, text, embedding, metadata}
278
309
  `, {
279
310
  id: relId,
280
311
  entity_id: relEntityId,
312
+ session_id: relSessionId,
313
+ task_id: relTaskId,
281
314
  text: relText,
315
+ embedding: relEmbedding,
282
316
  metadata: updatedMetadata,
283
317
  created_at: relCreatedAt
284
318
  });
@@ -34,15 +34,51 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.MemoryService = void 0;
37
+ const emotional_salience_js_1 = require("./emotional-salience.js");
38
+ const zettelkasten_evolution_js_1 = require("./zettelkasten-evolution.js");
37
39
  const uuid_1 = require("uuid");
38
40
  const pdf_mjs_1 = require("pdfjs-dist/legacy/build/pdf.mjs");
39
41
  const fs = __importStar(require("fs"));
40
42
  class MemoryService {
41
43
  db;
42
44
  embeddings;
45
+ salienceService = null;
46
+ zettelkastenService = null;
43
47
  constructor(db, embeddings) {
44
48
  this.db = db;
45
49
  this.embeddings = embeddings;
50
+ // Services will be initialized when needed
51
+ }
52
+ getSalienceService() {
53
+ if (!this.salienceService) {
54
+ // Create a temporary CozoDb instance for salience service
55
+ // In production, this should use the actual CozoDB instance
56
+ const { CozoDb } = require('cozo-node');
57
+ const tempDb = new CozoDb();
58
+ this.salienceService = new emotional_salience_js_1.EmotionalSalienceService(tempDb, {
59
+ enableSalience: true,
60
+ salienceBoostFactor: 2.0,
61
+ decaySlowdownFactor: 0.5,
62
+ minSalienceThreshold: 0.3
63
+ });
64
+ }
65
+ return this.salienceService;
66
+ }
67
+ getZettelkastenService() {
68
+ if (!this.zettelkastenService) {
69
+ const { CozoDb } = require('cozo-node');
70
+ const tempDb = new CozoDb();
71
+ this.zettelkastenService = new zettelkasten_evolution_js_1.ZettelkastenEvolutionService(tempDb, this.embeddings, {
72
+ enableEvolution: true,
73
+ similarityThreshold: 0.7,
74
+ maxRelatedNotes: 5,
75
+ minKeywordFrequency: 2,
76
+ autoExtractKeywords: true,
77
+ autoBidirectionalLinks: true,
78
+ enrichmentDepth: 'shallow'
79
+ });
80
+ }
81
+ return this.zettelkastenService;
46
82
  }
47
83
  async createEntity(name, type, metadata = {}) {
48
84
  const id = (0, uuid_1.v4)();
@@ -73,6 +109,21 @@ class MemoryService {
73
109
  async addObservation(entityId, text, metadata = {}) {
74
110
  const id = (0, uuid_1.v4)();
75
111
  const embedding = await this.embeddings.embed(text);
112
+ // Calculate emotional salience automatically
113
+ const salienceService = this.getSalienceService();
114
+ const salienceResult = salienceService.calculateSalienceScore(text);
115
+ // Add salience metadata if score is above threshold
116
+ if (salienceResult.score >= 0.3) {
117
+ const boost = salienceService.calculateBoost(salienceResult.score);
118
+ metadata = {
119
+ ...metadata,
120
+ emotional_salience: salienceResult.score,
121
+ salience_category: salienceResult.category,
122
+ salience_keywords: salienceResult.keywords,
123
+ salience_boost_strength: boost.strengthMultiplier,
124
+ salience_boost_decay: boost.decayReduction
125
+ };
126
+ }
76
127
  const observation = {
77
128
  id: id,
78
129
  entity_id: entityId,
@@ -294,5 +345,22 @@ class MemoryService {
294
345
  total_chunks: chunks.length,
295
346
  };
296
347
  }
348
+ // Emotional Salience Methods
349
+ async getSalienceStats() {
350
+ return this.getSalienceService().getSalienceStats();
351
+ }
352
+ async getObservationSalience(observationId) {
353
+ return this.getSalienceService().getObservationSalience(observationId);
354
+ }
355
+ async applySalienceToExistingObservations(dryRun = false) {
356
+ return this.getSalienceService().applySalienceMetadata(dryRun);
357
+ }
358
+ // Zettelkasten Memory Evolution Methods
359
+ async getZettelkastenStats() {
360
+ return this.getZettelkastenService().getEvolutionStats();
361
+ }
362
+ async enrichObservationWithZettelkasten(observationId, observationText, observationEmbedding, entityId) {
363
+ return this.getZettelkastenService().enrichObservation(observationId, observationText, observationEmbedding, entityId);
364
+ }
297
365
  }
298
366
  exports.MemoryService = MemoryService;
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PreStorageReasoningService = void 0;
4
+ const ollama_1 = require("ollama");
5
+ class PreStorageReasoningService {
6
+ db;
7
+ config;
8
+ ollama;
9
+ constructor(db, config = {}) {
10
+ this.db = db;
11
+ this.config = {
12
+ enablePreStorage: true,
13
+ ollamaModel: 'llama3.2:3b',
14
+ ollamaHost: 'http://localhost:11434',
15
+ extractEntities: true,
16
+ extractRelationships: true,
17
+ detectContradictions: true,
18
+ minConfidence: 0.6,
19
+ timeout: 10000,
20
+ ...config
21
+ };
22
+ this.ollama = new ollama_1.Ollama({ host: this.config.ollamaHost });
23
+ }
24
+ /**
25
+ * Analyze observation text before storage
26
+ */
27
+ async analyzeBeforeStorage(text, entityId, existingContext) {
28
+ const startTime = Date.now();
29
+ if (!this.config.enablePreStorage) {
30
+ return {
31
+ text,
32
+ entitySuggestions: [],
33
+ relationshipSuggestions: [],
34
+ contradictionWarnings: [],
35
+ processingTime: 0,
36
+ model: 'disabled'
37
+ };
38
+ }
39
+ try {
40
+ const analysis = {
41
+ text,
42
+ entitySuggestions: [],
43
+ relationshipSuggestions: [],
44
+ contradictionWarnings: [],
45
+ processingTime: 0,
46
+ model: this.config.ollamaModel
47
+ };
48
+ // Get entity context
49
+ const entityContext = await this.getEntityContext(entityId);
50
+ // Extract entities
51
+ if (this.config.extractEntities) {
52
+ analysis.entitySuggestions = await this.extractEntities(text, entityContext);
53
+ }
54
+ // Extract relationships
55
+ if (this.config.extractRelationships) {
56
+ analysis.relationshipSuggestions = await this.extractRelationships(text, entityContext, analysis.entitySuggestions);
57
+ }
58
+ // Detect contradictions
59
+ if (this.config.detectContradictions && existingContext) {
60
+ analysis.contradictionWarnings = await this.detectContradictions(text, existingContext);
61
+ }
62
+ analysis.processingTime = Date.now() - startTime;
63
+ return analysis;
64
+ }
65
+ catch (error) {
66
+ console.error('[PreStorageReasoning] Error analyzing observation:', error);
67
+ return {
68
+ text,
69
+ entitySuggestions: [],
70
+ relationshipSuggestions: [],
71
+ contradictionWarnings: [],
72
+ processingTime: Date.now() - startTime,
73
+ model: this.config.ollamaModel
74
+ };
75
+ }
76
+ }
77
+ /**
78
+ * Extract implied entities from text
79
+ */
80
+ async extractEntities(text, entityContext) {
81
+ const prompt = `You are an entity extraction expert. Analyze the following text and extract any entities (people, organizations, technologies, concepts, etc.) that are mentioned or implied.
82
+
83
+ Context: ${entityContext}
84
+
85
+ Text to analyze: "${text}"
86
+
87
+ For each entity, provide:
88
+ 1. Name (exact name as it appears or should be named)
89
+ 2. Type (Person, Organization, Technology, Concept, Location, etc.)
90
+ 3. Confidence (0.0-1.0, how confident you are this is a distinct entity)
91
+ 4. Reason (brief explanation why this is an entity)
92
+
93
+ Respond ONLY with valid JSON array format:
94
+ [
95
+ {
96
+ "name": "Entity Name",
97
+ "type": "EntityType",
98
+ "confidence": 0.9,
99
+ "reason": "Brief reason"
100
+ }
101
+ ]
102
+
103
+ If no entities found, return empty array: []`;
104
+ try {
105
+ const response = await this.ollama.generate({
106
+ model: this.config.ollamaModel,
107
+ prompt,
108
+ stream: false,
109
+ options: {
110
+ temperature: 0.3,
111
+ num_predict: 500
112
+ }
113
+ });
114
+ const jsonMatch = response.response.match(/\[[\s\S]*\]/);
115
+ if (!jsonMatch) {
116
+ return [];
117
+ }
118
+ const entities = JSON.parse(jsonMatch[0]);
119
+ return entities.filter(e => e.confidence >= this.config.minConfidence);
120
+ }
121
+ catch (error) {
122
+ console.error('[PreStorageReasoning] Error extracting entities:', error);
123
+ return [];
124
+ }
125
+ }
126
+ /**
127
+ * Extract relationships from text
128
+ */
129
+ async extractRelationships(text, entityContext, extractedEntities) {
130
+ const entitiesStr = extractedEntities.length > 0
131
+ ? `\nExtracted entities: ${extractedEntities.map(e => e.name).join(', ')}`
132
+ : '';
133
+ const prompt = `You are a relationship extraction expert. Analyze the following text and extract relationships between entities.
134
+
135
+ Context: ${entityContext}${entitiesStr}
136
+
137
+ Text to analyze: "${text}"
138
+
139
+ For each relationship, provide:
140
+ 1. fromEntity (source entity name)
141
+ 2. toEntity (target entity name)
142
+ 3. relationType (works_on, uses, manages, part_of, related_to, etc.)
143
+ 4. Confidence (0.0-1.0)
144
+ 5. Reason (brief explanation)
145
+
146
+ Respond ONLY with valid JSON array format:
147
+ [
148
+ {
149
+ "fromEntity": "Entity A",
150
+ "toEntity": "Entity B",
151
+ "relationType": "relationship_type",
152
+ "confidence": 0.8,
153
+ "reason": "Brief reason"
154
+ }
155
+ ]
156
+
157
+ If no relationships found, return empty array: []`;
158
+ try {
159
+ const response = await this.ollama.generate({
160
+ model: this.config.ollamaModel,
161
+ prompt,
162
+ stream: false,
163
+ options: {
164
+ temperature: 0.3,
165
+ num_predict: 500
166
+ }
167
+ });
168
+ const jsonMatch = response.response.match(/\[[\s\S]*\]/);
169
+ if (!jsonMatch) {
170
+ return [];
171
+ }
172
+ const relationships = JSON.parse(jsonMatch[0]);
173
+ return relationships.filter(r => r.confidence >= this.config.minConfidence);
174
+ }
175
+ catch (error) {
176
+ console.error('[PreStorageReasoning] Error extracting relationships:', error);
177
+ return [];
178
+ }
179
+ }
180
+ /**
181
+ * Detect contradictions with existing observations
182
+ */
183
+ async detectContradictions(newText, existingObservations) {
184
+ if (existingObservations.length === 0) {
185
+ return [];
186
+ }
187
+ const existingContext = existingObservations.slice(0, 5).join('\n- ');
188
+ const prompt = `You are a contradiction detection expert. Compare the new observation with existing observations and identify any contradictions.
189
+
190
+ Existing observations:
191
+ - ${existingContext}
192
+
193
+ New observation: "${newText}"
194
+
195
+ For each contradiction, provide:
196
+ 1. existingObservation (the contradicting observation text)
197
+ 2. contradiction (what contradicts)
198
+ 3. Confidence (0.0-1.0)
199
+ 4. Reason (brief explanation)
200
+
201
+ Respond ONLY with valid JSON array format:
202
+ [
203
+ {
204
+ "existingObservation": "Previous observation text",
205
+ "contradiction": "What contradicts",
206
+ "confidence": 0.9,
207
+ "reason": "Brief reason"
208
+ }
209
+ ]
210
+
211
+ If no contradictions found, return empty array: []`;
212
+ try {
213
+ const response = await this.ollama.generate({
214
+ model: this.config.ollamaModel,
215
+ prompt,
216
+ stream: false,
217
+ options: {
218
+ temperature: 0.3,
219
+ num_predict: 500
220
+ }
221
+ });
222
+ const jsonMatch = response.response.match(/\[[\s\S]*\]/);
223
+ if (!jsonMatch) {
224
+ return [];
225
+ }
226
+ const contradictions = JSON.parse(jsonMatch[0]);
227
+ return contradictions.filter(c => c.confidence >= this.config.minConfidence);
228
+ }
229
+ catch (error) {
230
+ console.error('[PreStorageReasoning] Error detecting contradictions:', error);
231
+ return [];
232
+ }
233
+ }
234
+ /**
235
+ * Get entity context for analysis
236
+ */
237
+ async getEntityContext(entityId) {
238
+ try {
239
+ const result = await this.db.run(`
240
+ ?[name, type] := *entity{id, name, type}, id = $id
241
+ `, { id: entityId });
242
+ if (result.rows.length === 0) {
243
+ return 'No entity context available';
244
+ }
245
+ const [name, type] = result.rows[0];
246
+ return `Entity: ${name} (${type})`;
247
+ }
248
+ catch (error) {
249
+ return 'No entity context available';
250
+ }
251
+ }
252
+ /**
253
+ * Get existing observations for contradiction detection
254
+ */
255
+ async getExistingObservations(entityId, limit = 10) {
256
+ try {
257
+ const result = await this.db.run(`
258
+ ?[text] := *observation{entity_id, text}, entity_id = $entity_id
259
+ :limit $limit
260
+ `, { entity_id: entityId, limit });
261
+ return result.rows.map((row) => row[0]);
262
+ }
263
+ catch (error) {
264
+ console.error('[PreStorageReasoning] Error getting existing observations:', error);
265
+ return [];
266
+ }
267
+ }
268
+ /**
269
+ * Auto-apply suggestions (create entities and relationships)
270
+ */
271
+ async autoApplySuggestions(analysis, autoCreateEntities = false, autoCreateRelationships = false) {
272
+ let entitiesCreated = 0;
273
+ let relationshipsCreated = 0;
274
+ // Auto-create entities
275
+ if (autoCreateEntities) {
276
+ for (const entity of analysis.entitySuggestions) {
277
+ try {
278
+ // Check if entity already exists
279
+ const existing = await this.db.run(`
280
+ ?[id] := *entity{id, name}, name = $name
281
+ :limit 1
282
+ `, { name: entity.name });
283
+ if (existing.rows.length === 0) {
284
+ // Create new entity
285
+ const id = `entity-${Math.random().toString(36).substring(7)}`;
286
+ await this.db.run(`
287
+ ?[id, name, type, metadata] <- [[
288
+ $id,
289
+ $name,
290
+ $type,
291
+ {"source": "pre_storage_reasoning", "confidence": $confidence}
292
+ ]]
293
+ :put entity {id, name, type}
294
+ `, {
295
+ id,
296
+ name: entity.name,
297
+ type: entity.type,
298
+ confidence: entity.confidence
299
+ });
300
+ entitiesCreated++;
301
+ }
302
+ }
303
+ catch (error) {
304
+ console.error(`[PreStorageReasoning] Error creating entity ${entity.name}:`, error);
305
+ }
306
+ }
307
+ }
308
+ // Auto-create relationships
309
+ if (autoCreateRelationships) {
310
+ for (const rel of analysis.relationshipSuggestions) {
311
+ try {
312
+ // Find entity IDs
313
+ const fromResult = await this.db.run(`
314
+ ?[id] := *entity{id, name}, name = $name
315
+ :limit 1
316
+ `, { name: rel.fromEntity });
317
+ const toResult = await this.db.run(`
318
+ ?[id] := *entity{id, name}, name = $name
319
+ :limit 1
320
+ `, { name: rel.toEntity });
321
+ if (fromResult.rows.length > 0 && toResult.rows.length > 0) {
322
+ const fromId = fromResult.rows[0][0];
323
+ const toId = toResult.rows[0][0];
324
+ // Create relationship
325
+ await this.db.run(`
326
+ ?[from_id, to_id, relation_type, strength, metadata] <- [[
327
+ $from_id,
328
+ $to_id,
329
+ $relation_type,
330
+ $confidence,
331
+ {"source": "pre_storage_reasoning"}
332
+ ]]
333
+ :put relationship {from_id, to_id, relation_type, strength, metadata}
334
+ `, {
335
+ from_id: fromId,
336
+ to_id: toId,
337
+ relation_type: rel.relationType,
338
+ confidence: rel.confidence
339
+ });
340
+ relationshipsCreated++;
341
+ }
342
+ }
343
+ catch (error) {
344
+ console.error(`[PreStorageReasoning] Error creating relationship:`, error);
345
+ }
346
+ }
347
+ }
348
+ return { entitiesCreated, relationshipsCreated };
349
+ }
350
+ }
351
+ exports.PreStorageReasoningService = PreStorageReasoningService;