Project Files
packages / adapter-lmstudio / src / fusion.ts
import type { RetrievalResultEntry } from "@lmstudio/sdk";
import type { RetrievalFusionMethod } from "./types/retrieval";
function buildEntryKey(entry: RetrievalResultEntry) {
const fileName = entry.source.name ?? entry.source.identifier;
const content = entry.content.trim().toLowerCase();
return `${fileName}::${content}`;
}
export function fuseRetrievalEntries(
retrievalRuns: Array<Array<RetrievalResultEntry>>,
method: RetrievalFusionMethod,
limit: number
): Array<RetrievalResultEntry> {
const fusedEntries = new Map<
string,
{
entry: RetrievalResultEntry;
fusedScore: number;
bestScore: number;
}
>();
retrievalRuns.forEach((entries) => {
entries.forEach((entry, index) => {
const key = buildEntryKey(entry);
const existing = fusedEntries.get(key);
const reciprocalRankScore = 1 / (index + 1);
if (!existing) {
fusedEntries.set(key, {
entry,
fusedScore: reciprocalRankScore,
bestScore: entry.score,
});
return;
}
existing.bestScore = Math.max(existing.bestScore, entry.score);
existing.fusedScore += reciprocalRankScore;
if (entry.score > existing.entry.score) {
existing.entry = entry;
}
});
});
const scoredEntries = [...fusedEntries.values()].map((value) => ({
entry: {
...value.entry,
score: method === "max-score" ? value.bestScore : value.fusedScore,
},
sortScore: method === "max-score" ? value.bestScore : value.fusedScore,
}));
scoredEntries.sort((left, right) => right.sortScore - left.sortScore);
return scoredEntries.slice(0, limit).map((value) => value.entry);
}