- packages/llm: add FallbackLLMProvider (providers/fallback.ts) that tries each provider in order, skipping unconfigured or erroring ones; wire 'fallback' as a first-class LLMProviderType in factory + types - packages/llm: improve auto-detection in factory — PERPLEXITY_API_KEY and GEMINI_API_KEY trigger auto-selection when no explicit provider set - scripts/release.sh: new pipeline — rebase from origin/main, build, apply changesets, publish outdated packages to Gitea registry, push - scripts/run-registry-tests.sh: fix Gitea URL health-check to use a real package endpoint with auth header instead of bare registry root - docs: mark Vercel track-B prompts B1–B3 as complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
/**
|
|
* 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;
|
|
}
|