UNPKG

codecrucible-synth

Version:

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

466 lines (401 loc) 13.1 kB
/** * Request Processing Core Manager - Centralizes request preparation, queuing, and processing logic * Extracted from UnifiedModelClient to provide focused request processing management */ import { EventEmitter } from 'events'; import { logger } from '../logger.js'; import { ModelRequest, ProjectContext } from '../types.js'; import { ActiveProcess, ActiveProcessManager } from '../performance/active-process-manager.js'; import { ProviderType } from '../providers/provider-repository.js'; import { toError } from '../../utils/error-utils.js'; export interface RequestMetrics { provider: string; model: string; startTime: number; endTime?: number; tokenCount?: number; success: boolean; error?: string; } export interface QueuedRequest { id: string; request: ModelRequest; resolve: (value: any) => void; reject: (reason?: Error) => void; } export interface ExecutionStrategy { mode: 'fast' | 'quality'; // Remove 'auto' to match ExecutionMode provider: ProviderType; timeout: number; complexity: string; // Change to string to match client expectation } export interface RequestProcessingConfig { maxConcurrentRequests: number; defaultTimeoutMs: number; memoryThresholds: { base: number; lengthMultiplier: number; complexityMultiplier: number; }; } export interface IRequestProcessingCoreManager { /** * Estimate memory usage for a request */ estimateMemoryUsage(request: ModelRequest): number; /** * Determine process type based on request content */ getProcessType(request: ModelRequest): ActiveProcess['type']; /** * Determine request priority */ getRequestPriority(request: ModelRequest): ActiveProcess['priority']; /** * Assess complexity quickly for timeout determination */ assessComplexityFast(prompt: string): 'simple' | 'medium' | 'complex'; /** * Determine execution strategy for a request */ determineExecutionStrategy( request: ModelRequest, context?: ProjectContext, executionMode?: 'fast' | 'auto' | 'quality' ): ExecutionStrategy; /** * Create timeout promise for request handling */ createTimeoutPromise(timeoutMs: number): Promise<never>; /** * Queue request for processing when at capacity */ queueRequest( request: ModelRequest, activeRequestsCount: number, processRequest: (req: ModelRequest, ctx?: ProjectContext) => Promise<any>, context?: ProjectContext ): Promise<any>; /** * Process request queue */ processQueue( activeRequestsCount: number, maxConcurrent: number, processRequest: (req: ModelRequest) => Promise<any> ): Promise<void>; /** * Get current queue statistics */ getQueueStats(): { queueLength: number; isProcessing: boolean; totalProcessed: number; }; /** * Clear request queue (for shutdown) */ clearQueue(): void; /** * Cleanup resources */ cleanup(): Promise<void>; } export class RequestProcessingCoreManager extends EventEmitter implements IRequestProcessingCoreManager { private readonly config: RequestProcessingConfig; private requestQueue: QueuedRequest[] = []; private isProcessingQueue = false; private totalProcessed = 0; constructor(config?: Partial<RequestProcessingConfig>) { super(); this.config = { maxConcurrentRequests: 3, defaultTimeoutMs: 180000, memoryThresholds: { base: 50, // Base memory in MB lengthMultiplier: 0.01, // MB per character complexityMultiplier: 30, // Additional MB for complex operations }, ...config, }; logger.debug('RequestProcessingCoreManager initialized', { config: this.config }); } /** * Estimate memory usage for a request */ estimateMemoryUsage(request: ModelRequest): number { const baseMemory = this.config.memoryThresholds.base; const promptLength = request.prompt?.length || 0; // Estimate based on prompt length and complexity let estimatedMemory = baseMemory; // Length-based estimation estimatedMemory += promptLength * this.config.memoryThresholds.lengthMultiplier; // Add memory for complex operations if (request.prompt) { const prompt = request.prompt.toLowerCase(); if (prompt.includes('analyze') || prompt.includes('review')) { estimatedMemory += this.config.memoryThresholds.complexityMultiplier; } if (prompt.includes('generate') || prompt.includes('create')) { estimatedMemory += this.config.memoryThresholds.complexityMultiplier * 1.3; } if (prompt.includes('refactor') || prompt.includes('architecture')) { estimatedMemory += this.config.memoryThresholds.complexityMultiplier * 2; } } const finalEstimate = Math.round(estimatedMemory); logger.debug('Memory estimation', { promptLength, baseMemory, estimatedMemory: finalEstimate, }); return finalEstimate; } /** * Determine process type based on request content */ getProcessType(request: ModelRequest): ActiveProcess['type'] { if (!request.prompt) { return 'model_inference'; } const prompt = request.prompt.toLowerCase(); if (prompt.includes('analyze') || prompt.includes('review')) { return 'analysis'; } if (prompt.includes('generate') || prompt.includes('create') || prompt.includes('write')) { return 'generation'; } if (prompt.includes('stream')) { return 'streaming'; } return 'model_inference'; } /** * Determine request priority */ getRequestPriority(request: ModelRequest): ActiveProcess['priority'] { // Simple priority determination - could be enhanced with explicit priority in request if (request.prompt?.toLowerCase().includes('urgent') || request.stream) { return 'high'; } if (request.prompt?.toLowerCase().includes('background')) { return 'low'; } return 'medium'; } /** * OPTIMIZED: Fast complexity assessment for timeout determination */ assessComplexityFast(prompt: string): 'simple' | 'medium' | 'complex' { const length = prompt.length; // Fast bit-flag classification let flags = 0; if (length > 200) flags |= 1; if (prompt.includes('analyze')) flags |= 2; if (prompt.includes('review')) flags |= 2; if (prompt.includes('debug')) flags |= 2; if (prompt.includes('function')) flags |= 4; if (prompt.includes('class')) flags |= 4; if (prompt.includes('interface')) flags |= 4; // Fast O(1) classification if (flags >= 4 || flags & 2) return 'complex'; if (length < 50 && flags === 0) return 'simple'; return 'medium'; } /** * Determine execution strategy for a request */ determineExecutionStrategy( request: ModelRequest, context?: ProjectContext, executionMode: 'fast' | 'auto' | 'quality' = 'auto' ): ExecutionStrategy { // OPTIMIZED: Fast complexity assessment const complexity = this.assessComplexityFast(request.prompt || ''); // Auto-determine execution mode if not specified let mode = executionMode; if (mode === 'auto') { const hasContext = context && Object.keys(context).length > 0; if (complexity === 'simple' && !hasContext) { mode = 'fast'; } else if (complexity === 'complex' || (hasContext && context.files?.length > 10)) { mode = 'quality'; } else { mode = 'auto'; } } // Select provider based on mode and availability - simplified selection let provider: ProviderType = 'ollama'; // Default provider let timeout = this.config.defaultTimeoutMs; // OPTIMIZED: Adaptive timeouts based on complexity switch (mode) { case 'fast': provider = 'lm-studio'; // Prefer fast provider timeout = complexity === 'simple' ? 120000 : 180000; // 2min for simple, 3min for others break; case 'quality': provider = 'ollama'; // Prefer quality provider timeout = complexity === 'complex' ? 240000 : 180000; // 4min for complex, 3min for others break; case 'auto': default: mode = 'quality'; // Convert 'auto' to concrete mode provider = 'ollama'; // Balanced provider timeout = complexity === 'simple' ? 120000 : complexity === 'complex' ? 240000 : 180000; break; } const strategy: ExecutionStrategy = { mode, provider, timeout, complexity }; logger.debug('Execution strategy determined', strategy); this.emit('strategy-determined', { request: { prompt: request.prompt?.substring(0, 50) }, strategy, }); return strategy; } /** * Create timeout promise for request handling */ createTimeoutPromise(timeoutMs: number): Promise<never> { return new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Request timeout after ${timeoutMs}ms`)); }, timeoutMs); }); } /** * Queue request for processing when at capacity */ async queueRequest( request: ModelRequest, activeRequestsCount: number, processRequest: (req: ModelRequest, ctx?: ProjectContext) => Promise<any>, context?: ProjectContext ): Promise<any> { if (activeRequestsCount < this.config.maxConcurrentRequests) { return processRequest(request, context); } return new Promise((resolve, reject) => { const queuedRequest: QueuedRequest = { id: this.generateRequestId(), request, resolve, reject, }; this.requestQueue.push(queuedRequest); this.emit('request-queued', { requestId: queuedRequest.id, queueLength: this.requestQueue.length, }); logger.debug('Request queued', { requestId: queuedRequest.id, queueLength: this.requestQueue.length, activeRequests: activeRequestsCount, }); // Attempt to process queue this.processQueue( activeRequestsCount, this.config.maxConcurrentRequests, processRequest ).catch(error => { logger.error('Queue processing failed', error); }); }); } /** * Process request queue */ async processQueue( activeRequestsCount: number, maxConcurrent: number, processRequest: (req: ModelRequest) => Promise<any> ): Promise<void> { if (this.isProcessingQueue || this.requestQueue.length === 0) { return; } this.isProcessingQueue = true; logger.debug('Started processing request queue', { queueLength: this.requestQueue.length, activeRequests: activeRequestsCount, }); while (this.requestQueue.length > 0 && activeRequestsCount < maxConcurrent) { const queuedRequest = this.requestQueue.shift(); if (!queuedRequest) break; try { logger.debug('Processing queued request', { requestId: queuedRequest.id }); const response = await processRequest(queuedRequest.request); queuedRequest.resolve(response); this.totalProcessed++; this.emit('request-processed', { requestId: queuedRequest.id, success: true, totalProcessed: this.totalProcessed, }); } catch (error) { logger.error('Queued request failed', { requestId: queuedRequest.id, error: error instanceof Error ? error.message : error, }); queuedRequest.reject(toError(error)); this.emit('request-processed', { requestId: queuedRequest.id, success: false, error: error instanceof Error ? error.message : error, }); } } this.isProcessingQueue = false; logger.debug('Finished processing request queue', { remainingQueue: this.requestQueue.length, }); } /** * Get current queue statistics */ getQueueStats(): { queueLength: number; isProcessing: boolean; totalProcessed: number; } { return { queueLength: this.requestQueue.length, isProcessing: this.isProcessingQueue, totalProcessed: this.totalProcessed, }; } /** * Clear request queue (for shutdown) */ clearQueue(): void { const queuedCount = this.requestQueue.length; // Reject all queued requests this.requestQueue.forEach(queuedRequest => { queuedRequest.reject(new Error('System shutdown - request cancelled')); }); this.requestQueue = []; this.isProcessingQueue = false; if (queuedCount > 0) { logger.info(`Cleared ${queuedCount} queued requests during shutdown`); this.emit('queue-cleared', { clearedCount: queuedCount }); } } /** * Cleanup resources */ async cleanup(): Promise<void> { this.clearQueue(); this.removeAllListeners(); this.totalProcessed = 0; logger.debug('RequestProcessingCoreManager cleaned up'); } /** * Generate unique request ID */ private generateRequestId(): string { const timestamp = Date.now().toString(36); const randomStr = Math.random().toString(36).substr(2, 9); return `req_${timestamp}_${randomStr}`; } }