All files / providers AIProviderManager.ts

54.68% Statements 35/64
18.51% Branches 5/27
77.77% Functions 7/9
55.17% Lines 32/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124      1x 1x 1x 1x 1x   1x 1x   1x 5x 5x     5x 5x       4x   4x     4x       4x 4x           4x 4x           4x 4x           4x       4x   4x         4x 8x 8x                       1x       1x       1x 1x                                                                 1x        
import { AIProvider, ProviderType } from '../core/interfaces';
import { MananKanchuConfigManager } from '../core/config';
import { SecretManager } from '../core/SecretManager';
import { AnthropicProvider } from './AnthropicProvider';
import { OpenAIProvider } from './OpenAIProvider';
import { GeminiProvider } from './GeminiProvider';
import { OllamaProvider } from './OllamaProvider';
import { LMStudioProvider } from './LMStudioProvider';
 
const LOCAL_PRIORITY: ProviderType[] = ['ollama', 'lmstudio'];
const CLOUD_PRIORITY: ProviderType[] = ['anthropic', 'gemini', 'openai'];
 
export class AIProviderManager {
  private providers: Map<ProviderType, AIProvider> = new Map();
  private active: AIProvider | null = null;
 
  constructor(
    private readonly config: MananKanchuConfigManager,
    private readonly secrets: SecretManager
  ) {}
 
  async initialize(): Promise<void> {
    const timeout = this.config.requestTimeout;
 
    this.providers.set('ollama', new OllamaProvider(
      this.config.ollamaModel, this.config.ollamaEndpoint, timeout
    ));
    this.providers.set('lmstudio', new LMStudioProvider(
      this.config.lmstudioEndpoint, timeout
    ));
 
    const anthropicKey = await this.secrets.getApiKey('anthropic');
    Iif (anthropicKey) {
      this.providers.set('anthropic', new AnthropicProvider(
        anthropicKey, this.config.anthropicModel, this.config.anthropicBaseUrl, timeout
      ));
    }
 
    const openaiKey = await this.secrets.getApiKey('openai');
    Iif (openaiKey) {
      this.providers.set('openai', new OpenAIProvider(
        openaiKey, this.config.openaiModel, this.config.openaiBaseUrl, timeout
      ));
    }
 
    const geminiKey = await this.secrets.getApiKey('gemini');
    Iif (geminiKey) {
      this.providers.set('gemini', new GeminiProvider(
        geminiKey, this.config.geminiModel, this.config.geminiBaseUrl, timeout
      ));
    }
 
    this.active = await this.selectProvider();
  }
 
  private async selectProvider(): Promise<AIProvider | null> {
    const preferred = this.config.preferredProvider as ProviderType | 'auto';
 
    Iif (preferred !== 'auto') {
      const p = this.providers.get(preferred);
      Iif (p && await p.isAvailable()) return p;
    }
 
    for (const type of LOCAL_PRIORITY) {
      const p = this.providers.get(type);
      if (p && await p.isAvailable()) return p;
    }
 
    for (const type of CLOUD_PRIORITY) {
      const p = this.providers.get(type);
      Iif (p && await p.isAvailable()) return p;
    }
 
    return null;
  }
 
  getActive(): AIProvider | null {
    return this.active;
  }
 
  getAll(): AIProvider[] {
    return Array.from(this.providers.values());
  }
 
  async switchTo(type: ProviderType): Promise<boolean> {
    const p = this.providers.get(type);
    if (!p) return false;
    const available = await p.isAvailable();
    Iif (!available) return false;
    this.active = p;
    return true;
  }
 
  async refreshProvider(type: ProviderType, apiKey?: string): Promise<void> {
    const timeout = this.config.requestTimeout;
    if (type === 'anthropic' && apiKey) {
      await this.secrets.setApiKey('anthropic', apiKey);
      this.providers.set('anthropic', new AnthropicProvider(
        apiKey, this.config.anthropicModel, this.config.anthropicBaseUrl, timeout
      ));
    } else if (type === 'openai' && apiKey) {
      await this.secrets.setApiKey('openai', apiKey);
      this.providers.set('openai', new OpenAIProvider(
        apiKey, this.config.openaiModel, this.config.openaiBaseUrl, timeout
      ));
    } else Iif (type === 'gemini' && apiKey) {
      await this.secrets.setApiKey('gemini', apiKey);
      this.providers.set('gemini', new GeminiProvider(
        apiKey, this.config.geminiModel, this.config.geminiBaseUrl, timeout
      ));
    }
    this.active = await this.selectProvider();
  }
 
  async reselect(): Promise<void> {
    this.active = await this.selectProvider();
  }
 
  getActiveInfo(): { name: string; model: string; type: string } | null {
    if (!this.active) return null;
    return { name: this.active.name, model: this.active.modelName, type: this.active.type };
  }
}