/** * AI Provider Configuration * Manages configuration for different AI providers (Claude, Ollama, Cloudflare) */ // Default configuration const DEFAULT_PROVIDER = 'claude'; // Use 127.0.0.1 instead of localhost to avoid IPv6 resolution issues const DEFAULT_OLLAMA_URL = 'http://127.0.0.1:11434'; const DEFAULT_CLOUDFLARE_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID || ''; /** * Get the current AI provider * @returns {string} Provider name ('claude' or 'ollama') */ export function getProvider() { return process.env.AI_PROVIDER || DEFAULT_PROVIDER; } /** * Get Ollama API URL * @returns {string} Ollama base URL */ export function getOllamaUrl() { return process.env.OLLAMA_URL || DEFAULT_OLLAMA_URL; } /** * Get Anthropic API URL * @returns {string} Anthropic base URL */ export function getAnthropicUrl() { return process.env.ANTHROPIC_API_URL || 'https://api.anthropic.com'; } /** * Get Cloudflare Account ID * @returns {string} Cloudflare account ID */ export function getCloudflareAccountId() { return process.env.CLOUDFLARE_ACCOUNT_ID || DEFAULT_CLOUDFLARE_ACCOUNT_ID; } /** * Get Cloudflare API Token * @returns {string} Cloudflare API token */ export function getCloudflareApiToken() { return process.env.CLOUDFLARE_API_TOKEN || ''; } /** * Get Cloudflare Workers AI base URL * @returns {string} Cloudflare Workers AI base URL */ export function getCloudflareUrl() { const accountId = getCloudflareAccountId(); return `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run`; } /** * Get provider configuration for a specific provider * @param {string} provider - Provider name * @returns {object} Provider configuration */ export function getProviderConfig(provider) { switch (provider) { case 'ollama': return { name: 'ollama', baseUrl: getOllamaUrl(), chatEndpoint: '/api/chat', requiresAuth: false }; case 'cloudflare': return { name: 'cloudflare', baseUrl: getCloudflareUrl(), chatEndpoint: '', // Model is appended to baseUrl requiresAuth: true, apiKey: getCloudflareApiToken(), accountId: getCloudflareAccountId() }; case 'claude': default: return { name: 'claude', baseUrl: getAnthropicUrl(), chatEndpoint: '/v1/messages', requiresAuth: true, apiKey: process.env.ANTHROPIC_API_KEY }; } } /** * Determine provider from model ID * @param {string} modelId - Model identifier * @returns {string} Provider name */ export function getProviderFromModel(modelId) { if (!modelId) return getProvider(); // Claude models start with 'claude-' if (modelId.startsWith('claude-')) { return 'claude'; } // Cloudflare models start with '@cf/' or '@hf/' if (modelId.startsWith('@cf/') || modelId.startsWith('@hf/')) { return 'cloudflare'; } // Known Ollama models const ollamaModels = [ 'llama', 'mistral', 'qwen', 'codellama', 'phi', 'gemma', 'neural-chat', 'starling', 'orca', 'vicuna', 'deepseek', 'dolphin', 'nous-hermes', 'openhermes' ]; for (const prefix of ollamaModels) { if (modelId.toLowerCase().startsWith(prefix)) { return 'ollama'; } } // Default to configured provider return getProvider(); } export default { getProvider, getOllamaUrl, getAnthropicUrl, getCloudflareAccountId, getCloudflareApiToken, getCloudflareUrl, getProviderConfig, getProviderFromModel };