UNPKG

codecrucible-synth

Version:

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

194 lines (166 loc) 6.02 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 { ModelRequest, ModelResponse, ProjectContext } from '../types.js'; import { ISecurityValidator } from '../security/security-validator.js'; import { ICacheCoordinator } from '../caching/cache-coordinator.js'; import { StreamToken, IStreamingManager } from './streaming-manager.js'; import { getErrorMessage } from '../../utils/error-utils.js'; export interface StreamProcessingConfig { validateSecurity: boolean; enableCaching: boolean; requestTimeoutMs: number; } export interface IStreamProcessingManager { /** * Process a streaming request with security validation and caching */ processStreamRequest( request: ModelRequest, onToken: (token: StreamToken) => void, context?: ProjectContext ): Promise<ModelResponse>; /** * Cleanup resources */ cleanup(): Promise<void>; } export class StreamProcessingManager extends EventEmitter implements IStreamProcessingManager { private readonly config: StreamProcessingConfig; private readonly securityValidator: ISecurityValidator; private readonly cacheCoordinator: ICacheCoordinator; private readonly streamingManager: IStreamingManager; private readonly processRequestDelegate: ( request: ModelRequest, context?: ProjectContext ) => Promise<ModelResponse>; private readonly generateRequestId: () => string; constructor( securityValidator: ISecurityValidator, cacheCoordinator: ICacheCoordinator, streamingManager: IStreamingManager, processRequestDelegate: ( request: ModelRequest, context?: ProjectContext ) => Promise<ModelResponse>, generateRequestId: () => string, config?: Partial<StreamProcessingConfig> ) { 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: ModelRequest, onToken: (token: StreamToken) => void, context?: ProjectContext ): Promise<ModelResponse> { 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 */ private async simulateStreamFromCache( response: ModelResponse, onToken: (token: StreamToken) => void ): Promise<void> { 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(): Promise<void> { this.removeAllListeners(); logger.debug('StreamProcessingManager cleaned up'); } }