UNPKG

@codai/glass-mcp

Version:

High-Performance Enterprise AI Project Management MCP Server with advanced optimization and multi-agent coordination

403 lines (396 loc) 18 kB
import { OpenAI } from 'openai'; import { AgentType } from '../types/index.js'; import NodeCache from 'node-cache'; export class HighPerformanceAIService { client; cache; config; retryConfig; requestQueue = new Map(); rateLimiter; constructor() { // Load configuration with enhanced defaults this.config = { endpoint: process.env.AZURE_OPENAI_ENDPOINT || '', apiKey: process.env.AZURE_OPENAI_KEY || '', deploymentName: process.env.AZURE_OPENAI_DEPLOYMENT || 'gpt-4', apiVersion: process.env.AZURE_OPENAI_API_VERSION || '2024-02-01', maxRetries: parseInt(process.env.AZURE_OPENAI_MAX_RETRIES || '3'), timeout: parseInt(process.env.AZURE_OPENAI_TIMEOUT || '30000') }; // Initialize Azure OpenAI client with optimized settings this.client = new OpenAI({ apiKey: this.config.apiKey, baseURL: `${this.config.endpoint}/openai/deployments/${this.config.deploymentName}`, defaultQuery: { 'api-version': this.config.apiVersion }, defaultHeaders: { 'api-key': this.config.apiKey, }, timeout: this.config.timeout, maxRetries: this.config.maxRetries }); // High-performance cache with smart TTL this.cache = new NodeCache({ stdTTL: 1800, // 30 minutes default checkperiod: 300, // Check every 5 minutes useClones: false, // Better performance maxKeys: 5000, // Prevent memory overflow deleteOnExpire: true }); // Retry configuration with exponential backoff this.retryConfig = { maxAttempts: 3, baseDelay: 1000, // 1 second maxDelay: 30000, // 30 seconds max backoffFactor: 2 }; // Rate limiting (Azure OpenAI has limits) this.rateLimiter = { requests: 0, resetTime: Date.now() + 60000, // 1 minute window maxRequests: 100, // Conservative limit windowMs: 60000 }; } generateCacheKey(method, params) { const serialized = JSON.stringify(params, Object.keys(params).sort()); return `ai:${method}:${Buffer.from(serialized).toString('base64').slice(0, 32)}`; } async checkRateLimit() { const now = Date.now(); if (now > this.rateLimiter.resetTime) { // Reset window this.rateLimiter.requests = 0; this.rateLimiter.resetTime = now + this.rateLimiter.windowMs; } if (this.rateLimiter.requests >= this.rateLimiter.maxRequests) { const waitTime = this.rateLimiter.resetTime - now; console.warn(`Rate limit reached. Waiting ${waitTime}ms before next request.`); await new Promise(resolve => setTimeout(resolve, waitTime)); this.rateLimiter.requests = 0; this.rateLimiter.resetTime = Date.now() + this.rateLimiter.windowMs; } this.rateLimiter.requests++; } async retryOperation(operation, context) { let lastError = null; for (let attempt = 1; attempt <= this.retryConfig.maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (attempt === this.retryConfig.maxAttempts) { console.error(`${context} failed after ${attempt} attempts:`, lastError); throw lastError; } // Calculate backoff delay const delay = Math.min(this.retryConfig.baseDelay * Math.pow(this.retryConfig.backoffFactor, attempt - 1), this.retryConfig.maxDelay); console.warn(`${context} attempt ${attempt} failed. Retrying in ${delay}ms...`, lastError.message); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } async deduplicateRequest(key, operation) { // Check if same request is already in progress if (this.requestQueue.has(key)) { return this.requestQueue.get(key); } // Start new request const promise = operation().finally(() => { this.requestQueue.delete(key); }); this.requestQueue.set(key, promise); return promise; } async analyzePlan(plan, projectId) { const cacheKey = this.generateCacheKey('analyzePlan', { plan, projectId }); // Check cache first const cached = this.cache.get(cacheKey); if (cached) { console.log('Cache hit for plan analysis'); return cached; } // Deduplicate concurrent requests return this.deduplicateRequest(cacheKey, async () => { await this.checkRateLimit(); return this.retryOperation(async () => { console.log('Analyzing plan with Azure OpenAI...'); const completion = await this.client.chat.completions.create({ model: this.config.deploymentName, messages: [ { role: 'system', content: `You are an expert AI project manager. Analyze the project plan and provide structured analysis. Return a JSON object with this exact structure: { "tasks": [ { "complexity": 0-100, "estimatedHours": number, "suggestedCategory": "development|testing|documentation|research|planning|deployment|maintenance|review", "suggestedPriority": "low|medium|high|critical", "requiredCapabilities": ["programming", "javascript", "typescript", "react", "node_js", "python", "testing", "deployment", "analysis", "design", "research", "security", "ui_ux", "devops", "databases", "machine_learning", "documentation", "code_review", "project_management"], "potentialRisks": ["risk1", "risk2"], "dependencies": ["dependency1", "dependency2"], "confidence": 0-100 } ], "suggestedTasks": [ { "title": "Task Title", "description": "Detailed description", "priority": "low|medium|high|critical", "category": "development|testing|documentation|research|planning|deployment|maintenance|review", "estimatedHours": number, "tags": ["tag1", "tag2"], "confidence": 0-100 } ], "projectComplexity": 0-100, "estimatedDuration": number, "suggestedAgents": ["senior_developer", "qa_engineer", "devops_engineer", "ux_designer", "security_engineer", "project_manager", "data_scientist"], "riskAssessment": ["risk1", "risk2"], "recommendations": ["recommendation1", "recommendation2"] }` }, { role: 'user', content: `Analyze this project plan and break it down into tasks:\n\n${plan}` } ], temperature: 0.3, // Lower temperature for more consistent results max_tokens: 4000, response_format: { type: "json_object" } }); const content = completion.choices[0]?.message?.content; if (!content) { throw new Error('No response content from Azure OpenAI'); } try { const analysis = JSON.parse(content); // Validate and set defaults const validatedAnalysis = { tasks: analysis.tasks || [], suggestedTasks: analysis.suggestedTasks || [], projectComplexity: Math.max(0, Math.min(100, analysis.projectComplexity || 50)), estimatedDuration: Math.max(1, analysis.estimatedDuration || 1), suggestedAgents: analysis.suggestedAgents || [], riskAssessment: analysis.riskAssessment || ['Analyze project requirements thoroughly'], recommendations: analysis.recommendations || ['Break down complex tasks', 'Establish clear dependencies'] }; // Cache result with smart TTL based on complexity const cacheTTL = validatedAnalysis.projectComplexity > 80 ? 900 : 1800; // 15min for complex, 30min for simple this.cache.set(cacheKey, validatedAnalysis, cacheTTL); console.log(`Plan analysis completed. Complexity: ${validatedAnalysis.projectComplexity}, Tasks: ${validatedAnalysis.tasks.length}`); return validatedAnalysis; } catch (parseError) { console.error('Failed to parse AI response:', parseError); console.error('Raw response:', content); // Return fallback analysis return { tasks: [], suggestedTasks: [], projectComplexity: 50, estimatedDuration: 4, suggestedAgents: [AgentType.SENIOR_DEVELOPER], riskAssessment: ['Unable to parse AI analysis - manual review required'], recommendations: ['Review project requirements manually', 'Break down into smaller components'] }; } }, 'Plan Analysis'); }); } async suggestTaskAssignment(taskDescription, availableAgents) { const cacheKey = this.generateCacheKey('suggestTaskAssignment', { taskDescription, agentIds: availableAgents.map(a => a.id).sort() }); const cached = this.cache.get(cacheKey); if (cached) { console.log('Cache hit for task assignment suggestion'); return cached; } return this.deduplicateRequest(cacheKey, async () => { await this.checkRateLimit(); return this.retryOperation(async () => { const completion = await this.client.chat.completions.create({ model: this.config.deploymentName, messages: [ { role: 'system', content: `You are an expert AI project coordinator. Analyze the task and suggest the best agent assignment. Return a JSON array of agent suggestions: [ { "agentId": "agent-id", "agentName": "Agent Name", "confidence": 0-100, "reasoning": "Why this agent is suitable for this task" } ] Consider agent capabilities, workload, and task requirements. Rank by suitability.` }, { role: 'user', content: `Task: ${taskDescription} Available Agents: ${availableAgents.map(agent => ` - ${agent.name} (${agent.type}) ID: ${agent.id} Capabilities: ${agent.capabilities.join(', ')} `).join('')} Suggest the best agent(s) for this task.` } ], temperature: 0.2, max_tokens: 2000, response_format: { type: "json_object" } }); const content = completion.choices[0]?.message?.content; if (!content) { throw new Error('No response content from Azure OpenAI'); } try { const parsed = JSON.parse(content); const suggestions = parsed.suggestions || parsed || []; const validatedSuggestions = suggestions .filter((s) => s.agentId && availableAgents.some(a => a.id === s.agentId)) .map((s) => ({ agentId: s.agentId, agentName: s.agentName || availableAgents.find(a => a.id === s.agentId)?.name || 'Unknown', confidence: Math.max(0, Math.min(100, s.confidence || 50)), reasoning: s.reasoning || 'AI recommendation based on capabilities' })) .sort((a, b) => b.confidence - a.confidence); // Cache with shorter TTL since agents change frequently this.cache.set(cacheKey, validatedSuggestions, 600); // 10 minutes return validatedSuggestions; } catch (parseError) { console.error('Failed to parse task assignment response:', parseError); // Fallback: return first available agent if (availableAgents.length > 0) { return [{ agentId: availableAgents[0].id, agentName: availableAgents[0].name, confidence: 50, reasoning: 'Fallback assignment - AI parsing failed' }]; } return []; } }, 'Task Assignment Suggestion'); }); } async generateTaskRecommendations(projectContext, currentTasks) { const cacheKey = this.generateCacheKey('generateTaskRecommendations', { projectContext, currentTasks }); const cached = this.cache.get(cacheKey); if (cached) { console.log('Cache hit for task recommendations'); return cached; } return this.deduplicateRequest(cacheKey, async () => { await this.checkRateLimit(); return this.retryOperation(async () => { const completion = await this.client.chat.completions.create({ model: this.config.deploymentName, messages: [ { role: 'system', content: 'You are an expert project manager. Based on the project context and current tasks, suggest 3-5 additional tasks that would be valuable. Return a JSON array of task titles.' }, { role: 'user', content: `Project: ${projectContext} Current Tasks: ${currentTasks.map(task => `- ${task}`).join('\n')} Suggest additional tasks that would improve this project:` } ], temperature: 0.4, max_tokens: 1000, response_format: { type: "json_object" } }); const content = completion.choices[0]?.message?.content; if (!content) { throw new Error('No response content from Azure OpenAI'); } try { const parsed = JSON.parse(content); const recommendations = parsed.recommendations || parsed.tasks || []; const validatedRecommendations = Array.isArray(recommendations) ? recommendations.filter(r => typeof r === 'string' && r.trim().length > 0) : []; // Cache for 20 minutes this.cache.set(cacheKey, validatedRecommendations, 1200); return validatedRecommendations; } catch (parseError) { console.error('Failed to parse task recommendations:', parseError); return [ 'Review and refine requirements', 'Add comprehensive testing', 'Create deployment documentation', 'Plan post-launch monitoring' ]; } }, 'Task Recommendations'); }); } // Performance monitoring methods getCacheStats() { return { keys: this.cache.keys().length, hits: this.cache.getStats().hits, misses: this.cache.getStats().misses, hitRate: this.cache.getStats().hits / (this.cache.getStats().hits + this.cache.getStats().misses) || 0, queueSize: this.requestQueue.size, rateLimitInfo: { requests: this.rateLimiter.requests, maxRequests: this.rateLimiter.maxRequests, resetTime: new Date(this.rateLimiter.resetTime).toISOString() } }; } // Health check async healthCheck() { try { const startTime = Date.now(); // Simple test request await this.client.chat.completions.create({ model: this.config.deploymentName, messages: [{ role: 'user', content: 'Hello' }], max_tokens: 5 }); const responseTime = Date.now() - startTime; return { status: responseTime < 5000 ? 'healthy' : 'degraded', details: { responseTime, cacheStats: this.getCacheStats(), connectionStatus: 'connected', lastCheck: new Date().toISOString() } }; } catch (error) { return { status: 'unhealthy', details: { error: error.message, lastCheck: new Date().toISOString() } }; } } // Cleanup method async shutdown() { this.cache.flushAll(); this.requestQueue.clear(); console.log('AI Service shut down gracefully'); } } //# sourceMappingURL=HighPerformanceAIService.js.map