lexguard-backend / src /services /ragCaseLawService.js
github-actions[bot]
Deploy to Hugging Face
b921752
Raw
History Blame Contribute Delete
2.78 kB
const CaseLaw = require('../models/CaseLaw');
const { generateEmbedding } = require('./embeddingService');
// Optimized in-memory Cosine Similarity
function cosineSimilarity(vecA, vecB) {
let dotProduct = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < vecA.length; i++) {
dotProduct += vecA[i] * vecB[i];
normA += vecA[i] * vecA[i];
normB += vecB[i] * vecB[i];
}
if (normA === 0 || normB === 0) return 0;
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
async function retrieveCaseLawPrecedents(queryText, topK = 3, threshold = 0.5) {
try {
// 1. Embed the query text
let queryVector;
if (Array.isArray(queryText)) {
queryVector = queryText;
} else {
queryVector = await generateEmbedding(queryText, 'search_query');
}
if (!queryVector) {
console.warn(`⚠️ [RAG] Failed to embed query text for case law retrieval.`);
return [];
}
// 2. Execute Native Atlas Vector Search
const statutoryMatches = await CaseLaw.aggregate([
{
$vectorSearch: {
index: "lexguard_caselaw_vector_index", // The Atlas index the user is creating
path: "embedding",
queryVector: queryVector,
numCandidates: 100,
limit: 10
}
},
{
$project: {
case_title: 1,
citation: 1,
legal_domain: 1,
summary: 1,
similarityScore: { $meta: "vectorSearchScore" }
}
}
]);
// 3. Filter by threshold and take top K
const relevantMatches = statutoryMatches
.filter(match => match.similarityScore >= threshold)
.slice(0, topK);
if (relevantMatches.length === 0) return [];
// 4. Format results into structured prompt blocks
return relevantMatches.map(doc => (
`\n\n=== SUPREME COURT PRECEDENT ===\nCase: ${doc.case_title} [${doc.citation}]\nHolding: ${doc.summary}\n=================================\n`
));
} catch (error) {
console.error(`🚨 [Case Law RAG Failure]:`, error.message);
return []; // Fail gracefully, return no precedents
}
}
/**
* Convenience function to seed a new case law entry.
* Generates the embedding automatically before saving.
*/
async function seedCaseLaw(case_title, citation, legal_domain, summary) {
const embedding = await generateEmbedding(summary, 'search_document');
if (!embedding) throw new Error("Failed to generate embedding for case law.");
const newCase = await CaseLaw.create({
case_title,
citation,
legal_domain,
summary,
embedding
});
console.log(`📚 Seeded Case Law: ${case_title}`);
return newCase;
}
module.exports = {
retrieveCaseLawPrecedents,
seedCaseLaw
};