/**
 * Local Cross-Encoder Search Re-ranker
 *
 * Re-ranks search results using a local cross-encoder model (ms-marco-MiniLM-L-12-v2)
 * via the Python reranker worker. 100% local, no cloud dependency.
 *
 * If the local model is not available (sentence-transformers not installed, model not
 * downloaded, Python error), returns results as-is with a console.error() warning.
 *
 * CRITICAL: NEVER use console.log() - stdout is reserved for JSON-RPC protocol.
 *
 * @module services/search/reranker
 */
import { localRerank } from './local-reranker.js';
/**
 * Re-rank search results using a local cross-encoder model.
 *
 * Takes the top results (max 20), sends them to the local Python cross-encoder
 * for relevance scoring, and returns sorted results.
 *
 * If the local model is unavailable, returns the original results in their existing
 * order with reasoning indicating no reranking was performed.
 *
 * @param query - The original search query
 * @param results - Search results with original_text field
 * @param maxResults - Maximum results to return after re-ranking (default: 10)
 * @returns Re-ranked results with scores and reasoning
 */
export async function rerankResults(query, results, maxResults = 10) {
    if (results.length === 0)
        return [];
    // Take top results to re-rank (max 20 to stay within token limits)
    const toRerank = results.slice(0, Math.min(results.length, 20));
    // Build passages for the local reranker
    const passages = toRerank.map((r, i) => {
        let originalScore = 0;
        if (typeof r.bm25_score === 'number') {
            originalScore = r.bm25_score;
        }
        else if (typeof r.score === 'number') {
            originalScore = r.score;
        }
        return { index: i, text: String(r.original_text), original_score: originalScore };
    });
    const localResults = await localRerank(query, passages);
    // Check if the reranker returned an error object
    if ('error' in localResults && localResults.error === true) {
        const errResult = localResults;
        console.error(`[reranker] Local cross-encoder failed: [${errResult.error_type}] ${errResult.message}`);
        return toRerank.slice(0, maxResults).map((_, i) => ({
            original_index: i,
            relevance_score: 0,
            reasoning: 'reranker failed, original order preserved',
            reranker_failed: true,
            reranker_error_message: `[${errResult.error_type}] ${errResult.message}`,
        }));
    }
    // At this point, localResults is guaranteed to be LocalRerankResult[] (error case returned above)
    const rerankResults_arr = localResults;
    // Filter to valid indices only
    const validResults = rerankResults_arr.filter((r) => r.index >= 0 && r.index < toRerank.length);
    if (validResults.length === 0 && toRerank.length > 0) {
        const msg = `Cross-encoder returned 0 valid results from ${toRerank.length} inputs`;
        console.error(`[reranker] ${msg}. Returning results without reranking.`);
        return toRerank.slice(0, maxResults).map((_, i) => ({
            original_index: i,
            relevance_score: 0,
            reasoning: 'cross-encoder returned no valid results, original order preserved',
            reranker_failed: true,
            reranker_error_message: msg,
        }));
    }
    // Sort by relevance score descending, take maxResults
    return validResults
        .sort((a, b) => b.relevance_score - a.relevance_score)
        .slice(0, maxResults)
        .map((r) => ({
        original_index: r.index,
        relevance_score: r.relevance_score,
        reasoning: `cross-encoder score: ${r.relevance_score.toFixed(4)}`,
    }));
}
//# sourceMappingURL=reranker.js.map