const DEFAULT_RERANK_TOP_N = 50;
const DEFAULT_MODEL = "Xenova/ms-marco-MiniLM-L-6-v2";
const pipelineCache = new Map();
const failedModels = new Set();
let loadWarned = false;
async function loadPipeline(model) {
    const modelName = model ?? DEFAULT_MODEL;
    const cached = pipelineCache.get(modelName);
    if (cached)
        return cached;
    if (failedModels.has(modelName))
        return null;
    try {
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const transformers = await import("@huggingface/transformers");
        const pipelineFn = transformers.pipeline ?? transformers.default?.pipeline;
        if (!pipelineFn) {
            failedModels.add(modelName);
            return null;
        }
        const classifier = await pipelineFn("text-classification", modelName, {
            quantized: true,
        });
        const rerankerFn = async (pairs) => {
            // Batch: send all inputs at once for better throughput
            const inputs = pairs.map(([q, t]) => `${q} [SEP] ${t}`);
            const outputs = await classifier(inputs, { topk: 1 });
            // Normalize: pipeline returns single object for 1 input, array for N
            const results = [];
            for (let i = 0; i < pairs.length; i++) {
                const out = Array.isArray(outputs[i]) ? outputs[i][0] : outputs[i] ?? outputs;
                const score = out?.score ?? 0;
                results.push({ score: typeof score === "number" ? score : 0 });
            }
            return results;
        };
        pipelineCache.set(modelName, rerankerFn);
        return rerankerFn;
    }
    catch {
        failedModels.add(modelName);
        return null;
    }
}
/**
 * Rerank SearchResult[] using a cross-encoder model.
 * Returns results reordered by cross-encoder score.
 * Falls back to original order if the model is unavailable.
 */
export async function rerankResults(query, results, topN, model) {
    if (results.length <= 1)
        return results;
    const limit = Math.min(topN ?? DEFAULT_RERANK_TOP_N, results.length);
    const candidates = results.slice(0, limit);
    const remainder = results.slice(limit);
    const reranker = await loadPipeline(model);
    if (!reranker) {
        if (!loadWarned) {
            loadWarned = true;
            console.error("[codesift] Cross-encoder reranking unavailable. Install @huggingface/transformers for improved search quality.");
        }
        return results;
    }
    const pairs = candidates.map((r) => {
        const text = buildCandidateText(r);
        return [query, text];
    });
    const scores = await reranker(pairs);
    const scored = candidates.map((r, i) => ({
        result: r,
        ceScore: scores[i]?.score ?? 0,
    }));
    scored.sort((a, b) => b.ceScore - a.ceScore);
    return [...scored.map((s) => s.result), ...remainder];
}
/**
 * Rerank chunk IDs using a cross-encoder model.
 * Returns reordered chunk IDs.
 */
export async function rerankChunkIds(query, chunkIds, chunks, topN, model) {
    if (chunkIds.length <= 1)
        return chunkIds;
    const limit = Math.min(topN ?? DEFAULT_RERANK_TOP_N, chunkIds.length);
    const candidates = chunkIds.slice(0, limit);
    const remainder = chunkIds.slice(limit);
    const reranker = await loadPipeline(model);
    if (!reranker) {
        if (!loadWarned) {
            loadWarned = true;
            console.error("[codesift] Cross-encoder reranking unavailable. Install @huggingface/transformers for improved search quality.");
        }
        return chunkIds;
    }
    const pairs = candidates.map((id) => {
        const chunk = chunks.get(id);
        const text = chunk?.text ?? id;
        return [query, text];
    });
    const scores = await reranker(pairs);
    const scored = candidates.map((id, i) => ({
        id,
        ceScore: scores[i]?.score ?? 0,
    }));
    scored.sort((a, b) => b.ceScore - a.ceScore);
    return [...scored.map((s) => s.id), ...remainder];
}
function buildCandidateText(r) {
    const parts = [];
    if (r.symbol.kind)
        parts.push(r.symbol.kind);
    parts.push(r.symbol.name);
    if (r.symbol.signature)
        parts.push(r.symbol.signature);
    if (r.symbol.source)
        parts.push(r.symbol.source.slice(0, 500));
    else if (r.symbol.docstring)
        parts.push(r.symbol.docstring.slice(0, 200));
    return parts.join(" ");
}
/** Reset caches for testing. */
export function _resetReranker() {
    pipelineCache.clear();
    failedModels.clear();
    loadWarned = false;
}
//# sourceMappingURL=reranker.js.map