UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

209 lines 7.97 kB
import axios from 'axios'; import { configManager } from '../core/config.js'; class OpenRouterModelsService { cache = null; toolSupportedCache = null; cacheTimestamp = 0; CACHE_DURATION = 15 * 60 * 1000; API_URL = 'https://openrouter.ai/api/v1/models'; async fetchModels(forceRefresh = false, toolsOnly = false) { const cacheKey = toolsOnly ? 'tools' : 'all'; if (!forceRefresh && this.cache && Date.now() - this.cacheTimestamp < this.CACHE_DURATION) { if (toolsOnly) { return this.cache.filter(model => model.supported_parameters?.includes('tools')); } return this.cache; } try { const apiKey = configManager.getApiKey('openrouter'); if (!apiKey) { console.warn('OpenRouter API key not found, using cached/default models'); return this.getDefaultModels(); } const url = toolsOnly ? `${this.API_URL}?supported_parameters=tools` : this.API_URL; const response = await axios.get(url, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, timeout: 10000 }); if (response.data && response.data.data) { if (toolsOnly) { this.toolSupportedCache = response.data.data; } else { this.cache = response.data.data; this.cacheTimestamp = Date.now(); } this.saveToLocalCache(response.data.data, cacheKey); return response.data.data; } throw new Error('Invalid response format from OpenRouter models API'); } catch (error) { console.error('Failed to fetch models from OpenRouter:', error); const localCache = this.loadFromLocalCache(cacheKey); if (localCache) { return localCache; } return this.getDefaultModels(); } } saveToLocalCache(models, cacheKey = 'all') { try { const config = configManager.getConfig(); if (!config.cache) config.cache = {}; const cacheData = { models, timestamp: Date.now() }; if (cacheKey === 'tools') { config.cache.openrouterToolModels = cacheData; } else { config.cache.openrouterModels = cacheData; } configManager.setConfig('cache', config.cache); } catch (error) { console.error('Failed to save models to cache:', error); } } loadFromLocalCache(cacheKey = 'all') { try { const config = configManager.getConfig(); const cached = cacheKey === 'tools' ? config.cache?.openrouterToolModels : config.cache?.openrouterModels; if (cached && cached.models && Date.now() - cached.timestamp < 24 * 60 * 60 * 1000) { return cached.models; } } catch (error) { console.error('Failed to load models from cache:', error); } return null; } getDefaultModels() { return [ { id: 'openai/gpt-4o', canonical_slug: 'openai/gpt-4o', name: 'GPT-4o', created: Date.now(), description: 'OpenAI GPT-4o model', context_length: 128000, architecture: { input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'cl100k_base', instruct_type: null }, pricing: { prompt: '0.0000025', completion: '0.00001', request: '0', image: '0', web_search: '0', internal_reasoning: '0', input_cache_read: '0', input_cache_write: '0' }, top_provider: { context_length: 128000, max_completion_tokens: 4096, is_moderated: false }, per_request_limits: null, supported_parameters: ['tools', 'tool_choice', 'max_tokens', 'temperature', 'top_p', 'stop', 'frequency_penalty', 'presence_penalty', 'seed'] }, { id: 'anthropic/claude-opus-4', canonical_slug: 'anthropic/claude-opus-4', name: 'Claude Opus 4', created: Date.now(), description: 'Anthropic Claude Opus 4 model', context_length: 200000, architecture: { input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'claude', instruct_type: null }, pricing: { prompt: '0.000015', completion: '0.000075', request: '0', image: '0', web_search: '0', internal_reasoning: '0', input_cache_read: '0', input_cache_write: '0' }, top_provider: { context_length: 200000, max_completion_tokens: 4096, is_moderated: false }, per_request_limits: null, supported_parameters: ['max_tokens', 'temperature', 'top_p', 'stop'] } ]; } getModelsByProvider(provider) { const models = this.toolSupportedCache || this.cache || this.getDefaultModels(); return models.filter(model => model.id.startsWith(`${provider}/`)); } getModel(modelId) { const models = this.toolSupportedCache || this.cache || this.getDefaultModels(); return models.find(model => model.id === modelId); } getAvailableProviders() { const models = this.toolSupportedCache || this.cache || this.getDefaultModels(); const providers = new Set(); models.forEach(model => { const provider = model.id.split('/')[0]; if (provider) { providers.add(provider); } }); return Array.from(providers).sort(); } getModelPricing(modelId) { const model = this.getModel(modelId); if (!model) return null; return { prompt: parseFloat(model.pricing.prompt) || 0, completion: parseFloat(model.pricing.completion) || 0 }; } getModelContextLength(modelId) { const model = this.getModel(modelId); return model?.context_length || 128000; } modelSupports(modelId, parameter) { const model = this.getModel(modelId); return model?.supported_parameters?.includes(parameter) || false; } isModelAvailable(modelId, _mode) { const model = this.getModel(modelId); if (!model) return false; const params = model.supported_parameters || []; return params.includes('tools') && params.includes('tool_choice'); } getToolSupportedModels() { const models = this.cache || this.getDefaultModels(); return models.filter(model => { const params = model.supported_parameters || []; return params.includes('tools') && params.includes('tool_choice'); }); } } export const openRouterModelsService = new OpenRouterModelsService(); //# sourceMappingURL=openrouter-models.js.map