char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
521a64cd0
char/aliceai-t5-moe
1import { CACHE } from '$lib/constants' ; 2 3/** 4* TTL Cache - Time-To-Live cache implementation for memory optimization 5* 6* Provides automatic expiration of cached entries to prevent memory bloat 7* in long-running sessions. 8* 9* @example 10* ```ts 11* const cache = new TTLCache<string, ApiData>({ ttlMs: 5 * 60 * 1000 }); // 5 minutes 12* cache.set('key', data); 13* const value = cache.get('key'); // null if expired 14* ``` 15*/ 16 17export interface TTLCacheOptions { 18/** Time-to-live in milliseconds. Default: 5 minutes */ 19ttlMs ?:number ; 20/** Maximum number of entries. Oldest entries are evicted when exceeded. Default: 100 */ 21maxEntries ?:number ; 22/** Callback when an entry expires or is evicted */ 23onEvict ?:( key :string , value :unknown ) => void ; 24} 25 26interface CacheEntry < T > { 27value :T ; 28expiresAt :number ; 29lastAccessed :number ; 30} 31 32export class TTLCache < K extends string , V > { 33private cache = new Map < K , CacheEntry < V >>(); 34private readonly maxEntries :number ; 35private readonly onEvict ?:( key :string , value :unknown ) => void ; 36private readonly ttlMs :number ; 37 38/** 39* Get the number of entries (including potentially expired ones). 40*/ 41get size () :number { 42return this . cache . size ; 43} 44 45/** 46* Clear all entries from cache. 47*/ 48clear () :void { 49if ( this . onEvict ) { 50for ( const [ key , entry ] of this . cache ) { 51this . onEvict ( key , entry . value ); 52} 53} 54 55this . cache . clear (); 56} 57 58constructor ( options :TTLCacheOptions = {}) { 59this . ttlMs = options . ttlMs ?? CACHE . DEFAULT_TTL_MS ; 60this . maxEntries = options . maxEntries ?? CACHE . DEFAULT_MAX_ENTRIES ; 61this . onEvict = options . onEvict ; 62} 63 64/** 65* Delete a specific key from cache. 66*/ 67delete ( key :K ) :boolean { 68const entry = this . cache . get ( key ); 69 70if ( entry && this . onEvict ) { 71this . onEvict ( key , entry . value ); 72} 73 74return this . cache . delete ( key ); 75} 76 77/** 78* Get a value from cache. Returns null if expired or not found. 79*/ 80get ( key :K ) :V | null { 81const entry = this . cache . get ( key ); 82 83if ( ! entry ) return null ; 84 85if ( Date . now () > entry . expiresAt ) { 86this . delete ( key ); 87 88return null ; 89} 90 91// Update last accessed time for LRU-like behavior 92entry . lastAccessed = Date . now (); 93 94return entry . value ; 95} 96 97/** 98* Check if key exists and is not expired. 99*/ 100has ( key :K ) :boolean { 101const entry = this . cache . get ( key ); 102 103if ( ! entry ) return false ; 104 105if ( Date . now () > entry . expiresAt ) { 106this . delete ( key ); 107 108return false ; 109} 110 111return true ; 112} 113 114/** 115* Get all valid (non-expired) keys. 116*/ 117keys () :K [] { 118const now = Date . now (); 119const validKeys :K [] = []; 120 121for ( const [ key , entry ] of this . cache ) { 122if ( now <= entry . expiresAt ) { 123validKeys . push ( key ); 124} 125} 126 127return validKeys ; 128} 129 130/** 131* Remove all expired entries from cache. 132* Call periodically for proactive cleanup. 133*/ 134prune () :number { 135const now = Date . now (); 136 137let pruned = 0 ; 138 139for ( const [ key , entry ] of this . cache ) { 140if ( now > entry . expiresAt ) { 141this . delete ( key ); 142pruned ++ ; 143} 144} 145 146return pruned ; 147} 148 149/** 150* Set a value in cache with TTL. 151*/ 152set ( key :K , value :V , customTtlMs ?:number ) :void { 153// Evict oldest entries if at capacity 154if ( this . cache . size >= this . maxEntries && ! this . cache . has ( key )) { 155this . evictOldest (); 156} 157 158const ttl = customTtlMs ?? this . ttlMs ; 159const now = Date . now (); 160 161this . cache . set ( key , { 162expiresAt :now + ttl , 163lastAccessed :now , 164 value 165}); 166} 167 168/** 169* Refresh TTL for an existing entry without changing the value. 170*/ 171touch ( key :K ) :boolean { 172const entry = this . cache . get ( key ); 173 174if ( ! entry ) return false ; 175 176const now = Date . now (); 177 178if ( now > entry . expiresAt ) { 179this . delete ( key ); 180 181return false ; 182} 183 184entry . expiresAt = now + this . ttlMs ; 185entry . lastAccessed = now ; 186 187return true ; 188} 189 190/** 191* Evict the oldest (least recently accessed) entry. 192*/ 193private evictOldest () :void { 194let oldestKey :K | null = null ; 195let oldestTime = Infinity ; 196 197for ( const [ key , entry ] of this . cache ) { 198if ( entry . lastAccessed < oldestTime ) { 199oldestTime = entry . lastAccessed ; 200oldestKey = key ; 201} 202} 203 204if ( oldestKey !== null ) { 205this . delete ( oldestKey ); 206} 207} 208} 209 210/** 211* Reactive TTL Map for Svelte stores 212* Wraps SvelteMap with TTL functionality 213*/ 214export class ReactiveTTLMap < K extends string , V > { 215private entries = $state < Map < K , CacheEntry < V >>>( new Map ()); 216private readonly maxEntries :number ; 217private readonly ttlMs :number ; 218 219get size () :number { 220return this . entries . size ; 221} 222 223clear () :void { 224this . entries . clear (); 225} 226 227constructor ( options :TTLCacheOptions = {}) { 228this . ttlMs = options . ttlMs ?? CACHE . DEFAULT_TTL_MS ; 229this . maxEntries = options . maxEntries ?? CACHE . DEFAULT_MAX_ENTRIES ; 230} 231 232delete ( key :K ) :boolean { 233return this . entries . delete ( key ); 234} 235 236get ( key :K ) :V | null { 237const entry = this . entries . get ( key ); 238 239if ( ! entry ) return null ; 240 241if ( Date . now () > entry . expiresAt ) { 242this . entries . delete ( key ); 243 244return null ; 245} 246 247entry . lastAccessed = Date . now (); 248 249return entry . value ; 250} 251 252has ( key :K ) :boolean { 253const entry = this . entries . get ( key ); 254 255if ( ! entry ) return false ; 256 257if ( Date . now () > entry . expiresAt ) { 258this . entries . delete ( key ); 259 260return false ; 261} 262 263return true ; 264} 265 266prune () :number { 267const now = Date . now (); 268 269let pruned = 0 ; 270 271for ( const [ key , entry ] of this . entries ) { 272if ( now > entry . expiresAt ) { 273this . entries . delete ( key ); 274pruned ++ ; 275} 276} 277 278return pruned ; 279} 280 281set ( key :K , value :V , customTtlMs ?:number ) :void { 282if ( this . entries . size >= this . maxEntries && ! this . entries . has ( key )) { 283this . evictOldest (); 284} 285 286const ttl = customTtlMs ?? this . ttlMs ; 287const now = Date . now (); 288 289this . entries . set ( key , { 290expiresAt :now + ttl , 291lastAccessed :now , 292 value 293}); 294} 295 296private evictOldest () :void { 297let oldestKey :K | null = null ; 298let oldestTime = Infinity ; 299 300for ( const [ key , entry ] of this . entries ) { 301if ( entry . lastAccessed < oldestTime ) { 302oldestTime = entry . lastAccessed ; 303oldestKey = key ; 304} 305} 306 307if ( oldestKey !== null ) { 308this . entries . delete ( oldestKey ); 309} 310} 311}