codecrucible-synth
Version:
Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability
557 lines (500 loc) • 19.3 kB
text/typescript
import { EventEmitter } from 'events';
import { logger } from '../core/logger.js';
import {
ProjectContext,
ModelRequest,
ModelResponse,
UnifiedClientConfig as BaseUnifiedClientConfig,
MetricsData,
ComplexityAnalysis,
TaskType,
} from '../core/types.js';
import { IModelClient, StreamToken } from '../core/interfaces/client-interfaces.js';
import { SecurityValidator, ISecurityValidator } from '../core/security/security-validator.js';
import { PerformanceMonitor } from '../utils/performance.js';
import { IntegratedCodeCrucibleSystem } from './integrated-system.js';
import { HardwareAwareModelSelector } from '../core/performance/hardware-aware-model-selector.js';
import { getGlobalToolIntegration } from '../core/tools/tool-integration.js';
import { getGlobalEnhancedToolIntegration } from '../core/tools/enhanced-tool-integration.js';
import { ActiveProcessManager, ActiveProcess } from '../core/performance/active-process-manager.js';
import { ProviderManager } from './provider-manager.js';
import { getErrorMessage, toError } from '../utils/error-utils.js';
import { createHash } from 'crypto';
import { StreamingManager, IStreamingManager } from '../core/streaming/streaming-manager.js';
import { CacheCoordinator, ICacheCoordinator } from '../core/caching/cache-coordinator.js';
import {
VoiceSynthesisManager,
IVoiceSynthesisManager,
} from '../core/voice-system/voice-synthesis-manager.js';
import {
ProviderSelectionStrategy,
IProviderSelectionStrategy,
ExecutionMode as StrategyExecutionMode,
} from '../core/providers/provider-selection-strategy.js';
import {
RequestExecutionManager,
IRequestExecutionManager,
} from '../core/execution/request-execution-manager.js';
import { HealthStatusManager, IHealthStatusManager } from '../core/health/health-status-manager.js';
import {
ConfigurationManager,
IConfigurationManager,
} from '../core/config/configuration-manager.js';
import {
RequestProcessingCoreManager,
IRequestProcessingCoreManager,
} from '../core/processing/request-processing-core-manager.js';
import {
ModelManagementManager,
IModelManagementManager,
} from '../core/models/model-management-manager.js';
import {
ResourceCleanupManager,
IResourceCleanupManager,
} from '../core/cleanup/resource-cleanup-manager.js';
import {
StreamProcessingManager,
IStreamProcessingManager,
} from '../core/streaming/stream-processing-manager.js';
import { RequestHandler } from './request-handler.js';
export class UnifiedModelClient extends EventEmitter implements IModelClient {
private config: any;
private providerManager: ProviderManager;
private performanceMonitor: PerformanceMonitor;
private securityValidator: ISecurityValidator;
private activeRequests: Map<string, any> = new Map();
private requestQueue: Array<any> = [];
private isProcessingQueue = false;
private cacheCoordinator: ICacheCoordinator;
private hardwareSelector: HardwareAwareModelSelector;
private processManager: ActiveProcessManager;
private currentModel: string | null = null;
private readonly HEALTH_CACHE_TTL = 30000; // 30 seconds
private streamingManager: IStreamingManager;
private integratedSystem: IntegratedCodeCrucibleSystem | null = null;
private hybridRouter: any | null = null;
private lastMemoryWarningTime = 0;
private abortController: AbortController;
private isShuttingDown = false;
private initialized = false;
private voiceSynthesisManager: IVoiceSynthesisManager;
private providerSelectionStrategy: IProviderSelectionStrategy;
private requestExecutionManager: IRequestExecutionManager;
private healthStatusManager: IHealthStatusManager;
private configurationManager: IConfigurationManager;
private requestProcessingCoreManager: IRequestProcessingCoreManager;
private modelManagementManager: IModelManagementManager;
private resourceCleanupManager: IResourceCleanupManager;
private streamProcessingManager: IStreamProcessingManager;
private requestHandler: RequestHandler;
constructor(config: any, injectedDependencies?: any) {
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: any) => {
throw new Error('Provider creation not supported with injected repository');
}, // Not needed when using injected repository
getProvider: (type: string) => injectedDependencies.providerRepository.getProvider(type),
} as any; // 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: any) => 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: ModelRequest, context?: ProjectContext): Promise<ModelResponse> {
return this.requestHandler.processRequest(request, context);
}
async streamRequest(
request: ModelRequest,
onToken: (token: StreamToken) => void,
context?: ProjectContext
): Promise<ModelResponse> {
return this.streamProcessingManager.processStreamRequest(request, onToken, context);
}
async generateText(prompt: string, options?: any): Promise<string> {
const request: ModelRequest = {
prompt,
...options,
};
const response = await this.processRequest(request);
return response.content;
}
async synthesize(request: ModelRequest): Promise<ModelResponse> {
// 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(): Promise<Record<string, boolean>> {
const cachedStatus = this.healthStatusManager.getCachedHealthStatus();
return cachedStatus || {};
}
getProviders(): Map<string, any> {
return this.providerManager.getProviders();
}
async initialize(): Promise<void> {
if (this.initialized) return;
// Initialize providers with config
await this.providerManager.initializeProviders(this.config.providers || []);
await this.integratedSystem?.initialize();
this.initialized = true;
}
async shutdown(): Promise<void> {
this.isShuttingDown = true;
await this.resourceCleanupManager.shutdown();
}
async destroy(): Promise<void> {
await this.shutdown();
}
// Additional methods needed by existing code
async checkHealth(): Promise<Record<string, boolean>> {
return this.healthCheck();
}
async getAllAvailableModels(): Promise<any[]> {
return this.modelManagementManager.getAllAvailableModels();
}
async generate(request: ModelRequest): Promise<ModelResponse> {
return this.processRequest(request);
}
getCurrentModel(): string | null {
return this.currentModel;
}
getProcessManager(): ActiveProcessManager {
return this.processManager;
}
getRequestExecutionManager(): IRequestExecutionManager {
return this.requestExecutionManager;
}
getRequestProcessingCoreManager(): IRequestProcessingCoreManager {
return this.requestProcessingCoreManager;
}
getActiveRequests(): Map<string, any> {
return this.activeRequests;
}
getProviderManager(): ProviderManager {
return this.providerManager;
}
async initializeProvidersAsync(): Promise<void> {
await this.providerManager.initialize();
}
generateRequestId(): string {
return createHash('sha256')
.update(`${Date.now()}-${Math.random()}`)
.digest('hex')
.substring(0, 16);
}
// Missing methods that RequestHandler and other components expect
getCacheCoordinator(): ICacheCoordinator {
return this.cacheCoordinator;
}
getHybridRouter(): any {
return this.hybridRouter;
}
getStreamingManager(): IStreamingManager {
return this.streamingManager;
}
getSecurityValidator(): ISecurityValidator {
return this.securityValidator;
}
inferTaskType(prompt: string): string {
// 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: ModelRequest): any {
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: ModelRequest): any {
return {
complexity: this.analyzeComplexity(request),
taskType: this.inferTaskType(request.prompt),
estimatedTokens: Math.ceil(request.prompt.length / 4),
};
}
modelSupportsTools(model?: string): boolean {
// Most modern models support function calling
return true;
}
assessQuality(response: string): number {
// 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: ModelRequest): any {
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: ModelRequest): number {
return Math.max(50, request.prompt.length * 0.01);
}
getProcessType(request: ModelRequest): string {
return this.inferTaskType(request.prompt);
}
getRequestPriority(request: ModelRequest): 'low' | 'medium' | 'high' {
const complexity = this.analyzeComplexity(request);
if (complexity.level === 'simple') return 'low';
if (complexity.level === 'medium') return 'medium';
return 'high';
}
async getProjectStructure(projectRoot: string): Promise<string> {
// 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: any): Promise<any> {
return this.requestHandler.makeRequest(request);
}
getConfig(): any {
return this.config;
}
getDefaultConfig(): any {
return {
providers: [
{
type: 'auto',
endpoint: 'http://localhost:11434',
},
],
executionMode: 'auto' as const,
fallbackChain: ['ollama', 'lm-studio', 'auto'] as const,
performanceThresholds: {
fastModeMaxTokens: 1000,
timeoutMs: 30000,
maxConcurrentRequests: 3,
},
security: {
enableSandbox: true,
maxInputLength: 10000,
allowedCommands: ['npm', 'node', 'git'],
},
};
}
// Property getters for backward compatibility
get providerRepository(): any {
return this.providerManager;
}
private registerCleanupResources(): void {
// Setup cleanup handlers
process.on('SIGINT', async () => this.shutdown());
process.on('SIGTERM', async () => this.shutdown());
}
private initializeHybridRouter(): void {
// 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');
}
}
private setupModelSwitchingEvents(): void {
this.on('modelSwitch', (newModel: string) => {
this.currentModel = newModel;
logger.info(`Switched to model: ${newModel}`);
});
}
}