config-provider.tsā¢5.92 kB
/**
 * Configuration Provider Pattern
 * 
 * Provides testable configuration management with dependency injection support.
 * Follows TypeScript best practices and DRY principles.
 */
import { OpenRouterConfig } from '../types/workflow.js';
import { OpenRouterConfigManager, ConfigValidationResult } from './openrouter-config-manager.js';
import logger from '../logger.js';
/**
 * Configuration provider interface for dependency injection
 */
export interface ConfigurationProvider {
  getOpenRouterConfig(): Promise<OpenRouterConfig>;
  getModelForTask(taskName: string): string;
  validateConfiguration(): ConfigValidationResult;
  isTestEnvironment(): boolean;
  isCIEnvironment(): boolean;
}
/**
 * Production configuration provider using the real OpenRouterConfigManager
 */
export class ProductionConfigProvider implements ConfigurationProvider {
  async getOpenRouterConfig(): Promise<OpenRouterConfig> {
    return OpenRouterConfigManager.getInstance().getOpenRouterConfig();
  }
  
  getModelForTask(taskName: string): string {
    return OpenRouterConfigManager.getInstance().getModelForTask(taskName);
  }
  
  validateConfiguration(): ConfigValidationResult {
    return OpenRouterConfigManager.getInstance().validateConfiguration();
  }
  
  isTestEnvironment(): boolean {
    return process.env.NODE_ENV === 'test';
  }
  isCIEnvironment(): boolean {
    return (
      process.env.CI === 'true' ||
      process.env.GITHUB_ACTIONS === 'true' ||
      process.env.NODE_ENV === 'test' ||
      process.env.VITEST === 'true'
    );
  }
}
/**
 * Test configuration provider with safe defaults for CI/test environments
 */
export class TestConfigProvider implements ConfigurationProvider {
  private mockConfig: OpenRouterConfig = {
    baseUrl: 'https://test.openrouter.ai/api/v1',
    apiKey: 'ci-test-key-safe-provider',
    geminiModel: 'google/gemini-2.5-flash-preview-05-20',
    perplexityModel: 'perplexity/sonar',
    llm_mapping: { 
      default_generation: 'google/gemini-2.5-flash-preview-05-20',
      task_decomposition: 'google/gemini-2.5-flash-preview-05-20',
      intent_recognition: 'google/gemini-2.5-flash-preview-05-20'
    }
  };
  async getOpenRouterConfig(): Promise<OpenRouterConfig> {
    // Return a deep copy to prevent external modifications
    return {
      baseUrl: this.mockConfig.baseUrl,
      apiKey: this.mockConfig.apiKey,
      geminiModel: this.mockConfig.geminiModel,
      perplexityModel: this.mockConfig.perplexityModel,
      llm_mapping: this.mockConfig.llm_mapping ? { ...this.mockConfig.llm_mapping } : undefined
    };
  }
  
  getModelForTask(taskName: string): string {
    return this.mockConfig.llm_mapping?.[taskName] ?? this.mockConfig.geminiModel;
  }
  
  validateConfiguration(): ConfigValidationResult {
    return { 
      valid: true, 
      errors: [], 
      warnings: ['Using test configuration provider'], 
      suggestions: [] 
    };
  }
  
  isTestEnvironment(): boolean {
    return true;
  }
  isCIEnvironment(): boolean {
    return true;
  }
  /**
   * Update mock configuration for specific test scenarios
   */
  updateMockConfig(updates: Partial<OpenRouterConfig>): void {
    this.mockConfig = { ...this.mockConfig, ...updates };
    logger.debug({ updates }, 'Updated test configuration provider');
  }
  /**
   * Reset to default mock configuration
   */
  resetMockConfig(): void {
    this.mockConfig = {
      baseUrl: 'https://test.openrouter.ai/api/v1',
      apiKey: 'ci-test-key-safe-provider',
      geminiModel: 'google/gemini-2.5-flash-preview-05-20',
      perplexityModel: 'perplexity/sonar',
      llm_mapping: { 
        default_generation: 'google/gemini-2.5-flash-preview-05-20',
        task_decomposition: 'google/gemini-2.5-flash-preview-05-20',
        intent_recognition: 'google/gemini-2.5-flash-preview-05-20'
      }
    };
    logger.debug('Reset test configuration provider to defaults');
  }
}
/**
 * Factory function to create appropriate configuration provider
 * Uses environment detection to determine the right provider
 */
export function createConfigProvider(): ConfigurationProvider {
  const isCIOrTest = (
    process.env.CI === 'true' ||
    process.env.NODE_ENV === 'test' ||
    process.env.VITEST === 'true' ||
    process.env.CI_SAFE_MODE === 'true'
  );
  
  if (isCIOrTest && process.env.FORCE_REAL_LLM_CONFIG !== 'true') {
    logger.debug('Creating test configuration provider for CI/test environment');
    return new TestConfigProvider();
  } else {
    logger.debug('Creating production configuration provider');
    return new ProductionConfigProvider();
  }
}
/**
 * Global configuration provider instance
 * Can be overridden for testing
 */
let globalConfigProvider: ConfigurationProvider | null = null;
/**
 * Get the global configuration provider instance
 */
export function getConfigProvider(): ConfigurationProvider {
  if (!globalConfigProvider) {
    globalConfigProvider = createConfigProvider();
  }
  return globalConfigProvider;
}
/**
 * Override the global configuration provider (for testing)
 */
export function setConfigProvider(provider: ConfigurationProvider): void {
  globalConfigProvider = provider;
  logger.debug('Global configuration provider overridden');
}
/**
 * Reset the global configuration provider to auto-detect
 */
export function resetConfigProvider(): void {
  globalConfigProvider = null;
  logger.debug('Global configuration provider reset');
}
/**
 * Convenience function to get OpenRouter configuration using the global provider
 */
export async function getOpenRouterConfig(): Promise<OpenRouterConfig> {
  const provider = getConfigProvider();
  return await provider.getOpenRouterConfig();
}
/**
 * Convenience function to get model for task using the global provider
 */
export function getModelForTask(taskName: string): string {
  const provider = getConfigProvider();
  return provider.getModelForTask(taskName);
}