Source: api/features/MemoryAPI.js

import BaseAPI from '../common/BaseAPI.js';
import { v4 as uuidv4 } from 'uuid';

/**
 * Memory API handler for managing semantic memory operations
 */
export default class MemoryAPI extends BaseAPI {
    constructor(config = {}) {
        super(config);
        this.memoryManager = null;
        this.similarityThreshold = config.similarityThreshold || 40;
        this.defaultLimit = config.defaultLimit || 10;
    }

    async initialize() {
        await super.initialize();
        
        // Get dependency from registry
        const registry = this.config.registry;
        if (!registry) {
            throw new Error('Registry is required for MemoryAPI');
        }
        
        try {
            this.memoryManager = registry.get('memory');
            this.logger.info('MemoryAPI initialized successfully');
        } catch (error) {
            this.logger.error('Failed to initialize MemoryAPI:', error);
            throw error;
        }
    }

    /**
     * Execute a memory operation
     */
    async executeOperation(operation, params) {
        this._validateParams(params);
        
        const start = Date.now();
        
        try {
            let result;
            
            switch (operation) {
                case 'store':
                    result = await this.storeInteraction(params);
                    break;
                case 'search':
                    result = await this.retrieveInteractions(params);
                    break;
                case 'embedding':
                    result = await this.generateEmbedding(params);
                    break;
                case 'concepts':
                    result = await this.extractConcepts(params);
                    break;
                default:
                    throw new Error(`Unknown operation: ${operation}`);
            }
            
            const duration = Date.now() - start;
            this._emitMetric(`operation.${operation}.duration`, duration);
            this._emitMetric(`operation.${operation}.count`, 1);
            
            return result;
        } catch (error) {
            this._emitMetric(`operation.${operation}.errors`, 1);
            throw error;
        }
    }

    /**
     * Store an interaction in memory
     */
    async storeInteraction({ prompt, response, metadata = {} }) {
        if (!prompt || !response) {
            throw new Error('Prompt and response are required');
        }

        try {
            // Generate embedding
            const embedding = await this.memoryManager.generateEmbedding(
                `${prompt} ${response}`
            );
            
            // Extract concepts
            const concepts = await this.memoryManager.extractConcepts(
                `${prompt} ${response}`
            );
            
            // Add interaction to memory
            const id = uuidv4();
            await this.memoryManager.addInteraction(
                prompt, 
                response, 
                embedding, 
                concepts,
                {
                    id,
                    timestamp: Date.now(),
                    ...metadata
                }
            );
            
            this._emitMetric('memory.store.count', 1);
            return { 
                id, 
                concepts, 
                timestamp: Date.now(),
                success: true 
            };
        } catch (error) {
            this._emitMetric('memory.store.errors', 1);
            throw error;
        }
    }

    /**
     * Retrieve interactions from memory
     */
    async retrieveInteractions(params) {
        this.logger.info('retrieveInteractions called with params:', params);
        
        const { query, threshold, limit = this.defaultLimit } = params || {};
        
        if (!query) {
            throw new Error('Query is required');
        }

        try {
            const similarityThreshold = threshold || this.similarityThreshold;
            
            // Debug: Check if memory manager is available
            if (!this.memoryManager) {
                throw new Error('Memory manager not initialized');
            }
            
            this.logger.info('Starting search with params:', {
                query,
                similarityThreshold,
                limit
            });
            
            const results = await this.memoryManager.retrieveRelevantInteractions(
                query,
                similarityThreshold,
                0, // excludeLastN
                limit // limit
            );
            
            this.logger.info('Search results received:', {
                resultsType: typeof results,
                resultsLength: Array.isArray(results) ? results.length : 'not array',
                firstResult: results?.[0] ? Object.keys(results[0]) : 'no first result'
            });
            
            // Format the results according to API schema
            // Note: combineResults returns flattened interaction objects with totalScore
            const formattedResults = results.map(item => ({
                id: item.id,
                prompt: item.prompt,
                output: item.output,
                concepts: item.concepts,
                timestamp: item.timestamp,
                accessCount: item.accessCount,
                similarity: item.totalScore || 0
            }));
            
            this._emitMetric('memory.search.count', 1);
            return { 
                results: formattedResults, 
                count: formattedResults.length 
            };
        } catch (error) {
            this._emitMetric('memory.search.errors', 1);
            this.logger.error('Search error details:', {
                message: error.message,
                stack: error.stack,
                query,
                threshold: similarityThreshold,
                limit
            });
            throw error;
        }
    }

    /**
     * Generate embedding for text
     */
    async generateEmbedding({ text, model }) {
        if (!text) {
            throw new Error('Text is required');
        }

        try {
            const embedding = await this.memoryManager.generateEmbedding(text, model);
            
            this._emitMetric('memory.embedding.count', 1);
            return { 
                embedding, 
                model: model || 'default',
                dimension: embedding.length
            };
        } catch (error) {
            this._emitMetric('memory.embedding.errors', 1);
            throw error;
        }
    }

    /**
     * Extract concepts from text
     */
    async extractConcepts({ text }) {
        if (!text) {
            throw new Error('Text is required');
        }

        try {
            const concepts = await this.memoryManager.extractConcepts(text);
            
            this._emitMetric('memory.concepts.count', 1);
            return { 
                concepts, 
                text
            };
        } catch (error) {
            this._emitMetric('memory.concepts.errors', 1);
            throw error;
        }
    }

    /**
     * Get memory API metrics
     */
    async getMetrics() {
        const baseMetrics = await super.getMetrics();
        
        return {
            ...baseMetrics,
            operations: {
                store: {
                    count: await this._getMetricValue('memory.store.count'),
                    errors: await this._getMetricValue('memory.store.errors')
                },
                search: {
                    count: await this._getMetricValue('memory.search.count'),
                    errors: await this._getMetricValue('memory.search.errors')
                },
                embedding: {
                    count: await this._getMetricValue('memory.embedding.count'),
                    errors: await this._getMetricValue('memory.embedding.errors')
                },
                concepts: {
                    count: await this._getMetricValue('memory.concepts.count'),
                    errors: await this._getMetricValue('memory.concepts.errors')
                }
            }
        };
    }

    async _getMetricValue(metricName) {
        // In a real implementation, this would fetch from a metrics store
        return 0;
    }
}