UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

340 lines 14.6 kB
/** * Enhanced AI Processor Module for Requirements Gathering Agent * * Core AI processing engine with improved error handling, response processing, * and multi-provider support for robust document generation. * * @version 2.1.3 * @author Requirements Gathering Agent Team * @created 2024 * @updated June 2025 * * Key Features: * - Multi-provider AI integration (Google AI, Azure OpenAI, GitHub AI, Ollama) * - Advanced error handling and retry mechanisms * - Performance metrics and monitoring * - Response validation and processing * - Configuration management and provider switching * * @filepath c:\Users\menno\Source\Repos\requirements-gathering-agent\src\modules\ai\AIProcessor.ts */ import { AIClientManager } from "./AIClientManager.js"; import { MetricsManager } from "./MetricsManager.js"; import { RetryManager } from "./RetryManager.js"; import { ConfigurationManager } from "./ConfigurationManager.js"; import { ProviderManager } from "./ProviderManager.js"; import { ProviderFallbackManager } from "./ProviderFallbackManager.js"; export class AIProcessor { static instance; clientManager; metricsManager; retryManager; config; providerManager; fallbackManager; initializationPromise = null; constructor() { this.clientManager = AIClientManager.getInstance(); this.metricsManager = MetricsManager.getInstance(); this.retryManager = RetryManager.getInstance(); this.config = ConfigurationManager.getInstance(); this.providerManager = new ProviderManager(); this.fallbackManager = ProviderFallbackManager.getInstance(); this.initializationPromise = this.providerManager.validateConfigurations().then(() => { }); } async ensureInitialized() { if (this.initializationPromise) { await this.initializationPromise; this.initializationPromise = null; } } static getInstance() { if (!AIProcessor.instance) { AIProcessor.instance = new AIProcessor(); } return AIProcessor.instance; } async processAIRequest(operation, operationName) { await this.ensureInitialized(); // Always use provider from config unless fallback is required let provider = this.config.getAIProvider(); if (!provider) { throw new Error('No AI provider configured'); } // Ensure the client for the provider is initialized if (!this.clientManager.getClient(provider)) { console.log(`🔧 Initializing client for provider: ${provider}`); await this.clientManager.initializeSpecificProvider(provider); } let status = this.providerManager.getProviderStatus(provider); if (!status.available || !status.withinRateLimit || !status.withinQuota) { // Try fallback only if the configured provider is unavailable if (await this.providerManager.switchProvider()) { const fallbackProvider = this.providerManager.getActiveProvider(); if (fallbackProvider && fallbackProvider !== provider) { console.log(`Switched to fallback provider: ${fallbackProvider}`); provider = fallbackProvider; this.config.setProvider(provider); // update config for this session if (!this.clientManager.getClient(provider)) { await this.clientManager.initializeSpecificProvider(provider); } status = this.providerManager.getProviderStatus(provider); } } else { throw new Error('No available AI providers'); } } return this.retryManager.executeWithRetry(operation, operationName, provider); } createMessages(systemPrompt, userPrompt) { return [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt } ]; } async makeAICall(messages, maxTokens) { await this.ensureInitialized(); const defaultTokens = this.config.getMaxResponseTokens(); maxTokens = maxTokens ?? defaultTokens; const startTime = Date.now(); // Use enhanced fallback manager for automatic provider switching return this.fallbackManager.executeWithFallback(async (provider) => { // Ensure the client for the provider is initialized let client = this.clientManager.getClient(provider); if (!client) { console.log(`🔧 Initializing client for provider: ${provider}`); await this.clientManager.initializeSpecificProvider(provider); client = this.clientManager.getClient(provider); if (!client) { throw new Error(`Failed to initialize client for provider: ${provider}`); } } // Check provider status const status = this.providerManager.getProviderStatus(provider); if (!status.withinRateLimit) { throw new Error(`Rate limit exceeded for provider ${provider}`); } if (!status.withinQuota) { throw new Error(`Quota exceeded for provider ${provider}`); } // Make the AI call const result = await this.routeProviderCall(provider, client, messages, maxTokens); const endTime = Date.now(); const responseTime = endTime - startTime; const tokensUsed = this.estimateTokens(result); // Update provider metrics on success this.providerManager.updateMetrics(provider, true, responseTime, tokensUsed); return { content: result, metadata: { provider, responseTime, tokensUsed } }; }, 'AI_Call'); } async routeProviderCall(provider, client, messages, maxTokens) { const callMethods = { 'google-ai': () => this.makeGoogleAICall(client, messages, maxTokens), 'azure-openai': () => this.makeAzureOpenAICall(client, messages, maxTokens), 'azure-openai-key': () => this.makeAzureOpenAICall(client, messages, maxTokens), 'azure-openai-entra': () => this.makeAzureOpenAICall(client, messages, maxTokens), 'azure-ai-studio': () => this.makeAzureAIStudioCall(client, messages, maxTokens), 'github-ai': () => this.makeGitHubAICall(client, messages, maxTokens), 'ollama': () => this.makeOllamaCall(client, messages, maxTokens) }; const callMethod = callMethods[provider]; if (!callMethod) { throw new Error(`Unsupported provider: ${provider}`); } return await callMethod(); } async makeGoogleAICall(client, messages, maxTokens) { const modelName = "gemini-1.5-flash"; const model = client.getGenerativeModel({ model: modelName }); const modelMaxTokens = this.config.getModelTokenLimit(modelName); // Convert messages to Google AI format const prompt = this.convertMessagesToGoogleFormat(messages); const result = await model.generateContent({ contents: prompt, generationConfig: { maxOutputTokens: Math.min(maxTokens, modelMaxTokens), temperature: 0.7, }, }); const response = await result.response; return response.text(); } async makeAzureOpenAICall(client, messages, maxTokens) { const deployment = this.config.get('AZURE_OPENAI_DEPLOYMENT_NAME'); const modelMaxTokens = this.config.getModelTokenLimit(deployment); const completion = await client.chat.completions.create({ model: deployment, messages: messages, max_tokens: Math.min(maxTokens, modelMaxTokens), temperature: 0.7 }); return completion.choices[0]?.message?.content || ''; } async makeAzureAIStudioCall(client, messages, maxTokens) { const deployment = this.config.get('AZURE_OPENAI_DEPLOYMENT_NAME'); const apiVersion = this.config.get('AZURE_OPENAI_API_VERSION'); const apiPath = `/openai/deployments/${deployment}/chat/completions?api-version=${apiVersion}`; const modelMaxTokens = this.config.getModelTokenLimit(deployment); const result = await client.path(apiPath).post({ body: { messages: messages, model: deployment, max_tokens: Math.min(maxTokens, modelMaxTokens), temperature: 0.7 } }); if (result.status !== "200") { throw new Error(`Azure AI Studio call failed: ${result.status}`); } return result.body.choices[0]?.message?.content || ''; } async makeGitHubAICall(client, messages, maxTokens) { const modelName = "gpt-4o-mini"; const modelMaxTokens = this.config.getModelTokenLimit(modelName); const result = await client.path("/chat/completions").post({ body: { messages: messages, model: modelName, max_tokens: Math.min(maxTokens, modelMaxTokens), temperature: 0.7 } }); if (result.status !== "200") { throw new Error(`GitHub AI call failed: ${result.status}`); } return result.body.choices[0]?.message?.content || ''; } async makeOllamaCall(client, messages, maxTokens) { const { endpoint } = client; const modelName = 'llama2'; const modelMaxTokens = this.config.getModelTokenLimit(modelName); const response = await fetch(`${endpoint}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ model: modelName, messages: messages, stream: false, options: { temperature: 0.7, num_predict: Math.min(maxTokens, modelMaxTokens) } }) }); if (!response.ok) { throw new Error(`Ollama call failed: ${response.status}`); } const result = await response.json(); return result.message?.content || ''; } convertMessagesToGoogleFormat(messages) { return messages.map(msg => ({ role: msg.role === 'assistant' ? 'model' : 'user', parts: [{ text: msg.content }] })); } estimateTokens(text) { // Simple token estimation (roughly 4 characters per token) return Math.ceil(text.length / 4); } extractContent(response) { return response.content; } /** * Populates enhanced context from analysis results * @param analysisResults The analysis results to populate context from * @returns Enhanced context object */ static async populateEnhancedContextFromAnalysis(analysisResults) { try { const enhancedContext = { requirements: analysisResults.requirements || [], architecture: analysisResults.architecture || [], stakeholders: analysisResults.stakeholders || [], planning: analysisResults.planning || [], technical: analysisResults.technical || [], metadata: analysisResults.metadata || {}, analysis: { contextScore: analysisResults.analysis?.contextScore || 0, contentScore: analysisResults.analysis?.contentScore || 0, structureScore: analysisResults.analysis?.structureScore || 0, completenessScore: analysisResults.analysis?.completenessScore || 0, } }; return enhancedContext; } catch (error) { console.warn('Error populating enhanced context:', error); return {}; } } // Performance monitoring methods async testConnection(provider) { try { await this.ensureInitialized(); if (provider) { const isValid = await this.providerManager.validateProvider(provider); if (!isValid) { return false; } } // Use active provider if none specified const activeProvider = provider || this.providerManager.getActiveProvider(); if (!activeProvider) { return false; } // Ensure the client for the active provider is initialized if (!this.clientManager.getClient(activeProvider)) { console.log(`🔧 Initializing client for test connection: ${activeProvider}`); await this.clientManager.initializeSpecificProvider(activeProvider); } const client = this.clientManager.getClient(activeProvider); if (!client) { return false; } const testMessages = this.createMessages("You are a helpful assistant.", "Say 'Hello' in response."); await this.makeAICall(testMessages, 10); return true; } catch (error) { console.error(`Connection test failed:`, error); return false; } } getPerformanceMetrics() { return { metrics: this.metricsManager.getAllMetrics(), circuitBreakers: this.retryManager.getCircuitBreakerStatus(), clientHealth: this.clientManager.getClientHealth(), configuration: this.config.getConfigSummary(), providers: Array.from(this.providerManager.getProviderMetrics()).reduce((acc, [key, value]) => { acc[key] = value; return acc; }, {}) }; } async refreshConnections() { await this.clientManager.refreshConnection(); } // Cleanup method cleanup() { this.clientManager.cleanup(); this.metricsManager.resetMetrics(); this.retryManager.resetCircuitBreaker(); } } // Export singleton getter function export function getAIProcessor() { return AIProcessor.getInstance(); } // (Removed duplicate export to avoid redeclaration error) //# sourceMappingURL=AIProcessor.js.map