import type { EmbeddedChunk, ScoredChunk } from "../types"; /** * Cosine similarity between two vectors. */ export function cosineSimilarity(a: Float32Array, b: Float32Array): number { let dot = 0; let normA = 0; let normB = 0; for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; normA += a[i] * a[i]; normB += b[i] * b[i]; } return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } /** * Search embedded chunks by cosine similarity to query embedding. * Returns the top-K results sorted by descending similarity score. */ export function vectorSearch( queryEmbedding: Float32Array, chunks: EmbeddedChunk[], topK: number = 20, ): ScoredChunk[] { const scored = chunks.map((chunk) => ({ chunk, score: cosineSimilarity(queryEmbedding, chunk.embedding), source: "vector" as const, })); scored.sort((a, b) => b.score - a.score); return scored.slice(0, topK); }