UNPKG

promptforge

Version:

Adaptive Prompt Intelligence & Orchestration SDK - Manage, optimize, and serve prompts for LLMs with versioning, feedback loops, and multi-provider support

139 lines 4.06 kB
import { v4 as uuidv4 } from 'uuid'; import { CacheEntrySchema } from '../types.js'; import crypto from 'crypto'; export class SemanticCache { cache; config; constructor(config = {}) { this.cache = new Map(); this.config = { enabled: true, ttlSeconds: 3600, similarityThreshold: 0.95, ...config, }; } /** * Get cached result for similar input */ async get(promptId, input) { if (!this.config.enabled) { return null; } const inputHash = this.hashInput(input); // Find exact match first for (const [, entry] of this.cache) { if (entry.promptId === promptId && entry.inputHash === inputHash) { // Check if expired if (entry.expiresAt && new Date() > entry.expiresAt) { this.cache.delete(entry.id); continue; } // Increment hit count entry.hitCount += 1; return entry; } } // TODO: Semantic similarity search using embeddings // For now, return null if no exact match return null; } /** * Set cache entry */ async set(promptId, input, output, metadata) { if (!this.config.enabled) { return; } const inputHash = this.hashInput(input); const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + this.config.ttlSeconds); const entry = { id: uuidv4(), promptId, inputHash, inputEmbedding: [], // TODO: Generate embedding output, provider: metadata.provider, model: metadata.model, hitCount: 0, createdAt: new Date(), expiresAt, metadata: { input }, }; const validatedEntry = CacheEntrySchema.parse(entry); this.cache.set(entry.id, validatedEntry); // Check max entries limit if (this.config.maxEntries && this.cache.size > this.config.maxEntries) { this.evictOldest(); } } /** * Invalidate cache for a prompt */ async invalidate(promptId) { let count = 0; for (const [id, entry] of this.cache) { if (entry.promptId === promptId) { this.cache.delete(id); count++; } } return count; } /** * Clear all cache */ async clear() { this.cache.clear(); } /** * Get cache statistics */ getStats() { const entries = Array.from(this.cache.values()); const totalHits = entries.reduce((sum, e) => sum + e.hitCount, 0); return { size: this.cache.size, totalHits, hitRate: this.cache.size > 0 ? totalHits / this.cache.size : 0, }; } /** * Hash input for exact matching */ hashInput(input) { const sorted = Object.keys(input) .sort() .map(key => `${key}:${input[key]}`) .join('|'); return crypto.createHash('sha256').update(sorted).digest('hex'); } /** * Evict oldest entries */ evictOldest() { const entries = Array.from(this.cache.entries()); entries.sort((a, b) => a[1].createdAt.getTime() - b[1].createdAt.getTime()); // Remove oldest 10% const toRemove = Math.floor(entries.length * 0.1); for (let i = 0; i < toRemove; i++) { this.cache.delete(entries[i][0]); } } /** * Cleanup expired entries */ async cleanup() { let count = 0; const now = new Date(); for (const [id, entry] of this.cache) { if (entry.expiresAt && now > entry.expiresAt) { this.cache.delete(id); count++; } } return count; } } //# sourceMappingURL=cache.js.map