UNPKG

pulse-ai-utils

Version:

Utility functions and helpers for AI-powered applications

247 lines 9.69 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SupabaseQueryCache = void 0; const index_1 = require("./index"); const openai_helper_1 = __importDefault(require("../helpers/openai-helper")); class SupabaseQueryCache { constructor() { this.openai = new openai_helper_1.default(); } /** * Get cached result using vector similarity search */ async getCachedResult(prompt, location) { try { // Generate embedding for the prompt const promptEmbedding = await this.openai.generateEmbedding(prompt); // Search for similar prompts with matching location using available function const { data, error } = await index_1.supabase.rpc('search_query_cache_json', { prompt_embedding: promptEmbedding, // Send as array, not JSON string area_filter: location.area, region_filter: location.region || null, country_filter: location.country || null, timeline_filter: location.timeline || null, button_click_count_filter: location.buttonClickCount || null, similarity_threshold: 0.9 // Match the 90% threshold from original LLMCache }); if (error) { console.error('Error searching query cache:', error); return null; } if (!data || data.length === 0) { return null; } // Return the most similar result return data[0].result; } catch (error) { console.error('Error in getCachedResult:', error); return null; } } /** * Set cached result with vector embedding */ async setCachedResult(prompt, location, result) { try { // Generate embedding for the prompt const promptEmbedding = await this.openai.generateEmbedding(prompt); // Calculate expiration (24 hours from now) const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // Generate consistent ID: promptHash_area_region_country_date const dateStr = new Date().toISOString().split('T')[0]; const promptHash = this.hashString(prompt); const id = [ promptHash, location.area || 'unknown', location.region || 'noregion', location.country || 'nocountry', dateStr ].join('_'); // Prepare data for insertion with optimized format const cacheData = { id, // Use consistent ID format prompt, area: location.area, region: location.region || null, country: location.country || null, timeline: location.timeline || null, count: location.count || null, // Handle additional fields result, prompt_embedding: JSON.stringify(promptEmbedding), // Serialize for storage expire_at: expireAt.toISOString() // Fixed column name }; // Use upsert for better performance and avoid conflicts const { error } = await index_1.supabase.from('query_cache').upsert(cacheData, { onConflict: 'id' }); if (error) { console.error('Error inserting into query cache:', error); throw error; } } catch (error) { console.error('Error in setCachedResult:', error); throw error; } } /** * Batch insert multiple cache entries for better performance */ async batchSetCachedResults(entries) { try { // Prepare batch data with embeddings const batchData = await Promise.all(entries.map(async (entry) => { const promptEmbedding = await this.openai.generateEmbedding(entry.prompt); const expireAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // Generate consistent ID const dateStr = new Date().toISOString().split('T')[0]; const promptHash = this.hashString(entry.prompt); const id = [ promptHash, entry.location.area || 'unknown', entry.location.region || 'noregion', entry.location.country || 'nocountry', dateStr ].join('_'); return { id, // Use consistent ID format prompt: entry.prompt, area: entry.location.area, region: entry.location.region || null, country: entry.location.country || null, timeline: entry.location.timeline || null, result: entry.result, prompt_embedding: JSON.stringify(promptEmbedding), expire_at: expireAt.toISOString() }; })); // Use batch upsert for better performance const { error } = await index_1.supabase.from('query_cache').upsert(batchData); if (error) { console.error('Error batch inserting cache entries:', error); throw error; } console.log(`Batch inserted ${batchData.length} cache entries`); } catch (error) { console.error('Error in batchSetCachedResults:', error); throw error; } } /** * Search cache by location filters without prompt similarity */ async searchByLocation(location, limit = 10) { try { let query = index_1.supabase .from('query_cache') .select('*') .eq('area', location.area) .gte('expire_at', new Date().toISOString()) .order('timestamp', { ascending: false }) .limit(limit); // Add optional location filters if (location.region) query = query.eq('region', location.region); if (location.country) query = query.eq('country', location.country); if (location.timeline) query = query.eq('timeline', location.timeline); const { data, error } = await query; if (error) { console.error('Error searching cache by location:', error); return []; } return data || []; } catch (error) { console.error('Error in searchByLocation:', error); return []; } } /** * Clean up expired cache entries */ async cleanupExpired() { try { const { data, error } = await index_1.supabase .from('query_cache') .delete() .lt('expire_at', new Date().toISOString()) .select('id'); if (error) { console.error('Error cleaning up expired cache:', error); return 0; } const deletedCount = data?.length || 0; console.log(`Cleaned up ${deletedCount} expired cache entries`); return deletedCount; } catch (error) { console.error('Error in cleanupExpired:', error); return 0; } } /** * Get cache statistics */ async getStats() { try { // Get total count const { count: totalCount, error: totalError } = await index_1.supabase .from('query_cache') .select('*', { count: 'exact', head: true }); if (totalError) throw totalError; // Get expired count const { count: expiredCount, error: expiredError } = await index_1.supabase .from('query_cache') .select('*', { count: 'exact', head: true }) .lt('expire_at', new Date().toISOString()); if (expiredError) throw expiredError; // Get counts by area const { data: areaData, error: areaError } = await index_1.supabase .from('query_cache') .select('area') .gte('expire_at', new Date().toISOString()); if (areaError) throw areaError; const byArea = {}; areaData?.forEach(row => { byArea[row.area] = (byArea[row.area] || 0) + 1; }); return { totalEntries: totalCount || 0, expiredEntries: expiredCount || 0, byArea }; } catch (error) { console.error('Error getting cache stats:', error); return { totalEntries: 0, expiredEntries: 0, byArea: {} }; } } /** * Hash function for generating consistent IDs */ hashString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return Math.abs(hash).toString(36); } } exports.SupabaseQueryCache = SupabaseQueryCache; //# sourceMappingURL=query-cache-supabase.js.map