pulse-ai-utils
Version:
Utility functions and helpers for AI-powered applications
247 lines • 9.69 kB
JavaScript
;
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