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
JavaScript
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