Introduced `llm-augmentation.md`, detailing the creation, training, testing, exporting, and deployment of language models using Brainy's graph database. Added examples showcasing practical usage and pipeline integration. Included class implementations in `llmAugmentations.ts`.
1690 lines
46 KiB
TypeScript
1690 lines
46 KiB
TypeScript
/**
|
|
* 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
|
|
}
|
|
|
|
/**
|
|
* 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<string, {
|
|
model: tf.LayersModel,
|
|
metadata: LLMModelMetadata,
|
|
tokenizer?: Map<string, number>
|
|
}> = 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
|
|
}
|
|
|
|
constructor(name: string = 'llm-cognition') {
|
|
this.name = name
|
|
}
|
|
|
|
getType(): AugmentationType {
|
|
return AugmentationType.COGNITION
|
|
}
|
|
|
|
/**
|
|
* Initialize the augmentation
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
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<void> {
|
|
// 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
|
|
* @param config Configuration for the model
|
|
* @returns Model ID and metadata
|
|
*/
|
|
async createModel(
|
|
config: Partial<LLMModelConfig> = {}
|
|
): Promise<AugmentationResponse<{ modelId: string, metadata: LLMModelMetadata }>> {
|
|
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<tf.LayersModel> {
|
|
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 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<AugmentationResponse<{
|
|
modelId: string,
|
|
metadata: LLMModelMetadata,
|
|
trainingHistory: tf.History,
|
|
metrics?: {
|
|
loss: number,
|
|
accuracy: number,
|
|
epochs: number
|
|
}
|
|
}>> {
|
|
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: this.defaultModelConfig.epochs,
|
|
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<string, number>,
|
|
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<string, number> {
|
|
const tokenizer = new Map<string, number>()
|
|
const wordFreq = new Map<string, number>()
|
|
|
|
// 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('<pad>', 0)
|
|
tokenizer.set('<unk>', 1)
|
|
tokenizer.set('<start>', 2)
|
|
tokenizer.set('<end>', 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<string, number>): number[] {
|
|
const words = text.toLowerCase().split(/\s+/)
|
|
return words.map(word => tokenizer.get(word) || tokenizer.get('<unk>')!)
|
|
}
|
|
|
|
/**
|
|
* 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<T>(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<AugmentationResponse<{
|
|
modelId: string,
|
|
metrics: Record<string, number>,
|
|
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<string, number> = {
|
|
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<number, string>()
|
|
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) || '<unk>'
|
|
|
|
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<AugmentationResponse<{
|
|
modelId: string,
|
|
format: string,
|
|
exportPath?: string,
|
|
modelJSON?: string,
|
|
path?: string,
|
|
size?: number
|
|
}>> {
|
|
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<AugmentationResponse<{
|
|
modelId: string,
|
|
deploymentTarget: string,
|
|
deploymentUrl?: string,
|
|
status: string,
|
|
url?: string,
|
|
deploymentId?: 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`
|
|
}
|
|
}
|
|
|
|
// 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
|
|
* @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<AugmentationResponse<{
|
|
text: string,
|
|
tokens?: number,
|
|
timeMs?: number
|
|
}>> {
|
|
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<number, string>()
|
|
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) || '<unk>'
|
|
|
|
// 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<string, unknown>
|
|
): 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<string, unknown>): AugmentationResponse<Record<string, unknown>> {
|
|
// 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<string, unknown>): AugmentationResponse<boolean> {
|
|
// This would apply logical rules based on the trained model
|
|
return {
|
|
success: true,
|
|
data: true
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure the augmentation is initialized
|
|
*/
|
|
private async ensureInitialized(): Promise<void> {
|
|
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<void> {
|
|
if (this.isInitialized) {
|
|
return
|
|
}
|
|
|
|
this.isInitialized = true
|
|
}
|
|
|
|
/**
|
|
* Shut down the augmentation
|
|
*/
|
|
async shutDown(): Promise<void> {
|
|
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<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
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 'trainModel':
|
|
return this.handleTrainModel(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 || {})
|
|
default:
|
|
return {
|
|
success: false,
|
|
data: null,
|
|
error: `Unknown action: ${actionName}`
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle the createModel action
|
|
* @param parameters Action parameters
|
|
*/
|
|
private handleCreateModel(
|
|
parameters: Record<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
return {
|
|
success: true,
|
|
data: this.cognitionAugmentation!.createModel(parameters as Partial<LLMModelConfig>)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle the trainModel action
|
|
* @param parameters Action parameters
|
|
*/
|
|
private handleTrainModel(
|
|
parameters: Record<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
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<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
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<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
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<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
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 generateText action
|
|
* @param parameters Action parameters
|
|
*/
|
|
private handleGenerateText(
|
|
parameters: Record<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
const modelId = parameters.modelId as string
|
|
const prompt = parameters.prompt as string
|
|
const options = parameters.options as Record<string, unknown> || {}
|
|
|
|
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<string | Record<string, unknown>> {
|
|
// 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<string, unknown>
|
|
): AugmentationResponse<unknown> {
|
|
// 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
|
|
}
|
|
}
|