/**
 * Model recommendation engine.
 *
 * Suggests the best model for a given intent based on:
 * - Intent match (code→coding models, analysis→reasoning models)
 * - Real performance data (TPS, success rate, latency)
 * - Cost efficiency
 * - Model size/capability tier
 */
import { listConfiguredModels } from './config.js';
import { getModelPerfSummary } from './perf-tracker.js';
import { getCostProfile, extractParamCount } from './cost-estimator.js';
import { resolveSustainedWorkCapability } from './model-capabilities.js';
// Intent → preferred model keywords (higher specificity = higher score)
const INTENT_KEYWORDS = {
    code: ['coder', 'code', 'instruct', 'dev'],
    analysis: ['reasoning', 'think', 'analyst', 'math'],
    search: ['search', 'retrieval', 'rag'],
    chat: ['chat', 'assistant', 'dialog'],
    general: [],
};
// Minimum success rate to consider a model healthy
const MIN_SUCCESS_RATE = 0.5;
/**
 * Score a model for a given intent.
 */
function scoreModel(model, intent, perf, costProfile) {
    let score = 50; // Base score
    const reasons = [];
    const backendModel = model.backendModel.toLowerCase();
    const sustainedWork = resolveSustainedWorkCapability(model);
    // Intent keyword match (+20 per keyword)
    const keywords = INTENT_KEYWORDS[intent];
    for (const kw of keywords) {
        if (backendModel.includes(kw)) {
            score += 20;
            reasons.push(`name matches "${kw}"`);
        }
    }
    // Performance bonus (if we have real data)
    if (perf && perf.requestCount >= 2) {
        // TPS bonus: higher = better (max +15)
        if (perf.avgOutputTps > 50) {
            const tpsBonus = Math.min(15, Math.round(perf.avgOutputTps / 10));
            score += tpsBonus;
            reasons.push(`${perf.avgOutputTps} tok/s (+${tpsBonus})`);
        }
        // Success rate penalty
        if (perf.successRate < MIN_SUCCESS_RATE) {
            score -= 30;
            reasons.push(`low success ${(perf.successRate * 100).toFixed(0)}%`);
        }
        else if (perf.successRate >= 0.95) {
            score += 10;
            reasons.push('reliable (≥95%)');
        }
        // Latency: fast response bonus (max +10)
        if (perf.avgDurationMs < 1000) {
            score += 10;
            reasons.push('fast (<1s avg)');
        }
        else if (perf.avgDurationMs < 3000) {
            score += 5;
            reasons.push('moderate latency');
        }
    }
    else {
        reasons.push('no perf data');
    }
    // Cost efficiency: cheaper = small bonus for general/chat, penalty for code/analysis
    const paramCount = extractParamCount(model.backendModel);
    const isLargeModel = paramCount !== null && paramCount >= 40;
    const isSmallModel = paramCount !== null && paramCount < 15;
    if (intent === 'code' || intent === 'analysis') {
        if (!sustainedWork.longTaskRecommendationEligible) {
            score -= 80;
            reasons.push('not signed off for sustained work');
        }
        if (isLargeModel) {
            score += 10;
            reasons.push('large model (preferred for complexity)');
        }
    }
    else {
        if (isSmallModel) {
            score += 10;
            reasons.push('cost-efficient');
        }
    }
    return { modelId: model.id, score, reasons, perf: perf ?? undefined, cost: costProfile };
}
/**
 * Get model recommendation for an intent.
 */
export function recommendModel(config, intent) {
    const models = listConfiguredModels(config);
    if (models.length === 0) {
        return {
            intent,
            recommended: { modelId: 'none', score: 0, reasons: ['no models configured'] },
            alternatives: [],
        };
    }
    const scored = models.map(model => {
        const perf = getModelPerfSummary(model.id) ?? getModelPerfSummary(model.backendModel);
        const costProfile = getCostProfile(model.backendModel);
        return scoreModel(model, intent, perf, costProfile);
    });
    scored.sort((a, b) => b.score - a.score);
    return {
        intent,
        recommended: scored[0],
        alternatives: scored.slice(1, 4), // Top 3 alternatives
    };
}
/**
 * Format recommendation for display.
 */
export function formatRecommendation(rec) {
    const lines = [
        `Intent: ${rec.intent}`,
        `Recommended: ${rec.recommended.modelId} (score: ${rec.recommended.score})`,
        `  Reasons: ${rec.recommended.reasons.join(', ')}`,
    ];
    if (rec.alternatives.length > 0) {
        lines.push('Alternatives:');
        for (const alt of rec.alternatives) {
            lines.push(`  ${alt.modelId} (score: ${alt.score}) — ${alt.reasons.join(', ')}`);
        }
    }
    return lines.join('\n');
}
//# sourceMappingURL=model-recommender.js.map