azureai-optimizer
Version:
AI-Powered Azure Infrastructure Optimization via Model Context Protocol
248 lines (237 loc) ⢠12 kB
JavaScript
/**
* 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 /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 /optimizer server start
`);
}
}
//# sourceMappingURL=config.js.map