devibe
Version:
Intelligent repository cleanup with auto mode, AI learning, markdown consolidation, auto-consolidate workflow, context-aware classification, and cost optimization
138 lines âĸ 5.53 kB
JavaScript
/**
* AI Provider Resolver
*
* Determines which AI provider and model to use based on:
* 1. Explicitly configured devibe AI key (primary)
* 2. Environment variables (fallback)
* 3. Model selection (AI_MODEL env var or config)
*/
import { getKeyManager } from './ai-key-manager.js';
import { AVAILABLE_MODELS } from './ai-model-config.js';
export class AIProviderResolver {
/**
* Resolve which provider and model to use
* Returns null if no API keys are available
*/
async resolve() {
const keyManager = getKeyManager();
// Step 1: Check if user explicitly set AI_MODEL
const explicitModel = process.env.AI_MODEL;
if (explicitModel && explicitModel in AVAILABLE_MODELS) {
const model = AVAILABLE_MODELS[explicitModel];
// Try devibe-stored key first
let apiKey = await keyManager.getKey(model.provider);
let source = 'devibe-config';
// Fallback to environment
if (!apiKey) {
apiKey = this.getEnvKey(model.provider);
source = 'environment';
}
if (apiKey) {
return { provider: model.provider, model, apiKey, source };
}
}
// Step 2: Check devibe-configured keys (primary source)
const configuredProviders = await keyManager.getConfiguredProviders();
if (configuredProviders.length > 0) {
// Prioritize: Google (cheapest) > Anthropic > OpenAI
const preferredOrder = ['google', 'anthropic', 'openai'];
for (const provider of preferredOrder) {
if (configuredProviders.includes(provider)) {
const apiKey = await keyManager.getKey(provider);
if (apiKey) {
// Get best model for this provider
const model = this.getBestModelForProvider(provider);
return { provider, model, apiKey, source: 'devibe-config' };
}
}
}
}
// Step 3: Fallback to environment variables
const envProviders = ['anthropic', 'openai', 'google'];
for (const provider of envProviders) {
const apiKey = this.getEnvKey(provider);
if (apiKey) {
const model = this.getBestModelForProvider(provider);
return { provider, model, apiKey, source: 'environment' };
}
}
// Step 4: No keys available
return null;
}
/**
* Get environment variable for provider
*/
getEnvKey(provider) {
switch (provider) {
case 'anthropic':
return process.env.ANTHROPIC_API_KEY;
case 'openai':
return process.env.OPENAI_API_KEY;
case 'google':
return process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
}
}
/**
* Get the best (cheapest good quality) model for a provider
*/
getBestModelForProvider(provider) {
const providerModels = Object.values(AVAILABLE_MODELS).filter(m => m.provider === provider);
// Sort by price (cheapest first) but exclude "best" quality (too expensive)
const affordable = providerModels.filter(m => m.quality !== 'best');
affordable.sort((a, b) => a.inputPricePerMillion - b.inputPricePerMillion);
// Return cheapest affordable model, or first available
return affordable[0] || providerModels[0];
}
/**
* Check if any AI provider is available
*/
async isAvailable() {
const resolved = await this.resolve();
return resolved !== null;
}
/**
* Get a human-readable description of the resolved provider
*/
async getProviderDescription() {
const resolved = await this.resolve();
if (!resolved)
return null;
const sourceLabel = resolved.source === 'devibe-config'
? 'configured in devibe'
: 'from environment variable';
return `${resolved.model.name} (${sourceLabel})`;
}
/**
* Log which provider/model is being used (for debugging)
*/
async logProviderInfo(verbose = false) {
const resolved = await this.resolve();
if (!resolved) {
if (verbose) {
console.log(' âšī¸ AI classification: Not available (no API keys)');
}
return;
}
if (verbose) {
const sourceIcon = resolved.source === 'devibe-config' ? 'đ§' : 'đ';
console.log(` ${sourceIcon} AI: ${resolved.model.name}`);
console.log(` Provider: ${resolved.provider}`);
console.log(` Source: ${resolved.source === 'devibe-config' ? 'DevIbe config' : 'Environment variable'}`);
console.log(` Context: ${resolved.model.contextWindow.toLocaleString()} tokens`);
console.log(` Cost: $${resolved.model.inputPricePerMillion}/M input`);
}
else {
const sourceLabel = resolved.source === 'devibe-config' ? '(configured)' : '(env)';
console.log(` âšī¸ Using AI: ${resolved.model.name} ${sourceLabel}`);
}
}
}
// Singleton instance
let resolverInstance = null;
export function getAIResolver() {
if (!resolverInstance) {
resolverInstance = new AIProviderResolver();
}
return resolverInstance;
}
//# sourceMappingURL=ai-provider-resolver.js.map