myavana-bot-test-core 2.1.7 โ 2.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cacheManager.js +116 -19
package/package.json
CHANGED
package/src/cacheManager.js
CHANGED
|
@@ -1,24 +1,88 @@
|
|
|
1
|
-
// cacheManager.js
|
|
1
|
+
// cacheManager.js - OPTIMIZED VERSION
|
|
2
2
|
|
|
3
3
|
const ai = require('./ai');
|
|
4
4
|
const { googleAI } = require('@genkit-ai/google-genai');
|
|
5
5
|
const { pgClient, redisClient } = require('./database');
|
|
6
|
-
const
|
|
6
|
+
const { GoogleGenAI } = require("@google/genai");
|
|
7
7
|
const config = require('./config');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Cache Manager for Gemini API
|
|
11
11
|
* Handles creation, retrieval, and expiration of cached system instructions
|
|
12
|
+
* OPTIMIZED: Check cache validity before expensive prompt construction
|
|
12
13
|
*/
|
|
13
14
|
const cacheAi = new GoogleGenAI({ apiKey: config.genkitApiKey });
|
|
15
|
+
|
|
14
16
|
class CacheManager {
|
|
15
17
|
constructor() {
|
|
16
18
|
this.CACHE_TTL_HOURS = 24;
|
|
17
19
|
this.CACHE_TTL_SECONDS = this.CACHE_TTL_HOURS * 60 * 60; // 24 hours in seconds
|
|
20
|
+
this.CACHE_REFRESH_THRESHOLD_HOURS = 1; // Refresh if cache expires in < 1 hour
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* NEW: Check if user has a valid cache WITHOUT constructing prompt
|
|
25
|
+
* This is fast and should be called BEFORE expensive prompt construction
|
|
26
|
+
* @param {string} userId - User identifier
|
|
27
|
+
* @returns {Promise<{hasValidCache: boolean, cacheName: string|null, needsRefresh: boolean}>}
|
|
28
|
+
*/
|
|
29
|
+
async checkCacheValidity(userId) {
|
|
30
|
+
console.log('๐ Quick cache check for user:', userId);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const existingCache = await this.getUserCache(userId);
|
|
34
|
+
|
|
35
|
+
if (!existingCache) {
|
|
36
|
+
console.log('๐ญ No cache exists - will need to create one');
|
|
37
|
+
return {
|
|
38
|
+
hasValidCache: false,
|
|
39
|
+
cacheName: null,
|
|
40
|
+
needsRefresh: false,
|
|
41
|
+
reason: 'no_cache'
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.isCacheExpired(existingCache)) {
|
|
46
|
+
console.log('โฐ Cache expired - will need to create new one');
|
|
47
|
+
return {
|
|
48
|
+
hasValidCache: false,
|
|
49
|
+
cacheName: null,
|
|
50
|
+
needsRefresh: true,
|
|
51
|
+
reason: 'expired'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const remainingHours = this.getCacheRemainingHours(existingCache);
|
|
56
|
+
const needsRefresh = remainingHours < this.CACHE_REFRESH_THRESHOLD_HOURS;
|
|
57
|
+
|
|
58
|
+
console.log('โ
Valid cache found:', {
|
|
59
|
+
cacheName: existingCache.cache_name,
|
|
60
|
+
remainingHours: remainingHours,
|
|
61
|
+
needsRefresh: needsRefresh
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
hasValidCache: true,
|
|
66
|
+
cacheName: existingCache.cache_name,
|
|
67
|
+
needsRefresh: needsRefresh,
|
|
68
|
+
remainingHours: remainingHours,
|
|
69
|
+
reason: 'valid'
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('โ Cache check failed:', error.message);
|
|
74
|
+
return {
|
|
75
|
+
hasValidCache: false,
|
|
76
|
+
cacheName: null,
|
|
77
|
+
needsRefresh: false,
|
|
78
|
+
reason: 'error'
|
|
79
|
+
};
|
|
80
|
+
}
|
|
18
81
|
}
|
|
19
82
|
|
|
20
83
|
/**
|
|
21
84
|
* Get or create a cache for a user's system prompt
|
|
85
|
+
* NOW: Can be called conditionally based on checkCacheValidity result
|
|
22
86
|
* @param {string} userId - User identifier
|
|
23
87
|
* @param {string} systemPrompt - The large system instruction to cache
|
|
24
88
|
* @returns {Promise<{cacheName: string, isNewCache: boolean}>}
|
|
@@ -54,9 +118,10 @@ class CacheManager {
|
|
|
54
118
|
// Create new cache
|
|
55
119
|
console.log('๐ Creating new cache for user:', userId);
|
|
56
120
|
const cacheStartTime = performance.now();
|
|
121
|
+
const modelName = "gemini-3-flash-preview";
|
|
57
122
|
|
|
58
123
|
const cache = await cacheAi.caches.create({
|
|
59
|
-
model:
|
|
124
|
+
model: modelName,
|
|
60
125
|
config: {
|
|
61
126
|
systemInstruction: systemPrompt,
|
|
62
127
|
},
|
|
@@ -100,6 +165,7 @@ class CacheManager {
|
|
|
100
165
|
|
|
101
166
|
/**
|
|
102
167
|
* Get user's cache info from database
|
|
168
|
+
* FIXED: Using correct PostgreSQL syntax (not MySQL)
|
|
103
169
|
* @param {string} userId
|
|
104
170
|
* @returns {Promise<object|null>}
|
|
105
171
|
*/
|
|
@@ -110,20 +176,20 @@ class CacheManager {
|
|
|
110
176
|
const query = `
|
|
111
177
|
SELECT cache_name, created_at, expires_at
|
|
112
178
|
FROM user_gemini_caches
|
|
113
|
-
WHERE user_id =
|
|
179
|
+
WHERE user_id = $1
|
|
114
180
|
ORDER BY created_at DESC
|
|
115
181
|
LIMIT 1
|
|
116
182
|
`;
|
|
117
183
|
|
|
118
184
|
const result = await pgClient.query(query, [userId]);
|
|
119
185
|
|
|
120
|
-
if (result && result.length > 0) {
|
|
186
|
+
if (result && result.rows && result.rows.length > 0) {
|
|
121
187
|
console.log('โ
Found existing cache record:', {
|
|
122
|
-
cacheName: result[0].cache_name,
|
|
123
|
-
createdAt: result[0].created_at,
|
|
124
|
-
expiresAt: result[0].expires_at
|
|
188
|
+
cacheName: result.rows[0].cache_name,
|
|
189
|
+
createdAt: result.rows[0].created_at,
|
|
190
|
+
expiresAt: result.rows[0].expires_at
|
|
125
191
|
});
|
|
126
|
-
return result[0];
|
|
192
|
+
return result.rows[0];
|
|
127
193
|
}
|
|
128
194
|
|
|
129
195
|
console.log('๐ญ No existing cache found for user');
|
|
@@ -137,6 +203,7 @@ class CacheManager {
|
|
|
137
203
|
|
|
138
204
|
/**
|
|
139
205
|
* Save cache info to database
|
|
206
|
+
* FIXED: Using PostgreSQL syntax instead of MySQL
|
|
140
207
|
* @param {string} userId
|
|
141
208
|
* @param {string} cacheName
|
|
142
209
|
*/
|
|
@@ -149,14 +216,16 @@ class CacheManager {
|
|
|
149
216
|
try {
|
|
150
217
|
const expiresAt = new Date(Date.now() + this.CACHE_TTL_SECONDS * 1000);
|
|
151
218
|
|
|
219
|
+
// PostgreSQL syntax with UPSERT (ON CONFLICT)
|
|
152
220
|
const query = `
|
|
153
221
|
INSERT INTO user_gemini_caches
|
|
154
|
-
|
|
155
|
-
VALUES (
|
|
156
|
-
ON
|
|
157
|
-
|
|
222
|
+
(user_id, cache_name, created_at, expires_at)
|
|
223
|
+
VALUES ($1, $2, NOW(), $3)
|
|
224
|
+
ON CONFLICT (user_id)
|
|
225
|
+
DO UPDATE SET
|
|
226
|
+
cache_name = EXCLUDED.cache_name,
|
|
158
227
|
created_at = NOW(),
|
|
159
|
-
expires_at =
|
|
228
|
+
expires_at = EXCLUDED.expires_at
|
|
160
229
|
`;
|
|
161
230
|
|
|
162
231
|
await pgClient.query(query, [userId, cacheName, expiresAt]);
|
|
@@ -165,6 +234,7 @@ class CacheManager {
|
|
|
165
234
|
|
|
166
235
|
} catch (error) {
|
|
167
236
|
console.error('โ Failed to save cache info:', error.message);
|
|
237
|
+
console.error('โ Error details:', error);
|
|
168
238
|
// Don't throw - cache creation succeeded, DB save is secondary
|
|
169
239
|
}
|
|
170
240
|
}
|
|
@@ -200,7 +270,7 @@ class CacheManager {
|
|
|
200
270
|
const now = new Date();
|
|
201
271
|
const createdAt = new Date(cacheRecord.created_at);
|
|
202
272
|
const ageMs = now - createdAt;
|
|
203
|
-
return (ageMs / (1000 * 60 * 60)).toFixed(2);
|
|
273
|
+
return parseFloat((ageMs / (1000 * 60 * 60)).toFixed(2));
|
|
204
274
|
}
|
|
205
275
|
|
|
206
276
|
/**
|
|
@@ -212,11 +282,12 @@ class CacheManager {
|
|
|
212
282
|
const now = new Date();
|
|
213
283
|
const expiresAt = new Date(cacheRecord.expires_at);
|
|
214
284
|
const remainingMs = expiresAt - now;
|
|
215
|
-
return Math.max(0, (remainingMs / (1000 * 60 * 60)).toFixed(2));
|
|
285
|
+
return Math.max(0, parseFloat((remainingMs / (1000 * 60 * 60)).toFixed(2)));
|
|
216
286
|
}
|
|
217
287
|
|
|
218
288
|
/**
|
|
219
289
|
* Clean up expired caches from database (maintenance task)
|
|
290
|
+
* FIXED: PostgreSQL syntax
|
|
220
291
|
*/
|
|
221
292
|
async cleanupExpiredCaches() {
|
|
222
293
|
console.log('๐งน Cleaning up expired caches...');
|
|
@@ -230,7 +301,7 @@ class CacheManager {
|
|
|
230
301
|
const result = await pgClient.query(query);
|
|
231
302
|
|
|
232
303
|
console.log('โ
Cleanup complete:', {
|
|
233
|
-
deletedRecords: result.
|
|
304
|
+
deletedRecords: result.rowCount || 0
|
|
234
305
|
});
|
|
235
306
|
|
|
236
307
|
} catch (error) {
|
|
@@ -240,6 +311,7 @@ class CacheManager {
|
|
|
240
311
|
|
|
241
312
|
/**
|
|
242
313
|
* Get cache statistics for monitoring
|
|
314
|
+
* FIXED: PostgreSQL syntax with EXTRACT instead of TIMESTAMPDIFF
|
|
243
315
|
*/
|
|
244
316
|
async getCacheStats() {
|
|
245
317
|
try {
|
|
@@ -248,18 +320,43 @@ class CacheManager {
|
|
|
248
320
|
COUNT(*) as total_caches,
|
|
249
321
|
COUNT(CASE WHEN expires_at > NOW() THEN 1 END) as active_caches,
|
|
250
322
|
COUNT(CASE WHEN expires_at <= NOW() THEN 1 END) as expired_caches,
|
|
251
|
-
AVG(
|
|
323
|
+
AVG(EXTRACT(EPOCH FROM (NOW() - created_at)) / 3600) as avg_age_hours
|
|
252
324
|
FROM user_gemini_caches
|
|
253
325
|
`;
|
|
254
326
|
|
|
255
327
|
const result = await pgClient.query(query);
|
|
256
|
-
return result[0];
|
|
328
|
+
return result.rows[0];
|
|
257
329
|
|
|
258
330
|
} catch (error) {
|
|
259
331
|
console.error('โ Failed to get cache stats:', error.message);
|
|
260
332
|
return null;
|
|
261
333
|
}
|
|
262
334
|
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* NEW: Proactively refresh cache in background if it's about to expire
|
|
338
|
+
* This ensures users always have a fresh cache without waiting
|
|
339
|
+
* @param {string} userId
|
|
340
|
+
* @param {function} getSystemPromptFn - Function that returns the system prompt
|
|
341
|
+
*/
|
|
342
|
+
async refreshCacheIfNeeded(userId, getSystemPromptFn) {
|
|
343
|
+
const cacheStatus = await this.checkCacheValidity(userId);
|
|
344
|
+
|
|
345
|
+
if (cacheStatus.needsRefresh) {
|
|
346
|
+
console.log('๐ Cache needs refresh - starting background refresh');
|
|
347
|
+
|
|
348
|
+
// Do this asynchronously - don't wait
|
|
349
|
+
setImmediate(async () => {
|
|
350
|
+
try {
|
|
351
|
+
const systemPrompt = await getSystemPromptFn();
|
|
352
|
+
await this.getOrCreateCache(userId, systemPrompt);
|
|
353
|
+
console.log('โ
Background cache refresh completed');
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error('โ Background cache refresh failed:', error.message);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
263
360
|
}
|
|
264
361
|
|
|
265
362
|
module.exports = new CacheManager();
|