# Creating Augmentations for Brainy > **Updated for v4.0.0** - Includes metadata structure changes and type system improvements ## The BrainyAugmentation Interface Every augmentation implements this simple yet powerful interface: ```typescript interface BrainyAugmentation { // Identification name: string // Unique name for your augmentation // Execution control timing: 'before' | 'after' | 'around' | 'replace' // When to execute operations: string[] // Which operations to intercept priority: number // Execution order (higher = first) // Lifecycle methods initialize(context: AugmentationContext): Promise execute(operation: string, params: any, next: () => Promise): Promise shutdown?(): Promise // Optional cleanup } ``` ## v4.0.0 Breaking Changes for Augmentation Developers ### 1. Metadata Structure Separation v4.0.0 introduces strict metadata/vector separation for billion-scale performance: ```typescript // ✅ v4.0.0: Metadata has required type field interface NounMetadata { noun: NounType // Required! Must be a valid noun type [key: string]: any // Your custom metadata } interface VerbMetadata { verb: VerbType // Required! Must be a valid verb type sourceId: string targetId: string [key: string]: any } ``` ### 2. Storage Adapter Return Types Storage adapters now return different types at different boundaries: ```typescript // Internal methods: Pure structures (no metadata) abstract _getNoun(id: string): Promise // Public API: WithMetadata structures abstract getNoun(id: string): Promise ``` ### 3. Verb Property Renamed The verb relationship field changed from `type` to `verb`: ```typescript // ❌ v3.x verb.type === 'relatedTo' // ✅ v4.0.0 verb.verb === 'relatedTo' ``` ## Creating a Storage Augmentation Storage augmentations are special - they provide the storage backend for Brainy. ### Important: v4.0.0 Storage Requirements Your storage adapter MUST: 1. **Wrap metadata** with required `noun`/`verb` fields 2. **Return pure structures** from internal `_methods` 3. **Return WithMetadata types** from public methods ```typescript import { StorageAugmentation } from 'brainy/augmentations' import { BaseStorageAdapter, HNSWNoun, HNSWNounWithMetadata, NounMetadata } from 'brainy' export class MyCustomStorage extends BaseStorageAdapter { // Internal method: Returns pure structure async _getNoun(id: string): Promise { const data = await this.fetchFromDatabase(id) return data ? { id: data.id, vector: data.vector, nounType: data.type } : null } // Public method: Returns WithMetadata structure async getNoun(id: string): Promise { const noun = await this._getNoun(id) if (!noun) return null // Fetch metadata separately (v4.0.0 pattern) const metadata = await this.getNounMetadata(id) return { ...noun, metadata: metadata || { noun: noun.nounType || 'thing' } } } // CRITICAL: Always save with proper metadata structure async saveNoun(noun: HNSWNoun, metadata?: NounMetadata): Promise { // Validate metadata has required 'noun' field if (!metadata?.noun) { throw new Error('v4.0.0: NounMetadata requires "noun" field') } await this.database.save({ id: noun.id, vector: noun.vector, nounType: noun.nounType, metadata: metadata // Stored separately in v4.0.0 }) } } export class MyStorageAugmentation extends StorageAugmentation { private config: MyStorageConfig constructor(config: MyStorageConfig) { super() this.name = 'my-custom-storage' this.config = config } // Called during storage resolution phase async provideStorage(): Promise { const storage = new MyCustomStorage(this.config) this.storageAdapter = storage return storage } // Called during augmentation initialization protected async onInitialize(): Promise { await this.storageAdapter!.init() this.log(`Custom storage initialized`) } } ``` ### Using Your Storage Augmentation ```typescript // Register before brain.init() const brain = new Brainy() brain.augmentations.register(new MyStorageAugmentation({ connectionString: 'redis://localhost:6379' })) await brain.init() // Will use your storage! ``` ## Creating a Feature Augmentation Here's a complete example of a caching augmentation: ```typescript import { BaseAugmentation, BrainyAugmentation } from 'brainy/augmentations' export class CachingAugmentation extends BaseAugmentation { private cache = new Map() constructor() { super() this.name = 'smart-cache' this.timing = 'around' // Wrap operations this.operations = ['search'] // Only cache searches this.priority = 50 // Mid-priority } async execute(operation: string, params: any, next: () => Promise): Promise { if (operation === 'search') { // Check cache const cacheKey = JSON.stringify(params) if (this.cache.has(cacheKey)) { this.log('Cache hit!') return this.cache.get(cacheKey) } // Execute and cache const result = await next() this.cache.set(cacheKey, result) return result } // Pass through other operations return next() } protected async onInitialize(): Promise { this.log('Cache initialized') } async shutdown(): Promise { this.cache.clear() await super.shutdown() } } ``` ## The Four Timing Modes ### 1. `before` - Pre-processing ```typescript timing = 'before' async execute(op, params, next) { // Validate/transform input const validated = await validate(params) return next(validated) // Pass modified params } ``` ### 2. `after` - Post-processing ```typescript timing = 'after' async execute(op, params, next) { const result = await next() // Log, analyze, or modify result console.log(`Operation ${op} returned:`, result) return result } ``` ### 3. `around` - Wrapping (middleware) ```typescript timing = 'around' async execute(op, params, next) { console.log('Starting', op) try { const result = await next() console.log('Success', op) return result } catch (error) { console.log('Failed', op, error) throw error } } ``` ### 4. `replace` - Complete replacement ```typescript timing = 'replace' async execute(op, params, next) { // Don't call next() - replace entirely! return myCustomImplementation(params) } ``` ## Operations You Can Intercept Common operations in Brainy: - `'storage'` - Storage resolution (special) - `'add'` - Adding data - `'search'`, `'similar'` - Searching - `'update'`, `'delete'` - Modifications - `'saveNoun'`, `'saveVerb'` - Storage operations - `'all'` - Intercept everything ## Context Available to Augmentations ```typescript interface AugmentationContext { brain: Brainy // The brain instance storage: StorageAdapter // Storage backend config: BrainyConfig // Configuration log: (message: string, level?: 'info' | 'warn' | 'error') => void } ``` ## Real-World Examples ### 1. Redis Storage Augmentation ```typescript export class RedisStorageAugmentation extends StorageAugmentation { async provideStorage(): Promise { return new RedisAdapter({ host: 'localhost', port: 6379, // Implement full StorageAdapter interface }) } } ``` ### 2. Audit Trail Augmentation ```typescript export class AuditAugmentation extends BaseAugmentation { timing = 'after' operations = ['add', 'update', 'delete'] async execute(op, params, next) { const result = await next() // Log to audit trail await this.logAudit({ operation: op, params, result, timestamp: new Date(), user: this.context.config.currentUser }) return result } } ``` ### 3. Rate Limiting Augmentation ```typescript export class RateLimitAugmentation extends BaseAugmentation { timing = 'before' operations = ['search'] private limiter = new RateLimiter({ rps: 100 }) async execute(op, params, next) { await this.limiter.acquire() // Wait if rate limited return next() } } ``` ## Publishing to Brain Cloud Marketplace Future capability for premium augmentations: ```typescript // package.json { "name": "@brain-cloud/redis-storage", "brainy": { "type": "augmentation", "category": "storage", "premium": true } } // Users can install via: // brainy augment install redis-storage ``` ## Best Practices ### General Practices 1. **Use BaseAugmentation** - Provides common functionality 2. **Set appropriate priority** - Storage (100), System (80-99), Features (10-50) 3. **Be selective with operations** - Don't use 'all' unless necessary 4. **Handle errors gracefully** - Don't break the chain 5. **Clean up in shutdown()** - Release resources 6. **Log appropriately** - Use context.log() for consistent output 7. **Document your augmentation** - Include examples ### v4.0.0 Specific Best Practices 8. **Always include `noun` field** when creating/modifying NounMetadata: ```typescript const metadata: NounMetadata = { noun: 'thing', // REQUIRED! yourField: 'value' } ``` 9. **Use `verb` property** not `type` when working with relationships: ```typescript // ✅ Correct if (verb.verb === 'relatedTo') { ... } // ❌ Wrong (v3.x pattern) if (verb.type === 'relatedTo') { ... } ``` 10. **Access metadata correctly** from storage: ```typescript // ✅ Correct - metadata is already structured const nounType = noun.metadata.noun // ⚠️ Fallback pattern for robustness const nounType = noun.metadata?.noun || 'thing' ``` 11. **Respect the two-file storage pattern** - Don't mix vector and metadata operations: ```typescript // ✅ Good - Separate concerns await storage.saveNoun(noun) await storage.saveMetadata(noun.id, metadata) // ❌ Bad - Mixing concerns await storage.saveNounWithEverything(combinedData) ``` ## Testing Your Augmentation ```typescript import { Brainy } from 'brainy' import { MyAugmentation } from './my-augmentation' describe('MyAugmentation', () => { let brain: Brainy beforeEach(async () => { brain = new Brainy() brain.augmentations.register(new MyAugmentation()) await brain.init() }) afterEach(async () => { await brain.destroy() }) it('should enhance searches', async () => { // Test your augmentation's effect const results = await brain.search('test') expect(results).toHaveProperty('enhanced', true) }) }) ``` ## Summary Augmentations are Brainy's extension system. They can: - Replace storage backends - Add caching layers - Implement audit trails - Add rate limiting - Sync with external systems - Transform data - And much more! The unified BrainyAugmentation interface makes it easy to create powerful extensions while maintaining consistency across the entire system.