@soulcraft/brainy 1.5.0 → 2.0.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/LICENSE +2 -2
  3. package/README.md +201 -596
  4. package/bin/brainy-interactive.js +564 -0
  5. package/bin/brainy-ts.js +18 -0
  6. package/bin/brainy.js +672 -81
  7. package/dist/augmentationPipeline.d.ts +48 -220
  8. package/dist/augmentationPipeline.js +60 -508
  9. package/dist/augmentationRegistry.d.ts +22 -31
  10. package/dist/augmentationRegistry.js +28 -79
  11. package/dist/augmentations/apiServerAugmentation.d.ts +108 -0
  12. package/dist/augmentations/apiServerAugmentation.js +502 -0
  13. package/dist/augmentations/batchProcessingAugmentation.d.ts +95 -0
  14. package/dist/augmentations/batchProcessingAugmentation.js +567 -0
  15. package/dist/augmentations/brainyAugmentation.d.ts +153 -0
  16. package/dist/augmentations/brainyAugmentation.js +145 -0
  17. package/dist/augmentations/cacheAugmentation.d.ts +105 -0
  18. package/dist/augmentations/cacheAugmentation.js +238 -0
  19. package/dist/augmentations/conduitAugmentations.d.ts +54 -156
  20. package/dist/augmentations/conduitAugmentations.js +156 -1082
  21. package/dist/augmentations/connectionPoolAugmentation.d.ts +62 -0
  22. package/dist/augmentations/connectionPoolAugmentation.js +316 -0
  23. package/dist/augmentations/defaultAugmentations.d.ts +53 -0
  24. package/dist/augmentations/defaultAugmentations.js +88 -0
  25. package/dist/augmentations/entityRegistryAugmentation.d.ts +126 -0
  26. package/dist/augmentations/entityRegistryAugmentation.js +386 -0
  27. package/dist/augmentations/indexAugmentation.d.ts +117 -0
  28. package/dist/augmentations/indexAugmentation.js +284 -0
  29. package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +152 -0
  30. package/dist/augmentations/intelligentVerbScoringAugmentation.js +554 -0
  31. package/dist/augmentations/metricsAugmentation.d.ts +202 -0
  32. package/dist/augmentations/metricsAugmentation.js +291 -0
  33. package/dist/augmentations/monitoringAugmentation.d.ts +94 -0
  34. package/dist/augmentations/monitoringAugmentation.js +227 -0
  35. package/dist/augmentations/neuralImport.d.ts +50 -117
  36. package/dist/augmentations/neuralImport.js +255 -629
  37. package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +52 -0
  38. package/dist/augmentations/requestDeduplicatorAugmentation.js +162 -0
  39. package/dist/augmentations/serverSearchAugmentations.d.ts +43 -22
  40. package/dist/augmentations/serverSearchAugmentations.js +125 -72
  41. package/dist/augmentations/storageAugmentation.d.ts +54 -0
  42. package/dist/augmentations/storageAugmentation.js +93 -0
  43. package/dist/augmentations/storageAugmentations.d.ts +96 -0
  44. package/dist/augmentations/storageAugmentations.js +182 -0
  45. package/dist/augmentations/synapseAugmentation.d.ts +156 -0
  46. package/dist/augmentations/synapseAugmentation.js +312 -0
  47. package/dist/augmentations/walAugmentation.d.ts +108 -0
  48. package/dist/augmentations/walAugmentation.js +515 -0
  49. package/dist/brainyData.d.ts +404 -130
  50. package/dist/brainyData.js +1331 -853
  51. package/dist/chat/BrainyChat.d.ts +16 -8
  52. package/dist/chat/BrainyChat.js +60 -32
  53. package/dist/chat/ChatCLI.d.ts +1 -1
  54. package/dist/chat/ChatCLI.js +6 -6
  55. package/dist/cli/catalog.d.ts +3 -3
  56. package/dist/cli/catalog.js +116 -70
  57. package/dist/cli/commands/core.d.ts +61 -0
  58. package/dist/cli/commands/core.js +348 -0
  59. package/dist/cli/commands/neural.d.ts +25 -0
  60. package/dist/cli/commands/neural.js +508 -0
  61. package/dist/cli/commands/utility.d.ts +37 -0
  62. package/dist/cli/commands/utility.js +276 -0
  63. package/dist/cli/index.d.ts +7 -0
  64. package/dist/cli/index.js +167 -0
  65. package/dist/cli/interactive.d.ts +164 -0
  66. package/dist/cli/interactive.js +542 -0
  67. package/dist/cortex/neuralImport.js +5 -5
  68. package/dist/critical/model-guardian.js +11 -4
  69. package/dist/embeddings/lightweight-embedder.d.ts +23 -0
  70. package/dist/embeddings/lightweight-embedder.js +136 -0
  71. package/dist/embeddings/universal-memory-manager.d.ts +38 -0
  72. package/dist/embeddings/universal-memory-manager.js +206 -0
  73. package/dist/embeddings/worker-embedding.d.ts +7 -0
  74. package/dist/embeddings/worker-embedding.js +77 -0
  75. package/dist/embeddings/worker-manager.d.ts +28 -0
  76. package/dist/embeddings/worker-manager.js +162 -0
  77. package/dist/examples/basicUsage.js +7 -7
  78. package/dist/graph/pathfinding.d.ts +78 -0
  79. package/dist/graph/pathfinding.js +393 -0
  80. package/dist/hnsw/hnswIndex.d.ts +13 -0
  81. package/dist/hnsw/hnswIndex.js +35 -0
  82. package/dist/hnsw/hnswIndexOptimized.d.ts +1 -0
  83. package/dist/hnsw/hnswIndexOptimized.js +3 -0
  84. package/dist/index.d.ts +9 -11
  85. package/dist/index.js +21 -11
  86. package/dist/indices/fieldIndex.d.ts +76 -0
  87. package/dist/indices/fieldIndex.js +357 -0
  88. package/dist/mcp/brainyMCPAdapter.js +3 -2
  89. package/dist/mcp/mcpAugmentationToolset.js +11 -17
  90. package/dist/neural/embeddedPatterns.d.ts +41 -0
  91. package/dist/neural/embeddedPatterns.js +4044 -0
  92. package/dist/neural/naturalLanguageProcessor.d.ts +94 -0
  93. package/dist/neural/naturalLanguageProcessor.js +317 -0
  94. package/dist/neural/naturalLanguageProcessorStatic.d.ts +64 -0
  95. package/dist/neural/naturalLanguageProcessorStatic.js +151 -0
  96. package/dist/neural/neuralAPI.d.ts +255 -0
  97. package/dist/neural/neuralAPI.js +612 -0
  98. package/dist/neural/patternLibrary.d.ts +101 -0
  99. package/dist/neural/patternLibrary.js +313 -0
  100. package/dist/neural/patterns.d.ts +27 -0
  101. package/dist/neural/patterns.js +68 -0
  102. package/dist/neural/staticPatternMatcher.d.ts +35 -0
  103. package/dist/neural/staticPatternMatcher.js +153 -0
  104. package/dist/scripts/precomputePatternEmbeddings.d.ts +19 -0
  105. package/dist/scripts/precomputePatternEmbeddings.js +100 -0
  106. package/dist/storage/adapters/fileSystemStorage.d.ts +5 -0
  107. package/dist/storage/adapters/fileSystemStorage.js +20 -0
  108. package/dist/storage/adapters/s3CompatibleStorage.d.ts +5 -0
  109. package/dist/storage/adapters/s3CompatibleStorage.js +16 -0
  110. package/dist/storage/enhancedClearOperations.d.ts +83 -0
  111. package/dist/storage/enhancedClearOperations.js +345 -0
  112. package/dist/storage/storageFactory.js +31 -27
  113. package/dist/triple/TripleIntelligence.d.ts +134 -0
  114. package/dist/triple/TripleIntelligence.js +548 -0
  115. package/dist/types/augmentations.d.ts +45 -344
  116. package/dist/types/augmentations.js +5 -2
  117. package/dist/types/brainyDataInterface.d.ts +20 -10
  118. package/dist/types/graphTypes.d.ts +46 -0
  119. package/dist/types/graphTypes.js +16 -2
  120. package/dist/utils/BoundedRegistry.d.ts +29 -0
  121. package/dist/utils/BoundedRegistry.js +54 -0
  122. package/dist/utils/embedding.js +20 -3
  123. package/dist/utils/hybridModelManager.js +10 -5
  124. package/dist/utils/metadataFilter.d.ts +33 -19
  125. package/dist/utils/metadataFilter.js +58 -23
  126. package/dist/utils/metadataIndex.d.ts +37 -6
  127. package/dist/utils/metadataIndex.js +427 -64
  128. package/dist/utils/requestDeduplicator.d.ts +10 -0
  129. package/dist/utils/requestDeduplicator.js +24 -0
  130. package/dist/utils/unifiedCache.d.ts +103 -0
  131. package/dist/utils/unifiedCache.js +311 -0
  132. package/package.json +43 -128
  133. package/scripts/ensure-models.js +108 -0
  134. package/scripts/prepare-models.js +387 -0
  135. package/OFFLINE_MODELS.md +0 -56
  136. package/dist/intelligence/neuralEngine.d.ts +0 -207
  137. package/dist/intelligence/neuralEngine.js +0 -706
  138. package/dist/utils/modelLoader.d.ts +0 -32
  139. package/dist/utils/modelLoader.js +0 -219
  140. package/dist/utils/modelManager.d.ts +0 -77
  141. package/dist/utils/modelManager.js +0 -219
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Triple Intelligence Engine
3
+ * Revolutionary unified search combining Vector + Graph + Field intelligence
4
+ *
5
+ * This is Brainy's killer feature - no other database can do this!
6
+ */
7
+ import { Vector, SearchResult } from '../coreTypes.js';
8
+ import { BrainyData } from '../brainyData.js';
9
+ export interface TripleQuery {
10
+ like?: string | Vector | any;
11
+ similar?: string | Vector | any;
12
+ connected?: {
13
+ to?: string | string[];
14
+ from?: string | string[];
15
+ type?: string | string[];
16
+ depth?: number;
17
+ maxDepth?: number;
18
+ direction?: 'in' | 'out' | 'both';
19
+ };
20
+ where?: Record<string, any>;
21
+ limit?: number;
22
+ offset?: number;
23
+ mode?: 'auto' | 'vector' | 'graph' | 'metadata' | 'fusion';
24
+ boost?: 'recent' | 'popular' | 'verified' | string;
25
+ explain?: boolean;
26
+ threshold?: number;
27
+ }
28
+ export interface TripleResult extends SearchResult {
29
+ vectorScore?: number;
30
+ graphScore?: number;
31
+ fieldScore?: number;
32
+ fusionScore: number;
33
+ explanation?: {
34
+ plan: string;
35
+ timing: Record<string, number>;
36
+ boosts: string[];
37
+ };
38
+ }
39
+ export interface QueryPlan {
40
+ startWith: 'vector' | 'graph' | 'field';
41
+ canParallelize: boolean;
42
+ estimatedCost: number;
43
+ steps: QueryStep[];
44
+ }
45
+ export interface QueryStep {
46
+ type: 'vector' | 'graph' | 'field' | 'fusion';
47
+ operation: string;
48
+ estimated: number;
49
+ }
50
+ /**
51
+ * The Triple Intelligence Engine
52
+ * Unifies vector, graph, and field search into one beautiful API
53
+ */
54
+ export declare class TripleIntelligenceEngine {
55
+ private brain;
56
+ private planCache;
57
+ constructor(brain: BrainyData);
58
+ /**
59
+ * The magic happens here - one query to rule them all
60
+ */
61
+ find(query: TripleQuery): Promise<TripleResult[]>;
62
+ /**
63
+ * Generate optimal execution plan based on query shape
64
+ */
65
+ private optimizeQuery;
66
+ /**
67
+ * Execute searches in parallel for maximum speed
68
+ */
69
+ private parallelSearch;
70
+ /**
71
+ * Progressive filtering for efficiency
72
+ */
73
+ private progressiveSearch;
74
+ /**
75
+ * Vector similarity search
76
+ */
77
+ private vectorSearch;
78
+ /**
79
+ * Graph traversal
80
+ */
81
+ private graphTraversal;
82
+ /**
83
+ * Field-based filtering
84
+ */
85
+ private fieldFilter;
86
+ /**
87
+ * Fusion ranking combines all signals
88
+ */
89
+ private fusionRank;
90
+ /**
91
+ * Check if a filter is selective enough to use first
92
+ */
93
+ private isSelectiveFilter;
94
+ /**
95
+ * Apply field filter to existing candidates
96
+ */
97
+ private applyFieldFilter;
98
+ /**
99
+ * Check a single condition
100
+ */
101
+ private checkCondition;
102
+ /**
103
+ * Vector search within specific candidates
104
+ */
105
+ private vectorSearchWithin;
106
+ /**
107
+ * Expand graph from candidates
108
+ */
109
+ private graphExpand;
110
+ /**
111
+ * Apply boost strategies
112
+ */
113
+ private applyBoosts;
114
+ /**
115
+ * Add query explanations for debugging
116
+ */
117
+ private addExplanations;
118
+ /**
119
+ * Optimize plan based on historical patterns
120
+ */
121
+ /**
122
+ * Execute single signal query without fusion
123
+ */
124
+ private executeSingleSignal;
125
+ /**
126
+ * Clear query optimization cache
127
+ */
128
+ clearCache(): void;
129
+ /**
130
+ * Get optimization statistics
131
+ */
132
+ getStats(): any;
133
+ }
134
+ export declare function find(brain: BrainyData, query: TripleQuery): Promise<TripleResult[]>;
@@ -0,0 +1,548 @@
1
+ /**
2
+ * Triple Intelligence Engine
3
+ * Revolutionary unified search combining Vector + Graph + Field intelligence
4
+ *
5
+ * This is Brainy's killer feature - no other database can do this!
6
+ */
7
+ /**
8
+ * The Triple Intelligence Engine
9
+ * Unifies vector, graph, and field search into one beautiful API
10
+ */
11
+ export class TripleIntelligenceEngine {
12
+ constructor(brain) {
13
+ this.planCache = new Map();
14
+ this.brain = brain;
15
+ // Query history removed - unnecessary complexity for minimal gain
16
+ }
17
+ /**
18
+ * The magic happens here - one query to rule them all
19
+ */
20
+ async find(query) {
21
+ const startTime = Date.now();
22
+ // Generate optimal query plan
23
+ const plan = await this.optimizeQuery(query);
24
+ // Execute based on plan
25
+ let results;
26
+ if (plan.canParallelize) {
27
+ // Run all three paths in parallel for maximum speed
28
+ results = await this.parallelSearch(query, plan);
29
+ }
30
+ else {
31
+ // Progressive filtering for efficiency
32
+ results = await this.progressiveSearch(query, plan);
33
+ }
34
+ // Apply boosts if requested
35
+ if (query.boost) {
36
+ results = this.applyBoosts(results, query.boost);
37
+ }
38
+ // Add explanations if requested
39
+ if (query.explain) {
40
+ const timing = Date.now() - startTime;
41
+ results = this.addExplanations(results, plan, timing);
42
+ }
43
+ // Query history removed - no learning needed
44
+ // Apply limit
45
+ if (query.limit) {
46
+ results = results.slice(0, query.limit);
47
+ }
48
+ return results;
49
+ }
50
+ /**
51
+ * Generate optimal execution plan based on query shape
52
+ */
53
+ async optimizeQuery(query) {
54
+ // Short-circuit optimization for single-signal queries
55
+ const hasVector = !!(query.like || query.similar);
56
+ const hasGraph = !!(query.connected);
57
+ const hasField = !!(query.where && Object.keys(query.where).length > 0);
58
+ const signalCount = [hasVector, hasGraph, hasField].filter(Boolean).length;
59
+ // Single signal - skip fusion entirely!
60
+ if (signalCount === 1) {
61
+ const singleType = hasVector ? 'vector' : hasGraph ? 'graph' : 'field';
62
+ return {
63
+ startWith: singleType,
64
+ canParallelize: false,
65
+ estimatedCost: 1,
66
+ steps: [{
67
+ type: singleType,
68
+ operation: 'direct', // Direct execution, no fusion
69
+ estimated: 50
70
+ }]
71
+ };
72
+ }
73
+ // Check cache first
74
+ const cacheKey = JSON.stringify(query);
75
+ if (this.planCache.has(cacheKey)) {
76
+ return this.planCache.get(cacheKey);
77
+ }
78
+ // Multiple operations - optimize
79
+ let plan;
80
+ if (hasField && this.isSelectiveFilter(query.where)) {
81
+ // Start with field filter if it's selective
82
+ plan = {
83
+ startWith: 'field',
84
+ canParallelize: false,
85
+ estimatedCost: 2,
86
+ steps: [
87
+ { type: 'field', operation: 'filter', estimated: 50 },
88
+ { type: hasVector ? 'vector' : 'graph', operation: 'search', estimated: 200 },
89
+ { type: 'fusion', operation: 'rank', estimated: 50 }
90
+ ]
91
+ };
92
+ }
93
+ else if (hasVector && hasGraph) {
94
+ // Parallelize vector and graph for speed
95
+ plan = {
96
+ startWith: 'vector',
97
+ canParallelize: true,
98
+ estimatedCost: 3,
99
+ steps: [
100
+ { type: 'vector', operation: 'search', estimated: 150 },
101
+ { type: 'graph', operation: 'traverse', estimated: 150 },
102
+ { type: 'field', operation: 'filter', estimated: 50 },
103
+ { type: 'fusion', operation: 'rank', estimated: 100 }
104
+ ]
105
+ };
106
+ }
107
+ else {
108
+ // Default progressive plan
109
+ plan = {
110
+ startWith: 'vector',
111
+ canParallelize: false,
112
+ estimatedCost: 2,
113
+ steps: [
114
+ { type: 'vector', operation: 'search', estimated: 150 },
115
+ { type: hasGraph ? 'graph' : 'field', operation: 'filter', estimated: 100 },
116
+ { type: 'fusion', operation: 'rank', estimated: 50 }
117
+ ]
118
+ };
119
+ }
120
+ // Query history removed - use default plan
121
+ this.planCache.set(cacheKey, plan);
122
+ return plan;
123
+ }
124
+ /**
125
+ * Execute searches in parallel for maximum speed
126
+ */
127
+ async parallelSearch(query, plan) {
128
+ // Check for single-signal optimization
129
+ if (plan.steps.length === 1 && plan.steps[0].operation === 'direct') {
130
+ // Skip fusion for single signal queries
131
+ const results = await this.executeSingleSignal(query, plan.steps[0].type);
132
+ return results.map(r => ({
133
+ ...r,
134
+ fusionScore: r.score || 1.0,
135
+ score: r.score || 1.0
136
+ }));
137
+ }
138
+ const tasks = [];
139
+ // Vector search
140
+ if (query.like || query.similar) {
141
+ tasks.push(this.vectorSearch(query.like || query.similar, query.limit));
142
+ }
143
+ // Graph traversal
144
+ if (query.connected) {
145
+ tasks.push(this.graphTraversal(query.connected));
146
+ }
147
+ // Field filtering
148
+ if (query.where) {
149
+ tasks.push(this.fieldFilter(query.where));
150
+ }
151
+ // Run all in parallel
152
+ const results = await Promise.all(tasks);
153
+ // Fusion ranking combines all signals
154
+ return this.fusionRank(results, query);
155
+ }
156
+ /**
157
+ * Progressive filtering for efficiency
158
+ */
159
+ async progressiveSearch(query, plan) {
160
+ let candidates = [];
161
+ for (const step of plan.steps) {
162
+ switch (step.type) {
163
+ case 'field':
164
+ if (candidates.length === 0) {
165
+ // Initial field filter
166
+ candidates = await this.fieldFilter(query.where);
167
+ }
168
+ else {
169
+ // Filter existing candidates
170
+ candidates = this.applyFieldFilter(candidates, query.where);
171
+ }
172
+ break;
173
+ case 'vector':
174
+ if (candidates.length === 0) {
175
+ // Initial vector search
176
+ const results = await this.vectorSearch(query.like || query.similar, query.limit);
177
+ candidates = results;
178
+ }
179
+ else {
180
+ // Vector search within candidates
181
+ candidates = await this.vectorSearchWithin(query.like || query.similar, candidates);
182
+ }
183
+ break;
184
+ case 'graph':
185
+ if (candidates.length === 0) {
186
+ // Initial graph traversal
187
+ candidates = await this.graphTraversal(query.connected);
188
+ }
189
+ else {
190
+ // Graph expansion from candidates
191
+ candidates = await this.graphExpand(candidates, query.connected);
192
+ }
193
+ break;
194
+ case 'fusion':
195
+ // Final fusion ranking
196
+ return this.fusionRank([candidates], query);
197
+ }
198
+ }
199
+ return candidates;
200
+ }
201
+ /**
202
+ * Vector similarity search
203
+ */
204
+ async vectorSearch(query, limit) {
205
+ // Use clean internal vector search to avoid circular dependency
206
+ // This is the proper architecture: find() uses internal methods, not public search()
207
+ return this.brain._internalVectorSearch(query, limit || 100);
208
+ }
209
+ /**
210
+ * Graph traversal
211
+ */
212
+ async graphTraversal(connected) {
213
+ const results = [];
214
+ // Get starting nodes
215
+ const startNodes = connected.from ?
216
+ (Array.isArray(connected.from) ? connected.from : [connected.from]) :
217
+ connected.to ?
218
+ (Array.isArray(connected.to) ? connected.to : [connected.to]) :
219
+ [];
220
+ // Traverse graph
221
+ for (const nodeId of startNodes) {
222
+ // Get verbs connected to this node (both as source and target)
223
+ const [sourceVerbs, targetVerbs] = await Promise.all([
224
+ this.brain.getVerbsBySource(nodeId),
225
+ this.brain.getVerbsByTarget(nodeId)
226
+ ]);
227
+ const allVerbs = [...sourceVerbs, ...targetVerbs];
228
+ const connections = allVerbs.map((v) => ({
229
+ id: v.targetId === nodeId ? v.sourceId : v.targetId,
230
+ type: v.type,
231
+ score: v.weight || 0.5
232
+ }));
233
+ results.push(...connections);
234
+ }
235
+ return results;
236
+ }
237
+ /**
238
+ * Field-based filtering
239
+ */
240
+ async fieldFilter(where) {
241
+ // CRITICAL OPTIMIZATION: Use MetadataIndex directly for O(log n) performance!
242
+ // NOT vector search which would be O(n) and slow
243
+ if (!where || Object.keys(where).length === 0) {
244
+ // Return all items (should use a more efficient method)
245
+ const allNouns = this.brain.index.getNouns();
246
+ return Array.from(allNouns.keys()).slice(0, 1000).map(id => ({ id, score: 1.0 }));
247
+ }
248
+ // Use the MetadataIndex directly for FAST field queries!
249
+ // This uses B-tree indexes for O(log n) range queries
250
+ // and hash indexes for O(1) exact matches
251
+ const matchingIds = await this.brain.metadataIndex?.getIdsForFilter(where) || [];
252
+ // Convert to result format with metadata
253
+ const results = [];
254
+ for (const id of matchingIds.slice(0, 1000)) {
255
+ const noun = await this.brain.getNoun(id);
256
+ if (noun) {
257
+ results.push({
258
+ id,
259
+ score: 1.0, // Field matches are binary - either match or don't
260
+ metadata: noun.metadata || {}
261
+ });
262
+ }
263
+ }
264
+ return results;
265
+ }
266
+ /**
267
+ * Fusion ranking combines all signals
268
+ */
269
+ fusionRank(resultSets, query) {
270
+ // PERFORMANCE CRITICAL: When metadata filters are present, use INTERSECTION not UNION
271
+ // This ensures O(log n) performance with millions of items
272
+ // Determine which result sets we have based on query
273
+ let vectorResultsIdx = -1;
274
+ let graphResultsIdx = -1;
275
+ let metadataResultsIdx = -1;
276
+ let currentIdx = 0;
277
+ if (query.like || query.similar) {
278
+ vectorResultsIdx = currentIdx++;
279
+ }
280
+ if (query.connected) {
281
+ graphResultsIdx = currentIdx++;
282
+ }
283
+ if (query.where) {
284
+ metadataResultsIdx = currentIdx++;
285
+ }
286
+ // If we have metadata filters AND other searches, apply intersection
287
+ if (metadataResultsIdx >= 0 && resultSets.length > 1) {
288
+ const metadataResults = resultSets[metadataResultsIdx];
289
+ // CRITICAL: If metadata filter returned no results, entire query should return empty
290
+ // This ensures correct behavior for non-matching filters
291
+ if (metadataResults.length === 0) {
292
+ // Return empty results immediately
293
+ return [];
294
+ }
295
+ const metadataIds = new Set(metadataResults.map(r => r.id || r));
296
+ // Filter ALL other result sets to only include items that match metadata
297
+ for (let i = 0; i < resultSets.length; i++) {
298
+ if (i !== metadataResultsIdx) {
299
+ resultSets[i] = resultSets[i].filter(r => metadataIds.has(r.id || r));
300
+ }
301
+ }
302
+ }
303
+ // Combine and deduplicate results
304
+ const allResults = new Map();
305
+ // Need to capture indices for closure
306
+ const vectorIdx = vectorResultsIdx;
307
+ const graphIdx = graphResultsIdx;
308
+ const metadataIdx = metadataResultsIdx;
309
+ // Process each result set
310
+ resultSets.forEach((results, index) => {
311
+ const weight = 1.0 / resultSets.length;
312
+ results.forEach(r => {
313
+ const id = r.id || r;
314
+ if (!allResults.has(id)) {
315
+ allResults.set(id, {
316
+ ...r,
317
+ id,
318
+ vectorScore: 0,
319
+ graphScore: 0,
320
+ fieldScore: 0,
321
+ fusionScore: 0
322
+ });
323
+ }
324
+ const result = allResults.get(id);
325
+ // Assign scores based on source (using the indices we calculated)
326
+ if (index === vectorIdx) {
327
+ result.vectorScore = r.score || 1.0;
328
+ }
329
+ else if (index === graphIdx) {
330
+ result.graphScore = r.score || 1.0;
331
+ }
332
+ else if (index === metadataIdx) {
333
+ result.fieldScore = r.score || 1.0;
334
+ }
335
+ });
336
+ });
337
+ // Calculate fusion scores
338
+ const results = Array.from(allResults.values());
339
+ results.forEach(r => {
340
+ // Weighted combination of signals
341
+ const vectorWeight = (query.like || query.similar) ? 0.4 : 0;
342
+ const graphWeight = query.connected ? 0.3 : 0;
343
+ const fieldWeight = query.where ? 0.3 : 0;
344
+ // Normalize weights
345
+ const totalWeight = vectorWeight + graphWeight + fieldWeight;
346
+ if (totalWeight > 0) {
347
+ r.fusionScore = ((r.vectorScore || 0) * vectorWeight +
348
+ (r.graphScore || 0) * graphWeight +
349
+ (r.fieldScore || 0) * fieldWeight) / totalWeight;
350
+ }
351
+ else {
352
+ r.fusionScore = r.score || 0;
353
+ }
354
+ });
355
+ // Sort by fusion score
356
+ results.sort((a, b) => b.fusionScore - a.fusionScore);
357
+ return results;
358
+ }
359
+ /**
360
+ * Check if a filter is selective enough to use first
361
+ */
362
+ isSelectiveFilter(where) {
363
+ // Heuristic: filters with exact matches or small ranges are selective
364
+ for (const [key, value] of Object.entries(where)) {
365
+ if (typeof value === 'object' && value !== null) {
366
+ // Check for operators that are selective
367
+ if (value.equals || value.is || value.oneOf) {
368
+ return true;
369
+ }
370
+ if (value.between && Array.isArray(value.between)) {
371
+ const [min, max] = value.between;
372
+ if (typeof min === 'number' && typeof max === 'number') {
373
+ // Small numeric range is selective
374
+ if ((max - min) / Math.max(Math.abs(min), Math.abs(max), 1) < 0.1) {
375
+ return true;
376
+ }
377
+ }
378
+ }
379
+ }
380
+ else {
381
+ // Exact match is selective
382
+ return true;
383
+ }
384
+ }
385
+ return false;
386
+ }
387
+ /**
388
+ * Apply field filter to existing candidates
389
+ */
390
+ applyFieldFilter(candidates, where) {
391
+ return candidates.filter(c => {
392
+ for (const [key, condition] of Object.entries(where)) {
393
+ const value = c.metadata?.[key] ?? c[key];
394
+ if (typeof condition === 'object' && condition !== null) {
395
+ // Handle operators
396
+ for (const [op, operand] of Object.entries(condition)) {
397
+ if (!this.checkCondition(value, op, operand)) {
398
+ return false;
399
+ }
400
+ }
401
+ }
402
+ else {
403
+ // Direct equality
404
+ if (value !== condition) {
405
+ return false;
406
+ }
407
+ }
408
+ }
409
+ return true;
410
+ });
411
+ }
412
+ /**
413
+ * Check a single condition
414
+ */
415
+ checkCondition(value, operator, operand) {
416
+ switch (operator) {
417
+ case 'equals':
418
+ case 'is':
419
+ return value === operand;
420
+ case 'greaterThan':
421
+ return value > operand;
422
+ case 'lessThan':
423
+ return value < operand;
424
+ case 'oneOf':
425
+ return Array.isArray(operand) && operand.includes(value);
426
+ case 'contains':
427
+ return Array.isArray(value) && value.includes(operand);
428
+ default:
429
+ return true;
430
+ }
431
+ }
432
+ /**
433
+ * Vector search within specific candidates
434
+ */
435
+ async vectorSearchWithin(query, candidates) {
436
+ const ids = candidates.map(c => c.id || c);
437
+ return this.brain.searchWithinItems(query, ids, candidates.length);
438
+ }
439
+ /**
440
+ * Expand graph from candidates
441
+ */
442
+ async graphExpand(candidates, connected) {
443
+ const expanded = [];
444
+ for (const candidate of candidates) {
445
+ // Get verbs connected to this candidate
446
+ const nodeId = candidate.id || candidate;
447
+ const [sourceVerbs, targetVerbs] = await Promise.all([
448
+ this.brain.getVerbsBySource(nodeId),
449
+ this.brain.getVerbsByTarget(nodeId)
450
+ ]);
451
+ const allVerbs = [...sourceVerbs, ...targetVerbs];
452
+ const connections = allVerbs.map((v) => ({
453
+ id: v.targetId === nodeId ? v.sourceId : v.targetId,
454
+ type: v.type,
455
+ score: v.weight || 0.5
456
+ }));
457
+ expanded.push(...connections);
458
+ }
459
+ return expanded;
460
+ }
461
+ /**
462
+ * Apply boost strategies
463
+ */
464
+ applyBoosts(results, boost) {
465
+ return results.map(r => {
466
+ let boostFactor = 1.0;
467
+ switch (boost) {
468
+ case 'recent':
469
+ // Boost recent items
470
+ const age = Date.now() - (r.metadata?.timestamp || 0);
471
+ boostFactor = Math.exp(-age / (30 * 24 * 60 * 60 * 1000)); // 30-day half-life
472
+ break;
473
+ case 'popular':
474
+ // Boost by view count or connections
475
+ boostFactor = Math.log10((r.metadata?.views || 0) + 10) / 2;
476
+ break;
477
+ case 'verified':
478
+ // Boost verified content
479
+ boostFactor = r.metadata?.verified ? 1.5 : 1.0;
480
+ break;
481
+ }
482
+ return {
483
+ ...r,
484
+ fusionScore: r.fusionScore * boostFactor
485
+ };
486
+ });
487
+ }
488
+ /**
489
+ * Add query explanations for debugging
490
+ */
491
+ addExplanations(results, plan, totalTime) {
492
+ return results.map(r => ({
493
+ ...r,
494
+ explanation: {
495
+ plan: plan.steps.map(s => `${s.type}:${s.operation}`).join(' → '),
496
+ timing: {
497
+ total: totalTime,
498
+ ...plan.steps.reduce((acc, step) => ({
499
+ ...acc,
500
+ [step.type]: step.estimated
501
+ }), {})
502
+ },
503
+ boosts: []
504
+ }
505
+ }));
506
+ }
507
+ // Query learning removed - unnecessary complexity
508
+ /**
509
+ * Optimize plan based on historical patterns
510
+ */
511
+ // Query optimization from history removed
512
+ /**
513
+ * Execute single signal query without fusion
514
+ */
515
+ async executeSingleSignal(query, type) {
516
+ switch (type) {
517
+ case 'vector':
518
+ return this.vectorSearch(query.like || query.similar, query.limit);
519
+ case 'graph':
520
+ return this.graphTraversal(query.connected);
521
+ case 'field':
522
+ return this.fieldFilter(query.where);
523
+ default:
524
+ return [];
525
+ }
526
+ }
527
+ /**
528
+ * Clear query optimization cache
529
+ */
530
+ clearCache() {
531
+ this.planCache.clear();
532
+ }
533
+ /**
534
+ * Get optimization statistics
535
+ */
536
+ getStats() {
537
+ return {
538
+ cachedPlans: this.planCache.size,
539
+ historySize: 0 // Query history removed
540
+ };
541
+ }
542
+ }
543
+ // Export a beautiful, simple API
544
+ export async function find(brain, query) {
545
+ const engine = new TripleIntelligenceEngine(brain);
546
+ return engine.find(query);
547
+ }
548
+ //# sourceMappingURL=TripleIntelligence.js.map