Spaces:
Sleeping
Sleeping
| 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 | |
| }; | |