@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
JavaScript
/**
* 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