brainy/docs/CREATING-AUGMENTATIONS.md
David Snelling 2128ef5607 docs: add deprecation warnings for addNoun and addVerb methods
- Add @deprecated JSDoc tags to TypeScript definitions
- Update all documentation examples to use modern add() and relate() API
- Preserve batch operations (addNouns, addVerbs) as they remain current
- Mark deprecated methods in both source and compiled definitions

Migration guide:
- addNoun(data, type, metadata) → add(data, { nounType: type, ...metadata })
- addVerb(source, target, type, metadata) → relate(source, target, type, metadata)
2025-09-17 10:48:41 -07:00

7.4 KiB

Creating Augmentations for Brainy

The BrainyAugmentation Interface

Every augmentation implements this simple yet powerful interface:

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<void>
  execute<T>(operation: string, params: any, next: () => Promise<T>): Promise<T>
  shutdown?(): Promise<void>      // Optional cleanup
}

Creating a Storage Augmentation

Storage augmentations are special - they provide the storage backend for Brainy:

import { StorageAugmentation } from 'brainy/augmentations'
import { MyCustomStorage } from './my-storage'

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<StorageAdapter> {
    const storage = new MyCustomStorage(this.config)
    this.storageAdapter = storage
    return storage
  }
  
  // Called during augmentation initialization
  protected async onInitialize(): Promise<void> {
    await this.storageAdapter!.init()
    this.log(`Custom storage initialized`)
  }
}

Using Your Storage Augmentation

// Register before brain.init()
const brain = new BrainyData()
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:

import { BaseAugmentation, BrainyAugmentation } from 'brainy/augmentations'

export class CachingAugmentation extends BaseAugmentation {
  private cache = new Map<string, any>()
  
  constructor() {
    super()
    this.name = 'smart-cache'
    this.timing = 'around'        // Wrap operations
    this.operations = ['search']   // Only cache searches
    this.priority = 50             // Mid-priority
  }
  
  async execute<T>(operation: string, params: any, next: () => Promise<T>): Promise<T> {
    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<void> {
    this.log('Cache initialized')
  }
  
  async shutdown(): Promise<void> {
    this.cache.clear()
    await super.shutdown()
  }
}

The Four Timing Modes

1. before - Pre-processing

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

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)

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

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

interface AugmentationContext {
  brain: BrainyData         // The brain instance
  storage: StorageAdapter   // Storage backend
  config: BrainyDataConfig  // Configuration
  log: (message: string, level?: 'info' | 'warn' | 'error') => void
}

Real-World Examples

1. Redis Storage Augmentation

export class RedisStorageAugmentation extends StorageAugmentation {
  async provideStorage(): Promise<StorageAdapter> {
    return new RedisAdapter({
      host: 'localhost',
      port: 6379,
      // Implement full StorageAdapter interface
    })
  }
}

2. Audit Trail Augmentation

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

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:

// 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

  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

Testing Your Augmentation

import { BrainyData } from 'brainy'
import { MyAugmentation } from './my-augmentation'

describe('MyAugmentation', () => {
  let brain: BrainyData
  
  beforeEach(async () => {
    brain = new BrainyData()
    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.