UNPKG

codecrucible-synth

Version:

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

477 lines (410 loc) 13.4 kB
/** * Intelligent Model Detector - Iteration 7: Enhanced Model Management & Auto-Configuration * Automatically detects available models and configures optimal dual-agent setup */ import { Logger } from '../logger.js'; export interface ModelInfo { name: string; platform: 'ollama' | 'lmstudio'; size: string; sizeBytes?: number; capabilities: ModelCapability[]; performance: PerformanceProfile; availability: 'available' | 'downloading' | 'unavailable'; lastUsed?: Date; } export interface ModelCapability { type: 'code_generation' | 'code_review' | 'analysis' | 'conversation' | 'reasoning'; score: number; // 0-1 specialization?: string[]; } export interface PerformanceProfile { speed: 'fast' | 'medium' | 'slow'; quality: 'basic' | 'good' | 'excellent'; memoryUsage: 'low' | 'medium' | 'high'; contextWindow: number; estimatedTokensPerSecond?: number; } export interface OptimalConfiguration { writer: { model: string; platform: 'ollama' | 'lmstudio'; reasoning: string; }; auditor: { model: string; platform: 'ollama' | 'lmstudio'; reasoning: string; }; fallback?: { model: string; platform: 'ollama' | 'lmstudio'; reasoning: string; }; confidence: number; // 0-1 } export class IntelligentModelDetector { private logger: Logger; private detectedModels: ModelInfo[] = []; private lastScan: Date | null = null; private platformHealth = { ollama: false, lmstudio: false, }; constructor() { this.logger = new Logger('ModelDetector'); } /** * Scan all platforms and detect available models */ async scanAvailableModels(forceRefresh = false): Promise<ModelInfo[]> { if (!forceRefresh && this.lastScan && Date.now() - this.lastScan.getTime() < 30000) { return this.detectedModels; } this.logger.info('Scanning for available models...'); this.detectedModels = []; // Scan Ollama const ollamaModels = await this.scanOllamaModels(); this.detectedModels.push(...ollamaModels); // Scan LM Studio const lmStudioModels = await this.scanLMStudioModels(); this.detectedModels.push(...lmStudioModels); this.lastScan = new Date(); this.logger.info(`Found ${this.detectedModels.length} total models`); return this.detectedModels; } /** * Scan Ollama for available models */ private async scanOllamaModels(): Promise<ModelInfo[]> { const models: ModelInfo[] = []; try { const response = await fetch('http://localhost:11434/api/tags'); if (!response.ok) { this.logger.warn('Ollama not available'); this.platformHealth.ollama = false; return []; } this.platformHealth.ollama = true; const data = await response.json(); for (const model of data.models || []) { const modelInfo = this.analyzeOllamaModel(model); models.push(modelInfo); } this.logger.info(`Found ${models.length} Ollama models`); } catch (error) { this.logger.warn('Failed to scan Ollama models:', error); this.platformHealth.ollama = false; } return models; } /** * Scan LM Studio for available models */ private async scanLMStudioModels(): Promise<ModelInfo[]> { const models: ModelInfo[] = []; try { const response = await fetch('http://localhost:1234/v1/models'); if (!response.ok) { this.logger.warn('LM Studio not available'); this.platformHealth.lmstudio = false; return []; } this.platformHealth.lmstudio = true; const data = await response.json(); for (const model of data.data || []) { const modelInfo = this.analyzeLMStudioModel(model); models.push(modelInfo); } this.logger.info(`Found ${models.length} LM Studio models`); } catch (error) { this.logger.warn('Failed to scan LM Studio models:', error); this.platformHealth.lmstudio = false; } return models; } /** * Analyze Ollama model and extract capabilities */ private analyzeOllamaModel(model: any): ModelInfo { const name = model.name || 'unknown'; const size = this.formatSize(model.size); return { name, platform: 'ollama', size, sizeBytes: model.size, capabilities: this.inferCapabilities(name, model.size), performance: this.inferPerformance(name, model.size), availability: 'available', lastUsed: model.modified_at ? new Date(model.modified_at) : undefined, }; } /** * Analyze LM Studio model and extract capabilities */ private analyzeLMStudioModel(model: any): ModelInfo { const name = model.id || 'unknown'; return { name, platform: 'lmstudio', size: 'unknown', capabilities: this.inferCapabilities(name), performance: this.inferPerformance(name), availability: 'available', }; } /** * Infer model capabilities from name and size */ private inferCapabilities(name: string, sizeBytes?: number): ModelCapability[] { const capabilities: ModelCapability[] = []; const nameLower = name.toLowerCase(); // Code generation capability if ( nameLower.includes('code') || nameLower.includes('deepseek') || nameLower.includes('codellama') || nameLower.includes('starcoder') ) { capabilities.push({ type: 'code_generation', score: 0.9, specialization: ['programming', 'debugging', 'refactoring'], }); } else { capabilities.push({ type: 'code_generation', score: 0.6, }); } // Analysis/Review capability (better for larger models) const reviewScore = sizeBytes && sizeBytes > 10 * 1024 * 1024 * 1024 ? 0.9 : 0.7; // >10GB capabilities.push({ type: 'code_review', score: reviewScore, specialization: ['security', 'quality', 'best_practices'], }); // Reasoning capability if ( nameLower.includes('qwq') || nameLower.includes('reasoning') || nameLower.includes('o1') || nameLower.includes('gpt') ) { capabilities.push({ type: 'reasoning', score: 0.95, specialization: ['logic', 'problem_solving', 'analysis'], }); } return capabilities; } /** * Infer performance profile from model characteristics */ private inferPerformance(name: string, sizeBytes?: number): PerformanceProfile { const nameLower = name.toLowerCase(); // Size-based performance estimation let speed: PerformanceProfile['speed'] = 'medium'; let quality: PerformanceProfile['quality'] = 'good'; let memoryUsage: PerformanceProfile['memoryUsage'] = 'medium'; let contextWindow = 4096; if (sizeBytes) { const sizeGB = sizeBytes / (1024 * 1024 * 1024); if (sizeGB < 3) { speed = 'fast'; quality = 'basic'; memoryUsage = 'low'; contextWindow = 2048; } else if (sizeGB < 8) { speed = 'fast'; quality = 'good'; memoryUsage = 'medium'; contextWindow = 4096; } else if (sizeGB < 15) { speed = 'medium'; quality = 'good'; memoryUsage = 'medium'; contextWindow = 8192; } else { speed = 'slow'; quality = 'excellent'; memoryUsage = 'high'; contextWindow = 16384; } } // Model-specific adjustments if (nameLower.includes('gemma:2b') || nameLower.includes('3.2:latest')) { speed = 'fast'; memoryUsage = 'low'; } if (nameLower.includes('qwq:32b') || nameLower.includes('27b')) { speed = 'slow'; quality = 'excellent'; memoryUsage = 'high'; contextWindow = 32768; } return { speed, quality, memoryUsage, contextWindow, estimatedTokensPerSecond: speed === 'fast' ? 50 : speed === 'medium' ? 20 : 10, }; } /** * Find optimal dual-agent configuration */ async findOptimalConfiguration(): Promise<OptimalConfiguration> { await this.scanAvailableModels(); const codeGenerationModels = this.detectedModels .filter(m => m.availability === 'available') .filter(m => this.getCapabilityScore(m, 'code_generation') > 0.5) .sort((a, b) => { // Prefer fast models for writing if (a.performance.speed !== b.performance.speed) { const speedOrder = { fast: 3, medium: 2, slow: 1 }; return speedOrder[b.performance.speed] - speedOrder[a.performance.speed]; } return ( this.getCapabilityScore(b, 'code_generation') - this.getCapabilityScore(a, 'code_generation') ); }); const reviewModels = this.detectedModels .filter(m => m.availability === 'available') .filter(m => this.getCapabilityScore(m, 'code_review') > 0.5) .sort((a, b) => { // Prefer quality models for auditing const qualityOrder = { excellent: 3, good: 2, basic: 1 }; if (a.performance.quality !== b.performance.quality) { return qualityOrder[b.performance.quality] - qualityOrder[a.performance.quality]; } return ( this.getCapabilityScore(b, 'code_review') - this.getCapabilityScore(a, 'code_review') ); }); // Select best writer (fast generation) const writer = codeGenerationModels[0]; // Select best auditor (high quality, different from writer if possible) const auditor = reviewModels.find(m => m.name !== writer?.name) || reviewModels[0]; // Fallback model const fallback = this.detectedModels .filter( m => m.availability === 'available' && m.name !== writer?.name && m.name !== auditor?.name ) .sort((a, b) => this.getOverallScore(b) - this.getOverallScore(a))[0]; if (!writer || !auditor) { throw new Error('No suitable models found for dual-agent configuration'); } const confidence = this.calculateConfigurationConfidence(writer, auditor, fallback); return { writer: { model: writer.name, platform: writer.platform, reasoning: `Fast ${writer.performance.speed} model optimized for code generation (${writer.size})`, }, auditor: { model: auditor.name, platform: auditor.platform, reasoning: `${auditor.performance.quality} quality model for thorough code review (${auditor.size})`, }, fallback: fallback ? { model: fallback.name, platform: fallback.platform, reasoning: `Backup model for redundancy`, } : undefined, confidence, }; } /** * Get capability score for a model */ private getCapabilityScore(model: ModelInfo, capability: ModelCapability['type']): number { const cap = model.capabilities.find(c => c.type === capability); return cap?.score || 0; } /** * Calculate overall model score */ private getOverallScore(model: ModelInfo): number { const avgCapability = model.capabilities.reduce((sum, cap) => sum + cap.score, 0) / model.capabilities.length; const qualityBonus = model.performance.quality === 'excellent' ? 0.2 : model.performance.quality === 'good' ? 0.1 : 0; return avgCapability + qualityBonus; } /** * Calculate confidence in configuration */ private calculateConfigurationConfidence( writer: ModelInfo, auditor: ModelInfo, fallback?: ModelInfo ): number { let confidence = 0.5; // Base confidence // Platform diversity bonus if (writer.platform !== auditor.platform) { confidence += 0.2; } // Quality bonus if (writer.performance.speed === 'fast') confidence += 0.1; if (auditor.performance.quality === 'excellent') confidence += 0.2; // Capability scores confidence += this.getCapabilityScore(writer, 'code_generation') * 0.1; confidence += this.getCapabilityScore(auditor, 'code_review') * 0.1; // Fallback bonus if (fallback) confidence += 0.1; return Math.min(confidence, 1.0); } /** * Format size in human readable format */ private formatSize(bytes?: number): string { if (!bytes) return 'unknown'; const gb = bytes / (1024 * 1024 * 1024); if (gb < 1) { const mb = bytes / (1024 * 1024); return `${mb.toFixed(0)}MB`; } return `${gb.toFixed(1)}GB`; } /** * Get platform health status */ getPlatformHealth(): { ollama: boolean; lmstudio: boolean } { return { ...this.platformHealth }; } /** * Get detected models summary */ getModelsSummary(): any { return { total: this.detectedModels.length, byPlatform: { ollama: this.detectedModels.filter(m => m.platform === 'ollama').length, lmstudio: this.detectedModels.filter(m => m.platform === 'lmstudio').length, }, bySpeed: { fast: this.detectedModels.filter(m => m.performance.speed === 'fast').length, medium: this.detectedModels.filter(m => m.performance.speed === 'medium').length, slow: this.detectedModels.filter(m => m.performance.speed === 'slow').length, }, lastScan: this.lastScan?.toISOString(), }; } /** * Force refresh model list */ async refresh(): Promise<ModelInfo[]> { return await this.scanAvailableModels(true); } } export default IntelligentModelDetector;