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

495 lines 19.6 kB
/** * Template Maintenance Manager * * Ensures Expert Profile Templates remain current as OpenRouter models change. * Provides automatic validation, updating, and migration of templates and user profiles. */ import { EXPERT_TEMPLATES } from './expert-profile-templates.js'; export class TemplateMaintenanceManager { config; constructor(config) { this.config = { enableAutoUpdate: true, enableProfileMigration: true, checkIntervalHours: 24, confidenceThreshold: 0.8, fallbackStrategies: ['similar-provider', 'similar-tier', 'proven-stable'], ...config }; } /** * Validate all Expert Templates against current OpenRouter models */ async validateAllTemplates() { const reports = []; for (const template of EXPERT_TEMPLATES) { const report = await this.validateTemplate(template); reports.push(report); } return reports; } /** * Validate a single template */ async validateTemplate(template) { const issues = []; const suggestions = []; // Validate each stage model for (const [stage, modelConfig] of Object.entries(template.models)) { const validation = await this.validateModel(modelConfig.model); if (!validation.isValid) { issues.push(`${stage}: ${modelConfig.model} - ${validation.reason}`); if (validation.suggestion) { suggestions.push({ stage, currentModel: modelConfig.model, suggestedModel: validation.suggestion, reason: validation.reason || 'Model unavailable', confidence: 0.9 }); } } } return { templateId: template.id, templateName: template.name, isHealthy: issues.length === 0, issues, suggestions, lastChecked: new Date().toISOString() }; } /** * Validate if a model exists and is active in OpenRouter */ async validateModel(modelId) { try { // 🛡️ FIRST: Check for pseudo-models (immediate rejection) const pseudoModelPatterns = [ 'openrouter/auto', 'openrouter/best', '/auto', '/best', '/router', '/routing', 'auto-select', 'best-select', 'auto/', 'best/', 'router/', 'routing/' ]; const isPseudoModel = pseudoModelPatterns.some(pattern => modelId.toLowerCase().includes(pattern)); if (isPseudoModel) { console.log(`🛡️ Rejected pseudo-model in template validation: ${modelId}`); return { isValid: false, exists: false, isActive: false, reason: 'Pseudo-model detected - these cannot be used in expert profiles', suggestion: await this.suggestRealModelReplacement(modelId) }; } const { getDatabase } = await import('../../storage/unified-database.js'); const database = await getDatabase(); const model = await database.get('SELECT * FROM openrouter_models WHERE openrouter_id = ? AND is_active = 1', [modelId]); if (!model) { // Model doesn't exist or is inactive - find a suggestion const suggestion = await this.findModelReplacement(modelId); return { isValid: false, exists: false, isActive: false, suggestion, reason: 'Model not found or inactive in OpenRouter' }; } return { isValid: true, exists: true, isActive: true }; } catch (error) { return { isValid: false, exists: false, isActive: false, reason: `Validation error: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } /** * Find a suitable replacement for an unavailable model */ async findModelReplacement(unavailableModelId) { try { const { getDatabase } = await import('../../storage/unified-database.js'); const database = await getDatabase(); // Extract provider and model name const [provider, modelName] = unavailableModelId.split('/'); const baseModelName = modelName?.toLowerCase() || ''; // Strategy 1: Same provider, similar model if (provider && modelName) { const similarModels = await database.all(` SELECT openrouter_id, name, provider_name FROM openrouter_models WHERE provider_name = ? AND is_active = 1 AND (LOWER(name) LIKE ? OR LOWER(openrouter_id) LIKE ?) ORDER BY CASE WHEN LOWER(openrouter_id) LIKE ? THEN 1 WHEN LOWER(name) LIKE ? THEN 2 ELSE 3 END, name LIMIT 3 `, [ provider, `%${baseModelName}%`, `%${baseModelName}%`, `%${baseModelName}%`, `%${baseModelName}%` ]); if (similarModels.length > 0) { return similarModels[0].openrouter_id; } } // Strategy 2: Same provider, latest/best model if (provider) { const providerModels = await database.all(` SELECT openrouter_id, name FROM openrouter_models WHERE provider_name = ? AND is_active = 1 ORDER BY CASE WHEN LOWER(name) LIKE '%sonnet%' THEN 1 WHEN LOWER(name) LIKE '%4o%' THEN 2 WHEN LOWER(name) LIKE '%pro%' THEN 3 WHEN LOWER(name) LIKE '%turbo%' THEN 4 ELSE 5 END, name DESC LIMIT 1 `, [provider]); if (providerModels.length > 0) { return providerModels[0].openrouter_id; } } // Strategy 3: Cross-provider replacement based on model tier const tierReplacements = await this.findTierReplacement(unavailableModelId); if (tierReplacements) { return tierReplacements; } return undefined; } catch (error) { console.error('Error finding model replacement:', error); return undefined; } } /** * Find replacement model based on tier (GPT-4 class, Claude Sonnet class, etc.) */ async findTierReplacement(unavailableModelId) { const { getDatabase } = await import('../../storage/unified-database.js'); const database = await getDatabase(); const modelName = unavailableModelId.toLowerCase(); // Define model tiers and their current representatives const tierMappings = [ // Premium tier { patterns: ['opus', 'gpt-4o', 'o1', 'gemini-pro', 'llama-3.1-405b'], replacements: [ 'anthropic/claude-3-opus', 'openai/gpt-4o', 'google/gemini-pro-1.5', 'openai/o1-mini' ] }, // High-quality tier { patterns: ['sonnet', 'gpt-4', 'gemini-1.5', 'llama-3.1-70b'], replacements: [ 'anthropic/claude-3.5-sonnet', 'openai/gpt-4o', 'google/gemini-pro-1.5', 'meta-llama/llama-3.1-70b-instruct' ] }, // Balanced tier { patterns: ['haiku', 'gpt-4o-mini', 'gemini-flash', 'llama-3.1-8b'], replacements: [ 'anthropic/claude-3-haiku', 'openai/gpt-4o-mini', 'google/gemini-flash-1.5' ] }, // Budget tier { patterns: ['gpt-3.5', 'gemini-flash', 'llama-3-8b'], replacements: [ 'openai/gpt-3.5-turbo', 'google/gemini-flash-1.5' ] } ]; // Find matching tier for (const tier of tierMappings) { if (tier.patterns.some(pattern => modelName.includes(pattern))) { // Try each replacement in order for (const replacement of tier.replacements) { const exists = await database.get('SELECT openrouter_id FROM openrouter_models WHERE openrouter_id = ? AND is_active = 1', [replacement]); if (exists) { return replacement; } } } } return undefined; } /** * Auto-update templates with better model suggestions */ async updateTemplatesWithSuggestions(reports) { if (!this.config.enableAutoUpdate) { console.log('📋 Template auto-update disabled'); return; } for (const report of reports) { if (!report.isHealthy && report.suggestions.length > 0) { console.log(`🔄 Updating template: ${report.templateName}`); // Apply high-confidence suggestions const highConfidenceSuggestions = report.suggestions.filter(s => s.confidence >= this.config.confidenceThreshold); if (highConfidenceSuggestions.length > 0) { await this.applyTemplateSuggestions(report.templateId, highConfidenceSuggestions); } } } } /** * Apply model suggestions to a template */ async applyTemplateSuggestions(templateId, suggestions) { // This would update the template definition // For now, we'll log the suggested changes console.log(`📝 Template ${templateId} suggested updates:`); suggestions.forEach(suggestion => { console.log(` ${suggestion.stage}: ${suggestion.currentModel}${suggestion.suggestedModel}`); console.log(` Reason: ${suggestion.reason} (confidence: ${(suggestion.confidence * 100).toFixed(0)}%)`); }); // TODO: Implement actual template updating mechanism // This could involve: // 1. Creating new template versions // 2. Updating the EXPERT_TEMPLATES array // 3. Versioning templates for rollback capability } /** * Migrate existing user profiles when their models become unavailable */ async migrateUserProfiles() { if (!this.config.enableProfileMigration) { console.log('👥 Profile migration disabled'); return; } try { const { getAllPipelineProfiles, getDatabase } = await import('../../storage/unified-database.js'); const profiles = await getAllPipelineProfiles(); const database = await getDatabase(); for (const profile of profiles) { const modelsToCheck = [ profile.generator_model, profile.refiner_model, profile.validator_model, profile.curator_model ]; let needsMigration = false; const migrations = []; for (let i = 0; i < modelsToCheck.length; i++) { const modelId = modelsToCheck[i]; if (!modelId) continue; const validation = await this.validateModel(modelId); if (!validation.isValid && validation.suggestion) { needsMigration = true; const stages = ['generator', 'refiner', 'validator', 'curator']; migrations.push({ stage: stages[i], from: modelId, to: validation.suggestion }); } } if (needsMigration) { console.log(`🔄 Migrating profile: ${profile.name}`); migrations.forEach(migration => { console.log(` ${migration.stage}: ${migration.from}${migration.to}`); }); // TODO: Apply the migrations to the actual profile // This would involve updating the pipeline_profiles table } } } catch (error) { console.error('Error migrating user profiles:', error); } } /** * Run complete maintenance check */ async runMaintenanceCheck() { console.log('🔍 Running template maintenance check...'); try { // 1. Validate all templates const reports = await this.validateAllTemplates(); // 2. Report issues const unhealthyTemplates = reports.filter(r => !r.isHealthy); if (unhealthyTemplates.length > 0) { console.log(`⚠️ Found ${unhealthyTemplates.length} templates with issues:`); unhealthyTemplates.forEach(template => { console.log(` ${template.templateName}: ${template.issues.join(', ')}`); }); } else { console.log('✅ All templates are healthy'); } // 3. Auto-update templates if enabled await this.updateTemplatesWithSuggestions(reports); // 4. Migrate user profiles if needed await this.migrateUserProfiles(); // 5. Store maintenance results await this.storeMaintenanceResults(reports); console.log('✅ Template maintenance check completed'); } catch (error) { console.error('❌ Template maintenance check failed:', error); } } /** * Store maintenance results for tracking */ async storeMaintenanceResults(reports) { try { const { setConfig } = await import('../../storage/unified-database.js'); const maintenanceData = { timestamp: new Date().toISOString(), templateCount: reports.length, healthyCount: reports.filter(r => r.isHealthy).length, reports: reports.map(r => ({ templateId: r.templateId, templateName: r.templateName, isHealthy: r.isHealthy, issueCount: r.issues.length, suggestionCount: r.suggestions.length })) }; await setConfig('last_template_maintenance', JSON.stringify(maintenanceData)); } catch (error) { console.error('Error storing maintenance results:', error); } } /** * Check if maintenance is due */ async isMaintenanceDue() { try { const { getConfig } = await import('../../storage/unified-database.js'); const lastMaintenanceStr = await getConfig('last_template_maintenance'); if (!lastMaintenanceStr) { return true; // Never run before } const lastMaintenance = JSON.parse(lastMaintenanceStr); const lastCheck = new Date(lastMaintenance.timestamp); const now = new Date(); const hoursSinceLastCheck = (now.getTime() - lastCheck.getTime()) / (1000 * 60 * 60); return hoursSinceLastCheck >= this.config.checkIntervalHours; } catch (error) { console.error('Error checking maintenance schedule:', error); return true; // Default to running maintenance } } /** * 🛡️ Suggest a real model replacement for pseudo-models */ async suggestRealModelReplacement(pseudoModelId) { try { const { getDatabase } = await import('../../storage/unified-database.js'); const database = await getDatabase(); // Get top-ranked real models from rankings const topModels = await database.all(` SELECT om.openrouter_id, om.name, mr.rank_position, mr.relative_score FROM openrouter_models om JOIN model_rankings mr ON mr.model_internal_id = om.internal_id WHERE om.is_active = 1 AND mr.ranking_source = 'openrouter_programming_weekly' AND LOWER(om.openrouter_id) NOT LIKE '%/auto' AND LOWER(om.openrouter_id) NOT LIKE '%/best' AND LOWER(om.openrouter_id) NOT LIKE '%/router%' AND LOWER(om.openrouter_id) NOT LIKE '%auto%' ORDER BY mr.rank_position ASC LIMIT 5 `); if (topModels.length > 0) { console.log(`💡 Suggesting ${topModels[0].openrouter_id} as replacement for pseudo-model ${pseudoModelId}`); return topModels[0].openrouter_id; } // Fallback: Get any high-quality model const fallbackModels = await database.all(` SELECT openrouter_id FROM openrouter_models WHERE is_active = 1 AND LOWER(openrouter_id) NOT LIKE '%/auto' AND LOWER(openrouter_id) NOT LIKE '%/best' AND ( LOWER(openrouter_id) LIKE '%claude%' OR LOWER(openrouter_id) LIKE '%gpt-4%' OR LOWER(openrouter_id) LIKE '%gemini%' ) ORDER BY CASE WHEN LOWER(openrouter_id) LIKE '%sonnet%' THEN 1 WHEN LOWER(openrouter_id) LIKE '%4o%' THEN 2 WHEN LOWER(openrouter_id) LIKE '%pro%' THEN 3 ELSE 4 END LIMIT 1 `); if (fallbackModels.length > 0) { console.log(`💡 Fallback suggestion: ${fallbackModels[0].openrouter_id} for pseudo-model ${pseudoModelId}`); return fallbackModels[0].openrouter_id; } return undefined; } catch (error) { console.warn('⚠️ Failed to suggest replacement for pseudo-model:', error); return undefined; } } } /** * Run template maintenance as part of setup/sync process */ export async function runTemplateMaintenanceIfDue() { const manager = new TemplateMaintenanceManager(); if (await manager.isMaintenanceDue()) { console.log('🔄 Template maintenance is due, running check...'); await manager.runMaintenanceCheck(); } else { console.log('✅ Template maintenance is up to date'); } } /** * Force run template maintenance (for manual trigger) */ export async function forceRunTemplateMaintenance() { const manager = new TemplateMaintenanceManager(); await manager.runMaintenanceCheck(); } //# sourceMappingURL=template-maintenance.js.map