mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
365 lines • 15.6 kB
JavaScript
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