From cbc007b6cf2a8b0374c09efedd57e590fc12d966 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Thu, 19 Jun 2025 15:00:26 -0700 Subject: [PATCH] **remove: delete LLM augmentations implementation** ### Changes: - Removed the `src/augmentations/llmAugmentations.ts` file: - Contained cognition augmentations for creating, testing, exporting, and deploying LLM models. - Included TensorFlow.js-based model configuration, training, testing, and deployment functionalities. - Implemented various presets (`tiny`, `small`, `medium`, `large`) for simplified LLM model creation. ### Purpose: This change removes legacy LLM-related augmentations and functionalities no longer relevant to the project's current focus, in alignment with the recent transition to TensorFlow-based embedding workflows. Cleaning up eliminates unused code and maintains a streamlined, up-to-date codebase. --- src/augmentations/llmAugmentations.ts | 1943 ----------------- src/augmentations/llmTrainingAugmentations.ts | 636 ------ test/validateTypes.js | 63 - 3 files changed, 2642 deletions(-) delete mode 100644 src/augmentations/llmAugmentations.ts delete mode 100644 src/augmentations/llmTrainingAugmentations.ts delete mode 100644 test/validateTypes.js diff --git a/src/augmentations/llmAugmentations.ts b/src/augmentations/llmAugmentations.ts deleted file mode 100644 index f804d28c..00000000 --- a/src/augmentations/llmAugmentations.ts +++ /dev/null @@ -1,1943 +0,0 @@ -/** - * LLM Augmentations - * - * This file implements cognition augmentations for creating, testing, exporting, and deploying - * LLM models from the data in Brainy (nouns and verbs). - */ - -import { - AugmentationType, - ICognitionAugmentation, - IActivationAugmentation, - AugmentationResponse -} from '../types/augmentations.js' -import { v4 as uuidv4 } from 'uuid' -import { BrainyData } from '../brainyData.js' -import * as tf from '@tensorflow/tfjs' -import { NounType, VerbType, GraphNoun, GraphVerb } from '../types/graphTypes.js' - -/** - * Interface for LLM model configuration - */ -interface LLMModelConfig { - name: string - description?: string - modelType: 'simple' | 'transformer' | 'custom' - vocabSize?: number - embeddingDim?: number - hiddenDim?: number - numLayers?: number - numHeads?: number - dropoutRate?: number - maxSequenceLength?: number - learningRate?: number - batchSize?: number - epochs?: number - customModelPath?: string -} - -/** - * Interface for LLM model metadata - */ -interface LLMModelMetadata { - id: string - name: string - description?: string - createdAt: Date - updatedAt: Date - modelType: 'simple' | 'transformer' | 'custom' - vocabSize: number - embeddingDim: number - trainedOn: { - nounCount: number - verbCount: number - nounTypes: string[] - verbTypes: string[] - } - performance?: { - accuracy?: number - loss?: number - perplexity?: number - } - exportFormats?: string[] -} - -/** - * Interface for LLM training options - */ -interface LLMTrainingOptions { - nounTypes?: NounType[] - verbTypes?: VerbType[] - maxSamples?: number - validationSplit?: number - includeMetadata?: boolean - includeEmbeddings?: boolean - augmentData?: boolean - earlyStoppingPatience?: number - epochs?: number - batchSize?: number -} - -/** - * Interface for LLM testing options - */ -interface LLMTestingOptions { - testSize?: number - randomSeed?: number - metrics?: string[] - generateSamples?: boolean - sampleCount?: number -} - -/** - * Interface for LLM export options - */ -interface LLMExportOptions { - format: 'tfjs' | 'onnx' | 'savedmodel' | 'json' - quantize?: boolean - outputPath?: string - includeMetadata?: boolean - includeVocab?: boolean -} - -/** - * Interface for LLM deployment options - */ -interface LLMDeploymentOptions { - target: 'browser' | 'node' | 'cloud' - cloudProvider?: 'aws' | 'gcp' | 'azure' - endpoint?: string - apiKey?: string - region?: string - containerize?: boolean - autoScale?: boolean - memory?: string - cpu?: string -} - -/** - * LLMCognitionAugmentation - * - * A cognition augmentation that provides functionality for creating, testing, exporting, - * and deploying LLM models from the data in Brainy. - */ -export class LLMCognitionAugmentation implements ICognitionAugmentation { - readonly name: string - readonly description: string = 'Cognition augmentation for LLM model creation and inference' - enabled: boolean = true - private isInitialized = false - private brainyDb: BrainyData | null = null - private models: Map - }> = new Map() - private defaultModelConfig: LLMModelConfig = { - name: 'default-llm-model', - modelType: 'simple', - vocabSize: 10000, - embeddingDim: 128, - hiddenDim: 256, - numLayers: 2, - numHeads: 4, - dropoutRate: 0.1, - maxSequenceLength: 100, - learningRate: 0.001, - batchSize: 32, - epochs: 10 - } - - // Simplified model presets for easy model creation - private modelPresets: Record> = { - 'tiny': { - name: 'tiny-llm-model', - modelType: 'simple', - vocabSize: 3000, - embeddingDim: 32, - hiddenDim: 64, - numLayers: 1, - dropoutRate: 0.1, - maxSequenceLength: 50, - batchSize: 16, - epochs: 5 - }, - 'small': { - name: 'small-llm-model', - modelType: 'simple', - vocabSize: 5000, - embeddingDim: 64, - hiddenDim: 128, - numLayers: 2, - dropoutRate: 0.1, - maxSequenceLength: 100, - batchSize: 32, - epochs: 10 - }, - 'medium': { - name: 'medium-llm-model', - modelType: 'transformer', - vocabSize: 8000, - embeddingDim: 128, - hiddenDim: 256, - numLayers: 3, - numHeads: 4, - dropoutRate: 0.1, - maxSequenceLength: 200, - batchSize: 32, - epochs: 15 - }, - 'large': { - name: 'large-llm-model', - modelType: 'transformer', - vocabSize: 10000, - embeddingDim: 256, - hiddenDim: 512, - numLayers: 4, - numHeads: 8, - dropoutRate: 0.1, - maxSequenceLength: 300, - batchSize: 16, - epochs: 20 - } - } - - constructor(name: string = 'llm-cognition') { - this.name = name - } - - getType(): AugmentationType { - return AugmentationType.COGNITION - } - - /** - * Initialize the augmentation - */ - async initialize(): Promise { - if (this.isInitialized) { - return - } - - try { - // Initialize TensorFlow.js - await tf.ready() - - // Initialize Brainy instance if not provided - if (!this.brainyDb) { - this.brainyDb = new BrainyData() - await this.brainyDb.init() - } - - this.isInitialized = true - console.log(`${this.name} initialized successfully`) - } catch (error) { - console.error(`Failed to initialize ${this.name}:`, error) - throw new Error(`Failed to initialize ${this.name}: ${error}`) - } - } - - /** - * Shut down the augmentation - */ - async shutDown(): Promise { - // Clean up resources - for (const [_, modelData] of this.models) { - modelData.model.dispose() - } - this.models.clear() - this.isInitialized = false - } - - /** - * Get the status of the augmentation - */ - async getStatus(): Promise<'active' | 'inactive' | 'error'> { - return this.isInitialized ? 'active' : 'inactive' - } - - /** - * Set the Brainy database instance - * @param db The Brainy instance to use - */ - setBrainyDb(db: BrainyData): void { - this.brainyDb = db - } - - /** - * Get the Brainy database instance - * @returns The Brainy instance - */ - getBrainyDb(): BrainyData | null { - return this.brainyDb - } - - /** - * Create a new LLM model from Brainy data using a preset - * @param presetName Name of the preset to use ('tiny', 'small', 'medium', 'large') - * @param customName Optional custom name for the model - * @returns Model ID and metadata - */ - async createModelFromPreset( - presetName: string = 'small', - customName?: string - ): Promise> { - const preset = this.modelPresets[presetName]; - if (!preset) { - return { - success: false, - data: null as any, - error: `Unknown preset: ${presetName}. Available presets: ${Object.keys(this.modelPresets).join(', ')}` - }; - } - - const config = { ...preset }; - if (customName) { - config.name = customName; - } - - return this.createModel(config); - } - - /** - * Create a new LLM model from Brainy data - * @param config Configuration for the model - * @returns Model ID and metadata - */ - async createModel( - config: Partial = {} - ): Promise> { - await this.ensureInitialized() - - try { - // Merge with default config - const modelConfig: LLMModelConfig = { - ...this.defaultModelConfig, - ...config, - name: config.name || `llm-model-${uuidv4().slice(0, 8)}` - } - - // Create model architecture based on type - const model = await this.buildModelArchitecture(modelConfig) - - // Create model metadata - const modelId = uuidv4() - const metadata: LLMModelMetadata = { - id: modelId, - name: modelConfig.name, - description: modelConfig.description, - createdAt: new Date(), - updatedAt: new Date(), - modelType: modelConfig.modelType, - vocabSize: modelConfig.vocabSize || this.defaultModelConfig.vocabSize!, - embeddingDim: modelConfig.embeddingDim || this.defaultModelConfig.embeddingDim!, - trainedOn: { - nounCount: 0, - verbCount: 0, - nounTypes: [], - verbTypes: [] - } - } - - // Store the model - this.models.set(modelId, { - model, - metadata - }) - - return { - success: true, - data: { - modelId, - metadata - } - } - } catch (error) { - console.error('Error creating LLM model:', error) - return { - success: false, - data: null as any, - error: `Error creating LLM model: ${error}` - } - } - } - - /** - * Build a model architecture based on the configuration - * @param config Model configuration - * @returns TensorFlow.js model - */ - private async buildModelArchitecture(config: LLMModelConfig): Promise { - switch (config.modelType) { - case 'simple': - return this.buildSimpleModel(config) - case 'transformer': - return this.buildTransformerModel(config) - case 'custom': - if (config.customModelPath) { - return tf.loadLayersModel(config.customModelPath) - } - throw new Error('Custom model path is required for custom model type') - default: - throw new Error(`Unsupported model type: ${config.modelType}`) - } - } - - /** - * Build a simple sequence model - * @param config Model configuration - * @returns TensorFlow.js model - */ - private buildSimpleModel(config: LLMModelConfig): tf.LayersModel { - const { - vocabSize = this.defaultModelConfig.vocabSize!, - embeddingDim = this.defaultModelConfig.embeddingDim!, - hiddenDim = this.defaultModelConfig.hiddenDim!, - numLayers = this.defaultModelConfig.numLayers!, - dropoutRate = this.defaultModelConfig.dropoutRate!, - maxSequenceLength = this.defaultModelConfig.maxSequenceLength! - } = config - - // Create a sequential model - const model = tf.sequential() - - // Add embedding layer - model.add(tf.layers.embedding({ - inputDim: vocabSize, - outputDim: embeddingDim, - inputLength: maxSequenceLength - })) - - // Add LSTM layers - for (let i = 0; i < numLayers; i++) { - model.add(tf.layers.lstm({ - units: hiddenDim, - returnSequences: i < numLayers - 1, - dropout: dropoutRate - })) - } - - // Add output layer - model.add(tf.layers.dense({ - units: vocabSize, - activation: 'softmax' - })) - - // Compile the model - model.compile({ - optimizer: tf.train.adam(config.learningRate), - loss: 'sparseCategoricalCrossentropy', - metrics: ['accuracy'] - }) - - return model - } - - /** - * Build a transformer model - * @param config Model configuration - * @returns TensorFlow.js model - */ - private buildTransformerModel(config: LLMModelConfig): tf.LayersModel { - const { - vocabSize = this.defaultModelConfig.vocabSize!, - embeddingDim = this.defaultModelConfig.embeddingDim!, - numLayers = this.defaultModelConfig.numLayers!, - numHeads = this.defaultModelConfig.numHeads!, - dropoutRate = this.defaultModelConfig.dropoutRate!, - maxSequenceLength = this.defaultModelConfig.maxSequenceLength! - } = config - - // Input layer - const input = tf.input({ shape: [maxSequenceLength] }) - - // Embedding layer - let x = tf.layers.embedding({ - inputDim: vocabSize, - outputDim: embeddingDim, - inputLength: maxSequenceLength - }).apply(input) as tf.SymbolicTensor - - // Positional encoding (simplified) - const positionEncoding = this.getPositionalEncoding(maxSequenceLength, embeddingDim) - const positionTensor = tf.tensor(positionEncoding) - // Convert positionTensor to SymbolicTensor before using it with apply - const symbolicPositionTensor = tf.layers.inputLayer({inputShape: positionTensor.shape}).apply(positionTensor) as tf.SymbolicTensor - x = tf.layers.add().apply([x, symbolicPositionTensor]) as tf.SymbolicTensor - - // Transformer blocks - for (let i = 0; i < numLayers; i++) { - // Multi-head attention - // Simplified self-attention mechanism using existing layers - // Project input to query, key, value - const query = tf.layers.dense({ - units: embeddingDim, - activation: 'linear', - name: `attention_${i}_query` - }).apply(x) as tf.SymbolicTensor - - const key = tf.layers.dense({ - units: embeddingDim, - activation: 'linear', - name: `attention_${i}_key` - }).apply(x) as tf.SymbolicTensor - - const value = tf.layers.dense({ - units: embeddingDim, - activation: 'linear', - name: `attention_${i}_value` - }).apply(x) as tf.SymbolicTensor - - // Combine them to simulate attention - const attention = tf.layers.add().apply([query, key, value]) as tf.SymbolicTensor - - // Apply dropout - const attentionWithDropout = tf.layers.dropout({ - rate: dropoutRate - }).apply(attention) as tf.SymbolicTensor - - // Add & Norm - x = tf.layers.add().apply([x, attentionWithDropout]) as tf.SymbolicTensor - x = tf.layers.layerNormalization().apply(x) as tf.SymbolicTensor - - // Feed-forward network - const ffn = tf.layers.dense({ - units: embeddingDim * 4, - activation: 'relu' - }).apply(x) as tf.SymbolicTensor - const ffnOutput = tf.layers.dense({ - units: embeddingDim - }).apply(ffn) as tf.SymbolicTensor - - // Add & Norm - x = tf.layers.add().apply([x, ffnOutput]) as tf.SymbolicTensor - x = tf.layers.layerNormalization().apply(x) as tf.SymbolicTensor - } - - // Global average pooling - x = tf.layers.globalAveragePooling1d().apply(x) as tf.SymbolicTensor - - // Output layer - const output = tf.layers.dense({ - units: vocabSize, - activation: 'softmax' - }).apply(x) as tf.SymbolicTensor - - // Create and compile model - const model = tf.model({ inputs: input, outputs: output }) - model.compile({ - optimizer: tf.train.adam(config.learningRate), - loss: 'sparseCategoricalCrossentropy', - metrics: ['accuracy'] - }) - - return model - } - - /** - * Generate positional encoding for transformer model - * @param length Sequence length - * @param depth Embedding dimension - * @returns Positional encoding matrix - */ - private getPositionalEncoding(length: number, depth: number): number[][] { - const positionEncoding = Array(length).fill(0).map((_, pos) => { - return Array(depth).fill(0).map((_, i) => { - const angle = pos / Math.pow(10000, (2 * Math.floor(i / 2)) / depth) - return i % 2 === 0 ? Math.sin(angle) : Math.cos(angle) - }) - }) - return positionEncoding - } - - /** - * Train an LLM model with simplified options - * @param modelId ID of the model to train - * @param trainingLevel Training level ('quick', 'standard', 'thorough') - * @returns Training results - */ - async trainModelSimple( - modelId: string, - trainingLevel: 'quick' | 'standard' | 'thorough' = 'standard' - ): Promise> { - // Define training options based on level - const trainingOptions: LLMTrainingOptions = {}; - - switch (trainingLevel) { - case 'quick': - trainingOptions.maxSamples = 100; - trainingOptions.epochs = 5; - trainingOptions.batchSize = 32; - trainingOptions.validationSplit = 0.1; - break; - case 'standard': - trainingOptions.maxSamples = 500; - trainingOptions.epochs = 10; - trainingOptions.batchSize = 32; - trainingOptions.validationSplit = 0.2; - break; - case 'thorough': - trainingOptions.maxSamples = 1000; - trainingOptions.epochs = 20; - trainingOptions.batchSize = 16; - trainingOptions.validationSplit = 0.2; - trainingOptions.earlyStoppingPatience = 5; - break; - } - - return this.trainModel(modelId, trainingOptions); - } - - /** - * Train an LLM model on Brainy data - * @param modelId ID of the model to train - * @param options Training options - * @returns Training results - */ - async trainModel( - modelId: string, - options: LLMTrainingOptions = {} - ): Promise> { - await this.ensureInitialized() - - try { - // Get the model - const modelData = this.models.get(modelId) - if (!modelData) { - return { - success: false, - data: null as any, - error: `Model with ID ${modelId} not found` - } - } - - // Prepare training data from Brainy - const { - trainData, - tokenizer, - nounCount, - verbCount, - nounTypes, - verbTypes - } = await this.prepareTrainingData(options) - - if (!trainData || trainData.xs.length === 0 || trainData.ys.length === 0) { - return { - success: false, - data: null as any, - error: 'No training data available' - } - } - - // Convert data to tensors - const { model } = modelData - const maxSequenceLength = model.inputs[0].shape[1] as number - - // Tokenize and pad sequences - const sequences = trainData.xs.map(text => { - const tokens = this.tokenizeText(text, tokenizer) - return this.padSequence(tokens, maxSequenceLength) - }) - - // Convert labels to token IDs - const labels = trainData.ys.map(text => { - const tokens = this.tokenizeText(text, tokenizer) - return tokens[0] // Just use the first token as the target for simplicity - }) - - // Create TensorFlow tensors - const xs = tf.tensor2d(sequences, [sequences.length, maxSequenceLength]) - const ys = tf.tensor1d(labels, 'int32') - - // Train the model - const history = await model.fit(xs, ys, { - epochs: options.epochs || this.defaultModelConfig.epochs, - batchSize: options.batchSize || this.defaultModelConfig.batchSize, - validationSplit: options.validationSplit || 0.2, - callbacks: tf.callbacks.earlyStopping({ - monitor: 'val_loss', - patience: options.earlyStoppingPatience || 3 - }) - }) - - // Update model metadata - modelData.metadata.updatedAt = new Date() - modelData.metadata.trainedOn = { - nounCount, - verbCount, - nounTypes: nounTypes.map(t => t.toString()), - verbTypes: verbTypes.map(t => t.toString()) - } - modelData.metadata.performance = { - accuracy: typeof history.history.accuracy[history.history.accuracy.length - 1] === 'number' - ? history.history.accuracy[history.history.accuracy.length - 1] as number - : (history.history.accuracy[history.history.accuracy.length - 1] as tf.Tensor).dataSync()[0], - loss: typeof history.history.loss[history.history.loss.length - 1] === 'number' - ? history.history.loss[history.history.loss.length - 1] as number - : (history.history.loss[history.history.loss.length - 1] as tf.Tensor).dataSync()[0] - } - modelData.tokenizer = tokenizer - - // Clean up tensors - xs.dispose() - ys.dispose() - - return { - success: true, - data: { - modelId, - metadata: modelData.metadata, - trainingHistory: history, - metrics: { - loss: modelData.metadata.performance.loss || 0, - accuracy: modelData.metadata.performance.accuracy || 0, - epochs: history.epoch.length - } - } - } - } catch (error) { - console.error('Error training LLM model:', error) - return { - success: false, - data: null as any, - error: `Error training LLM model: ${error}` - } - } - } - - /** - * Prepare training data from Brainy - * @param options Training options - * @returns Training data, tokenizer, and statistics - */ - private async prepareTrainingData(options: LLMTrainingOptions = {}): Promise<{ - trainData: { xs: string[], ys: string[] }, - tokenizer: Map, - nounCount: number, - verbCount: number, - nounTypes: NounType[], - verbTypes: VerbType[] - }> { - if (!this.brainyDb) { - throw new Error('Brainy database not initialized') - } - - // Get nouns and verbs from Brainy - const nounTypes = options.nounTypes || Object.values(NounType) - const verbTypes = options.verbTypes || Object.values(VerbType) - - // Get all nouns - const status = await this.brainyDb.status() - const allNouns = [] - - // Collect nouns by type - for (const nounType of nounTypes) { - const nouns = await this.brainyDb.searchByNounTypes(['dummy'], 1000, [nounType]) - allNouns.push(...nouns) - } - - // Get all verbs - const allVerbs = await this.brainyDb.getAllVerbs() - - // Filter verbs by type if specified - const filteredVerbs = verbTypes.length > 0 - ? allVerbs.filter(verb => { - // Check for verb type in either verb.verb or verb.type - const verbType = (verb as any).verb || (verb as any).type; - return typeof verbType === 'string' && verbTypes.includes(verbType as VerbType); - }) - : allVerbs - - // Prepare training data - const trainData = { - xs: [] as string[], - ys: [] as string[] - } - - // Process nouns - for (const noun of allNouns) { - // SearchResult might not have label/noun directly, they could be in metadata - const metadata = noun.metadata as GraphNoun | undefined; - const label = metadata?.label || noun.id; - const nounType = metadata?.noun || ''; - - if (label) { - trainData.xs.push(label) - trainData.ys.push(nounType.toString()) - } - } - - // Process verbs - for (const verb of filteredVerbs) { - // Handle both source/target and sourceId/targetId possibilities - const sourceId = (verb as any).sourceId || (verb as any).source; - const targetId = (verb as any).targetId || (verb as any).target; - - // Handle both label/verb and type possibilities for the verb label - const verbType = (verb as any).verb || (verb as any).type; - const verbLabel = (verb as any).label || (typeof verbType === 'string' ? verbType : ''); - - if (verbLabel) { - // Get source and target nouns - const sourceNoun = await this.brainyDb.get(sourceId) - const targetNoun = await this.brainyDb.get(targetId) - - if (sourceNoun && targetNoun) { - // Create training examples from verb relationships - // Handle both direct properties and metadata - const sourceMetadata = sourceNoun.metadata as GraphNoun | undefined; - const targetMetadata = targetNoun.metadata as GraphNoun | undefined; - - const sourceLabel = sourceMetadata?.label || sourceNoun.id; - const targetLabel = targetMetadata?.label || targetNoun.id; - - // Source + verb -> target - trainData.xs.push(`${sourceLabel} ${verbLabel}`) - trainData.ys.push(targetLabel) - - // Target + inverse verb -> source - trainData.xs.push(`${targetLabel} is ${verbLabel} by`) - trainData.ys.push(sourceLabel) - } - } - } - - // Limit samples if specified - if (options.maxSamples && trainData.xs.length > options.maxSamples) { - const indices = new Array(trainData.xs.length).fill(0).map((_, i) => i) - const selectedIndices = this.shuffleArray(indices).slice(0, options.maxSamples) - - trainData.xs = selectedIndices.map(i => trainData.xs[i]) - trainData.ys = selectedIndices.map(i => trainData.ys[i]) - } - - // Build vocabulary and tokenizer - const allTexts = [...trainData.xs, ...trainData.ys] - const tokenizer = this.buildTokenizer(allTexts) - - return { - trainData, - tokenizer, - nounCount: allNouns.length, - verbCount: filteredVerbs.length, - nounTypes, - verbTypes - } - } - - /** - * Build a tokenizer from text data - * @param texts Array of text samples - * @returns Tokenizer mapping - */ - private buildTokenizer(texts: string[]): Map { - const tokenizer = new Map() - const wordFreq = new Map() - - // Count word frequencies - for (const text of texts) { - const words = text.toLowerCase().split(/\s+/) - for (const word of words) { - const count = wordFreq.get(word) || 0 - wordFreq.set(word, count + 1) - } - } - - // Sort by frequency - const sortedWords = Array.from(wordFreq.entries()) - .sort((a, b) => b[1] - a[1]) - .map(entry => entry[0]) - - // Create tokenizer with special tokens - tokenizer.set('', 0) - tokenizer.set('', 1) - tokenizer.set('', 2) - tokenizer.set('', 3) - - // Add words to tokenizer - let idx = 4 - for (const word of sortedWords) { - if (!tokenizer.has(word)) { - tokenizer.set(word, idx++) - if (idx >= this.defaultModelConfig.vocabSize!) { - break - } - } - } - - return tokenizer - } - - /** - * Tokenize text using the tokenizer - * @param text Text to tokenize - * @param tokenizer Tokenizer mapping - * @returns Array of token IDs - */ - private tokenizeText(text: string, tokenizer: Map): number[] { - const words = text.toLowerCase().split(/\s+/) - return words.map(word => tokenizer.get(word) || tokenizer.get('')!) - } - - /** - * Pad a sequence to the specified length - * @param sequence Sequence to pad - * @param length Target length - * @returns Padded sequence - */ - private padSequence(sequence: number[], length: number): number[] { - if (sequence.length >= length) { - return sequence.slice(0, length) - } - return [...sequence, ...Array(length - sequence.length).fill(0)] - } - - /** - * Shuffle an array in-place - * @param array Array to shuffle - * @returns Shuffled array - */ - private shuffleArray(array: T[]): T[] { - const result = [...array] - for (let i = result.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)) - ;[result[i], result[j]] = [result[j], result[i]] - } - return result - } - - /** - * Test an LLM model on Brainy data - * @param modelId ID of the model to test - * @param options Testing options - * @returns Test results - */ - async testModel( - modelId: string, - options: LLMTestingOptions = {} - ): Promise, - samples?: Array<{ input: string, expected: string, generated: string }> - }>> { - await this.ensureInitialized() - - try { - // Get the model - const modelData = this.models.get(modelId) - if (!modelData) { - return { - success: false, - data: null as any, - error: `Model with ID ${modelId} not found` - } - } - - // Prepare test data - const { trainData, tokenizer } = await this.prepareTrainingData({ - maxSamples: options.testSize || 100 - }) - - if (!trainData || trainData.xs.length === 0 || trainData.ys.length === 0) { - return { - success: false, - data: null as any, - error: 'No test data available' - } - } - - // Convert data to tensors - const { model } = modelData - const maxSequenceLength = model.inputs[0].shape[1] as number - - // Tokenize and pad sequences - const sequences = trainData.xs.map(text => { - const tokens = this.tokenizeText(text, tokenizer) - return this.padSequence(tokens, maxSequenceLength) - }) - - // Convert labels to token IDs - const labels = trainData.ys.map(text => { - const tokens = this.tokenizeText(text, tokenizer) - return tokens[0] // Just use the first token as the target for simplicity - }) - - // Create TensorFlow tensors - const xs = tf.tensor2d(sequences, [sequences.length, maxSequenceLength]) - const ys = tf.tensor1d(labels, 'int32') - - // Evaluate the model - const evalResult = await model.evaluate(xs, ys) as tf.Scalar[] - const metrics: Record = { - loss: await evalResult[0].dataSync()[0], - accuracy: await evalResult[1].dataSync()[0] - } - - // Generate samples if requested - let samples - if (options.generateSamples) { - samples = [] - const sampleCount = Math.min(options.sampleCount || 5, trainData.xs.length) - const indices = this.shuffleArray(Array.from({ length: trainData.xs.length }, (_, i) => i)) - .slice(0, sampleCount) - - // Create a reverse tokenizer for decoding - const reverseTokenizer = new Map() - for (const [word, id] of tokenizer.entries()) { - reverseTokenizer.set(id, word) - } - - for (const idx of indices) { - const input = trainData.xs[idx] - const expected = trainData.ys[idx] - - // Generate prediction - const inputTensor = tf.tensor2d([sequences[idx]], [1, maxSequenceLength]) - const prediction = model.predict(inputTensor) as tf.Tensor - const predictionData = await prediction.argMax(1).dataSync()[0] - const generated = reverseTokenizer.get(predictionData) || '' - - samples.push({ input, expected, generated }) - - // Clean up tensors - inputTensor.dispose() - prediction.dispose() - } - } - - // Clean up tensors - xs.dispose() - ys.dispose() - evalResult.forEach(t => t.dispose()) - - return { - success: true, - data: { - modelId, - metrics, - samples - } - } - } catch (error) { - console.error('Error testing LLM model:', error) - return { - success: false, - data: null as any, - error: `Error testing LLM model: ${error}` - } - } - } - - /** - * Export an LLM model for deployment - * @param modelId ID of the model to export - * @param options Export options - * @returns Export results - */ - async exportModel( - modelId: string, - options: LLMExportOptions - ): Promise> { - await this.ensureInitialized() - - try { - // Get the model - const modelData = this.models.get(modelId) - if (!modelData) { - return { - success: false, - data: null as any, - error: `Model with ID ${modelId} not found` - } - } - - const { model, metadata, tokenizer } = modelData - - switch (options.format) { - case 'tfjs': - // Export as TensorFlow.js format - if (options.outputPath) { - await model.save(`file://${options.outputPath}`) - return { - success: true, - data: { - modelId, - format: 'tfjs', - exportPath: options.outputPath, - path: options.outputPath, - size: 1024 * 10 // Placeholder size of 10KB - } - } - } else { - // Return model as JSON - const modelJSON = await model.toJSON() - return { - success: true, - data: { - modelId, - format: 'tfjs', - modelJSON: JSON.stringify(modelJSON), - size: JSON.stringify(modelJSON).length // Size in bytes - } - } - } - - case 'json': - // Export as JSON (model architecture and weights) - const modelJSON = await model.toJSON() - let exportData: any = { - model: modelJSON, - metadata - } - - // Include vocabulary if requested - if (options.includeVocab && tokenizer) { - exportData.vocabulary = Array.from(tokenizer.entries()) - } - - if (options.outputPath) { - // In a real implementation, we would write to the file system here - // For this example, we'll just return the JSON - return { - success: true, - data: { - modelId, - format: 'json', - exportPath: options.outputPath, - modelJSON: JSON.stringify(exportData), - path: options.outputPath, - size: JSON.stringify(exportData).length // Size in bytes - } - } - } else { - return { - success: true, - data: { - modelId, - format: 'json', - modelJSON: JSON.stringify(exportData), - size: JSON.stringify(exportData).length // Size in bytes - } - } - } - - case 'onnx': - return { - success: false, - data: null as any, - error: 'ONNX export is not implemented in this version' - } - - case 'savedmodel': - return { - success: false, - data: null as any, - error: 'SavedModel export is not implemented in this version' - } - - default: - return { - success: false, - data: null as any, - error: `Unsupported export format: ${options.format}` - } - } - } catch (error) { - console.error('Error exporting LLM model:', error) - return { - success: false, - data: null as any, - error: `Error exporting LLM model: ${error}` - } - } - } - - /** - * Deploy an LLM model to the specified target - * @param modelId ID of the model to deploy - * @param options Deployment options - * @returns Deployment results - */ - async deployModel( - modelId: string, - options: LLMDeploymentOptions - ): Promise> { - await this.ensureInitialized() - - try { - // Get the model - const modelData = this.models.get(modelId) - if (!modelData) { - return { - success: false, - data: null as any, - error: `Model with ID ${modelId} not found` - } - } - - // Handle different deployment targets - switch (options.target) { - case 'browser': - // For browser deployment, we just need to export the model in TFJS format - const exportResult = await this.exportModel(modelId, { - format: 'tfjs' - }) - - if (!exportResult.success) { - return { - success: false, - data: null as any, - error: `Failed to export model for browser deployment: ${exportResult.error}` - } - } - - return { - success: true, - data: { - modelId, - deploymentTarget: 'browser', - status: 'Model exported for browser deployment', - url: 'http://localhost:3000/models/' + modelId, - deploymentId: 'browser-' + modelId - } - } - - case 'node': - // For Node.js deployment, we also export in TFJS format - const nodeExportResult = await this.exportModel(modelId, { - format: 'tfjs' - }) - - if (!nodeExportResult.success) { - return { - success: false, - data: null as any, - error: `Failed to export model for Node.js deployment: ${nodeExportResult.error}` - } - } - - return { - success: true, - data: { - modelId, - deploymentTarget: 'node', - status: 'Model exported for Node.js deployment', - url: 'http://localhost:8080/models/' + modelId, - deploymentId: 'node-' + modelId - } - } - - case 'cloud': - // Cloud deployment would require additional implementation - // This is a placeholder for future implementation - return { - success: false, - data: null as any, - error: 'Cloud deployment is not implemented in this version' - } - - default: - return { - success: false, - data: null as any, - error: `Unsupported deployment target: ${options.target}` - } - } - } catch (error) { - console.error('Error deploying LLM model:', error) - return { - success: false, - data: null as any, - error: `Error deploying LLM model: ${error}` - } - } - } - - /** - * Generate text using the LLM model with simplified creativity options - * @param modelId ID of the model to use - * @param prompt Input prompt - * @param creativity Creativity level ('conservative', 'balanced', 'creative') - * @returns Generated text - */ - async generateTextSimple( - modelId: string, - prompt: string, - creativity: 'conservative' | 'balanced' | 'creative' = 'balanced' - ): Promise> { - // Define generation options based on creativity level - const generationOptions: { - maxLength?: number, - temperature?: number, - topK?: number - } = {}; - - switch (creativity) { - case 'conservative': - generationOptions.temperature = 0.3; - generationOptions.topK = 3; - generationOptions.maxLength = 50; - break; - case 'balanced': - generationOptions.temperature = 0.7; - generationOptions.topK = 5; - generationOptions.maxLength = 100; - break; - case 'creative': - generationOptions.temperature = 1.0; - generationOptions.topK = 10; - generationOptions.maxLength = 200; - break; - } - - return this.generateText(modelId, prompt, generationOptions); - } - - /** - * Generate text using the LLM model - * @param modelId ID of the model to use - * @param prompt Input prompt - * @param options Generation options - * @returns Generated text - */ - async generateText( - modelId: string, - prompt: string, - options: { - maxLength?: number, - temperature?: number, - topK?: number - } = {} - ): Promise> { - await this.ensureInitialized() - - try { - // Get the model - const modelData = this.models.get(modelId) - if (!modelData) { - return { - success: false, - data: { - text: '' - }, - error: `Model with ID ${modelId} not found` - } - } - - const { model, tokenizer } = modelData - if (!tokenizer) { - return { - success: false, - data: { - text: '' - }, - error: 'Model has no tokenizer' - } - } - - // Create a reverse tokenizer for decoding - const reverseTokenizer = new Map() - for (const [word, id] of tokenizer.entries()) { - reverseTokenizer.set(id, word) - } - - // Tokenize the prompt - const maxSequenceLength = model.inputs[0].shape[1] as number - const tokens = this.tokenizeText(prompt, tokenizer) - const paddedTokens = this.padSequence(tokens, maxSequenceLength) - - // Convert to tensor - const inputTensor = tf.tensor2d([paddedTokens], [1, maxSequenceLength]) - - // Generate prediction - const prediction = model.predict(inputTensor) as tf.Tensor - const predictionData = await prediction.dataSync() - - // Apply temperature if specified - let probabilities = Array.from(predictionData) - if (options.temperature && options.temperature > 0) { - probabilities = probabilities.map(p => Math.pow(p, 1 / options.temperature!)) - const sum = probabilities.reduce((a, b) => a + b, 0) - probabilities = probabilities.map(p => p / sum) - } - - // Apply top-k if specified - if (options.topK && options.topK > 0) { - const indices = Array.from({ length: probabilities.length }, (_, i) => i) - const sortedIndices = indices.sort((a, b) => probabilities[b] - probabilities[a]) - const topKIndices = sortedIndices.slice(0, options.topK) - const topKProbabilities = topKIndices.map(i => probabilities[i]) - const sum = topKProbabilities.reduce((a, b) => a + b, 0) - - // Zero out non-top-k probabilities - probabilities = probabilities.map((p, i) => - topKIndices.includes(i) ? p / sum : 0 - ) - } - - // Sample from the distribution - let nextToken = 0 - const r = Math.random() - let cumulativeProb = 0 - for (let i = 0; i < probabilities.length; i++) { - cumulativeProb += probabilities[i] - if (r < cumulativeProb) { - nextToken = i - break - } - } - - // Decode the token - const generatedWord = reverseTokenizer.get(nextToken) || '' - - // Clean up tensors - inputTensor.dispose() - prediction.dispose() - - // Use a fixed generation time for now - const generationTime = 10; // 10ms is a reasonable placeholder - - return { - success: true, - data: { - text: generatedWord, - tokens: 1, // Just one token for now - timeMs: generationTime - } - } - } catch (error) { - console.error('Error generating text:', error) - return { - success: false, - data: { - text: '' - }, - error: `Error generating text: ${error}` - } - } - } - - /** - * Performs a reasoning operation based on current knowledge - * @param query The specific reasoning task or question - * @param context Optional additional context for the reasoning - */ - reason( - query: string, - context?: Record - ): AugmentationResponse<{ - inference: string - confidence: number - }> { - // Use the most recently trained model for reasoning - const modelIds = Array.from(this.models.keys()) - if (modelIds.length === 0) { - return { - success: false, - data: { - inference: '', - confidence: 0 - }, - error: 'No models available for reasoning' - } - } - - // Find the most recently updated model - let latestModelId = modelIds[0] - let latestDate = new Date(0) - - for (const modelId of modelIds) { - const modelData = this.models.get(modelId) - if (modelData && modelData.metadata.updatedAt > latestDate) { - latestModelId = modelId - latestDate = modelData.metadata.updatedAt - } - } - - // Use the model to generate a response - return { - success: true, - data: { - inference: `This would use model ${latestModelId} to answer: ${query}`, - confidence: 0.85 - } - } - } - - /** - * Infers relationships or new facts from existing data - * @param dataSubset A subset of data to infer from - */ - infer(dataSubset: Record): AugmentationResponse> { - // This would use the trained model to infer new relationships - return { - success: true, - data: { - inferredRelationships: [], - message: 'Inference functionality would be implemented here' - } - } - } - - /** - * Executes a logical operation or rule set - * @param ruleId The identifier of the rule or logic to apply - * @param input Data to apply the logic to - */ - executeLogic(ruleId: string, input: Record): AugmentationResponse { - // This would apply logical rules based on the trained model - return { - success: true, - data: true - } - } - - /** - * Ensure the augmentation is initialized - */ - private async ensureInitialized(): Promise { - if (!this.isInitialized) { - await this.initialize() - } - } -} - -/** - * LLMActivationAugmentation - * - * An activation augmentation that provides actions for LLM functionality. - */ -export class LLMActivationAugmentation implements IActivationAugmentation { - readonly name: string - readonly description: string = 'Activation augmentation for LLM model creation and inference' - enabled: boolean = true - private isInitialized = false - private cognitionAugmentation: LLMCognitionAugmentation | null = null - - constructor(name: string = 'llm-activation') { - this.name = name - } - - getType(): AugmentationType { - return AugmentationType.ACTIVATION - } - - /** - * Initialize the augmentation - */ - async initialize(): Promise { - if (this.isInitialized) { - return - } - - this.isInitialized = true - } - - /** - * Shut down the augmentation - */ - async shutDown(): Promise { - this.isInitialized = false - } - - /** - * Get the status of the augmentation - */ - async getStatus(): Promise<'active' | 'inactive' | 'error'> { - return this.isInitialized ? 'active' : 'inactive' - } - - /** - * Set the cognition augmentation to use for LLM operations - * @param cognition The LLMCognitionAugmentation to use - */ - setCognitionAugmentation(cognition: LLMCognitionAugmentation): void { - this.cognitionAugmentation = cognition - } - - /** - * Trigger an action based on a processed command or internal state - * @param actionName The name of the action to trigger - * @param parameters Optional parameters for the action - */ - triggerAction( - actionName: string, - parameters?: Record - ): AugmentationResponse { - if (!this.cognitionAugmentation) { - return { - success: false, - data: null, - error: 'Cognition augmentation not set' - } - } - - // Handle different actions - switch (actionName) { - case 'createModel': - return this.handleCreateModel(parameters || {}) - case 'createModelSimple': - return this.handleCreateModelSimple(parameters || {}) - case 'trainModel': - return this.handleTrainModel(parameters || {}) - case 'trainModelSimple': - return this.handleTrainModelSimple(parameters || {}) - case 'testModel': - return this.handleTestModel(parameters || {}) - case 'exportModel': - return this.handleExportModel(parameters || {}) - case 'deployModel': - return this.handleDeployModel(parameters || {}) - case 'generateText': - return this.handleGenerateText(parameters || {}) - case 'generateTextSimple': - return this.handleGenerateTextSimple(parameters || {}) - default: - return { - success: false, - data: null, - error: `Unknown action: ${actionName}` - } - } - } - - /** - * Handle the createModel action - * @param parameters Action parameters - */ - private handleCreateModel( - parameters: Record - ): AugmentationResponse { - return { - success: true, - data: this.cognitionAugmentation!.createModel(parameters as Partial) - } - } - - /** - * Handle the trainModel action - * @param parameters Action parameters - */ - private handleTrainModel( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const options = parameters.options as LLMTrainingOptions || {} - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.trainModel(modelId, options) - } - } - - /** - * Handle the testModel action - * @param parameters Action parameters - */ - private handleTestModel( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const options = parameters.options as LLMTestingOptions || {} - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.testModel(modelId, options) - } - } - - /** - * Handle the exportModel action - * @param parameters Action parameters - */ - private handleExportModel( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const options = parameters.options as LLMExportOptions - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - if (!options || !options.format) { - return { - success: false, - data: null, - error: 'options.format parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.exportModel(modelId, options) - } - } - - /** - * Handle the deployModel action - * @param parameters Action parameters - */ - private handleDeployModel( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const options = parameters.options as LLMDeploymentOptions - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - if (!options || !options.target) { - return { - success: false, - data: null, - error: 'options.target parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.deployModel(modelId, options) - } - } - - /** - * Handle the createModelSimple action - * @param parameters Action parameters - */ - private handleCreateModelSimple( - parameters: Record - ): AugmentationResponse { - const presetName = parameters.preset as string || 'small' - const customName = parameters.name as string - - return { - success: true, - data: this.cognitionAugmentation!.createModelFromPreset(presetName, customName) - } - } - - /** - * Handle the trainModelSimple action - * @param parameters Action parameters - */ - private handleTrainModelSimple( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const trainingLevel = parameters.level as 'quick' | 'standard' | 'thorough' || 'standard' - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.trainModelSimple(modelId, trainingLevel) - } - } - - /** - * Handle the generateTextSimple action - * @param parameters Action parameters - */ - private handleGenerateTextSimple( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const prompt = parameters.prompt as string - const creativity = parameters.creativity as 'conservative' | 'balanced' | 'creative' || 'balanced' - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - if (!prompt) { - return { - success: false, - data: null, - error: 'prompt parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.generateTextSimple(modelId, prompt, creativity) - } - } - - /** - * Handle the generateText action - * @param parameters Action parameters - */ - private handleGenerateText( - parameters: Record - ): AugmentationResponse { - const modelId = parameters.modelId as string - const prompt = parameters.prompt as string - const options = parameters.options as Record || {} - - if (!modelId) { - return { - success: false, - data: null, - error: 'modelId parameter is required' - } - } - - if (!prompt) { - return { - success: false, - data: null, - error: 'prompt parameter is required' - } - } - - return { - success: true, - data: this.cognitionAugmentation!.generateText(modelId, prompt, options) - } - } - - /** - * Generates an expressive output or response from Brainy - * @param knowledgeId The identifier of the knowledge to express - * @param format The desired output format (e.g., 'text', 'json') - */ - generateOutput( - knowledgeId: string, - format: string - ): AugmentationResponse> { - // This method is not used for LLM functionality - return { - success: false, - data: '', - error: 'generateOutput is not implemented for LLMActivationAugmentation' - } - } - - /** - * Interacts with an external system or API - * @param systemId The identifier of the external system - * @param payload The data to send to the external system - */ - interactExternal( - systemId: string, - payload: Record - ): AugmentationResponse { - // This method is not used for LLM functionality - return { - success: false, - data: null, - error: 'interactExternal is not implemented for LLMActivationAugmentation' - } - } -} - -/** - * Factory function to create LLM augmentations - * @param options Additional options - * @returns An object containing the created augmentations - */ -export async function createLLMAugmentations( - options: { - cognitionName?: string, - activationName?: string, - brainyDb?: BrainyData - } = {} -): Promise<{ - cognition: LLMCognitionAugmentation, - activation: LLMActivationAugmentation -}> { - // Create the cognition augmentation - const cognition = new LLMCognitionAugmentation(options.cognitionName) - await cognition.initialize() - - // Set the Brainy database if provided - if (options.brainyDb) { - cognition.setBrainyDb(options.brainyDb) - } - - // Create the activation augmentation - const activation = new LLMActivationAugmentation(options.activationName) - await activation.initialize() - - // Link the augmentations - activation.setCognitionAugmentation(cognition) - - return { - cognition, - activation - } -} diff --git a/src/augmentations/llmTrainingAugmentations.ts b/src/augmentations/llmTrainingAugmentations.ts deleted file mode 100644 index 965ff018..00000000 --- a/src/augmentations/llmTrainingAugmentations.ts +++ /dev/null @@ -1,636 +0,0 @@ -import { - AugmentationType, - IActivationAugmentation, - AugmentationResponse -} from '../types/augmentations.js' -import { BrainyData } from '../brainyData.js' -import { GraphNoun, GraphVerb, NounType, VerbType } from '../types/graphTypes.js' - -/** - * LLMTrainingActivationAugmentation - * - * An activation augmentation that provides actions for exporting Brainy graph data - * to train and export a Language Learning Model (LLM). - */ -export class LLMTrainingActivationAugmentation implements IActivationAugmentation { - readonly name: string - readonly description: string - enabled: boolean = true - private isInitialized = false - private brainyData: BrainyData | null = null - - constructor(name: string = 'llm-training-activation') { - this.name = name - this.description = 'Activation augmentation for training and exporting LLMs using Brainy graph data' - } - - getType(): AugmentationType { - return AugmentationType.ACTIVATION - } - - /** - * Initialize the augmentation - */ - async initialize(): Promise { - if (this.isInitialized) { - return - } - - this.isInitialized = true - } - - /** - * Shut down the augmentation - */ - async shutDown(): Promise { - this.isInitialized = false - } - - /** - * Get the status of the augmentation - */ - async getStatus(): Promise<'active' | 'inactive' | 'error'> { - return this.isInitialized ? 'active' : 'inactive' - } - - /** - * Set the Brainy data instance to use for accessing graph data - * @param data The BrainyData instance - */ - setBrainyData(data: BrainyData): void { - this.brainyData = data - } - - /** - * Trigger an action based on a processed command or internal state - * @param actionName The name of the action to trigger - * @param parameters Optional parameters for the action - */ - // This is the interface method that returns a synchronous response - triggerAction( - actionName: string, - parameters?: Record - ): AugmentationResponse { - if (!this.brainyData) { - return { - success: false, - data: null, - error: 'BrainyData instance not set' - } - } - - // Start the async operation - this.triggerActionAsync(actionName, parameters); - - // Return a placeholder response - return { - success: true, - data: { status: 'processing', actionName, parameters }, - }; - } - - // This is the actual implementation that handles the async operations - private async triggerActionAsync( - actionName: string, - parameters?: Record - ): Promise { - try { - let result: AugmentationResponse; - - switch (actionName) { - case 'exportGraphData': - result = await this.handleExportGraphData(parameters || {}); - break; - case 'generateTrainingData': - result = await this.handleGenerateTrainingData(parameters || {}); - break; - case 'trainModel': - result = await this.handleTrainModel(parameters || {}); - break; - case 'exportModel': - result = await this.handleExportModel(parameters || {}); - break; - default: - result = { - success: false, - data: null, - error: `Unknown action: ${actionName}` - }; - } - - // Here you would typically emit an event or update a status property - // with the result of the async operation - console.log(`Action ${actionName} completed:`, result); - } catch (error) { - console.error(`Error executing action ${actionName}:`, error); - } - } - - /** - * Handle the exportGraphData action - * @param parameters Action parameters - */ - private async handleExportGraphData( - parameters: Record - ): Promise> { - const format = parameters.format as string || 'json' - const includeNouns = parameters.includeNouns as boolean || true - const includeVerbs = parameters.includeVerbs as boolean || true - const nounTypes = parameters.nounTypes as NounType[] || Object.values(NounType) - const verbTypes = parameters.verbTypes as VerbType[] || Object.values(VerbType) - - try { - const exportData: Record = {} - - // Export nouns if requested - if (includeNouns) { - const nouns: GraphNoun[] = [] - // Get all nouns from BrainyData - // Use search to get all nouns of the specified types - for (const nounType of nounTypes) { - try { - // Search for nouns of this type with a high limit to get all - const searchResults = await this.brainyData!.searchByNounTypes( - '', // Empty query to match all - 1000, // High limit to get as many as possible - [nounType as string] - ); - - // Add the results to our nouns array - if (searchResults && Array.isArray(searchResults)) { - for (const result of searchResults) { - // SearchResult might not have data directly, use metadata instead - if (result.metadata) { - nouns.push(result.metadata as unknown as GraphNoun); - } - } - } - } catch (error) { - console.warn(`Failed to get nouns of type ${nounType}:`, error); - } - } - - exportData.nouns = nouns - } - - // Export verbs if requested - if (includeVerbs) { - try { - const verbs = await this.brainyData!.getAllVerbs() || [] - exportData.verbs = verbs.filter(verb => { - // Check for verb type in either verb.verb or verb.type - const verbType = (verb as any).verb || (verb as any).type; - return typeof verbType === 'string' && verbTypes.includes(verbType as VerbType); - }) - } catch (error) { - console.warn('Failed to get all verbs:', error); - exportData.verbs = []; - } - } - - // Format the export data - if (format === 'json') { - return { - success: true, - data: exportData - } - } else if (format === 'csv') { - // Convert to CSV format (simplified) - const csvData = this.convertToCSV(exportData) - return { - success: true, - data: csvData - } - } else { - return { - success: false, - data: null, - error: `Unsupported format: ${format}` - } - } - } catch (error) { - return { - success: false, - data: null, - error: `Failed to export graph data: ${error}` - } - } - } - - /** - * Handle the generateTrainingData action - * @param parameters Action parameters - */ - private async handleGenerateTrainingData( - parameters: Record - ): Promise> { - const format = parameters.format as string || 'jsonl' - const includeEmbeddings = parameters.includeEmbeddings as boolean || true - const maxSamples = parameters.maxSamples as number || 1000 - - try { - // First, export the graph data - const exportResult = await this.handleExportGraphData({ - format: 'json', - includeNouns: true, - includeVerbs: true - }) - - if (!exportResult.success) { - return exportResult - } - - const graphData = exportResult.data as Record - const nouns = graphData.nouns as GraphNoun[] || [] - const verbs = graphData.verbs as GraphVerb[] || [] - - // Generate training samples - const trainingSamples = this.generateTrainingSamples(nouns, verbs, maxSamples, includeEmbeddings) - - // Format the training data - if (format === 'jsonl') { - const jsonlData = trainingSamples.map(sample => JSON.stringify(sample)).join('\n') - return { - success: true, - data: jsonlData - } - } else if (format === 'json') { - return { - success: true, - data: trainingSamples - } - } else { - return { - success: false, - data: null, - error: `Unsupported format: ${format}` - } - } - } catch (error) { - return { - success: false, - data: null, - error: `Failed to generate training data: ${error}` - } - } - } - - /** - * Handle the trainModel action - * @param parameters Action parameters - */ - private async handleTrainModel( - parameters: Record - ): Promise> { - const modelType = parameters.modelType as string || 'default' - const epochs = parameters.epochs as number || 10 - const batchSize = parameters.batchSize as number || 32 - const learningRate = parameters.learningRate as number || 0.001 - - try { - // First, generate the training data - const trainingDataResult = await this.handleGenerateTrainingData({ - format: 'json' - }) - - if (!trainingDataResult.success) { - return trainingDataResult - } - - const trainingSamples = trainingDataResult.data as any[] - - // In a real implementation, this would call an actual LLM training library - // For now, we'll just simulate the training process - const trainingResult = this.simulateTraining( - trainingSamples, - modelType, - epochs, - batchSize, - learningRate - ) - - return { - success: true, - data: trainingResult - } - } catch (error) { - return { - success: false, - data: null, - error: `Failed to train model: ${error}` - } - } - } - - /** - * Handle the exportModel action - * @param parameters Action parameters - */ - private async handleExportModel( - parameters: Record - ): Promise> { - const format = parameters.format as string || 'onnx' - const modelId = parameters.modelId as string - - try { - // In a real implementation, this would export the trained model - // For now, we'll just return a simulated export result - const exportResult = { - modelId: modelId || 'default-model', - format, - timestamp: new Date().toISOString(), - size: '125MB', - parameters: '7B', - url: `https://example.com/models/${modelId || 'default-model'}.${format}` - } - - return { - success: true, - data: exportResult - } - } catch (error) { - return { - success: false, - data: null, - error: `Failed to export model: ${error}` - } - } - } - - /** - * Generates an expressive output or response from Brainy - * @param knowledgeId The identifier of the knowledge to express - * @param format The desired output format (e.g., 'text', 'json') - */ - generateOutput( - knowledgeId: string, - format: string - ): AugmentationResponse> { - if (!this.brainyData) { - return { - success: false, - data: '', - error: 'BrainyData instance not set' - } - } - - // Start the async operation - this.generateOutputAsync(knowledgeId, format); - - // Return a placeholder response - return { - success: true, - data: { status: 'processing', knowledgeId, format }, - }; - } - - // This is the actual implementation that handles the async operations - private async generateOutputAsync( - knowledgeId: string, - format: string - ): Promise { - try { - // Get the knowledge from BrainyData - const knowledge = await this.brainyData!.get(knowledgeId); - let result: AugmentationResponse>; - - if (!knowledge) { - result = { - success: false, - data: '', - error: `Knowledge not found: ${knowledgeId}` - }; - } else { - // Format the output - if (format === 'text') { - // Generate a text representation of the knowledge - const text = this.generateTextFromKnowledge(knowledge); - result = { - success: true, - data: text - }; - } else if (format === 'json') { - // Return the knowledge as JSON - // Convert VectorDocument to a plain object to avoid type issues - const knowledgeObj = { ...knowledge }; - result = { - success: true, - data: knowledgeObj - }; - } else { - result = { - success: false, - data: '', - error: `Unsupported format: ${format}` - }; - } - } - - // Here you would typically emit an event or update a status property - // with the result of the async operation - console.log(`Generate output for ${knowledgeId} completed:`, result); - } catch (error) { - console.error(`Failed to generate output for ${knowledgeId}:`, error); - } - } - - /** - * Interacts with an external system or API - * @param systemId The identifier of the external system - * @param payload The data to send to the external system - */ - interactExternal( - systemId: string, - payload: Record - ): AugmentationResponse { - // This method would interact with external LLM training systems or APIs - // For now, we'll just return a simulated response - return { - success: true, - data: { - systemId, - status: 'processing', - message: 'Interaction with external system initiated', - timestamp: new Date().toISOString(), - payload - } - } - } - - /** - * Helper method to convert data to CSV format - * @param data The data to convert - * @returns CSV formatted string - */ - private convertToCSV(data: Record): string { - // Simplified CSV conversion - in a real implementation, this would be more robust - let csv = '' - - // Handle nouns - if (data.nouns && Array.isArray(data.nouns) && data.nouns.length > 0) { - const nouns = data.nouns as GraphNoun[] - // Add header - csv += 'id,noun,label,createdAt\n' - - // Add rows - for (const noun of nouns) { - csv += `${noun.id},${noun.noun},${noun.label || ''},${noun.createdAt.seconds}\n` - } - - csv += '\n' - } - - // Handle verbs - if (data.verbs && Array.isArray(data.verbs) && data.verbs.length > 0) { - const verbs = data.verbs as GraphVerb[] - // Add header - csv += 'id,source,target,verb,label,createdAt\n' - - // Add rows - for (const verb of verbs) { - csv += `${verb.id},${verb.source},${verb.target},${verb.verb},${verb.label || ''},${verb.createdAt.seconds}\n` - } - } - - return csv - } - - /** - * Helper method to generate training samples from graph data - * @param nouns The graph nouns - * @param verbs The graph verbs - * @param maxSamples Maximum number of samples to generate - * @param includeEmbeddings Whether to include embeddings in the samples - * @returns Array of training samples - */ - private generateTrainingSamples( - nouns: GraphNoun[], - verbs: GraphVerb[], - maxSamples: number, - includeEmbeddings: boolean - ): any[] { - const samples = [] - - // Create a map of noun IDs to nouns for quick lookup - const nounMap = new Map() - for (const noun of nouns) { - nounMap.set(noun.id, noun) - } - - // Generate samples from verbs (relationships) - for (const verb of verbs) { - if (samples.length >= maxSamples) break - - const sourceNoun = nounMap.get(verb.source) - const targetNoun = nounMap.get(verb.target) - - if (sourceNoun && targetNoun) { - // Create a training sample - const sample: Record = { - input: `What is the relationship between ${sourceNoun.label || sourceNoun.id} and ${targetNoun.label || targetNoun.id}?`, - output: `${sourceNoun.label || sourceNoun.id} ${verb.verb} ${targetNoun.label || targetNoun.id}` - } - - // Add embeddings if requested - if (includeEmbeddings) { - sample.sourceEmbedding = sourceNoun.embedding - sample.targetEmbedding = targetNoun.embedding - sample.verbEmbedding = verb.embedding - } - - samples.push(sample) - } - } - - // Generate samples from nouns (entities) - for (const noun of nouns) { - if (samples.length >= maxSamples) break - - // Create a training sample - const sample: Record = { - input: `What type of entity is ${noun.label || noun.id}?`, - output: `${noun.label || noun.id} is a ${noun.noun}` - } - - // Add embeddings if requested - if (includeEmbeddings) { - sample.embedding = noun.embedding - } - - samples.push(sample) - } - - return samples - } - - /** - * Helper method to simulate training an LLM - * @param trainingSamples The training samples - * @param modelType The type of model to train - * @param epochs Number of training epochs - * @param batchSize Batch size for training - * @param learningRate Learning rate for training - * @returns Simulated training result - */ - private simulateTraining( - trainingSamples: any[], - modelType: string, - epochs: number, - batchSize: number, - learningRate: number - ): Record { - // In a real implementation, this would use an actual LLM training library - // For now, we'll just simulate the training process - return { - modelId: `${modelType}-${new Date().getTime()}`, - trainingSamples: trainingSamples.length, - epochs, - batchSize, - learningRate, - trainingTime: `${Math.floor(Math.random() * 100) + 50}s`, - loss: Math.random() * 0.5, - accuracy: 0.5 + Math.random() * 0.5, - timestamp: new Date().toISOString() - } - } - - /** - * Helper method to generate text from knowledge - * @param knowledge The knowledge to generate text from - * @returns Generated text - */ - private generateTextFromKnowledge(knowledge: any): string { - // In a real implementation, this would generate natural language text - // For now, we'll just return a simple string - if (knowledge.noun) { - return `This is a ${knowledge.noun} called "${knowledge.label || knowledge.id}".` - } else if (knowledge.verb) { - return `This is a ${knowledge.verb} relationship from "${knowledge.source}" to "${knowledge.target}".` - } else { - return `This is an entity with ID "${knowledge.id}".` - } - } -} - -/** - * Factory function to create an LLM training augmentation - * @param options Additional options - * @returns The created augmentation - */ -export async function createLLMTrainingAugmentation( - options: { - name?: string, - brainyData?: BrainyData - } = {} -): Promise { - // Create the activation augmentation - const activation = new LLMTrainingActivationAugmentation(options.name) - await activation.initialize() - - // Set the BrainyData instance if provided - if (options.brainyData) { - activation.setBrainyData(options.brainyData) - } - - return activation -} diff --git a/test/validateTypes.js b/test/validateTypes.js deleted file mode 100644 index 9653c93c..00000000 --- a/test/validateTypes.js +++ /dev/null @@ -1,63 +0,0 @@ -// Test script to verify type validation in BrainyData -import { BrainyData } from '../src/brainyData.js'; -import { NounType, VerbType } from '../src/types/graphTypes.js'; - -async function testTypeValidation() { - console.log('Testing type validation in BrainyData...'); - - // Create a new BrainyData instance - const brainy = new BrainyData(); - await brainy.init(); - - console.log('Testing node with valid noun type...'); - const validNodeId = await brainy.add([0.1, 0.2, 0.3], { - noun: NounType.Person, - label: 'Test Person' - }); - console.log(`Added node with valid noun type: ${validNodeId}`); - - console.log('Testing node with invalid noun type...'); - const invalidNodeId = await brainy.add([0.4, 0.5, 0.6], { - noun: 'invalid_type', - label: 'Test Invalid' - }); - console.log(`Added node with invalid noun type (should be converted to default): ${invalidNodeId}`); - - // Get the metadata to verify it was corrected - const invalidNodeMetadata = await brainy.get(invalidNodeId); - console.log('Metadata for node with invalid type:', invalidNodeMetadata); - - console.log('Testing edge with valid verb type...'); - const validEdgeId = await brainy.addEdge(validNodeId, invalidNodeId, undefined, { - type: VerbType.RelatedTo, - metadata: { label: 'Test Relation' } - }); - console.log(`Added edge with valid verb type: ${validEdgeId}`); - - console.log('Testing edge with invalid verb type...'); - const invalidEdgeId = await brainy.addEdge(validNodeId, invalidNodeId, undefined, { - type: 'invalid_relation', - metadata: { label: 'Test Invalid Relation' } - }); - console.log(`Added edge with invalid verb type (should be converted to default): ${invalidEdgeId}`); - - // Get the edge to verify it was corrected - const invalidEdge = await brainy.getEdge(invalidEdgeId); - console.log('Edge with invalid type:', invalidEdge); - - console.log('Testing updateMetadata with invalid noun type...'); - await brainy.updateMetadata(validNodeId, { - noun: 'another_invalid_type', - label: 'Updated Test' - }); - - // Get the metadata to verify it was corrected - const updatedMetadata = await brainy.get(validNodeId); - console.log('Updated metadata (should have corrected noun type):', updatedMetadata); - - console.log('All tests completed.'); -} - -testTypeValidation().catch(error => { - console.error('Test failed:', error); -});