UNPKG

azureai-optimizer

Version:

AI-Powered Azure Infrastructure Optimization via Model Context Protocol

248 lines (237 loc) • 12 kB
/** * Configuration Management * Centralized configuration loading and validation */ import { Logger } from '../utils/logger.js'; export class Config { static logger = new Logger('Config'); static load() { try { this.logger.info('šŸ“‹ Loading configuration...'); // Load from environment variables const config = { environment: process.env.NODE_ENV || 'development', server: { transport: process.env.AZUREAI_TRANSPORT || 'stdio', ...(process.env.AZUREAI_PORT ? { port: parseInt(process.env.AZUREAI_PORT) } : {}), host: process.env.AZUREAI_HOST || 'localhost', logLevel: process.env.AZUREAI_LOG_LEVEL || 'info' }, azure: { subscriptionId: this.getRequiredEnv('AZURE_SUBSCRIPTION_ID'), ...(process.env.AZURE_TENANT_ID ? { tenantId: process.env.AZURE_TENANT_ID } : {}), ...(process.env.AZURE_CLIENT_ID ? { clientId: process.env.AZURE_CLIENT_ID } : {}), ...(process.env.AZURE_CLIENT_SECRET ? { clientSecret: process.env.AZURE_CLIENT_SECRET } : {}), ...(process.env.AZURE_MANAGED_IDENTITY_CLIENT_ID ? { managedIdentityClientId: process.env.AZURE_MANAGED_IDENTITY_CLIENT_ID } : {}) }, tools: { ...(process.env.AZUREAI_ENABLED_TOOLS ? { enabledTools: process.env.AZUREAI_ENABLED_TOOLS.split(',') } : {}), aiProviders: { ...(process.env.OPENROUTER_API_KEY ? { openrouter: { apiKey: process.env.OPENROUTER_API_KEY, baseUrl: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1', models: { cost_analysis: process.env.OPENROUTER_COST_MODEL || 'openai/gpt-4.1-preview', security_analysis: process.env.OPENROUTER_SECURITY_MODEL || 'anthropic/claude-sonnet-4', performance_analysis: process.env.OPENROUTER_PERFORMANCE_MODEL || 'openai/gpt-4.1-preview', general: process.env.OPENROUTER_GENERAL_MODEL || 'openai/gpt-4.1-preview' } } } : {}), ...(process.env.OPENAI_API_KEY ? { openai: { apiKey: process.env.OPENAI_API_KEY, model: process.env.OPENAI_MODEL || 'gpt-4-turbo-preview' } } : {}), ...(process.env.ANTHROPIC_API_KEY ? { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY, model: process.env.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20241022' } } : {}), ollama: { baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434', model: process.env.OLLAMA_MODEL || 'llama3.1:8b' } }, caching: { enabled: process.env.AZUREAI_CACHE_ENABLED !== 'false', ttl: parseInt(process.env.AZUREAI_CACHE_TTL || '300') } }, monitoring: { enabled: process.env.AZUREAI_MONITORING_ENABLED === 'true', ...(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING ? { applicationInsightsKey: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING } : {}), logLevel: process.env.AZUREAI_LOG_LEVEL || 'info' } }; // Validate configuration this.validateConfig(config); this.logger.info('āœ… Configuration loaded successfully'); this.logger.debug('šŸ“‹ Configuration:', this.sanitizeConfig(config)); return config; } catch (error) { this.logger.error('āŒ Configuration loading failed:', error); throw error; } } static getRequiredEnv(name) { const value = process.env[name]; if (!value) { // In MCP stdio mode, throw error without logging to stdout const isMCPStdio = process.env.MCP_TRANSPORT === 'stdio' || (!process.env.MCP_TRANSPORT && process.argv.includes('server') && process.argv.includes('start')); if (!isMCPStdio) { console.error(`\nāŒ Error: Required environment variable ${name} is not set`); console.error(`\nPlease set ${name} before starting the server.`); console.error(`Example: export ${name}="your-value"\n`); } throw new Error(`Required environment variable ${name} is not set`); } return value; } static validateConfig(config) { // Validate Azure subscription ID format const subscriptionIdRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; if (!subscriptionIdRegex.test(config.azure.subscriptionId)) { throw new Error('Invalid Azure subscription ID format'); } // Validate transport type if (!['stdio', 'sse', 'websocket'].includes(config.server.transport)) { throw new Error(`Invalid transport type: ${config.server.transport}`); } // Validate port for SSE transport if (config.server.transport === 'sse' && !config.server.port) { config.server.port = 8080; this.logger.warn('āš ļø No port specified for SSE transport, using default: 8080'); } // Validate log level const validLogLevels = ['debug', 'info', 'warn', 'error']; if (!validLogLevels.includes(config.server.logLevel)) { this.logger.warn(`āš ļø Invalid log level: ${config.server.logLevel}, using 'info'`); config.server.logLevel = 'info'; } // Check AI provider configuration const hasAIProvider = !!(config.tools.aiProviders?.openrouter?.apiKey || config.tools.aiProviders?.openai?.apiKey || config.tools.aiProviders?.anthropic?.apiKey || config.tools.aiProviders?.ollama); if (!hasAIProvider) { this.logger.warn('āš ļø No AI providers configured - AI-powered analysis will be limited'); this.logger.warn('šŸ’” Configure OPENROUTER_API_KEY (recommended) or OPENAI_API_KEY/ANTHROPIC_API_KEY for enhanced analysis'); } // Log configured AI providers if (config.tools.aiProviders?.openrouter?.apiKey) { this.logger.info('šŸš€ OpenRouter configured with latest flagship models'); const models = config.tools.aiProviders.openrouter.models; if (models) { this.logger.info(` Cost Analysis: ${models.cost_analysis}`); this.logger.info(` Security Analysis: ${models.security_analysis}`); this.logger.info(` Performance Analysis: ${models.performance_analysis}`); } } this.logger.info('āœ… Configuration validation passed'); } static sanitizeConfig(config) { // Create a sanitized version for logging (remove sensitive data) const sanitized = JSON.parse(JSON.stringify(config)); // Mask sensitive values if (sanitized.azure.clientSecret) { sanitized.azure.clientSecret = '***masked***'; } if (sanitized.tools.aiProviders?.openai?.apiKey) { sanitized.tools.aiProviders.openai.apiKey = '***masked***'; } if (sanitized.tools.aiProviders?.anthropic?.apiKey) { sanitized.tools.aiProviders.anthropic.apiKey = '***masked***'; } return sanitized; } static getDefaultConfig() { return { server: { transport: 'stdio', host: 'localhost', logLevel: 'info' }, tools: { caching: { enabled: true, ttl: 300 } }, monitoring: { enabled: false, logLevel: 'info' } }; } static validateEnvironment() { const required = ['AZURE_SUBSCRIPTION_ID']; const recommended = ['AZURE_TENANT_ID', 'OPENROUTER_API_KEY']; const optional = ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY']; const missing = required.filter(env => !process.env[env]); let warnings = recommended.filter(env => !process.env[env]); // If no OpenRouter, suggest other AI providers if (!process.env.OPENROUTER_API_KEY) { const hasOtherAI = optional.some(env => process.env[env]); if (!hasOtherAI) { warnings.push('No AI providers configured - consider OPENROUTER_API_KEY for best performance'); } } return { valid: missing.length === 0, missing, warnings }; } static displayConfigurationHelp() { console.log(` šŸ”§ AzureAI Optimizer Configuration Guide Required Environment Variables: AZURE_SUBSCRIPTION_ID Azure subscription ID to optimize Optional Environment Variables: AZURE_TENANT_ID Azure tenant ID (auto-detected if not provided) AZURE_CLIENT_ID Service principal client ID AZURE_CLIENT_SECRET Service principal client secret AI Provider Configuration (OpenRouter Recommended): OPENROUTER_API_KEY OpenRouter API key (recommended - access to latest models) OPENROUTER_BASE_URL OpenRouter base URL (default: https://openrouter.ai/api/v1) OPENROUTER_COST_MODEL Model for cost analysis (default: openai/gpt-4.1-preview) OPENROUTER_SECURITY_MODEL Model for security analysis (default: anthropic/claude-3.5-sonnet:beta) OPENROUTER_PERFORMANCE_MODEL Model for performance analysis (default: openai/gpt-4.1-preview) Direct Provider Configuration (Fallback): OPENAI_API_KEY OpenAI API key for direct access OPENAI_MODEL OpenAI model (default: gpt-4-turbo-preview) ANTHROPIC_API_KEY Anthropic API key for direct access ANTHROPIC_MODEL Anthropic model (default: claude-3-5-sonnet-20241022) Server Configuration: AZUREAI_TRANSPORT Transport type: stdio|sse|websocket (default: stdio) AZUREAI_PORT Server port for SSE transport (default: 8080) AZUREAI_HOST Server host (default: localhost) AZUREAI_LOG_LEVEL Log level: debug|info|warn|error (default: info) Tool Configuration: AZUREAI_ENABLED_TOOLS Comma-separated list of enabled tools AZUREAI_CACHE_ENABLED Enable caching (default: true) AZUREAI_CACHE_TTL Cache TTL in seconds (default: 300) Monitoring: AZUREAI_MONITORING_ENABLED Enable monitoring (default: false) APPLICATIONINSIGHTS_CONNECTION_STRING Application Insights connection string Example Configuration (OpenRouter - Recommended): export AZURE_SUBSCRIPTION_ID="12345678-1234-1234-1234-123456789012" export AZURE_TENANT_ID="87654321-4321-4321-4321-210987654321" export OPENROUTER_API_KEY="sk-or-v1-your-openrouter-key" export AZUREAI_LOG_LEVEL="info" npx @azureai/optimizer server start Example Configuration (Direct Providers): export AZURE_SUBSCRIPTION_ID="12345678-1234-1234-1234-123456789012" export OPENAI_API_KEY="sk-..." export ANTHROPIC_API_KEY="sk-ant-your-anthropic-key" npx @azureai/optimizer server start `); } } //# sourceMappingURL=config.js.map