/** * LLM provider factory. * * Creates an LLMProvider based on LLM_PROVIDER env var. * Auto-detects provider from endpoint/key env vars if not explicitly set. * * Provider selection priority: * LLM_PROVIDER env var > auto-detect from endpoint/key env vars > openai * * To use a fallback chain (e.g. perplexity → openai → gemini), set: * LLM_PROVIDER=fallback * LLM_FALLBACK_ORDER=perplexity,openai,gemini (default if unset) */ import { AzureOpenAIProvider } from './providers/azure-openai.js'; import { FallbackLLMProvider } from './providers/fallback.js'; import { GeminiProvider } from './providers/gemini.js'; import { MockLLMProvider } from './providers/mock.js'; import { OpenAIProvider } from './providers/openai.js'; import { PerplexityProvider } from './providers/perplexity.js'; import type { LLMProvider, LLMProviderType } from './types.js'; let _provider: LLMProvider | null = null; /** * Resolve provider type from env vars. * Priority: LLM_PROVIDER > OPENAI_PROVIDER > auto-detect from keys/endpoints. */ function resolveProviderType(): LLMProviderType { const explicit = (process.env.LLM_PROVIDER || process.env.OPENAI_PROVIDER || '').toLowerCase(); if (explicit === 'azure') return 'azure'; if (explicit === 'openai') return 'openai'; if (explicit === 'perplexity') return 'perplexity'; if (explicit === 'gemini') return 'gemini'; if (explicit === 'fallback') return 'fallback'; if (explicit === 'mock') return 'mock'; // Auto-detect from environment const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT ?? ''; const baseUrl = process.env.OPENAI_BASE_URL ?? ''; if (azureEndpoint.trim().length > 0) return 'azure'; if (baseUrl.includes('.cognitive.microsoft.com') || baseUrl.includes('.openai.azure.com')) return 'azure'; if (process.env.PERPLEXITY_API_KEY) return 'perplexity'; if (process.env.GEMINI_API_KEY) return 'gemini'; return 'openai'; } /** * Get the singleton LLM provider. */ export function getLLM(): LLMProvider { if (!_provider) { _provider = createLLMProvider(resolveProviderType()); } return _provider; } /** * Create an LLM provider by type. * For 'fallback', reads LLM_FALLBACK_ORDER env var (comma-separated provider names). */ export function createLLMProvider(type: LLMProviderType): LLMProvider { switch (type) { case 'azure': return new AzureOpenAIProvider(); case 'openai': return new OpenAIProvider(); case 'perplexity': return new PerplexityProvider(); case 'gemini': return new GeminiProvider(); case 'fallback': { const order = (process.env.LLM_FALLBACK_ORDER ?? 'perplexity,openai,gemini') .split(',') .map(s => s.trim() as LLMProviderType) .filter(name => name && name !== 'fallback'); // prevent infinite recursion if (order.length === 0) { throw new Error('LLM_FALLBACK_ORDER must contain at least one non-fallback provider'); } return new FallbackLLMProvider(order.map(createLLMProvider)); } case 'mock': return new MockLLMProvider(); default: throw new Error( `Unknown LLM_PROVIDER: '${type}'. Valid: azure, openai, perplexity, gemini, fallback, mock` ); } } /** * Set the singleton LLM provider (for testing). */ export function setLLM(provider: LLMProvider): void { _provider = provider; } /** * @internal */ export function _resetLLM(): void { _provider = null; }