scai
Version:
> AI-powered CLI tool for commit messages **and** pull request reviews — using local models.
72 lines (71 loc) • 2.72 kB
JavaScript
import { stringSimilarity } from 'string-similarity-js';
export function scoreFiles(query, embedding, candidates) {
const terms = query.toLowerCase().split(/\s+/);
const bm25Min = Math.min(...candidates.map(r => r.bm25Score));
const bm25Max = Math.max(...candidates.map(r => r.bm25Score));
return candidates.map(result => {
let finalScore = 0;
let sim = 0;
const path = result.path.toLowerCase();
const filename = result.filename.toLowerCase();
const summary = (result.summary || '').toLowerCase();
// 🎯 Normalize BM25
const normalizedBm25 = 1 - ((result.bm25Score - bm25Min) / (bm25Max - bm25Min + 1e-5));
// 🧠 Embedding similarity
if (result.embedding) {
try {
const vector = JSON.parse(result.embedding);
sim = cosineSimilarity(embedding, vector);
}
catch {
sim = 0;
}
}
// 🧩 Match ratio
let matchRatio = 0;
let matchedTerms = 0;
for (const term of terms) {
if (path.includes(term) || summary.includes(term))
matchedTerms++;
}
matchRatio = matchedTerms / terms.length;
const termBoost = matchRatio >= 1 ? 1.0 : matchRatio >= 0.5 ? 0.5 : 0;
// 🪜 Path heuristics
const isHtml = path.endsWith('.html');
const isSrc = path.includes('/src/') || path.includes('/controls/');
const isDoc = path.includes('/docs/') || path.includes('/mvndist/');
const isExactMatch = filename === `${terms[0]}.js`;
let weight = 1;
if (isHtml)
weight *= 0.85;
if (isDoc)
weight *= 0.8;
if (isSrc)
weight *= 1.2;
if (isExactMatch)
weight *= 1.5;
// 🧪 Fuzzy score
const fuzzyScore = stringSimilarity(query, `${path} ${summary}`);
// 🧮 Final composite score
finalScore =
(0.4 * normalizedBm25) +
(0.4 * sim) +
(0.2 * termBoost) +
(fuzzyScore * 0.5); // scale fuzzy match moderately
finalScore *= weight;
return {
id: result.id,
path: result.path,
summary: result.summary,
score: finalScore,
sim,
bm25: result.bm25Score,
};
}).sort((a, b) => b.score - a.score);
}
function cosineSimilarity(a, b) {
const dot = a.reduce((sum, ai, i) => sum + ai * b[i], 0);
const magA = Math.sqrt(a.reduce((sum, ai) => sum + ai * ai, 0));
const magB = Math.sqrt(b.reduce((sum, bi) => sum + bi * bi, 0));
return dot / (magA * magB);
}