@twelvehart/supermemory-runtime 1.0.0-next.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/.env.example +57 -0
- package/README.md +374 -0
- package/dist/index.js +189 -0
- package/dist/mcp/index.js +1132 -0
- package/docker-compose.prod.yml +91 -0
- package/docker-compose.yml +358 -0
- package/drizzle/0000_dapper_the_professor.sql +159 -0
- package/drizzle/0001_api_keys.sql +51 -0
- package/drizzle/meta/0000_snapshot.json +1532 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +20 -0
- package/package.json +114 -0
- package/scripts/add-extraction-job.ts +122 -0
- package/scripts/benchmark-pgvector.ts +122 -0
- package/scripts/bootstrap.sh +209 -0
- package/scripts/check-runtime-pack.ts +111 -0
- package/scripts/claude-mcp-config.ts +336 -0
- package/scripts/docker-entrypoint.sh +183 -0
- package/scripts/doctor.ts +377 -0
- package/scripts/init-db.sql +33 -0
- package/scripts/install.sh +1110 -0
- package/scripts/mcp-setup.ts +271 -0
- package/scripts/migrations/001_create_pgvector_extension.sql +31 -0
- package/scripts/migrations/002_create_memory_embeddings_table.sql +75 -0
- package/scripts/migrations/003_create_hnsw_index.sql +94 -0
- package/scripts/migrations/004_create_memory_embeddings_standalone.sql +70 -0
- package/scripts/migrations/005_create_chunks_table.sql +95 -0
- package/scripts/migrations/006_create_processing_queue.sql +45 -0
- package/scripts/migrations/generate_test_data.sql +42 -0
- package/scripts/migrations/phase1_comprehensive_test.sql +204 -0
- package/scripts/migrations/run_migrations.sh +286 -0
- package/scripts/migrations/test_hnsw_index.sql +255 -0
- package/scripts/pre-commit-secrets +282 -0
- package/scripts/run-extraction-worker.ts +46 -0
- package/scripts/run-phase1-tests.sh +291 -0
- package/scripts/setup.ts +222 -0
- package/scripts/smoke-install.sh +12 -0
- package/scripts/test-health-endpoint.sh +328 -0
- package/src/api/index.ts +2 -0
- package/src/api/middleware/auth.ts +80 -0
- package/src/api/middleware/csrf.ts +308 -0
- package/src/api/middleware/errorHandler.ts +166 -0
- package/src/api/middleware/rateLimit.ts +360 -0
- package/src/api/middleware/validation.ts +514 -0
- package/src/api/routes/documents.ts +286 -0
- package/src/api/routes/profiles.ts +237 -0
- package/src/api/routes/search.ts +71 -0
- package/src/api/stores/index.ts +58 -0
- package/src/config/bootstrap-env.ts +3 -0
- package/src/config/env.ts +71 -0
- package/src/config/feature-flags.ts +25 -0
- package/src/config/index.ts +140 -0
- package/src/config/secrets.config.ts +291 -0
- package/src/db/client.ts +92 -0
- package/src/db/index.ts +73 -0
- package/src/db/postgres.ts +72 -0
- package/src/db/schema/chunks.schema.ts +31 -0
- package/src/db/schema/containers.schema.ts +46 -0
- package/src/db/schema/documents.schema.ts +49 -0
- package/src/db/schema/embeddings.schema.ts +32 -0
- package/src/db/schema/index.ts +11 -0
- package/src/db/schema/memories.schema.ts +72 -0
- package/src/db/schema/profiles.schema.ts +34 -0
- package/src/db/schema/queue.schema.ts +59 -0
- package/src/db/schema/relationships.schema.ts +42 -0
- package/src/db/schema.ts +223 -0
- package/src/db/worker-connection.ts +47 -0
- package/src/index.ts +235 -0
- package/src/mcp/CLAUDE.md +1 -0
- package/src/mcp/index.ts +1380 -0
- package/src/mcp/legacyState.ts +22 -0
- package/src/mcp/rateLimit.ts +358 -0
- package/src/mcp/resources.ts +309 -0
- package/src/mcp/results.ts +104 -0
- package/src/mcp/tools.ts +401 -0
- package/src/queues/config.ts +119 -0
- package/src/queues/index.ts +289 -0
- package/src/sdk/client.ts +225 -0
- package/src/sdk/errors.ts +266 -0
- package/src/sdk/http.ts +560 -0
- package/src/sdk/index.ts +244 -0
- package/src/sdk/resources/base.ts +65 -0
- package/src/sdk/resources/connections.ts +204 -0
- package/src/sdk/resources/documents.ts +163 -0
- package/src/sdk/resources/index.ts +10 -0
- package/src/sdk/resources/memories.ts +150 -0
- package/src/sdk/resources/search.ts +60 -0
- package/src/sdk/resources/settings.ts +36 -0
- package/src/sdk/types.ts +674 -0
- package/src/services/chunking/index.ts +451 -0
- package/src/services/chunking.service.ts +650 -0
- package/src/services/csrf.service.ts +252 -0
- package/src/services/documents.repository.ts +219 -0
- package/src/services/documents.service.ts +191 -0
- package/src/services/embedding.service.ts +404 -0
- package/src/services/extraction.service.ts +300 -0
- package/src/services/extractors/code.extractor.ts +451 -0
- package/src/services/extractors/index.ts +9 -0
- package/src/services/extractors/markdown.extractor.ts +461 -0
- package/src/services/extractors/pdf.extractor.ts +315 -0
- package/src/services/extractors/text.extractor.ts +118 -0
- package/src/services/extractors/url.extractor.ts +243 -0
- package/src/services/index.ts +235 -0
- package/src/services/ingestion.service.ts +177 -0
- package/src/services/llm/anthropic.ts +400 -0
- package/src/services/llm/base.ts +460 -0
- package/src/services/llm/contradiction-detector.service.ts +526 -0
- package/src/services/llm/heuristics.ts +148 -0
- package/src/services/llm/index.ts +309 -0
- package/src/services/llm/memory-classifier.service.ts +383 -0
- package/src/services/llm/memory-extension-detector.service.ts +523 -0
- package/src/services/llm/mock.ts +470 -0
- package/src/services/llm/openai.ts +398 -0
- package/src/services/llm/prompts.ts +438 -0
- package/src/services/llm/types.ts +373 -0
- package/src/services/memory.repository.ts +1769 -0
- package/src/services/memory.service.ts +1338 -0
- package/src/services/memory.types.ts +234 -0
- package/src/services/persistence/index.ts +295 -0
- package/src/services/pipeline.service.ts +509 -0
- package/src/services/profile.repository.ts +436 -0
- package/src/services/profile.service.ts +560 -0
- package/src/services/profile.types.ts +270 -0
- package/src/services/relationships/detector.ts +1128 -0
- package/src/services/relationships/index.ts +268 -0
- package/src/services/relationships/memory-integration.ts +459 -0
- package/src/services/relationships/strategies.ts +132 -0
- package/src/services/relationships/types.ts +370 -0
- package/src/services/search.service.ts +761 -0
- package/src/services/search.types.ts +220 -0
- package/src/services/secrets.service.ts +384 -0
- package/src/services/vectorstore/base.ts +327 -0
- package/src/services/vectorstore/index.ts +444 -0
- package/src/services/vectorstore/memory.ts +286 -0
- package/src/services/vectorstore/migration.ts +295 -0
- package/src/services/vectorstore/mock.ts +403 -0
- package/src/services/vectorstore/pgvector.ts +695 -0
- package/src/services/vectorstore/types.ts +247 -0
- package/src/startup.ts +389 -0
- package/src/types/api.types.ts +193 -0
- package/src/types/document.types.ts +103 -0
- package/src/types/index.ts +241 -0
- package/src/types/profile.base.ts +133 -0
- package/src/utils/errors.ts +447 -0
- package/src/utils/id.ts +15 -0
- package/src/utils/index.ts +101 -0
- package/src/utils/logger.ts +313 -0
- package/src/utils/sanitization.ts +501 -0
- package/src/utils/secret-validation.ts +273 -0
- package/src/utils/synonyms.ts +188 -0
- package/src/utils/validation.ts +581 -0
- package/src/workers/chunking.worker.ts +242 -0
- package/src/workers/embedding.worker.ts +358 -0
- package/src/workers/extraction.worker.ts +346 -0
- package/src/workers/indexing.worker.ts +505 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Service Integration for Embedding-Based Relationship Detection
|
|
3
|
+
*
|
|
4
|
+
* Provides integration helpers and an enhanced memory service wrapper
|
|
5
|
+
* that uses embedding-based relationship detection instead of regex patterns.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Memory, Relationship, MemoryServiceConfig } from '../memory.types.js'
|
|
9
|
+
import type { EmbeddingService } from '../embedding.service.js'
|
|
10
|
+
import { getEmbeddingService } from '../embedding.service.js'
|
|
11
|
+
import { MemoryService, getMemoryService } from '../memory.service.js'
|
|
12
|
+
import { type MemoryRepository, getMemoryRepository } from '../memory.repository.js'
|
|
13
|
+
import { getLogger } from '../../utils/logger.js'
|
|
14
|
+
import type { RelationshipConfig, RelationshipDetectionResult, Contradiction, LLMProvider } from './types.js'
|
|
15
|
+
import {
|
|
16
|
+
EmbeddingRelationshipDetector,
|
|
17
|
+
InMemoryVectorStoreAdapter,
|
|
18
|
+
createEmbeddingRelationshipDetector,
|
|
19
|
+
} from './detector.js'
|
|
20
|
+
import { getSharedVectorStore } from './index.js'
|
|
21
|
+
import { isEmbeddingRelationshipsEnabled } from '../../config/feature-flags.js'
|
|
22
|
+
|
|
23
|
+
const logger = getLogger('EnhancedMemoryService')
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Enhanced Memory Service Configuration
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configuration for the enhanced memory service
|
|
31
|
+
*/
|
|
32
|
+
export interface EnhancedMemoryServiceConfig extends MemoryServiceConfig {
|
|
33
|
+
/** Configuration for embedding-based relationship detection */
|
|
34
|
+
relationshipDetection: Partial<RelationshipConfig>
|
|
35
|
+
|
|
36
|
+
/** Whether to use embedding-based detection (true) or regex (false) */
|
|
37
|
+
useEmbeddingDetection: boolean
|
|
38
|
+
|
|
39
|
+
/** Whether to automatically index memories for relationship detection */
|
|
40
|
+
autoIndexMemories: boolean
|
|
41
|
+
|
|
42
|
+
/** Whether to detect contradictions */
|
|
43
|
+
detectContradictions: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Default enhanced configuration
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_ENHANCED_CONFIG: Partial<EnhancedMemoryServiceConfig> = {
|
|
50
|
+
useEmbeddingDetection: isEmbeddingRelationshipsEnabled(),
|
|
51
|
+
autoIndexMemories: isEmbeddingRelationshipsEnabled(),
|
|
52
|
+
detectContradictions: true,
|
|
53
|
+
relationshipDetection: {
|
|
54
|
+
maxCandidates: 50,
|
|
55
|
+
enableLLMVerification: false,
|
|
56
|
+
enableContradictionDetection: true,
|
|
57
|
+
enableCausalDetection: true,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Enhanced Memory Service
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Enhanced Memory Service with embedding-based relationship detection.
|
|
67
|
+
*
|
|
68
|
+
* This extends the base MemoryService to use vector similarity for
|
|
69
|
+
* relationship detection instead of regex patterns.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const service = createEnhancedMemoryService();
|
|
74
|
+
*
|
|
75
|
+
* // Process content with embedding-based relationship detection
|
|
76
|
+
* const result = await service.processAndStoreMemoriesEnhanced(content, {
|
|
77
|
+
* containerTag: 'user-123',
|
|
78
|
+
* detectRelationships: true,
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* console.log(`Found ${result.relationships.length} relationships`);
|
|
82
|
+
* console.log(`Contradictions: ${result.contradictions.length}`);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export class EnhancedMemoryService {
|
|
86
|
+
private readonly baseService: MemoryService
|
|
87
|
+
private readonly repository: MemoryRepository
|
|
88
|
+
private readonly embeddingService: EmbeddingService
|
|
89
|
+
private readonly relationshipDetector: EmbeddingRelationshipDetector
|
|
90
|
+
private readonly config: EnhancedMemoryServiceConfig
|
|
91
|
+
private readonly vectorStore: InMemoryVectorStoreAdapter
|
|
92
|
+
|
|
93
|
+
constructor(
|
|
94
|
+
config: Partial<EnhancedMemoryServiceConfig> = {},
|
|
95
|
+
dependencies?: {
|
|
96
|
+
baseService?: MemoryService
|
|
97
|
+
repository?: MemoryRepository
|
|
98
|
+
embeddingService?: EmbeddingService
|
|
99
|
+
vectorStore?: InMemoryVectorStoreAdapter
|
|
100
|
+
llmProvider?: LLMProvider
|
|
101
|
+
}
|
|
102
|
+
) {
|
|
103
|
+
// Merge configuration
|
|
104
|
+
this.config = {
|
|
105
|
+
...DEFAULT_ENHANCED_CONFIG,
|
|
106
|
+
...config,
|
|
107
|
+
} as EnhancedMemoryServiceConfig
|
|
108
|
+
|
|
109
|
+
// Set up dependencies
|
|
110
|
+
this.baseService = dependencies?.baseService ?? getMemoryService(config)
|
|
111
|
+
this.repository = dependencies?.repository ?? getMemoryRepository()
|
|
112
|
+
this.embeddingService = dependencies?.embeddingService ?? getEmbeddingService()
|
|
113
|
+
this.vectorStore = dependencies?.vectorStore ?? getSharedVectorStore()
|
|
114
|
+
|
|
115
|
+
// Create relationship detector
|
|
116
|
+
this.relationshipDetector = createEmbeddingRelationshipDetector(
|
|
117
|
+
this.embeddingService,
|
|
118
|
+
this.vectorStore,
|
|
119
|
+
this.config.relationshipDetection,
|
|
120
|
+
dependencies?.llmProvider
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
logger.debug('EnhancedMemoryService initialized', {
|
|
124
|
+
useEmbeddingDetection: this.config.useEmbeddingDetection,
|
|
125
|
+
autoIndexMemories: this.config.autoIndexMemories,
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Enhanced Processing Methods
|
|
131
|
+
// ============================================================================
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Process and store memories with embedding-based relationship detection.
|
|
135
|
+
*
|
|
136
|
+
* This is the main entry point that replaces the base service's
|
|
137
|
+
* processAndStoreMemories method with enhanced detection.
|
|
138
|
+
*/
|
|
139
|
+
async processAndStoreMemoriesEnhanced(
|
|
140
|
+
content: string,
|
|
141
|
+
options: {
|
|
142
|
+
containerTag?: string
|
|
143
|
+
sourceId?: string
|
|
144
|
+
detectRelationships?: boolean
|
|
145
|
+
detectContradictions?: boolean
|
|
146
|
+
} = {}
|
|
147
|
+
): Promise<{
|
|
148
|
+
memories: Memory[]
|
|
149
|
+
relationships: Relationship[]
|
|
150
|
+
supersededMemoryIds: string[]
|
|
151
|
+
contradictions: Contradiction[]
|
|
152
|
+
detectionResults: RelationshipDetectionResult[]
|
|
153
|
+
}> {
|
|
154
|
+
const shouldDetectRelationships = options.detectRelationships ?? this.config.autoDetectRelationships
|
|
155
|
+
const shouldDetectContradictions = options.detectContradictions ?? this.config.detectContradictions
|
|
156
|
+
|
|
157
|
+
if (!this.config.useEmbeddingDetection || !shouldDetectRelationships) {
|
|
158
|
+
const baseResult = await this.baseService.processAndStoreMemories(content, {
|
|
159
|
+
containerTag: options.containerTag,
|
|
160
|
+
sourceId: options.sourceId,
|
|
161
|
+
detectRelationships: shouldDetectRelationships,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
...baseResult,
|
|
166
|
+
contradictions: [],
|
|
167
|
+
detectionResults: [],
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
logger.debug('Processing content with enhanced detection', {
|
|
172
|
+
containerTag: options.containerTag,
|
|
173
|
+
detectRelationships: shouldDetectRelationships,
|
|
174
|
+
detectContradictions: shouldDetectContradictions,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// Step 1: Extract memories using base service
|
|
178
|
+
const extractedMemories = await this.baseService.extractMemories(content)
|
|
179
|
+
|
|
180
|
+
// Update container tags and source info
|
|
181
|
+
for (const memory of extractedMemories) {
|
|
182
|
+
memory.containerTag = options.containerTag ?? this.config.defaultContainerTag
|
|
183
|
+
if (options.sourceId) {
|
|
184
|
+
memory.sourceId = options.sourceId
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Step 2: Generate embeddings for extracted memories
|
|
189
|
+
const embeddings = await this.embeddingService.batchEmbed(extractedMemories.map((m) => m.content))
|
|
190
|
+
|
|
191
|
+
// Attach embeddings to memories
|
|
192
|
+
for (let i = 0; i < extractedMemories.length; i++) {
|
|
193
|
+
const memory = extractedMemories[i]
|
|
194
|
+
const embedding = embeddings[i]
|
|
195
|
+
if (memory && embedding) {
|
|
196
|
+
memory.embedding = embedding
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const allRelationships: Relationship[] = []
|
|
201
|
+
const allSupersededIds: string[] = []
|
|
202
|
+
const allContradictions: Contradiction[] = []
|
|
203
|
+
const detectionResults: RelationshipDetectionResult[] = []
|
|
204
|
+
|
|
205
|
+
// Step 3: Process each memory
|
|
206
|
+
for (const memory of extractedMemories) {
|
|
207
|
+
// Store the memory
|
|
208
|
+
await this.repository.create(memory)
|
|
209
|
+
|
|
210
|
+
// Index for future relationship detection
|
|
211
|
+
if (this.config.autoIndexMemories && memory.embedding) {
|
|
212
|
+
this.vectorStore.addMemory(memory, memory.embedding)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Detect relationships if enabled
|
|
216
|
+
if (shouldDetectRelationships) {
|
|
217
|
+
const result = await this.relationshipDetector.detectRelationships(memory, {
|
|
218
|
+
containerTag: options.containerTag,
|
|
219
|
+
excludeIds: extractedMemories.map((m) => m.id),
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
detectionResults.push(result)
|
|
223
|
+
|
|
224
|
+
// Process detected relationships
|
|
225
|
+
for (const detected of result.relationships) {
|
|
226
|
+
allRelationships.push(detected.relationship)
|
|
227
|
+
|
|
228
|
+
// Mark superseded memories
|
|
229
|
+
if (detected.relationship.type === 'updates' || detected.relationship.type === 'supersedes') {
|
|
230
|
+
const target = await this.repository.findById(detected.relationship.targetMemoryId)
|
|
231
|
+
if (target && memory.containerTag && target.containerTag && memory.containerTag !== target.containerTag) {
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
await this.repository.markSuperseded(detected.relationship.targetMemoryId, memory.id)
|
|
236
|
+
allSupersededIds.push(detected.relationship.targetMemoryId)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Store relationships
|
|
241
|
+
if (result.relationships.length > 0) {
|
|
242
|
+
await this.repository.createRelationshipBatch(result.relationships.map((r) => r.relationship))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Collect contradictions
|
|
246
|
+
if (shouldDetectContradictions) {
|
|
247
|
+
allContradictions.push(...result.contradictions)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
logger.info('Enhanced processing complete', {
|
|
253
|
+
memoriesCount: extractedMemories.length,
|
|
254
|
+
relationshipsCount: allRelationships.length,
|
|
255
|
+
supersededCount: allSupersededIds.length,
|
|
256
|
+
contradictionsCount: allContradictions.length,
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
memories: extractedMemories,
|
|
261
|
+
relationships: allRelationships,
|
|
262
|
+
supersededMemoryIds: [...new Set(allSupersededIds)],
|
|
263
|
+
contradictions: allContradictions,
|
|
264
|
+
detectionResults,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Detect relationships for a single memory using embedding similarity.
|
|
270
|
+
*/
|
|
271
|
+
async detectRelationshipsEmbedding(
|
|
272
|
+
memory: Memory,
|
|
273
|
+
options: {
|
|
274
|
+
containerTag?: string
|
|
275
|
+
excludeIds?: string[]
|
|
276
|
+
} = {}
|
|
277
|
+
): Promise<RelationshipDetectionResult> {
|
|
278
|
+
// Ensure memory has embedding
|
|
279
|
+
if (!memory.embedding || memory.embedding.length === 0) {
|
|
280
|
+
memory.embedding = await this.embeddingService.generateEmbedding(memory.content)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return this.relationshipDetector.detectRelationships(memory, options)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Detect contradictions among a group of memories.
|
|
288
|
+
*/
|
|
289
|
+
async detectContradictions(memories: Memory[]): Promise<Contradiction[]> {
|
|
290
|
+
return this.relationshipDetector.detectContradictionsInGroup(memories)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Index a memory for relationship detection.
|
|
295
|
+
*/
|
|
296
|
+
async indexMemory(memory: Memory): Promise<void> {
|
|
297
|
+
const embedding = memory.embedding ?? (await this.embeddingService.generateEmbedding(memory.content))
|
|
298
|
+
this.vectorStore.addMemory(memory, embedding)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Batch index memories for relationship detection.
|
|
303
|
+
*/
|
|
304
|
+
async batchIndexMemories(memories: Memory[]): Promise<void> {
|
|
305
|
+
const memoriesToEmbed = memories.filter((m) => !m.embedding || m.embedding.length === 0)
|
|
306
|
+
|
|
307
|
+
if (memoriesToEmbed.length > 0) {
|
|
308
|
+
const embeddings = await this.embeddingService.batchEmbed(memoriesToEmbed.map((m) => m.content))
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < memoriesToEmbed.length; i++) {
|
|
311
|
+
const memory = memoriesToEmbed[i]
|
|
312
|
+
const embedding = embeddings[i]
|
|
313
|
+
if (memory && embedding) {
|
|
314
|
+
memory.embedding = embedding
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const items = memories.map((m) => ({
|
|
320
|
+
memory: m,
|
|
321
|
+
embedding: m.embedding!,
|
|
322
|
+
}))
|
|
323
|
+
|
|
324
|
+
this.vectorStore.addMemories(items)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Remove a memory from the relationship index.
|
|
329
|
+
*/
|
|
330
|
+
removeFromIndex(memoryId: string): boolean {
|
|
331
|
+
return this.vectorStore.removeMemory(memoryId)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Clear the relationship index.
|
|
336
|
+
*/
|
|
337
|
+
clearIndex(): void {
|
|
338
|
+
this.vectorStore.clear()
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Delegation Methods (pass through to base service)
|
|
343
|
+
// ============================================================================
|
|
344
|
+
|
|
345
|
+
async extractMemories(content: string): Promise<Memory[]> {
|
|
346
|
+
return this.baseService.extractMemories(content)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
classifyMemoryType(content: string) {
|
|
350
|
+
return this.baseService.classifyMemoryType(content)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async storeMemory(memory: Memory): Promise<Memory> {
|
|
354
|
+
const stored = await this.baseService.storeMemory(memory)
|
|
355
|
+
|
|
356
|
+
// Auto-index if enabled
|
|
357
|
+
if (this.config.autoIndexMemories) {
|
|
358
|
+
await this.indexMemory(stored)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return stored
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async getMemory(id: string): Promise<Memory | null> {
|
|
365
|
+
return this.baseService.getMemory(id)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async getAllMemories(): Promise<Memory[]> {
|
|
369
|
+
return this.baseService.getAllMemories()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async getLatestMemories(): Promise<Memory[]> {
|
|
373
|
+
return this.baseService.getLatestMemories()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ============================================================================
|
|
377
|
+
// Configuration
|
|
378
|
+
// ============================================================================
|
|
379
|
+
|
|
380
|
+
getConfig(): EnhancedMemoryServiceConfig {
|
|
381
|
+
return { ...this.config }
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Update relationship detection configuration.
|
|
386
|
+
*/
|
|
387
|
+
updateRelationshipConfig(updates: Partial<RelationshipConfig>): void {
|
|
388
|
+
this.relationshipDetector.updateConfig(updates)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Set LLM provider for verification.
|
|
393
|
+
*/
|
|
394
|
+
setLLMProvider(provider: LLMProvider): void {
|
|
395
|
+
this.relationshipDetector.setLLMProvider(provider)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get the underlying relationship detector.
|
|
400
|
+
*/
|
|
401
|
+
getRelationshipDetector(): EmbeddingRelationshipDetector {
|
|
402
|
+
return this.relationshipDetector
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Get the underlying base memory service.
|
|
407
|
+
*/
|
|
408
|
+
getBaseService(): MemoryService {
|
|
409
|
+
return this.baseService
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ============================================================================
|
|
414
|
+
// Factory Functions
|
|
415
|
+
// ============================================================================
|
|
416
|
+
|
|
417
|
+
let _enhancedServiceInstance: EnhancedMemoryService | null = null
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Create an enhanced memory service instance.
|
|
421
|
+
*/
|
|
422
|
+
export function createEnhancedMemoryService(
|
|
423
|
+
config?: Partial<EnhancedMemoryServiceConfig>,
|
|
424
|
+
dependencies?: {
|
|
425
|
+
baseService?: MemoryService
|
|
426
|
+
repository?: MemoryRepository
|
|
427
|
+
embeddingService?: EmbeddingService
|
|
428
|
+
vectorStore?: InMemoryVectorStoreAdapter
|
|
429
|
+
llmProvider?: LLMProvider
|
|
430
|
+
}
|
|
431
|
+
): EnhancedMemoryService {
|
|
432
|
+
return new EnhancedMemoryService(config, dependencies)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Get the singleton enhanced memory service instance.
|
|
437
|
+
*/
|
|
438
|
+
export function getEnhancedMemoryService(config?: Partial<EnhancedMemoryServiceConfig>): EnhancedMemoryService {
|
|
439
|
+
if (!_enhancedServiceInstance) {
|
|
440
|
+
_enhancedServiceInstance = createEnhancedMemoryService(config)
|
|
441
|
+
}
|
|
442
|
+
return _enhancedServiceInstance
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Reset the singleton instance (for testing).
|
|
447
|
+
*/
|
|
448
|
+
export function resetEnhancedMemoryService(): void {
|
|
449
|
+
_enhancedServiceInstance = null
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Proxy-based lazy singleton for backwards compatibility.
|
|
454
|
+
*/
|
|
455
|
+
export const enhancedMemoryService = new Proxy({} as EnhancedMemoryService, {
|
|
456
|
+
get(_, prop) {
|
|
457
|
+
return getEnhancedMemoryService()[prop as keyof EnhancedMemoryService]
|
|
458
|
+
},
|
|
459
|
+
})
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relationship Detection Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* This file has been simplified to contain only the core helper functions.
|
|
5
|
+
* The strategy pattern has been removed as it was over-engineered - all access
|
|
6
|
+
* went through HybridStrategy which internally called 3 strategies.
|
|
7
|
+
*
|
|
8
|
+
* The detection logic has been inlined directly into EmbeddingRelationshipDetector
|
|
9
|
+
* as private methods, reducing complexity by ~400 LOC.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { RelationshipType } from '../../types/index.js'
|
|
13
|
+
import type { Memory, Relationship } from '../memory.types.js'
|
|
14
|
+
import { generateId } from '../../utils/id.js'
|
|
15
|
+
import type { DetectedRelationship, RelationshipCandidate, DetectionStrategyType } from './types.js'
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Helper Functions
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a detected relationship object
|
|
23
|
+
*/
|
|
24
|
+
export function createDetectedRelationship(
|
|
25
|
+
sourceMemory: Memory,
|
|
26
|
+
targetMemory: Memory,
|
|
27
|
+
type: RelationshipType,
|
|
28
|
+
candidate: RelationshipCandidate,
|
|
29
|
+
strategy: string,
|
|
30
|
+
llmVerified: boolean = false,
|
|
31
|
+
llmConfidence?: number
|
|
32
|
+
): DetectedRelationship {
|
|
33
|
+
// Validate and cast strategy to DetectionStrategyType
|
|
34
|
+
const validStrategy: DetectionStrategyType =
|
|
35
|
+
strategy === 'similarity' ||
|
|
36
|
+
strategy === 'temporal' ||
|
|
37
|
+
strategy === 'entityOverlap' ||
|
|
38
|
+
strategy === 'llmVerification' ||
|
|
39
|
+
strategy === 'hybrid'
|
|
40
|
+
? strategy
|
|
41
|
+
: 'hybrid'
|
|
42
|
+
|
|
43
|
+
const relationship: Relationship = {
|
|
44
|
+
id: generateId(),
|
|
45
|
+
sourceMemoryId: sourceMemory.id,
|
|
46
|
+
targetMemoryId: targetMemory.id,
|
|
47
|
+
type,
|
|
48
|
+
confidence: candidate.combinedScore,
|
|
49
|
+
description: `${type} relationship detected via ${strategy} strategy`,
|
|
50
|
+
createdAt: new Date(),
|
|
51
|
+
metadata: {
|
|
52
|
+
vectorSimilarity: candidate.vectorSimilarity,
|
|
53
|
+
entityOverlap: candidate.entityOverlap,
|
|
54
|
+
temporalScore: candidate.temporalScore,
|
|
55
|
+
detectionStrategy: strategy,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
relationship,
|
|
61
|
+
score: candidate.combinedScore,
|
|
62
|
+
vectorSimilarity: candidate.vectorSimilarity,
|
|
63
|
+
entityOverlap: candidate.entityOverlap,
|
|
64
|
+
temporalScore: candidate.temporalScore,
|
|
65
|
+
llmVerified,
|
|
66
|
+
llmConfidence,
|
|
67
|
+
detectionStrategy: validStrategy,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if content contains update/correction indicators
|
|
73
|
+
*/
|
|
74
|
+
export function hasUpdateIndicators(content: string): boolean {
|
|
75
|
+
const patterns = [
|
|
76
|
+
/\b(?:update|updated|updating|correction|corrected)\b/i,
|
|
77
|
+
/\b(?:now|actually|instead)\b/i,
|
|
78
|
+
/\b(?:changed|revised|modified)\b/i,
|
|
79
|
+
/\b(?:no longer|used to be|previously)\b/i,
|
|
80
|
+
]
|
|
81
|
+
return patterns.some((p) => p.test(content))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if content contains extension indicators
|
|
86
|
+
*/
|
|
87
|
+
export function hasExtensionIndicators(content: string): boolean {
|
|
88
|
+
const patterns = [
|
|
89
|
+
/\b(?:also|additionally|furthermore|moreover)\b/i,
|
|
90
|
+
/\b(?:in addition|on top of|besides)\b/i,
|
|
91
|
+
/\b(?:extending|building on|adding to)\b/i,
|
|
92
|
+
]
|
|
93
|
+
return patterns.some((p) => p.test(content))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if content contains contradiction indicators
|
|
98
|
+
*/
|
|
99
|
+
export function hasContradictionIndicators(content: string): boolean {
|
|
100
|
+
const patterns = [
|
|
101
|
+
/\b(?:however|but|although|despite)\b/i,
|
|
102
|
+
/\b(?:contrary|opposite|different)\b/i,
|
|
103
|
+
/\b(?:not true|incorrect|wrong|false)\b/i,
|
|
104
|
+
/\b(?:disagree|dispute|reject)\b/i,
|
|
105
|
+
]
|
|
106
|
+
return patterns.some((p) => p.test(content))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if content contains supersession indicators
|
|
111
|
+
*/
|
|
112
|
+
export function hasSupersessionIndicators(content: string): boolean {
|
|
113
|
+
const patterns = [
|
|
114
|
+
/\b(?:replaces|supersedes|overrides)\b/i,
|
|
115
|
+
/\b(?:no longer|obsolete|deprecated)\b/i,
|
|
116
|
+
/\b(?:new version|latest|current)\b/i,
|
|
117
|
+
]
|
|
118
|
+
return patterns.some((p) => p.test(content))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if content contains causal/derivation indicators
|
|
123
|
+
*/
|
|
124
|
+
export function hasCausalIndicators(content: string): boolean {
|
|
125
|
+
const patterns = [
|
|
126
|
+
/\b(?:therefore|thus|hence|consequently)\b/i,
|
|
127
|
+
/\b(?:because|since|as a result)\b/i,
|
|
128
|
+
/\b(?:based on|derived from|follows from)\b/i,
|
|
129
|
+
/\b(?:leads to|results in|causes)\b/i,
|
|
130
|
+
]
|
|
131
|
+
return patterns.some((p) => p.test(content))
|
|
132
|
+
}
|