UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

448 lines 18.6 kB
import { EventEmitter } from 'events'; import { logger } from '../core/logger.js'; import { SecurityValidator } from '../core/security/security-validator.js'; import { PerformanceMonitor } from '../utils/performance.js'; import { HardwareAwareModelSelector } from '../core/performance/hardware-aware-model-selector.js'; import { ActiveProcessManager } from '../core/performance/active-process-manager.js'; import { ProviderManager } from './provider-manager.js'; import { createHash } from 'crypto'; import { StreamingManager } from '../core/streaming/streaming-manager.js'; import { CacheCoordinator } from '../core/caching/cache-coordinator.js'; import { VoiceSynthesisManager, } from '../core/voice-system/voice-synthesis-manager.js'; import { ProviderSelectionStrategy, } from '../core/providers/provider-selection-strategy.js'; import { RequestExecutionManager, } from '../core/execution/request-execution-manager.js'; import { HealthStatusManager } from '../core/health/health-status-manager.js'; import { ConfigurationManager, } from '../core/config/configuration-manager.js'; import { RequestProcessingCoreManager, } from '../core/processing/request-processing-core-manager.js'; import { ModelManagementManager, } from '../core/models/model-management-manager.js'; import { ResourceCleanupManager, } from '../core/cleanup/resource-cleanup-manager.js'; import { StreamProcessingManager, } from '../core/streaming/stream-processing-manager.js'; import { RequestHandler } from './request-handler.js'; export class UnifiedModelClient extends EventEmitter { config; providerManager; performanceMonitor; securityValidator; activeRequests = new Map(); requestQueue = []; isProcessingQueue = false; cacheCoordinator; hardwareSelector; processManager; currentModel = null; HEALTH_CACHE_TTL = 30000; // 30 seconds streamingManager; integratedSystem = null; hybridRouter = null; lastMemoryWarningTime = 0; abortController; isShuttingDown = false; initialized = false; voiceSynthesisManager; providerSelectionStrategy; requestExecutionManager; healthStatusManager; configurationManager; requestProcessingCoreManager; modelManagementManager; resourceCleanupManager; streamProcessingManager; requestHandler; constructor(config, injectedDependencies) { super(); this.setMaxListeners(50); this.abortController = new AbortController(); this.configurationManager = injectedDependencies?.configurationManager || new ConfigurationManager(); this.config = { endpoint: 'http://localhost:11434', ...this.getDefaultConfig(), ...config, }; this.performanceMonitor = injectedDependencies?.performanceMonitor || new PerformanceMonitor(); this.securityValidator = injectedDependencies?.securityValidator || new SecurityValidator({ enableSandbox: this.config.security?.enableSandbox, maxInputLength: this.config.security?.maxInputLength, }); this.hardwareSelector = new HardwareAwareModelSelector(); this.processManager = new ActiveProcessManager(this.hardwareSelector); this.streamingManager = injectedDependencies?.streamingManager || new StreamingManager(config.streaming); // Use injected provider repository or create new ProviderManager if (injectedDependencies?.providerRepository) { // Create a wrapper ProviderManager that uses the injected repository this.providerManager = { getProviderRepository: () => injectedDependencies.providerRepository, getProviders: () => injectedDependencies.providerRepository.getAvailableProviders(), initialize: async () => { }, // Already initialized in DI system createProvider: async (config) => { throw new Error('Provider creation not supported with injected repository'); }, // Not needed when using injected repository getProvider: (type) => injectedDependencies.providerRepository.getProvider(type), }; // TypeScript workaround for partial interface implementation } else { this.providerManager = new ProviderManager(); } this.cacheCoordinator = injectedDependencies?.cacheCoordinator || new CacheCoordinator(); this.voiceSynthesisManager = injectedDependencies?.voiceSynthesisManager || new VoiceSynthesisManager(undefined, async (request) => this.processRequest(request)); this.providerSelectionStrategy = injectedDependencies?.providerSelectionStrategy || new ProviderSelectionStrategy({ fallbackChain: this.config.fallbackChain, selectionStrategy: 'balanced', timeoutMs: this.config.performanceThresholds?.timeoutMs || 30000, maxRetries: 3, }, this.performanceMonitor); this.requestExecutionManager = injectedDependencies?.requestExecutionManager || new RequestExecutionManager({ maxConcurrentRequests: this.config.performanceThresholds?.maxConcurrentRequests || 3, defaultTimeout: this.config.performanceThresholds?.timeoutMs || 30000, complexityTimeouts: { simple: 1800000, // 30 minutes - industry standard medium: 3600000, // 1 hour - industry standard complex: 7200000, // 2 hours - industry standard }, memoryThresholds: { low: 100, medium: 500, high: 1000, }, }, this.processManager, this.providerManager.getProviderRepository()); this.healthStatusManager = injectedDependencies?.healthStatusManager || new HealthStatusManager(this.providerManager.getProviderRepository(), this.cacheCoordinator, this.performanceMonitor, { healthCheckTimeoutMs: 5000, overallTimeoutMs: 15000, cacheTtlMs: 30000, }); this.requestProcessingCoreManager = injectedDependencies?.requestProcessingCoreManager || new RequestProcessingCoreManager({ maxConcurrentRequests: this.config.performanceThresholds?.maxConcurrentRequests || 3, defaultTimeoutMs: this.config.performanceThresholds?.timeoutMs || 180000, memoryThresholds: { base: 50, lengthMultiplier: 0.01, complexityMultiplier: 30, }, }); this.modelManagementManager = injectedDependencies?.modelManagementManager || new ModelManagementManager({ endpoint: this.config.endpoint || 'http://localhost:11434', defaultModel: 'llama2', requestTimeoutMs: this.config.performanceThresholds?.timeoutMs || 30000, }, this.makeRequest.bind(this), this.generate.bind(this)); this.resourceCleanupManager = injectedDependencies?.resourceCleanupManager || new ResourceCleanupManager({ shutdownTimeoutMs: 10000, gracefulShutdown: true, }); this.streamProcessingManager = injectedDependencies?.streamProcessingManager || new StreamProcessingManager(this.securityValidator, this.cacheCoordinator, this.streamingManager, async (request, context) => this.processRequest(request, context), () => this.generateRequestId(), { validateSecurity: true, enableCaching: true, requestTimeoutMs: 30000, }); this.requestHandler = new RequestHandler(this); this.registerCleanupResources(); if (injectedDependencies?.hybridRouter) { this.hybridRouter = injectedDependencies.hybridRouter; logger.info('🚀 Using injected Hybrid LLM Router'); } else { this.initializeHybridRouter(); } this.setupModelSwitchingEvents(); } // IModelClient interface implementation async processRequest(request, context) { return this.requestHandler.processRequest(request, context); } async streamRequest(request, onToken, context) { return this.streamProcessingManager.processStreamRequest(request, onToken, context); } async generateText(prompt, options) { const request = { prompt, ...options, }; const response = await this.processRequest(request); return response.content; } async synthesize(request) { // Use voice synthesis to generate a response using multiple perspectives const voices = ['explorer', 'developer', 'architect']; // Default voices const perspectiveResult = await this.voiceSynthesisManager.synthesizeVoicePerspectives(voices, request.prompt, { /* temperature: request.temperature */ }); return { content: perspectiveResult.content, model: request.model || 'multi-voice', provider: 'voice-synthesis', metadata: { tokens: perspectiveResult.content.length / 4, // Rough token estimate latency: 0, // voices: perspectiveResult.voices // Remove invalid property }, }; } async healthCheck() { const cachedStatus = this.healthStatusManager.getCachedHealthStatus(); return cachedStatus || {}; } getProviders() { return this.providerManager.getProviders(); } async initialize() { if (this.initialized) return; // Initialize providers with config await this.providerManager.initializeProviders(this.config.providers || []); await this.integratedSystem?.initialize(); this.initialized = true; } async shutdown() { this.isShuttingDown = true; await this.resourceCleanupManager.shutdown(); } async destroy() { await this.shutdown(); } // Additional methods needed by existing code async checkHealth() { return this.healthCheck(); } async getAllAvailableModels() { return this.modelManagementManager.getAllAvailableModels(); } async generate(request) { return this.processRequest(request); } getCurrentModel() { return this.currentModel; } getProcessManager() { return this.processManager; } getRequestExecutionManager() { return this.requestExecutionManager; } getRequestProcessingCoreManager() { return this.requestProcessingCoreManager; } getActiveRequests() { return this.activeRequests; } getProviderManager() { return this.providerManager; } async initializeProvidersAsync() { await this.providerManager.initialize(); } generateRequestId() { return createHash('sha256') .update(`${Date.now()}-${Math.random()}`) .digest('hex') .substring(0, 16); } // Missing methods that RequestHandler and other components expect getCacheCoordinator() { return this.cacheCoordinator; } getHybridRouter() { return this.hybridRouter; } getStreamingManager() { return this.streamingManager; } getSecurityValidator() { return this.securityValidator; } inferTaskType(prompt) { // Simple task type inference based on keywords const lowerPrompt = prompt.toLowerCase(); if (lowerPrompt.includes('analyze') || lowerPrompt.includes('review')) return 'analysis'; if (lowerPrompt.includes('create') || lowerPrompt.includes('generate')) return 'generation'; if (lowerPrompt.includes('refactor') || lowerPrompt.includes('improve')) return 'refactoring'; if (lowerPrompt.includes('debug') || lowerPrompt.includes('fix')) return 'debug'; if (lowerPrompt.includes('document') || lowerPrompt.includes('explain')) return 'documentation'; if (lowerPrompt.includes('test') || lowerPrompt.includes('unit')) return 'testing'; return 'general'; } analyzeComplexity(request) { const complexity = { score: Math.min(request.prompt.length / 100, 10), level: request.prompt.length < 200 ? 'simple' : request.prompt.length < 1000 ? 'medium' : 'complex', factors: { promptLength: request.prompt.length, hasContext: !!request.context, hasFiles: !!request.files?.length, fileCount: request.files?.length || 0, }, }; return complexity; } convertToTaskMetrics(request) { return { complexity: this.analyzeComplexity(request), taskType: this.inferTaskType(request.prompt), estimatedTokens: Math.ceil(request.prompt.length / 4), }; } modelSupportsTools(model) { // Most modern models support function calling return true; } assessQuality(response) { // Simple quality assessment based on response characteristics if (!response || response.length < 10) return 0.1; if (response.length > 100 && response.includes('\n')) return 0.8; return 0.6; } determineExecutionStrategy(request) { const complexity = this.analyzeComplexity(request); // Create proper ExecutionStrategy object const strategy = { mode: 'balanced', provider: 'ollama', timeout: 30000, complexity: complexity.level, }; // Industry-standard timeouts for CLI AI agents (30min - 2 hours) if (complexity.level === 'simple') { strategy.mode = 'fast'; strategy.timeout = 1800000; // 30 minutes - industry standard for simple tasks } else if (complexity.level === 'complex') { strategy.mode = 'thorough'; strategy.timeout = 7200000; // 2 hours - industry standard for complex analysis } else { strategy.timeout = 3600000; // 1 hour - industry standard for balanced tasks } // Adjust based on request characteristics (but keep long-running) if (request.stream) { strategy.mode = 'fast'; strategy.timeout = Math.max(strategy.timeout, 1800000); // Minimum 30 minutes } if (request.tools && request.tools.length > 0) { strategy.provider = 'lm-studio'; strategy.timeout = Math.max(strategy.timeout, 3600000); // Minimum 1 hour for tool usage } return strategy; } estimateMemoryUsage(request) { return Math.max(50, request.prompt.length * 0.01); } getProcessType(request) { return this.inferTaskType(request.prompt); } getRequestPriority(request) { const complexity = this.analyzeComplexity(request); if (complexity.level === 'simple') return 'low'; if (complexity.level === 'medium') return 'medium'; return 'high'; } async getProjectStructure(projectRoot) { // Simple project structure analysis try { const { readdir, stat } = await import('fs/promises'); const { join } = await import('path'); const items = await readdir(projectRoot); const structure = []; for (const item of items.slice(0, 20)) { // Limit to first 20 items try { const itemPath = join(projectRoot, item); const stats = await stat(itemPath); const type = stats.isDirectory() ? 'directory' : 'file'; structure.push(`${type}: ${item}`); } catch (error) { // Skip items we can't read } } return `Project structure for ${projectRoot}:\n${structure.join('\n')}`; } catch (error) { return `Unable to read project structure: ${error instanceof Error ? error.message : 'Unknown error'}`; } } async makeRequest(request) { return this.requestHandler.makeRequest(request); } getConfig() { return this.config; } getDefaultConfig() { return { providers: [ { type: 'auto', endpoint: 'http://localhost:11434', }, ], executionMode: 'auto', fallbackChain: ['ollama', 'lm-studio', 'auto'], performanceThresholds: { fastModeMaxTokens: 1000, timeoutMs: 30000, maxConcurrentRequests: 3, }, security: { enableSandbox: true, maxInputLength: 10000, allowedCommands: ['npm', 'node', 'git'], }, }; } // Property getters for backward compatibility get providerRepository() { return this.providerManager; } registerCleanupResources() { // Setup cleanup handlers process.on('SIGINT', async () => this.shutdown()); process.on('SIGTERM', async () => this.shutdown()); } initializeHybridRouter() { // Initialize hybrid router if available try { // This would be set up with actual hybrid router if available this.hybridRouter = null; } catch (error) { logger.warn('Hybrid router not available, continuing without it'); } } setupModelSwitchingEvents() { this.on('modelSwitch', (newModel) => { this.currentModel = newModel; logger.info(`Switched to model: ${newModel}`); }); } } //# sourceMappingURL=unified-model-client.js.map