UNPKG

codecrucible-synth

Version:

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

125 lines 5.2 kB
/** * Stream Processing Manager - Centralizes streaming request processing * Extracted from UnifiedModelClient to provide focused streaming capabilities */ import { EventEmitter } from 'events'; import { logger } from '../logger.js'; import { createHash } from 'crypto'; import { getErrorMessage } from '../../utils/error-utils.js'; export class StreamProcessingManager extends EventEmitter { config; securityValidator; cacheCoordinator; streamingManager; processRequestDelegate; generateRequestId; constructor(securityValidator, cacheCoordinator, streamingManager, processRequestDelegate, generateRequestId, config) { super(); this.securityValidator = securityValidator; this.cacheCoordinator = cacheCoordinator; this.streamingManager = streamingManager; this.processRequestDelegate = processRequestDelegate; this.generateRequestId = generateRequestId; this.config = { validateSecurity: true, enableCaching: true, requestTimeoutMs: 30000, ...config, }; logger.debug('StreamProcessingManager initialized', { config: this.config }); } /** * Process a streaming request with security validation and caching */ async processStreamRequest(request, onToken, context) { const requestId = this.generateRequestId(); logger.info(`🌊 Streaming request ${requestId}`, { prompt: request.prompt.substring(0, 100) + '...', }); this.emit('stream-request-started', { requestId, prompt: request.prompt.substring(0, 100) }); try { // Security validation if enabled if (this.config.validateSecurity) { const validation = await this.securityValidator.validateRequest(request); if (!validation.isValid) { const error = new Error(`Security validation failed: ${validation.reason}`); this.emit('stream-request-failed', { requestId, error: error.message }); throw error; } // Use sanitized input if available if (validation.sanitizedInput) { request.prompt = validation.sanitizedInput; } } // Check semantic cache first if enabled if (this.config.enableCaching) { const promptKey = `ai:prompt:${createHash('sha256').update(request.prompt).digest('hex')}`; const cachedResponse = await this.cacheCoordinator.get(promptKey); if (cachedResponse?.hit) { logger.debug('Cache hit for streaming request', { source: cachedResponse.source, similarity: cachedResponse.similarity, }); // Simulate streaming from cached response await this.simulateStreamFromCache(cachedResponse.response, onToken); this.emit('stream-request-completed', { requestId, fromCache: true, source: cachedResponse.source, }); return cachedResponse.response; } } // No cache hit, process with streaming const streamConfig = { onToken, enableBackpressure: true, bufferSize: 1024, }; // Fallback to regular processing since streamProcess not available const response = await this.processRequestDelegate(request, context); this.emit('stream-request-completed', { requestId, fromCache: false, responseLength: response.content?.length, }); return response; } catch (error) { const errorMessage = getErrorMessage(error); logger.error('Stream request failed', { requestId, error: errorMessage }); this.emit('stream-request-failed', { requestId, error: errorMessage }); throw error; } } /** * Simulate streaming from cached response */ async simulateStreamFromCache(response, onToken) { if (!response.content) { return; } const content = response.content; const chunkSize = 10; // Characters per chunk for (let i = 0; i < content.length; i += chunkSize) { const chunk = content.slice(i, i + chunkSize); onToken({ content: chunk, timestamp: Date.now(), index: i, finished: i + chunkSize >= content.length, metadata: { source: 'cache-simulation', position: i }, }); // Small delay to simulate streaming await new Promise(resolve => setTimeout(resolve, 10)); } } /** * Cleanup resources */ async cleanup() { this.removeAllListeners(); logger.debug('StreamProcessingManager cleaned up'); } } //# sourceMappingURL=stream-processing-manager.js.map