Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
734 lines
No EOL
24 KiB
TypeScript
734 lines
No EOL
24 KiB
TypeScript
/**
|
|
* Scaled HNSW System - Integration of All Optimization Strategies
|
|
* Production-ready system for handling millions of vectors with sub-second search
|
|
*/
|
|
|
|
import { Vector, VectorDocument, HNSWConfig } from '../coreTypes.js'
|
|
import { PartitionedHNSWIndex, PartitionConfig } from './partitionedHNSWIndex.js'
|
|
import { OptimizedHNSWIndex, OptimizedHNSWConfig } from './optimizedHNSWIndex.js'
|
|
import { DistributedSearchSystem, SearchStrategy } from './distributedSearch.js'
|
|
import { EnhancedCacheManager } from '../storage/enhancedCacheManager.js'
|
|
import { BatchS3Operations } from '../storage/adapters/batchS3Operations.js'
|
|
import { ReadOnlyOptimizations } from '../storage/readOnlyOptimizations.js'
|
|
import { euclideanDistance } from '../utils/index.js'
|
|
import { autoConfigureBrainy, AutoConfiguration } from '../utils/autoConfiguration.js'
|
|
|
|
export interface ScaledHNSWConfig {
|
|
// Required: Basic dataset expectations (can be auto-detected if not provided)
|
|
expectedDatasetSize?: number // Auto-detected if not provided
|
|
maxMemoryUsage?: number // Auto-detected based on environment
|
|
targetSearchLatency?: number // Auto-configured based on environment
|
|
|
|
// Storage configuration (optional - auto-detects S3 availability)
|
|
s3Config?: {
|
|
bucketName: string
|
|
region: string
|
|
endpoint?: string
|
|
accessKeyId?: string // Falls back to env vars
|
|
secretAccessKey?: string // Falls back to env vars
|
|
}
|
|
|
|
// Auto-configuration options
|
|
autoConfigureEnvironment?: boolean // Default: true
|
|
learningEnabled?: boolean // Default: true - adapts to performance
|
|
|
|
// Manual overrides (optional - auto-configured if not provided)
|
|
enablePartitioning?: boolean
|
|
enableCompression?: boolean
|
|
enableDistributedSearch?: boolean
|
|
enablePredictiveCaching?: boolean
|
|
|
|
// Advanced manual tuning (optional)
|
|
partitionConfig?: Partial<PartitionConfig>
|
|
hnswConfig?: Partial<OptimizedHNSWConfig>
|
|
readOnlyMode?: boolean
|
|
}
|
|
|
|
/**
|
|
* High-performance HNSW system with all optimizations integrated
|
|
* Handles datasets from thousands to millions of vectors
|
|
*/
|
|
export class ScaledHNSWSystem {
|
|
private config: ScaledHNSWConfig & {
|
|
expectedDatasetSize: number
|
|
maxMemoryUsage: number
|
|
targetSearchLatency: number
|
|
autoConfigureEnvironment: boolean
|
|
learningEnabled: boolean
|
|
enablePartitioning: boolean
|
|
enableCompression: boolean
|
|
enableDistributedSearch: boolean
|
|
enablePredictiveCaching: boolean
|
|
readOnlyMode: boolean
|
|
}
|
|
private autoConfig: AutoConfiguration
|
|
private partitionedIndex?: PartitionedHNSWIndex
|
|
private distributedSearch?: DistributedSearchSystem
|
|
private cacheManager?: EnhancedCacheManager<any>
|
|
private batchOperations?: BatchS3Operations
|
|
private readOnlyOptimizations?: ReadOnlyOptimizations
|
|
|
|
// Performance monitoring and learning
|
|
private performanceMetrics = {
|
|
totalSearches: 0,
|
|
averageSearchTime: 0,
|
|
cacheHitRate: 0,
|
|
compressionRatio: 0,
|
|
memoryUsage: 0,
|
|
indexSize: 0,
|
|
lastLearningUpdate: Date.now()
|
|
}
|
|
|
|
constructor(config: ScaledHNSWConfig = {}) {
|
|
this.autoConfig = AutoConfiguration.getInstance()
|
|
|
|
// Set basic defaults - these will be overridden by auto-configuration
|
|
this.config = {
|
|
expectedDatasetSize: 100000,
|
|
maxMemoryUsage: 4 * 1024 * 1024 * 1024,
|
|
targetSearchLatency: 150,
|
|
autoConfigureEnvironment: true,
|
|
learningEnabled: true,
|
|
enablePartitioning: true,
|
|
enableCompression: true,
|
|
enableDistributedSearch: true,
|
|
enablePredictiveCaching: true,
|
|
readOnlyMode: false,
|
|
...config
|
|
}
|
|
|
|
this.initializeOptimizedSystem()
|
|
}
|
|
|
|
/**
|
|
* Initialize the optimized system based on configuration
|
|
*/
|
|
private async initializeOptimizedSystem(): Promise<void> {
|
|
console.log('Initializing Scaled HNSW System with auto-configuration...')
|
|
|
|
// Auto-configure if enabled
|
|
if (this.config.autoConfigureEnvironment) {
|
|
const autoConfigResult = await this.autoConfig.detectAndConfigure({
|
|
expectedDataSize: this.config.expectedDatasetSize,
|
|
s3Available: !!this.config.s3Config,
|
|
memoryBudget: this.config.maxMemoryUsage
|
|
})
|
|
|
|
console.log(`Detected environment: ${autoConfigResult.environment}`)
|
|
console.log(`Available memory: ${(autoConfigResult.availableMemory / 1024 / 1024 / 1024).toFixed(1)}GB`)
|
|
console.log(`CPU cores: ${autoConfigResult.cpuCores}`)
|
|
|
|
// Override config with auto-detected values
|
|
this.config = {
|
|
...this.config,
|
|
expectedDatasetSize: autoConfigResult.recommendedConfig.expectedDatasetSize,
|
|
maxMemoryUsage: autoConfigResult.recommendedConfig.maxMemoryUsage,
|
|
targetSearchLatency: autoConfigResult.recommendedConfig.targetSearchLatency,
|
|
enablePartitioning: autoConfigResult.recommendedConfig.enablePartitioning,
|
|
enableCompression: autoConfigResult.recommendedConfig.enableCompression,
|
|
enableDistributedSearch: autoConfigResult.recommendedConfig.enableDistributedSearch,
|
|
enablePredictiveCaching: autoConfigResult.recommendedConfig.enablePredictiveCaching
|
|
}
|
|
}
|
|
|
|
// Determine optimal configuration
|
|
const optimizedConfig = this.calculateOptimalConfiguration()
|
|
|
|
// Initialize partitioned index with semantic partitioning as default
|
|
if (this.config.enablePartitioning) {
|
|
this.partitionedIndex = new PartitionedHNSWIndex(
|
|
{
|
|
...optimizedConfig.partitionConfig,
|
|
partitionStrategy: 'semantic', // Always use semantic for better performance
|
|
autoTuneSemanticClusters: true // Enable auto-tuning
|
|
},
|
|
optimizedConfig.hnswConfig,
|
|
euclideanDistance
|
|
)
|
|
console.log('✓ Partitioned index initialized with semantic clustering')
|
|
}
|
|
|
|
// Initialize distributed search system
|
|
if (this.config.enableDistributedSearch && this.partitionedIndex) {
|
|
this.distributedSearch = new DistributedSearchSystem({
|
|
maxConcurrentSearches: optimizedConfig.maxConcurrentSearches,
|
|
searchTimeout: this.config.targetSearchLatency * 5,
|
|
adaptivePartitionSelection: true,
|
|
loadBalancing: true
|
|
})
|
|
console.log('✓ Distributed search system initialized')
|
|
}
|
|
|
|
// Initialize batch S3 operations
|
|
if (this.config.s3Config) {
|
|
this.batchOperations = new BatchS3Operations(
|
|
null as any, // Would be initialized with actual S3 client
|
|
this.config.s3Config.bucketName,
|
|
{
|
|
maxConcurrency: 50,
|
|
useS3Select: this.config.expectedDatasetSize > 100000
|
|
}
|
|
)
|
|
console.log('✓ Batch S3 operations initialized')
|
|
}
|
|
|
|
// Initialize enhanced caching
|
|
if (this.config.enablePredictiveCaching) {
|
|
this.cacheManager = new EnhancedCacheManager({
|
|
hotCacheMaxSize: optimizedConfig.hotCacheSize,
|
|
warmCacheMaxSize: optimizedConfig.warmCacheSize,
|
|
prefetchEnabled: true,
|
|
prefetchStrategy: 'hybrid' as any, // Type casting for enum compatibility
|
|
prefetchBatchSize: 50
|
|
})
|
|
|
|
if (this.batchOperations) {
|
|
this.cacheManager.setStorageAdapters(null as any, this.batchOperations)
|
|
}
|
|
console.log('✓ Enhanced cache manager initialized')
|
|
}
|
|
|
|
// Initialize read-only optimizations
|
|
if (this.config.readOnlyMode && this.config.enableCompression) {
|
|
this.readOnlyOptimizations = new ReadOnlyOptimizations({
|
|
compression: {
|
|
vectorCompression: 'quantization' as any,
|
|
metadataCompression: 'gzip' as any,
|
|
quantizationType: 'scalar' as any,
|
|
quantizationBits: 8
|
|
},
|
|
segmentSize: optimizedConfig.segmentSize,
|
|
memoryMapped: true,
|
|
cacheIndexInMemory: optimizedConfig.cacheIndexInMemory
|
|
})
|
|
console.log('✓ Read-only optimizations initialized')
|
|
}
|
|
|
|
console.log('Scaled HNSW System ready for', this.config.expectedDatasetSize, 'vectors')
|
|
}
|
|
|
|
/**
|
|
* Calculate optimal configuration based on dataset size and constraints
|
|
*/
|
|
private calculateOptimalConfiguration(): {
|
|
partitionConfig: PartitionConfig
|
|
hnswConfig: OptimizedHNSWConfig
|
|
hotCacheSize: number
|
|
warmCacheSize: number
|
|
maxConcurrentSearches: number
|
|
segmentSize: number
|
|
cacheIndexInMemory: boolean
|
|
} {
|
|
const size = this.config.expectedDatasetSize
|
|
const memoryBudget = this.config.maxMemoryUsage
|
|
|
|
let config: any = {}
|
|
|
|
if (size <= 10000) {
|
|
// Small dataset - optimize for speed
|
|
config = {
|
|
partitionConfig: {
|
|
maxNodesPerPartition: 10000,
|
|
partitionStrategy: 'hash' as const
|
|
},
|
|
hnswConfig: {
|
|
M: 16,
|
|
efConstruction: 200,
|
|
efSearch: 50,
|
|
targetSearchLatency: this.config.targetSearchLatency
|
|
},
|
|
hotCacheSize: 1000,
|
|
warmCacheSize: 5000,
|
|
maxConcurrentSearches: 4,
|
|
segmentSize: 5000,
|
|
cacheIndexInMemory: true
|
|
}
|
|
} else if (size <= 100000) {
|
|
// Medium dataset - balance performance and memory
|
|
config = {
|
|
partitionConfig: {
|
|
maxNodesPerPartition: 25000,
|
|
partitionStrategy: 'semantic' as const,
|
|
semanticClusters: 8
|
|
},
|
|
hnswConfig: {
|
|
M: 24,
|
|
efConstruction: 300,
|
|
efSearch: 75,
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
|
dynamicParameterTuning: true
|
|
},
|
|
hotCacheSize: 2000,
|
|
warmCacheSize: 15000,
|
|
maxConcurrentSearches: 8,
|
|
segmentSize: 10000,
|
|
cacheIndexInMemory: memoryBudget > 2 * 1024 * 1024 * 1024 // 2GB
|
|
}
|
|
} else if (size <= 1000000) {
|
|
// Large dataset - optimize for scale
|
|
config = {
|
|
partitionConfig: {
|
|
maxNodesPerPartition: 50000,
|
|
partitionStrategy: 'semantic' as const,
|
|
semanticClusters: 16
|
|
},
|
|
hnswConfig: {
|
|
M: 32,
|
|
efConstruction: 400,
|
|
efSearch: 100,
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
|
dynamicParameterTuning: true,
|
|
memoryBudget: memoryBudget
|
|
},
|
|
hotCacheSize: 5000,
|
|
warmCacheSize: 25000,
|
|
maxConcurrentSearches: 12,
|
|
segmentSize: 20000,
|
|
cacheIndexInMemory: memoryBudget > 8 * 1024 * 1024 * 1024 // 8GB
|
|
}
|
|
} else {
|
|
// Very large dataset - maximum optimization
|
|
config = {
|
|
partitionConfig: {
|
|
maxNodesPerPartition: 100000,
|
|
partitionStrategy: 'hybrid' as const,
|
|
semanticClusters: 32
|
|
},
|
|
hnswConfig: {
|
|
M: 48,
|
|
efConstruction: 500,
|
|
efSearch: 150,
|
|
targetSearchLatency: this.config.targetSearchLatency,
|
|
dynamicParameterTuning: true,
|
|
memoryBudget: memoryBudget,
|
|
diskCacheEnabled: true
|
|
},
|
|
hotCacheSize: 10000,
|
|
warmCacheSize: 50000,
|
|
maxConcurrentSearches: 20,
|
|
segmentSize: 50000,
|
|
cacheIndexInMemory: false // Too large for memory
|
|
}
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
/**
|
|
* Add vector to the scaled system
|
|
*/
|
|
public async addVector(item: VectorDocument): Promise<string> {
|
|
if (!this.partitionedIndex) {
|
|
throw new Error('System not properly initialized')
|
|
}
|
|
|
|
const startTime = Date.now()
|
|
const result = await this.partitionedIndex.addItem(item)
|
|
|
|
// Update performance metrics
|
|
this.performanceMetrics.indexSize = this.partitionedIndex.size()
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Bulk insert vectors with optimizations
|
|
*/
|
|
public async bulkInsert(items: VectorDocument[]): Promise<string[]> {
|
|
if (!this.partitionedIndex) {
|
|
throw new Error('System not properly initialized')
|
|
}
|
|
|
|
console.log(`Starting optimized bulk insert of ${items.length} vectors`)
|
|
const startTime = Date.now()
|
|
|
|
// Sort items for optimal insertion order
|
|
const sortedItems = this.optimizeInsertionOrder(items)
|
|
|
|
const results: string[] = []
|
|
const batchSize = this.calculateOptimalBatchSize(items.length)
|
|
|
|
// Process in batches
|
|
for (let i = 0; i < sortedItems.length; i += batchSize) {
|
|
const batch = sortedItems.slice(i, i + batchSize)
|
|
|
|
for (const item of batch) {
|
|
const id = await this.partitionedIndex.addItem(item)
|
|
results.push(id)
|
|
}
|
|
|
|
// Progress logging
|
|
if (i % (batchSize * 10) === 0) {
|
|
const progress = ((i / sortedItems.length) * 100).toFixed(1)
|
|
console.log(`Bulk insert progress: ${progress}%`)
|
|
}
|
|
}
|
|
|
|
const totalTime = Date.now() - startTime
|
|
console.log(`Bulk insert completed: ${results.length} vectors in ${totalTime}ms`)
|
|
|
|
return results
|
|
}
|
|
|
|
/**
|
|
* High-performance vector search with all optimizations
|
|
*/
|
|
public async search(
|
|
queryVector: Vector,
|
|
k: number = 10,
|
|
options: {
|
|
strategy?: SearchStrategy
|
|
useCache?: boolean
|
|
maxPartitions?: number
|
|
} = {}
|
|
): Promise<Array<[string, number]>> {
|
|
const startTime = Date.now()
|
|
|
|
try {
|
|
let results: Array<[string, number]>
|
|
|
|
if (this.distributedSearch && this.partitionedIndex) {
|
|
// Use distributed search for optimal performance
|
|
results = await this.distributedSearch.distributedSearch(
|
|
this.partitionedIndex,
|
|
queryVector,
|
|
k,
|
|
options.strategy || SearchStrategy.ADAPTIVE
|
|
)
|
|
} else if (this.partitionedIndex) {
|
|
// Fall back to partitioned search
|
|
results = await this.partitionedIndex.search(
|
|
queryVector,
|
|
k,
|
|
{ maxPartitions: options.maxPartitions }
|
|
)
|
|
} else {
|
|
throw new Error('No search system available')
|
|
}
|
|
|
|
// Update performance metrics and learn from performance
|
|
const searchTime = Date.now() - startTime
|
|
this.updateSearchMetrics(searchTime, results.length)
|
|
|
|
// Adaptive learning - adjust configuration based on performance
|
|
if (this.config.learningEnabled && this.shouldTriggerLearning()) {
|
|
await this.adaptivelyLearnFromPerformance()
|
|
}
|
|
|
|
return results
|
|
|
|
} catch (error) {
|
|
console.error('Search failed:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get system performance metrics
|
|
*/
|
|
public getPerformanceMetrics(): typeof this.performanceMetrics & {
|
|
partitionStats?: any
|
|
cacheStats?: any
|
|
compressionStats?: any
|
|
distributedSearchStats?: any
|
|
} {
|
|
const metrics = { ...this.performanceMetrics }
|
|
|
|
// Add subsystem metrics
|
|
if (this.partitionedIndex) {
|
|
(metrics as any).partitionStats = this.partitionedIndex.getPartitionStats()
|
|
}
|
|
|
|
if (this.cacheManager) {
|
|
(metrics as any).cacheStats = this.cacheManager.getStats()
|
|
}
|
|
|
|
if (this.readOnlyOptimizations) {
|
|
(metrics as any).compressionStats = this.readOnlyOptimizations.getCompressionStats()
|
|
}
|
|
|
|
if (this.distributedSearch) {
|
|
(metrics as any).distributedSearchStats = this.distributedSearch.getSearchStats()
|
|
}
|
|
|
|
return metrics
|
|
}
|
|
|
|
/**
|
|
* Optimize insertion order for better index quality
|
|
*/
|
|
private optimizeInsertionOrder(items: VectorDocument[]): VectorDocument[] {
|
|
if (items.length < 1000) {
|
|
return items // Not worth optimizing small batches
|
|
}
|
|
|
|
// Simple clustering-based approach for better HNSW construction
|
|
// In production, you might use more sophisticated clustering
|
|
return items.sort(() => Math.random() - 0.5)
|
|
}
|
|
|
|
/**
|
|
* Calculate optimal batch size based on system resources
|
|
*/
|
|
private calculateOptimalBatchSize(totalItems: number): number {
|
|
const memoryBudget = this.config.maxMemoryUsage
|
|
const estimatedItemSize = 1000 // Rough estimate per item in bytes
|
|
|
|
const maxBatch = Math.floor(memoryBudget * 0.1 / estimatedItemSize)
|
|
const targetBatch = Math.min(1000, Math.max(100, maxBatch))
|
|
|
|
return Math.min(targetBatch, totalItems)
|
|
}
|
|
|
|
/**
|
|
* Update search performance metrics
|
|
*/
|
|
private updateSearchMetrics(searchTime: number, resultCount: number): void {
|
|
this.performanceMetrics.totalSearches++
|
|
this.performanceMetrics.averageSearchTime =
|
|
(this.performanceMetrics.averageSearchTime + searchTime) / 2
|
|
|
|
// Update other metrics
|
|
if (this.cacheManager) {
|
|
const cacheStats = this.cacheManager.getStats()
|
|
const totalOps = cacheStats.hotCacheHits + cacheStats.hotCacheMisses +
|
|
cacheStats.warmCacheHits + cacheStats.warmCacheMisses
|
|
|
|
this.performanceMetrics.cacheHitRate = totalOps > 0 ?
|
|
(cacheStats.hotCacheHits + cacheStats.warmCacheHits) / totalOps : 0
|
|
}
|
|
|
|
if (this.readOnlyOptimizations) {
|
|
const compressionStats = this.readOnlyOptimizations.getCompressionStats()
|
|
this.performanceMetrics.compressionRatio = compressionStats.compressionRatio
|
|
}
|
|
|
|
// Estimate memory usage
|
|
this.performanceMetrics.memoryUsage = this.estimateMemoryUsage()
|
|
}
|
|
|
|
/**
|
|
* Estimate current memory usage
|
|
*/
|
|
private estimateMemoryUsage(): number {
|
|
let totalMemory = 0
|
|
|
|
if (this.partitionedIndex) {
|
|
// Rough estimate: 1KB per vector
|
|
totalMemory += this.partitionedIndex.size() * 1024
|
|
}
|
|
|
|
if (this.cacheManager) {
|
|
const cacheStats = this.cacheManager.getStats()
|
|
totalMemory += (cacheStats.hotCacheSize + cacheStats.warmCacheSize) * 1024
|
|
}
|
|
|
|
return totalMemory
|
|
}
|
|
|
|
/**
|
|
* Generate performance report
|
|
*/
|
|
public generatePerformanceReport(): string {
|
|
const metrics = this.getPerformanceMetrics()
|
|
|
|
return `
|
|
=== Scaled HNSW System Performance Report ===
|
|
|
|
Dataset Configuration:
|
|
- Expected Size: ${this.config.expectedDatasetSize.toLocaleString()} vectors
|
|
- Current Size: ${metrics.indexSize.toLocaleString()} vectors
|
|
- Memory Budget: ${(this.config.maxMemoryUsage / 1024 / 1024 / 1024).toFixed(1)}GB
|
|
- Target Latency: ${this.config.targetSearchLatency}ms
|
|
|
|
Performance Metrics:
|
|
- Total Searches: ${metrics.totalSearches.toLocaleString()}
|
|
- Average Search Time: ${metrics.averageSearchTime.toFixed(1)}ms
|
|
- Cache Hit Rate: ${(metrics.cacheHitRate * 100).toFixed(1)}%
|
|
- Memory Usage: ${(metrics.memoryUsage / 1024 / 1024).toFixed(1)}MB
|
|
- Compression Ratio: ${metrics.compressionRatio ? (metrics.compressionRatio * 100).toFixed(1) + '%' : 'N/A'}
|
|
|
|
System Status: ${this.getSystemStatus()}
|
|
`.trim()
|
|
}
|
|
|
|
/**
|
|
* Get overall system status
|
|
*/
|
|
private getSystemStatus(): string {
|
|
const metrics = this.getPerformanceMetrics()
|
|
|
|
if (metrics.averageSearchTime <= this.config.targetSearchLatency) {
|
|
return '✅ OPTIMAL'
|
|
} else if (metrics.averageSearchTime <= this.config.targetSearchLatency * 2) {
|
|
return '⚠️ ACCEPTABLE'
|
|
} else {
|
|
return '❌ NEEDS OPTIMIZATION'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if adaptive learning should be triggered
|
|
*/
|
|
private shouldTriggerLearning(): boolean {
|
|
const timeSinceLastLearning = Date.now() - this.performanceMetrics.lastLearningUpdate
|
|
const minLearningInterval = 30000 // 30 seconds
|
|
const minSearches = 20 // Minimum searches before learning
|
|
|
|
return timeSinceLastLearning > minLearningInterval &&
|
|
this.performanceMetrics.totalSearches > minSearches &&
|
|
this.performanceMetrics.totalSearches % 50 === 0 // Learn every 50 searches
|
|
}
|
|
|
|
/**
|
|
* Adaptively learn from performance and adjust configuration
|
|
*/
|
|
private async adaptivelyLearnFromPerformance(): Promise<void> {
|
|
try {
|
|
const currentMetrics = {
|
|
averageSearchTime: this.performanceMetrics.averageSearchTime,
|
|
memoryUsage: this.performanceMetrics.memoryUsage,
|
|
cacheHitRate: this.performanceMetrics.cacheHitRate,
|
|
errorRate: 0 // Could be tracked separately
|
|
}
|
|
|
|
const adjustments = await this.autoConfig.learnFromPerformance(currentMetrics)
|
|
|
|
if (Object.keys(adjustments).length > 0) {
|
|
console.log('🧠 Adaptive learning: Adjusting configuration based on performance')
|
|
|
|
// Apply learned adjustments
|
|
let configChanged = false
|
|
|
|
if (adjustments.enableDistributedSearch !== undefined &&
|
|
adjustments.enableDistributedSearch !== this.config.enableDistributedSearch) {
|
|
this.config.enableDistributedSearch = adjustments.enableDistributedSearch
|
|
configChanged = true
|
|
}
|
|
|
|
if (adjustments.enableCompression !== undefined &&
|
|
adjustments.enableCompression !== this.config.enableCompression) {
|
|
this.config.enableCompression = adjustments.enableCompression
|
|
configChanged = true
|
|
}
|
|
|
|
if (adjustments.enablePredictiveCaching !== undefined &&
|
|
adjustments.enablePredictiveCaching !== this.config.enablePredictiveCaching) {
|
|
this.config.enablePredictiveCaching = adjustments.enablePredictiveCaching
|
|
configChanged = true
|
|
}
|
|
|
|
// Apply partition adjustments
|
|
if (adjustments.maxNodesPerPartition &&
|
|
this.partitionedIndex &&
|
|
adjustments.maxNodesPerPartition !== this.partitionedIndex.getPartitionStats().averageNodesPerPartition) {
|
|
// This would require rebuilding the index in a real implementation
|
|
console.log(`Learning suggests partition size: ${adjustments.maxNodesPerPartition}`)
|
|
}
|
|
|
|
if (configChanged) {
|
|
console.log('✅ Configuration updated based on performance learning')
|
|
}
|
|
}
|
|
|
|
this.performanceMetrics.lastLearningUpdate = Date.now()
|
|
|
|
} catch (error) {
|
|
console.warn('Adaptive learning failed:', error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update dataset analysis for better auto-configuration
|
|
*/
|
|
public async updateDatasetAnalysis(vectorCount: number, vectorDimension?: number): Promise<void> {
|
|
if (this.config.autoConfigureEnvironment) {
|
|
const analysis = {
|
|
estimatedSize: vectorCount,
|
|
vectorDimension,
|
|
accessPatterns: this.inferAccessPatterns()
|
|
}
|
|
|
|
await this.autoConfig.adaptToDataset(analysis)
|
|
console.log(`📊 Dataset analysis updated: ${vectorCount} vectors${vectorDimension ? `, ${vectorDimension}D` : ''}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Infer access patterns from current metrics
|
|
*/
|
|
private inferAccessPatterns(): 'read-heavy' | 'write-heavy' | 'balanced' {
|
|
// Simple heuristic - in practice, this would track read/write ratios
|
|
if (this.performanceMetrics.totalSearches > 100) {
|
|
return 'read-heavy'
|
|
}
|
|
return 'balanced'
|
|
}
|
|
|
|
/**
|
|
* Cleanup system resources
|
|
*/
|
|
public cleanup(): void {
|
|
this.distributedSearch?.cleanup()
|
|
this.cacheManager?.clear()
|
|
this.readOnlyOptimizations?.cleanup()
|
|
this.partitionedIndex?.clear()
|
|
this.autoConfig.resetCache()
|
|
|
|
console.log('Scaled HNSW System cleaned up')
|
|
}
|
|
}
|
|
|
|
// Export convenience factory functions
|
|
|
|
/**
|
|
* Create a fully auto-configured Brainy system - minimal setup required!
|
|
* Just provide S3 config if you want persistence beyond the current session
|
|
*/
|
|
export function createAutoBrainy(s3Config?: {
|
|
bucketName: string
|
|
region?: string
|
|
accessKeyId?: string
|
|
secretAccessKey?: string
|
|
}): ScaledHNSWSystem {
|
|
return new ScaledHNSWSystem({
|
|
s3Config: s3Config ? {
|
|
bucketName: s3Config.bucketName,
|
|
region: s3Config.region || 'us-east-1',
|
|
accessKeyId: s3Config.accessKeyId,
|
|
secretAccessKey: s3Config.secretAccessKey
|
|
} : undefined,
|
|
autoConfigureEnvironment: true,
|
|
learningEnabled: true
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Create a Brainy system optimized for specific scenarios
|
|
*/
|
|
export async function createQuickBrainy(
|
|
scenario: 'small' | 'medium' | 'large' | 'enterprise',
|
|
s3Config?: { bucketName: string; region?: string }
|
|
): Promise<ScaledHNSWSystem> {
|
|
const { getQuickSetup } = await import('../utils/autoConfiguration.js')
|
|
const quickConfig = await getQuickSetup(scenario)
|
|
|
|
return new ScaledHNSWSystem({
|
|
...quickConfig,
|
|
s3Config: s3Config && quickConfig.s3Required ? {
|
|
bucketName: s3Config.bucketName,
|
|
region: s3Config.region || 'us-east-1',
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
} : undefined,
|
|
autoConfigureEnvironment: true,
|
|
learningEnabled: true
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Legacy factory function - still works but consider using createAutoBrainy() instead
|
|
*/
|
|
export function createScaledHNSWSystem(config: ScaledHNSWConfig = {}): ScaledHNSWSystem {
|
|
return new ScaledHNSWSystem(config)
|
|
} |