@alanhelmick/memorable
Version:
An AI memory system enabling personalized, context-aware interactions through advanced memory management and emotional intelligence
279 lines (240 loc) • 7.35 kB
JavaScript
import { promises as fs } from 'fs';
import { logger } from '../utils/logger.js';
import * as tf from '@tensorflow/tfjs-node';
import { NLP } from 'node-nlp';
import NodeWebcam from 'node-webcam';
import { Microphone } from 'node-microphone';
import { promisify } from 'util';
export class InputProcessor {
constructor() {
this.nlp = new NLP({ language: 'en' });
this.webcam = null;
this.microphone = null;
this.supportedTypes = ['text', 'vision', 'audio', 'video', 'file'];
}
async initialize() {
try {
// Initialize NLP
await this.nlp.train();
// Initialize webcam if enabled
if (process.env.ENABLE_CAMERA === 'true') {
this.webcam = NodeWebcam.create({
width: 1280,
height: 720,
quality: 100,
delay: 0,
saveShots: true,
output: 'jpeg',
device: false,
callbackReturn: 'buffer'
});
this.webcam.capture = promisify(this.webcam.capture);
}
// Initialize microphone if enabled
if (process.env.ENABLE_MICROPHONE === 'true') {
this.microphone = new Microphone();
}
logger.info('Input Processor initialized');
} catch (error) {
logger.error('Failed to initialize Input Processor:', error);
throw error;
}
}
async processInput(input, type) {
try {
if (!this.supportedTypes.includes(type)) {
throw new Error(`Unsupported input type: ${type}`);
}
const processedInput = await this[`process${type.charAt(0).toUpperCase() + type.slice(1)}`](input);
return {
type,
processed: processedInput,
timestamp: Date.now(),
metadata: this.extractMetadata(input, type)
};
} catch (error) {
logger.error(`Failed to process ${type} input:`, error);
throw error;
}
}
async processText(text) {
try {
// Process text input using NLP
const analysis = await this.nlp.process(text);
return {
original: text,
tokens: analysis.tokens,
sentiment: analysis.sentiment,
entities: analysis.entities,
intent: analysis.intent,
language: analysis.language,
embeddings: await this.generateTextEmbeddings(text)
};
} catch (error) {
logger.error('Text processing failed:', error);
throw error;
}
}
async processVision(input) {
try {
let imageBuffer;
if (input instanceof Buffer) {
imageBuffer = input;
} else if (typeof input === 'string') {
// Check if input is a file path or base64
if (input.startsWith('data:image')) {
imageBuffer = Buffer.from(input.split(',')[1], 'base64');
} else {
imageBuffer = await fs.readFile(input);
}
} else if (input === 'capture' && this.webcam) {
imageBuffer = await this.captureImage();
} else {
throw new Error('Invalid vision input format');
}
// Process image using TensorFlow.js
const tensor = tf.node.decodeImage(imageBuffer);
const processed = await this.processImageTensor(tensor);
tensor.dispose();
return processed;
} catch (error) {
logger.error('Vision processing failed:', error);
throw error;
}
}
async processAudio(input) {
try {
let audioBuffer;
if (input instanceof Buffer) {
audioBuffer = input;
} else if (typeof input === 'string') {
if (input === 'record' && this.microphone) {
audioBuffer = await this.recordAudio();
} else {
audioBuffer = await fs.readFile(input);
}
} else {
throw new Error('Invalid audio input format');
}
// Process audio data
const features = await this.extractAudioFeatures(audioBuffer);
return {
features,
duration: features.duration,
sampleRate: features.sampleRate,
embeddings: await this.generateAudioEmbeddings(features)
};
} catch (error) {
logger.error('Audio processing failed:', error);
throw error;
}
}
async processVideo(input) {
try {
// Extract frames and audio from video
const { frames, audio } = await this.extractVideoComponents(input);
// Process each component
const [processedFrames, processedAudio] = await Promise.all([
Promise.all(frames.map(frame => this.processVision(frame))),
this.processAudio(audio)
]);
return {
frames: processedFrames,
audio: processedAudio,
duration: processedAudio.duration,
embeddings: await this.generateVideoEmbeddings(processedFrames, processedAudio)
};
} catch (error) {
logger.error('Video processing failed:', error);
throw error;
}
}
async processFile(input) {
try {
const fileContent = await fs.readFile(input);
const fileType = await this.detectFileType(fileContent);
// Process based on detected file type
return await this.processInput(fileContent, fileType);
} catch (error) {
logger.error('File processing failed:', error);
throw error;
}
}
async captureImage() {
if (!this.webcam) {
throw new Error('Camera is not enabled');
}
return await this.webcam.capture('capture');
}
async recordAudio(duration = 5000) {
if (!this.microphone) {
throw new Error('Microphone is not enabled');
}
return new Promise((resolve, reject) => {
const chunks = [];
const mic = this.microphone.startRecording();
mic.on('data', chunk => chunks.push(chunk));
setTimeout(() => {
mic.stopRecording();
resolve(Buffer.concat(chunks));
}, duration);
mic.on('error', reject);
});
}
async processImageTensor(tensor) {
// Implement image processing using TensorFlow.js
// This could include object detection, face recognition, etc.
return {
shape: tensor.shape,
// Add more processing results
};
}
async extractAudioFeatures(buffer) {
// Implement audio feature extraction
// This could include spectral features, MFCC, etc.
return {
duration: 0,
sampleRate: 0,
// Add more features
};
}
async extractVideoComponents(input) {
// Implement video frame and audio extraction
return {
frames: [],
audio: Buffer.from([])
};
}
async generateTextEmbeddings(text) {
// Implement text embedding generation
return [];
}
async generateAudioEmbeddings(features) {
// Implement audio embedding generation
return [];
}
async generateVideoEmbeddings(frames, audio) {
// Implement video embedding generation
return [];
}
async detectFileType(buffer) {
// Implement file type detection
return 'text';
}
extractMetadata(input, type) {
// Extract metadata based on input type
return {
timestamp: Date.now(),
type,
size: input instanceof Buffer ? input.length : 0,
// Add more metadata
};
}
async cleanup() {
logger.info('Cleaning up Input Processor...');
if (this.microphone) {
this.microphone.stopRecording();
}
// Additional cleanup logic can be added here
}
}