UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

815 lines (814 loc) โ€ข 35.7 kB
/** * OpenRouter Rankings Intelligence System * * Comprehensive system for collecting, analyzing, and utilizing OpenRouter * programming model rankings to make intelligent model selection decisions. * * Features: * - Web scraping of OpenRouter programming rankings * - Model discovery with category filtering * - Real-time availability checking * - Cost optimization analysis * - Trend analysis and historical tracking * - Integration with existing sync system */ import { getDatabase } from '../storage/unified-database.js'; // ===== OPENROUTER RANKINGS CLASS ===== export class OpenRouterRankings { constructor() { this.baseUrl = 'https://openrouter.ai'; this.rankingsUrl = `${this.baseUrl}/rankings/programming`; this.modelsApiUrl = `${this.baseUrl}/api/v1/models`; } /** * Main function to collect and store all ranking intelligence */ async collectRankingIntelligence() { console.log('๐Ÿ” Collecting OpenRouter ranking intelligence...'); const stats = { rankingsCollected: 0, modelsDiscovered: 0, trendsAnalyzed: 0 }; try { // 1. Scrape current programming rankings const weeklyRankings = await this.scrapeProgrammingRankings('week'); const monthlyRankings = await this.scrapeProgrammingRankings('month'); // 2. Store rankings in database stats.rankingsCollected += await this.storeRankings(weeklyRankings, 'weekly'); stats.rankingsCollected += await this.storeRankings(monthlyRankings, 'monthly'); // 3. Discover models via API const discoveredModels = await this.discoverModelsViaAPI(); stats.modelsDiscovered = discoveredModels.length; // 4. Analyze trends const trends = await this.analyzeTrends(); stats.trendsAnalyzed = trends.length; // 5. Update sync metadata await this.updateSyncMetadata(); console.log(`โœ… Ranking intelligence collected: ${JSON.stringify(stats)}`); return stats; } catch (error) { console.error('โŒ Failed to collect ranking intelligence:', error); throw error; } } /** * Get OpenRouter programming rankings using the Models API */ async scrapeProgrammingRankings(period) { console.log(`๐Ÿ“Š Collecting ${period}ly programming rankings via OpenRouter Models API...`); try { // Use the official OpenRouter Models API to get programming models const response = await fetch(`${this.modelsApiUrl}`, { headers: { 'User-Agent': 'Hive.AI/1.7.0 (Rankings Intelligence)', 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`); } const data = await response.json(); const rankings = this.parseRankingsFromAPIData(data, period); console.log(`โœ… Successfully collected ${rankings.length} ${period}ly rankings from API`); return rankings; } catch (error) { console.warn(`โš ๏ธ Failed to get ${period}ly rankings via API:`, error); return await this.getFallbackRankings(period); } } /** * Parse model rankings from OpenRouter API data */ parseRankingsFromAPIData(data, period) { const rankings = []; try { if (!data.data || !Array.isArray(data.data)) { console.warn('โš ๏ธ Invalid API response format'); return rankings; } // Filter for programming-relevant models and sort by quality indicators const programmingModels = data.data .filter((model) => { const id = model.id?.toLowerCase() || ''; const description = model.description?.toLowerCase() || ''; const name = model.name?.toLowerCase() || ''; // ๐Ÿ›ก๏ธ FIRST: Filter out pseudo-models and routing models (never rank these) const pseudoModelPatterns = [ 'openrouter/auto', 'openrouter/best', '/auto', '/best', '/router', '/routing', 'auto-select', 'best-select', 'auto/', 'best/', 'router/', 'routing/' ]; // Exclude any pseudo-models immediately if (pseudoModelPatterns.some(pattern => id.includes(pattern))) { console.log(`๐Ÿ›ก๏ธ Filtered out pseudo-model: ${id}`); return false; } // ๐Ÿ” SECOND: Validate model is actually callable (has valid pricing/context) if (!model.pricing || (!model.pricing.prompt && !model.pricing.completion)) { console.log(`๐Ÿ›ก๏ธ Filtered out non-callable model: ${id} (no pricing)`); return false; } // ๐ŸŽฏ THIRD: Enhanced programming model detection const programmingKeywords = [ 'code', 'programming', 'development', 'software', 'coding', 'claude', 'gpt', 'gemini', 'llama', 'mistral', 'qwen', 'deepseek', 'anthropic', 'openai', 'google', 'meta', 'microsoft' ]; return programmingKeywords.some(keyword => id.includes(keyword) || description.includes(keyword) || name.includes(keyword)); }) .sort((a, b) => { // Sort by context window and cost efficiency as quality indicators const contextA = a.context_length || 0; const contextB = b.context_length || 0; const costA = a.pricing?.prompt || 0; const costB = b.pricing?.prompt || 0; // Calculate efficiency score (higher context, lower cost = better) const efficiencyA = contextA / Math.max(costA * 1000000, 1); // Cost per 1M tokens const efficiencyB = contextB / Math.max(costB * 1000000, 1); return efficiencyB - efficiencyA; }) .slice(0, 50); // Top 50 models // Convert to ranking format programmingModels.forEach((model, index) => { const rankPosition = index + 1; const usagePercentage = Math.max(1, 50 - index * 1); // Simulate usage percentage const relativeScore = Math.max(0.1, 1.0 - index * 0.015); // Gradually decrease score rankings.push({ modelId: model.id, internalId: 0, // Will be resolved when storing rankPosition, usagePercentage, relativeScore, provider: model.id.split('/')[0] || 'unknown', modelName: model.name || model.id.split('/').slice(1).join('/') || model.id, period: period === 'week' ? 'weekly' : 'monthly', dataQuality: 'api' }); }); console.log(`๐Ÿ“ˆ Parsed ${rankings.length} model rankings from API data for ${period}ly period`); return rankings; } catch (error) { console.warn('โš ๏ธ API data parsing failed:', error); return rankings; } } /** * Parse model rankings from OpenRouter HTML (legacy fallback) */ parseRankingsFromHTML(html, period) { const rankings = []; try { // Look for ranking data patterns in HTML // This regex pattern looks for model data in the rankings page const modelPattern = /<tr[^>]*>.*?<td[^>]*>.*?(\d+).*?<\/td>.*?<td[^>]*>.*?([^<]+\/[^<]+).*?<\/td>.*?<td[^>]*>.*?([\d.]+)%.*?<\/td>/gs; let match; let rankPosition = 1; while ((match = modelPattern.exec(html)) !== null && rankPosition <= 50) { const [, , modelId, usagePercentage] = match; if (modelId && usagePercentage) { const cleanModelId = modelId.trim(); const usage = parseFloat(usagePercentage); // Calculate relative score (top model gets 1.0, others proportional) const relativeScore = rankPosition === 1 ? 1.0 : Math.max(0.1, 1.0 - (rankPosition - 1) * 0.02); rankings.push({ modelId: cleanModelId, internalId: 0, // Will be resolved when storing rankPosition, usagePercentage: usage, relativeScore, provider: cleanModelId.split('/')[0] || 'unknown', modelName: cleanModelId.split('/').slice(1).join('/') || cleanModelId, period: period === 'week' ? 'weekly' : 'monthly', dataQuality: 'scraped' }); rankPosition++; } } // If HTML parsing fails, try alternative patterns if (rankings.length === 0) { return this.parseRankingsAlternative(html, period); } console.log(`๐Ÿ“ˆ Parsed ${rankings.length} model rankings for ${period}ly period`); return rankings; } catch (error) { console.warn('โš ๏ธ HTML parsing failed, using pattern matching fallback'); return this.parseRankingsAlternative(html, period); } } /** * Alternative parsing method using different patterns */ parseRankingsAlternative(html, period) { const rankings = []; // Look for JSON data that might be embedded in the page const jsonPattern = /"models":\s*\[(.*?)\]/s; const jsonMatch = html.match(jsonPattern); if (jsonMatch) { try { const modelsData = JSON.parse(`[${jsonMatch[1]}]`); modelsData.forEach((model, index) => { if (model.id) { rankings.push({ modelId: model.id, internalId: 0, rankPosition: index + 1, usagePercentage: model.usage || 0, relativeScore: Math.max(0.1, 1.0 - index * 0.02), provider: model.id.split('/')[0] || 'unknown', modelName: model.name || model.id, period: period === 'week' ? 'weekly' : 'monthly', dataQuality: 'scraped' }); } }); } catch (parseError) { console.warn('JSON parsing also failed, using hardcoded fallback'); } } return rankings; } /** * Generate intelligent rankings based on API model data and heuristics * (Legacy method - now using parseRankingsFromAPIData as primary) */ async generateIntelligentRankings(period) { console.log(`๐Ÿง  Generating intelligent ${period}ly rankings from API data...`); // This method is now a legacy fallback - redirect to the hardcoded fallback console.log(`โš ๏ธ Using hardcoded fallback rankings for ${period}ly period`); return this.getFallbackRankings(period); } /** * Fallback rankings when scraping fails */ getFallbackRankings(period) { const fallbackModels = [ 'anthropic/claude-3.5-sonnet', 'openai/gpt-4o', 'google/gemini-pro-1.5', 'anthropic/claude-3-opus', 'openai/gpt-4-turbo', 'meta-llama/llama-3.1-70b-instruct', 'anthropic/claude-3-haiku', 'google/gemini-1.5-pro', 'mistral/mistral-large', 'qwen/qwen-2.5-72b-instruct' ]; return fallbackModels.map((modelId, index) => ({ modelId, internalId: 0, rankPosition: index + 1, usagePercentage: Math.max(1, 25 - index * 2), relativeScore: Math.max(0.1, 1.0 - index * 0.05), provider: modelId.split('/')[0], modelName: modelId.split('/')[1] || modelId, period: period === 'week' ? 'weekly' : 'monthly', dataQuality: 'estimated' })); } /** * ๐Ÿงน Clean up pseudo-models from database rankings (self-healing) */ async cleanupPseudoModelRankings() { console.log('๐Ÿงน Starting pseudo-model cleanup (self-healing)...'); const db = await getDatabase(); let cleanedCount = 0; try { // Define pseudo-model patterns to remove const pseudoModelPatterns = [ 'openrouter/auto', 'openrouter/best', '/auto', '/best', '/router', '/routing', 'auto-select', 'best-select' ]; for (const pattern of pseudoModelPatterns) { // Find models matching pseudo patterns const pseudoModels = await db.all(` SELECT om.internal_id, om.openrouter_id, COUNT(mr.id) as ranking_count FROM openrouter_models om LEFT JOIN model_rankings mr ON mr.model_internal_id = om.internal_id WHERE LOWER(om.openrouter_id) LIKE ? GROUP BY om.internal_id, om.openrouter_id `, [`%${pattern}%`]); for (const model of pseudoModels) { if (model.ranking_count > 0) { // Remove rankings for this pseudo-model await db.run(` DELETE FROM model_rankings WHERE model_internal_id = ? `, [model.internal_id]); console.log(`๐Ÿ›ก๏ธ Removed ${model.ranking_count} rankings for pseudo-model: ${model.openrouter_id}`); cleanedCount += model.ranking_count; } // Also remove the pseudo-model itself if it exists await db.run(` DELETE FROM openrouter_models WHERE internal_id = ? `, [model.internal_id]); console.log(`๐Ÿ›ก๏ธ Removed pseudo-model: ${model.openrouter_id}`); } } // Additional cleanup: Remove rankings for models with invalid IDs const invalidRankings = await db.run(` DELETE FROM model_rankings WHERE model_internal_id IN ( SELECT mr.model_internal_id FROM model_rankings mr LEFT JOIN openrouter_models om ON mr.model_internal_id = om.internal_id WHERE om.internal_id IS NULL ) `); if (invalidRankings.changes && invalidRankings.changes > 0) { console.log(`๐Ÿ›ก๏ธ Removed ${invalidRankings.changes} orphaned rankings`); cleanedCount += invalidRankings.changes; } console.log(`โœ… Pseudo-model cleanup completed: ${cleanedCount} items cleaned`); return cleanedCount; } catch (error) { console.warn('โš ๏ธ Pseudo-model cleanup failed:', error); return cleanedCount; } } /** * Store rankings in database with internal ID resolution */ async storeRankings(rankings, period) { // ๐Ÿงน FIRST: Self-healing cleanup of any existing pseudo-models await this.cleanupPseudoModelRankings(); if (rankings.length === 0) { console.log(`โš ๏ธ No rankings to store for ${period} period`); return 0; } console.log(`๐Ÿ’พ Storing ${rankings.length} ${period} rankings in database...`); const db = await getDatabase(); let stored = 0; const periodStart = new Date(); periodStart.setDate(periodStart.getDate() - (period === 'weekly' ? 7 : 30)); const periodEnd = new Date(); for (const ranking of rankings) { try { console.log(`๐Ÿ” Processing ranking for model: ${ranking.modelId}`); // ๐Ÿ›ก๏ธ BULLETPROOF VALIDATION: Reject any pseudo-models immediately const pseudoModelPatterns = [ 'openrouter/auto', 'openrouter/best', '/auto', '/best', '/router', '/routing', 'auto-select', 'best-select' ]; if (pseudoModelPatterns.some(pattern => ranking.modelId.toLowerCase().includes(pattern))) { console.log(`๐Ÿ›ก๏ธ REJECTED pseudo-model during ranking storage: ${ranking.modelId}`); continue; // Skip this ranking entirely } // ๐Ÿ” VALIDATION: Ensure model ID follows valid format (provider/model) if (!ranking.modelId.includes('/') || ranking.modelId.split('/').length < 2) { console.log(`๐Ÿ›ก๏ธ REJECTED invalid model ID format: ${ranking.modelId}`); continue; } // ๐Ÿ” VALIDATION: Ensure provider is known/legitimate const [provider] = ranking.modelId.split('/'); const validProviders = [ 'openai', 'anthropic', 'google', 'meta', 'mistral', 'cohere', 'meta-llama', 'microsoft', 'qwen', 'deepseek', 'perplexity', 'nvidia', 'inflection', 'huggingfaceh4', 'nousresearch', 'cognitivecomputations', 'gryphe', 'lizpreciatior' ]; if (!validProviders.includes(provider.toLowerCase())) { console.log(`๐Ÿ›ก๏ธ REJECTED unknown provider: ${provider} in model ${ranking.modelId}`); continue; } // First try to resolve internal ID let modelResult = await db.get('SELECT internal_id FROM openrouter_models WHERE openrouter_id = ?', [ranking.modelId]); // If model doesn't exist, create it ONLY after validation if (!modelResult?.internal_id) { console.log(`๐Ÿ“ Creating new model entry for: ${ranking.modelId}`); const [provider, ...modelParts] = ranking.modelId.split('/'); const modelName = modelParts.join('/') || ranking.modelId; const providerId = provider || 'unknown'; // Ensure provider exists first await db.run(` INSERT OR IGNORE INTO openrouter_providers (id, name, display_name, last_updated) VALUES (?, ?, ?, ?) `, [ providerId, providerId, providerId.charAt(0).toUpperCase() + providerId.slice(1), new Date().toISOString() ]); // Now create the model await db.run(` INSERT OR IGNORE INTO openrouter_models (openrouter_id, name, provider_id, provider_name, created_at, last_updated) VALUES (?, ?, ?, ?, ?, ?) `, [ ranking.modelId, modelName, providerId, ranking.provider, // Use the actual provider from ranking Date.now(), new Date().toISOString() ]); // Try to get the internal ID again modelResult = await db.get('SELECT internal_id FROM openrouter_models WHERE openrouter_id = ?', [ranking.modelId]); } if (modelResult?.internal_id) { ranking.internalId = modelResult.internal_id; console.log(`โœ… Found/created internal_id ${ranking.internalId} for ${ranking.modelId}`); // Store ranking await db.run(` INSERT OR REPLACE INTO model_rankings (model_internal_id, ranking_source, rank_position, usage_percentage, relative_score, period_start, period_end, collected_at, data_quality) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ ranking.internalId, `openrouter_programming_${period}`, ranking.rankPosition, ranking.usagePercentage, ranking.relativeScore, periodStart.toISOString(), periodEnd.toISOString(), new Date().toISOString(), ranking.dataQuality ]); stored++; console.log(`โœ… Stored ranking #${ranking.rankPosition} for ${ranking.modelId}`); } else { console.error(`โŒ Could not resolve internal_id for ${ranking.modelId}`); } } catch (error) { console.error(`โŒ Failed to store ranking for ${ranking.modelId}:`, error); } } console.log(`๐Ÿ’พ Successfully stored ${stored}/${rankings.length} ${period} rankings in database`); return stored; } /** * Discover models via OpenRouter API */ async discoverModelsViaAPI(filters = {}) { console.log('๐Ÿ” Discovering models via OpenRouter API...'); try { let url = this.modelsApiUrl; // Add category filter if specified if (filters.category) { url += `?category=${filters.category}`; } const response = await fetch(url, { headers: { 'User-Agent': 'Hive.AI/1.7.0 (Model Discovery)', 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`API discovery failed: ${response.status}`); } const data = await response.json(); const models = []; if (data.data && Array.isArray(data.data)) { for (const model of data.data) { if (this.matchesFilters(model, filters)) { models.push({ modelId: model.id, capabilities: model.capabilities || [], inputModalities: model.input_modalities || ['text'], outputModalities: model.output_modalities || ['text'], contextWindow: model.context_length || 4096, pricingInput: model.pricing?.prompt || 0, pricingOutput: model.pricing?.completion || 0, isActive: true }); } } } console.log(`๐ŸŽฏ Discovered ${models.length} models matching filters`); return models; } catch (error) { console.warn('โš ๏ธ API discovery failed, using database fallback'); return await this.getModelsFromDatabase(filters); } } /** * Check if model matches discovery filters */ matchesFilters(model, filters) { // Provider filter if (filters.providers?.length) { const modelProvider = model.id?.split('/')[0]; if (!filters.providers.includes(modelProvider)) { return false; } } // Cost filter if (filters.maxCostPerToken && model.pricing?.prompt > filters.maxCostPerToken) { return false; } // Context window filter if (filters.minContextWindow && model.context_length < filters.minContextWindow) { return false; } // Capabilities filter if (filters.capabilities?.length) { const modelCaps = model.capabilities || []; const hasRequiredCaps = filters.capabilities.every(cap => modelCaps.some((modelCap) => modelCap.toLowerCase().includes(cap.toLowerCase()))); if (!hasRequiredCaps) { return false; } } return true; } /** * Fallback to get models from local database */ async getModelsFromDatabase(filters) { const db = await getDatabase(); const models = []; try { let query = ` SELECT openrouter_id, capabilities, input_modalities, output_modalities, context_window, pricing_input, pricing_output, is_active FROM openrouter_models WHERE is_active = 1 `; const params = []; // Add provider filter if (filters.providers?.length) { query += ` AND provider_name IN (${filters.providers.map(() => '?').join(',')})`; params.push(...filters.providers); } // Add cost filter if (filters.maxCostPerToken) { query += ` AND pricing_input <= ?`; params.push(filters.maxCostPerToken); } // Add context window filter if (filters.minContextWindow) { query += ` AND context_window >= ?`; params.push(filters.minContextWindow); } query += ` ORDER BY last_updated DESC LIMIT 100`; const results = await db.all(query, params); for (const row of results) { models.push({ modelId: row.openrouter_id, capabilities: JSON.parse(row.capabilities || '[]'), inputModalities: JSON.parse(row.input_modalities || '["text"]'), outputModalities: JSON.parse(row.output_modalities || '["text"]'), contextWindow: row.context_window || 4096, pricingInput: row.pricing_input || 0, pricingOutput: row.pricing_output || 0, isActive: Boolean(row.is_active) }); } } catch (error) { console.warn('Database model lookup failed:', error); } return models; } /** * Analyze ranking trends */ async analyzeTrends() { const db = await getDatabase(); const trends = []; try { // Get current and previous weekly rankings const currentRankings = await db.all(` SELECT mr.*, om.openrouter_id FROM model_rankings mr JOIN openrouter_models om ON mr.model_internal_id = om.internal_id WHERE mr.ranking_source = 'openrouter_programming_weekly' AND mr.collected_at >= date('now', '-7 days') ORDER BY mr.rank_position `); const previousRankings = await db.all(` SELECT mr.*, om.openrouter_id FROM model_rankings mr JOIN openrouter_models om ON mr.model_internal_id = om.internal_id WHERE mr.ranking_source = 'openrouter_programming_weekly' AND mr.collected_at >= date('now', '-14 days') AND mr.collected_at < date('now', '-7 days') ORDER BY mr.rank_position `); // Compare rankings to identify trends for (const current of currentRankings) { const previous = previousRankings.find(p => p.model_internal_id === current.model_internal_id); if (previous) { const rankChange = previous.rank_position - current.rank_position; // Positive = improvement const velocityScore = rankChange / previous.rank_position; // Normalize by position let trendDirection; if (rankChange > 2) trendDirection = 'rising'; else if (rankChange < -2) trendDirection = 'falling'; else trendDirection = 'stable'; trends.push({ modelId: current.openrouter_id, currentRank: current.rank_position, previousRank: previous.rank_position, rankChange, trendDirection, velocityScore }); } } if (trends.length > 0) { console.log(`๐Ÿ“ˆ Analyzed ${trends.length} model trends`); } else { console.log(`๐Ÿ“Š No trend data available (run 'hive sync' to collect rankings)`); } } catch (error) { console.warn('Trend analysis failed:', error); } return trends; } /** * Get comprehensive ranking analysis */ async getRankingAnalysis() { const db = await getDatabase(); // Get latest weekly rankings const topModels = await db.all(` SELECT mr.*, om.openrouter_id, om.provider_name FROM model_rankings mr JOIN openrouter_models om ON mr.model_internal_id = om.internal_id WHERE mr.ranking_source = 'openrouter_programming_weekly' AND mr.collected_at >= date('now', '-7 days') ORDER BY mr.rank_position LIMIT 20 `); const trends = await this.analyzeTrends(); // Convert to ModelRanking format const topModelsFormatted = topModels.map(row => ({ modelId: row.openrouter_id, internalId: row.model_internal_id, rankPosition: row.rank_position, usagePercentage: row.usage_percentage, relativeScore: row.relative_score, provider: row.provider_name, modelName: row.openrouter_id.split('/')[1] || row.openrouter_id, period: 'weekly', dataQuality: row.data_quality })); // Group by provider const topModelsByProvider = {}; for (const model of topModelsFormatted) { if (!topModelsByProvider[model.provider]) { topModelsByProvider[model.provider] = []; } topModelsByProvider[model.provider].push(model); } // Identify rising stars (models improving in rankings) const risingStars = topModelsFormatted.filter(model => { const trend = trends.find(t => t.modelId === model.modelId); return trend && trend.trendDirection === 'rising' && trend.rankChange >= 3; }); // Cost efficient models (good ranking vs cost ratio) const costEfficient = topModelsFormatted.filter(model => model.rankPosition <= 15 && model.relativeScore > 0.5); return { topModelsOverall: topModelsFormatted, topModelsByProvider, risingStars, costEfficient, trends, recommendations: { bestForSpeed: topModelsFormatted[0] || topModelsFormatted[0], bestForCost: costEfficient[0] || topModelsFormatted[5], bestOverall: topModelsFormatted[0] || topModelsFormatted[0], emergingModels: risingStars.slice(0, 3) } }; } /** * Update sync metadata */ async updateSyncMetadata() { const db = await getDatabase(); try { // First ensure the row exists (INSERT OR REPLACE pattern) await db.run(` INSERT OR REPLACE INTO sync_metadata (id, sync_type, started_at, completed_at, status, rankings_last_synced, intelligence_version, next_sync_due) VALUES ( COALESCE((SELECT id FROM sync_metadata WHERE sync_type = 'openrouter_models'), 'sync_' || lower(hex(randomblob(16)))), 'openrouter_models', datetime('now'), datetime('now'), 'completed', ?, '1.7.0', datetime('now', '+1 day') ) `, [new Date().toISOString()]); } catch (error) { console.warn('Failed to update sync metadata:', error); } } /** * Check if rankings need refresh (older than 24 hours or no rankings exist) */ async needsRankingRefresh() { const db = await getDatabase(); try { // First check if we have any rankings in the database at all const rankingCount = await db.get(` SELECT COUNT(*) as count FROM model_rankings WHERE ranking_source LIKE 'openrouter_programming_%' `); if (!rankingCount || rankingCount.count === 0) { console.log('๐Ÿ” No rankings found in database, forcing refresh'); return true; // No rankings exist, definitely need refresh } // Check if the column exists const columns = await db.all("PRAGMA table_info(sync_metadata)"); const hasRankingsColumn = columns.some(col => col.name === 'rankings_last_synced'); if (!hasRankingsColumn) { // Column doesn't exist yet, needs refresh return true; } const result = await db.get(` SELECT rankings_last_synced FROM sync_metadata WHERE sync_type = 'openrouter_models' `); if (!result?.rankings_last_synced) { return true; // No previous sync } const lastSync = new Date(result.rankings_last_synced); const now = new Date(); const hoursSinceSync = (now.getTime() - lastSync.getTime()) / (1000 * 60 * 60); return hoursSinceSync >= 24; // Refresh every 24 hours } catch (error) { console.warn('Failed to check sync status:', error); return true; // Default to refresh on error } } } // ===== CONVENIENCE FUNCTIONS ===== /** * Get top programming models from latest rankings */ export async function getTopProgrammingModels(limit = 10) { const rankings = new OpenRouterRankings(); const analysis = await rankings.getRankingAnalysis(); return analysis.topModelsOverall.slice(0, limit); } /** * Find best models by criteria */ export async function findBestModels(criteria) { const rankings = new OpenRouterRankings(); const analysis = await rankings.getRankingAnalysis(); let models = analysis.topModelsOverall; if (criteria.provider) { models = analysis.topModelsByProvider[criteria.provider] || []; } if (criteria.forCost) { models = analysis.costEfficient; } if (criteria.forSpeed) { // Top 5 models are typically fastest models = models.slice(0, 5); } return models; } /** * Sync rankings if needed (for integration with existing sync system) */ export async function syncRankingsIfNeeded() { const rankings = new OpenRouterRankings(); if (await rankings.needsRankingRefresh()) { console.log('๐Ÿ”„ Rankings data is stale, refreshing...'); await rankings.collectRankingIntelligence(); return true; } return false; // No sync needed } export default OpenRouterRankings;