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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cacheManager.js +116 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myavana-bot-test-core",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
4
4
  "description": "Shared bot functionality with enhanced features",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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 {GoogleGenAI} = require("@google/genai");
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: googleAI.model('gemini-3-flash-preview'),
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
- (user_id, cache_name, created_at, expires_at)
155
- VALUES (?, ?, NOW(), ?)
156
- ON DUPLICATE KEY UPDATE
157
- cache_name = VALUES(cache_name),
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 = VALUES(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.affectedRows || 0
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(TIMESTAMPDIFF(HOUR, created_at, NOW())) as avg_age_hours
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();