UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

365 lines 15.6 kB
import { spawn } from 'child_process'; import * as path from 'path'; import fs from 'fs-extra'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export class DirectPythonInterface { pythonMemoryDir; memoryDir; activeProcesses = new Set(); processCleanupCallbacks = []; constructor() { // Get the resolved memory directory from environment (set by CLI) this.memoryDir = process.env.MIRA_RESOLVED_MEMORY_DIR || process.cwd() + '/.mira'; // Find python-memory directory this.pythonMemoryDir = this.findPythonMemoryDir(); } // Register a cleanup callback for when processes need to be terminated onProcessCleanup(callback) { this.processCleanupCallbacks.push(callback); } // Terminate all active Python processes async terminateAllProcesses() { for (const process of this.activeProcesses) { try { process.kill('SIGTERM'); } catch (error) { // Process might already be dead } } this.activeProcesses.clear(); // Call all cleanup callbacks for (const callback of this.processCleanupCallbacks) { try { callback(); } catch (error) { // Ignore callback errors } } } findPythonMemoryDir() { const potentialPaths = [ path.join(__dirname, '../../python-memory'), // From dist/src/core path.join(__dirname, '../../../python-memory'), // From src/core path.join(__dirname, '../python-memory'), // From dist/src path.join(process.cwd(), 'mira-memory/python-memory'), // From workspace root path.join(process.cwd(), 'python-memory'), // Direct path.join(process.cwd(), 'dist/python-memory'), // Dist directory '/workspaces/MIRA/mira-memory/python-memory', // Absolute path '/workspaces/MIRA/mira-memory/dist/python-memory', // Absolute dist path ]; // Also check if direct_interface.py exists in the found directory const pythonDir = potentialPaths.find(p => { const exists = fs.pathExistsSync(p); const hasInterface = exists && fs.pathExistsSync(path.join(p, 'core/direct_interface.py')); if (process.env.DEBUG_MIRA && exists) { console.error(`Checking ${p}: exists=${exists}, hasInterface=${hasInterface}`); } return exists && hasInterface; }); if (!pythonDir) { console.error('Tried paths:', potentialPaths); throw new Error('Python memory directory with direct_interface.py not found'); } return pythonDir; } /** * Execute a Python command through the direct interface */ async executeCommand(command, args = {}, timeoutMs = 60000) { return new Promise((resolve, reject) => { const scriptPath = path.join(this.pythonMemoryDir, 'core/direct_interface.py'); // Debug logging if (process.env.DEBUG_MIRA) { console.error('DirectPythonInterface Debug:'); console.error(' pythonMemoryDir:', this.pythonMemoryDir); console.error(' scriptPath:', scriptPath); console.error(' command:', command); console.error(' memoryDir:', this.memoryDir); } // Convert args to JSON string if it's an object const argsString = typeof args === 'object' ? JSON.stringify(args) : String(args); const pythonArgs = [scriptPath, command, argsString]; const pythonProcess = spawn('python3', pythonArgs, { cwd: this.pythonMemoryDir, env: { ...process.env, PYTHONPATH: this.pythonMemoryDir, MIRA_MEMORY_DIR: this.memoryDir, // Pass resolved memory directory } }); // Track this process this.activeProcesses.add(pythonProcess); let resolved = false; const startTime = Date.now(); // Set up timeout with enhanced error information const timeout = setTimeout(() => { if (!resolved) { resolved = true; pythonProcess.kill('SIGTERM'); const errorResult = { success: false, error: `Python command '${command}' timed out after ${timeoutMs}ms`, error_type: 'timeout', error_details: { command, args, timeout_ms: timeoutMs, elapsed_ms: Date.now() - startTime, timestamp: new Date().toISOString() } }; resolve(errorResult); } }, timeoutMs); let stdout = ''; let stderr = ''; pythonProcess.stdout.on('data', (data) => { stdout += data.toString(); }); pythonProcess.stderr.on('data', (data) => { stderr += data.toString(); }); pythonProcess.on('close', (code) => { clearTimeout(timeout); // Remove from active processes this.activeProcesses.delete(pythonProcess); if (!resolved) { resolved = true; if (code !== 0) { // Enhanced error information for failed processes const errorResult = { success: false, error: `Python process exited with code ${code}`, output: stdout, error_type: 'python_error', error_details: { command, args, exit_code: code, stderr: stderr.trim(), stdout: stdout.trim(), timestamp: new Date().toISOString(), // Extract Python traceback if present python_traceback: stderr.includes('Traceback') ? stderr : undefined } }; resolve(errorResult); } else { try { // Try to parse as JSON first const result = JSON.parse(stdout); // Enhance successful results with metadata if (typeof result === 'object' && result !== null) { result.execution_time = Date.now() - startTime; result.command_executed = command; } resolve(result); } catch (parseError) { // Enhanced error for JSON parsing failures const errorResult = { success: false, error: `Failed to parse Python output as JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`, output: stdout.trim(), error_type: 'json_parse_error', error_details: { command, args, stderr: stderr.trim(), stdout: stdout.trim(), timestamp: new Date().toISOString() } }; resolve(errorResult); } } } }); pythonProcess.on('error', (error) => { clearTimeout(timeout); if (!resolved) { resolved = true; // Enhanced spawn error information const errorResult = { success: false, error: `Failed to spawn Python process: ${error.message}`, error_type: 'spawn_error', error_details: { command, args, timestamp: new Date().toISOString(), spawn_error: error.message } }; resolve(errorResult); } }); }); } /** * Journey command helpers */ async buildJourney() { return this.executeCommand('journey', { action: 'build' }); } async searchJourney(query, limit = 5) { return this.executeCommand('journey', { action: 'search', query, limit }); } async getJourneyStatus() { return this.executeCommand('journey', { action: 'status' }); } /** * Memory command helpers */ async storeMemory(content, type = 'user_memory') { // The Python interface expects the content as a direct argument, not wrapped in an object return this.executeCommand('store_memory', [content]); } async recallMemories(query = '') { return this.executeCommand('recall_memories', query); } /** * Other command helpers */ async getEssence(topic) { return this.executeCommand('essence', topic || ''); } async getNeuralState() { return this.executeCommand('neural_state'); } async runStartup() { // Give startup more time as it performs ML ingestion and analysis return this.executeCommand('startup', {}, 180000); // 3 minutes } async runRecover() { return this.executeCommand('recover'); } async runUnifiedAnalysis(query) { return this.executeCommand('unified_analysis', query); } async runAutoEnhance() { return this.executeCommand('auto_enhance'); } async runLearn(topic) { return this.executeCommand('learn', topic); } /** * Indexing and stats helpers */ async indexConversations(force = false) { // Indexing can take a long time for large conversation datasets return this.executeCommand('index', { force }, 300000); // 5 minutes } async getStats() { return this.executeCommand('stats'); } async searchConversations(query, limit = 10) { return this.executeCommand('search', { query, limit }); } async generateVideo() { return this.executeCommand('generate_video'); } async getIdentity() { return this.executeCommand('identity'); } /** * Neural consciousness helpers */ async generateNeuralResponse(context) { return this.executeCommand('generate_neural_response', { context }); } async indexCommandOutput(commandData) { return this.executeCommand('index_command_output', { commandData }); } /** * Get timeout for specific MCP tool operations */ getMCPTimeout(toolName) { // Method-specific timeouts for long-running operations const timeouts = { 'mira_status': 120000, // 2 minutes for comprehensive status 'mira_ask': 90000, // 1.5 minutes for complex searches 'mira_smart_search': 90000, // 1.5 minutes for smart search 'mira_analyze_behavior': 120000, // 2 minutes for behavioral analysis 'mira_work_context': 60000, // 1 minute for work context 'mira_predictive_memories': 90000, // 1.5 minutes for predictive surfacing 'mira_emotional_resonance': 60000, // 1 minute for emotional analysis 'mira_insights': 90000, // 1.5 minutes for insight generation 'mira_remember': 30000, // 30 seconds for storing memories 'mira_sync': 15000, // 15 seconds for sync operations 'mira_config': 30000 // 30 seconds for config operations }; return timeouts[toolName] || 60000; // Default 60 seconds } /** * MCP Gateway call method * Executes MCP tools through the Python gateway with method-specific timeouts */ async callMCPGateway(toolName, args = {}) { try { // Get appropriate timeout for this tool const timeout = this.getMCPTimeout(toolName); const result = await this.executeCommand('mcp_gateway_call', { tool_name: toolName, arguments: args }, timeout); if (result.success) { return result.data; } else { // Create enhanced MCP gateway error const mcpError = new Error(`MCP Gateway call failed for '${toolName}': ${result.error || 'Unknown error'}`); // Add error context for debugging if (result.error_details) { mcpError.errorDetails = { ...result.error_details, tool_name: toolName, tool_args: args, error_type: result.error_type || 'mcp_gateway_error' }; } // Log detailed error for debugging if (process.env.DEBUG_MIRA || process.env.MIRA_MCP_DEBUG) { console.error('🔴 MCP Gateway Error Details:'); console.error(` Tool: ${toolName}`); console.error(` Args:`, JSON.stringify(args, null, 2)); console.error(` Error:`, result.error); if (result.error_details?.python_traceback) { console.error(` Python Traceback:`, result.error_details.python_traceback); } if (result.error_details?.stderr) { console.error(` Python stderr:`, result.error_details.stderr); } } throw mcpError; } } catch (error) { // Re-throw if already an enhanced error if (error instanceof Error && error.errorDetails) { throw error; } // Create new enhanced error for unexpected failures const enhancedError = new Error(`MCP Gateway call failed for '${toolName}': ${error instanceof Error ? error.message : String(error)}`); enhancedError.errorDetails = { tool_name: toolName, tool_args: args, error_type: 'mcp_gateway_error', original_error: error instanceof Error ? error.message : String(error), timestamp: new Date().toISOString() }; throw enhancedError; } } } // Convenience function to get a direct interface instance export function getDirectPythonInterface() { return new DirectPythonInterface(); } //# sourceMappingURL=DirectPythonInterface.js.map