import log from "encore.dev/log"; import { CacheEntry } from "./types"; // Simple in-memory cache for LLM responses class SimpleCache { private cache: Map> = new Map(); private readonly ttl: number; private readonly maxEntries: number; constructor(ttlSeconds: number = 300, maxEntries: number = 100) { this.ttl = ttlSeconds * 1000; this.maxEntries = maxEntries; // Clean up expired entries every minute setInterval(() => this.cleanup(), 60000); } // Generate cache key from request parameters generateKey(params: Record): string { return JSON.stringify(params); } // Get cached value if it exists and is not expired get(key: string): T | null { const entry = this.cache.get(key); if (!entry) { return null; } if (Date.now() > entry.expiresAt) { this.cache.delete(key); return null; } log.info("Cache hit", { key: key.substring(0, 50) }); return entry.data; } set(key: string, value: T): void { if (this.cache.size >= this.maxEntries) { const firstKey = this.cache.keys().next().value as string | undefined; if (firstKey) { this.cache.delete(firstKey); } } this.cache.set(key, { data: value, timestamp: Date.now(), expiresAt: Date.now() + this.ttl, }); log.info("Cache set", { key: key.substring(0, 50), size: this.cache.size }); } // Remove expired entries private cleanup(): void { const now = Date.now(); let removed = 0; for (const [key, entry] of this.cache.entries()) { if (now > entry.expiresAt) { this.cache.delete(key); removed++; } } if (removed > 0) { log.info("Cache cleanup", { removed, remaining: this.cache.size }); } } // Clear all cache entries clear(): void { this.cache.clear(); log.info("Cache cleared"); } // Get cache statistics getStats() { return { size: this.cache.size, maxEntries: this.maxEntries, ttl: this.ttl / 1000, }; } } // Export singleton instances for different cache types export const chatCache = new SimpleCache(300, 100); export const ragCache = new SimpleCache(600, 50); export const analysisCache = new SimpleCache(900, 30);