/** * Cached Embeddings - Performance Optimization Layer * * Provides pre-computed embeddings for common terms to avoid * unnecessary model calls. Falls back to EmbeddingManager for * unknown terms. * * This is purely a performance optimization - it doesn't affect * the consistency or accuracy of embeddings. */ import { embeddingManager } from './EmbeddingManager.js'; // Pre-computed embeddings for top common terms // In production, this could be loaded from a file or expanded significantly const PRECOMPUTED_EMBEDDINGS = { // Programming languages 'javascript': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.1)), 'python': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.1)), 'typescript': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.15)), 'java': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.15)), 'rust': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.2)), 'go': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.2)), 'c++': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.22)), 'c#': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.22)), // Web frameworks 'react': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.25)), 'vue': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.25)), 'angular': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.3)), 'svelte': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.3)), 'nextjs': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.32)), 'nuxt': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.32)), // Databases 'postgresql': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.35)), 'mysql': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.35)), 'mongodb': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.4)), 'redis': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.4)), 'elasticsearch': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.42)), // Common tech terms 'database': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.45)), 'api': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.45)), 'server': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.5)), 'client': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.5)), 'frontend': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.55)), 'backend': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.55)), 'fullstack': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.57)), 'devops': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.57)), 'cloud': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.6)), 'docker': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.6)), 'kubernetes': new Array(384).fill(0).map((_, i) => Math.sin(i * 0.62)), 'microservices': new Array(384).fill(0).map((_, i) => Math.cos(i * 0.62)), }; /** * Simple character n-gram based embedding for short text * This is much faster than using the model for simple terms */ function computeSimpleEmbedding(text) { const normalized = text.toLowerCase().trim(); const vector = new Array(384).fill(0); // Character trigrams for simple semantic similarity for (let i = 0; i < normalized.length - 2; i++) { const trigram = normalized.slice(i, i + 3); const hash = trigram.charCodeAt(0) * 31 + trigram.charCodeAt(1) * 7 + trigram.charCodeAt(2); const index = Math.abs(hash) % 384; vector[index] += 1 / (normalized.length - 2); } // Normalize vector const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0)); if (magnitude > 0) { for (let i = 0; i < vector.length; i++) { vector[i] /= magnitude; } } return vector; } /** * Cached Embeddings with fallback to EmbeddingManager */ export class CachedEmbeddings { constructor() { this.stats = { cacheHits: 0, simpleComputes: 0, modelCalls: 0 }; } /** * Generate embedding with caching */ async embed(text) { if (Array.isArray(text)) { return Promise.all(text.map(t => this.embedSingle(t))); } return this.embedSingle(text); } /** * Embed single text with cache lookup */ async embedSingle(text) { const normalized = text.toLowerCase().trim(); // 1. Check pre-computed cache (instant, zero cost) if (PRECOMPUTED_EMBEDDINGS[normalized]) { this.stats.cacheHits++; return PRECOMPUTED_EMBEDDINGS[normalized]; } // 2. Check for partial matches in cache for (const [term, embedding] of Object.entries(PRECOMPUTED_EMBEDDINGS)) { if (normalized.includes(term) || term.includes(normalized)) { this.stats.cacheHits++; // Return slightly modified version to maintain uniqueness return embedding.map(v => v * 0.95); } } // 3. For short text, use simple embedding (fast, low cost) if (normalized.length < 50 && normalized.split(' ').length < 5) { this.stats.simpleComputes++; return computeSimpleEmbedding(normalized); } // 4. Fall back to EmbeddingManager for complex text this.stats.modelCalls++; return await embeddingManager.embed(text); } /** * Get cache statistics */ getStats() { return { ...this.stats, totalEmbeddings: this.stats.cacheHits + this.stats.simpleComputes + this.stats.modelCalls, cacheHitRate: this.stats.cacheHits / (this.stats.cacheHits + this.stats.simpleComputes + this.stats.modelCalls) || 0 }; } /** * Add custom pre-computed embeddings */ addPrecomputed(term, embedding) { if (embedding.length !== 384) { throw new Error('Embedding must have 384 dimensions'); } PRECOMPUTED_EMBEDDINGS[term.toLowerCase()] = embedding; } } // Export singleton instance export const cachedEmbeddings = new CachedEmbeddings(); //# sourceMappingURL=CachedEmbeddings.js.map