@peopl-health/nexus 2.4.7 → 2.4.9-fix-pdf-processing

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 (47) hide show
  1. package/examples/assistants/DoctorScheduleAssistant.js +3 -2
  2. package/lib/adapters/BaileysProvider.js +5 -4
  3. package/lib/adapters/TwilioProvider.js +20 -19
  4. package/lib/assistants/BaseAssistant.js +7 -6
  5. package/lib/config/awsConfig.js +14 -12
  6. package/lib/config/llmConfig.js +3 -2
  7. package/lib/config/mongoAuthConfig.js +5 -5
  8. package/lib/controllers/assistantController.js +12 -11
  9. package/lib/controllers/bugReportController.js +6 -5
  10. package/lib/controllers/conversationController.js +72 -71
  11. package/lib/controllers/interactionController.js +7 -6
  12. package/lib/controllers/mediaController.js +15 -13
  13. package/lib/controllers/messageController.js +7 -6
  14. package/lib/controllers/patientController.js +2 -1
  15. package/lib/controllers/qualityMessageController.js +5 -4
  16. package/lib/controllers/templateController.js +11 -9
  17. package/lib/controllers/uploadController.js +3 -1
  18. package/lib/core/NexusMessaging.js +18 -18
  19. package/lib/helpers/assistantHelper.js +8 -9
  20. package/lib/helpers/baileysHelper.js +4 -3
  21. package/lib/helpers/filesHelper.js +23 -14
  22. package/lib/helpers/llmsHelper.js +17 -10
  23. package/lib/helpers/mediaHelper.js +3 -2
  24. package/lib/helpers/messageHelper.js +12 -11
  25. package/lib/helpers/processHelper.js +2 -2
  26. package/lib/helpers/qrHelper.js +2 -1
  27. package/lib/helpers/twilioMediaProcessor.js +19 -29
  28. package/lib/helpers/whatsappHelper.js +3 -2
  29. package/lib/index.js +11 -14
  30. package/lib/interactive/index.js +11 -11
  31. package/lib/middleware/requestId.js +9 -14
  32. package/lib/models/messageModel.js +5 -4
  33. package/lib/providers/OpenAIAssistantsProvider.js +10 -9
  34. package/lib/providers/OpenAIResponsesProvider.js +24 -17
  35. package/lib/providers/OpenAIResponsesProviderTools.js +3 -5
  36. package/lib/providers/createProvider.js +2 -1
  37. package/lib/services/airtableService.js +6 -5
  38. package/lib/services/assistantService.js +73 -57
  39. package/lib/services/conversationService.js +16 -16
  40. package/lib/services/preprocessingHooks.js +3 -1
  41. package/lib/storage/MongoStorage.js +14 -14
  42. package/lib/utils/errorHandler.js +3 -1
  43. package/lib/utils/logger.js +35 -3
  44. package/lib/utils/mediaValidator.js +18 -14
  45. package/lib/utils/sanitizer.js +0 -6
  46. package/lib/utils/tracingDecorator.js +7 -1
  47. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  const moment = require('moment-timezone');
2
2
  const { Message } = require('../models/messageModel.js');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  const addMessageToThread = async (reply, messagesChat, provider, thread) => {
5
6
  const threadId = thread.getConversationId();
@@ -18,7 +19,7 @@ const addMessageToThread = async (reply, messagesChat, provider, thread) => {
18
19
  });
19
20
  }
20
21
 
21
- console.log(`[addMessageToThread] Message added - ID: ${reply.message_id}, Thread: ${threadId}, Origin: ${reply.origin}`);
22
+ logger.info(`[addMessageToThread] Message added - ID: ${reply.message_id}, Thread: ${threadId}, Origin: ${reply.origin}`);
22
23
  };
23
24
 
24
25
  const updateMessageRecord = async (reply, thread) => {
@@ -33,7 +34,7 @@ const updateMessageRecord = async (reply, thread) => {
33
34
  } }
34
35
  );
35
36
 
36
- console.log(`[updateMessageRecord] Record updated - ID: ${reply.message_id}, Thread: ${threadId}, Processed: true`);
37
+ logger.info(`[updateMessageRecord] Record updated - ID: ${reply.message_id}, Thread: ${threadId}, Processed: true`);
37
38
  };
38
39
 
39
40
 
@@ -44,21 +45,21 @@ async function getLastMessages(code) {
44
45
  .sort({ createdAt: 1 });
45
46
 
46
47
  if (!lastMessages || lastMessages.length === 0) {
47
- console.log(`[getLastMessages] No messages found for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
48
+ logger.info(`[getLastMessages] No messages found for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
48
49
  return null;
49
50
  }
50
51
 
51
52
  const unprocessedMessages = lastMessages.filter(msg => !msg.processed);
52
53
  if (unprocessedMessages.length === 0) {
53
- console.log(`[getLastMessages] No unprocessed messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
54
+ logger.info(`[getLastMessages] No unprocessed messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
54
55
  return null;
55
56
  }
56
57
 
57
- console.log(`[getLastMessages] Found ${unprocessedMessages.length} unprocessed messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
58
+ logger.info(`[getLastMessages] Found ${unprocessedMessages.length} unprocessed messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}`);
58
59
  return unprocessedMessages;
59
60
 
60
61
  } catch (error) {
61
- console.error(`[getLastMessages] Error for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}:`, error?.message || String(error));
62
+ logger.error(`[getLastMessages] Error for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}:`, error?.message || String(error));
62
63
  return null;
63
64
  }
64
65
  }
@@ -70,15 +71,15 @@ async function getLastNMessages(code, n) {
70
71
  .limit(n);
71
72
 
72
73
  if (!lastMessages || lastMessages.length === 0) {
73
- console.log(`[getLastNMessages] No messages found for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}`);
74
+ logger.info(`[getLastNMessages] No messages found for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}`);
74
75
  return [];
75
76
  }
76
77
 
77
- console.log(`[getLastNMessages] Found ${lastMessages.length} messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}`);
78
+ logger.info(`[getLastNMessages] Found ${lastMessages.length} messages for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}`);
78
79
  return lastMessages;
79
80
 
80
81
  } catch (error) {
81
- console.error(`[getLastNMessages] Error for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}:`, error?.message || String(error));
82
+ logger.error(`[getLastNMessages] Error for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}:`, error?.message || String(error));
82
83
  return [];
83
84
  }
84
85
  }
@@ -91,7 +92,7 @@ function formatMessage(reply) {
91
92
 
92
93
  const msgDate = new Date(reply.createdAt);
93
94
  if (isNaN(msgDate.getTime())) {
94
- console.warn(`[formatMessage] Invalid timestamp for message ID: ${reply.message_id}`);
95
+ logger.warn(`[formatMessage] Invalid timestamp for message ID: ${reply.message_id}`);
95
96
  return reply.body;
96
97
  }
97
98
 
@@ -102,7 +103,7 @@ function formatMessage(reply) {
102
103
 
103
104
  return `[${mexicoCityTime}] ${reply.body}`;
104
105
  } catch (error) {
105
- console.error(`[formatMessage] Error for message ID: ${reply.message_id}:`, error?.message || String(error));
106
+ logger.error(`[formatMessage] Error for message ID: ${reply.message_id}:`, error?.message || String(error));
106
107
  return null;
107
108
  }
108
109
  }
@@ -2,7 +2,7 @@ const fs = require('fs');
2
2
  const { generatePresignedUrl } = require('../config/awsConfig.js');
3
3
  const { analyzeImage } = require('./llmsHelper.js');
4
4
  const { cleanupFiles, downloadMediaAndCreateFile } = require('./filesHelper.js');
5
- const { formatMessage, addMessageToThread, updateMessageRecord } = require('./messageHelper.js');
5
+ const { formatMessage } = require('./messageHelper.js');
6
6
  const { sanitizeLogMetadata } = require('../utils/sanitizer.js');
7
7
 
8
8
  /**
@@ -66,7 +66,7 @@ const processImageFile = async (fileName, reply) => {
66
66
  fileName.toLowerCase().includes('/sticker/');
67
67
 
68
68
  try {
69
- imageAnalysis = await analyzeImage(fileName, isSticker);
69
+ imageAnalysis = await analyzeImage(fileName, isSticker, reply.media?.contentType);
70
70
 
71
71
  logger.info('processImageFile', {
72
72
  message_id: reply.message_id,
@@ -1,4 +1,5 @@
1
1
  const qrcode = require('qrcode');
2
+ const { logger } = require('../utils/logger');
2
3
 
3
4
 
4
5
  async function generateQRBuffer(text) {
@@ -6,7 +7,7 @@ async function generateQRBuffer(text) {
6
7
  const qrBuffer = await qrcode.toBuffer(text, { type: 'image/png' });
7
8
  return qrBuffer;
8
9
  } catch (err) {
9
- console.error('Error generating QR code:', err.stack);
10
+ logger.error('Error generating QR code', { error: err.message, stack: err.stack });
10
11
  throw err;
11
12
  }
12
13
  }
@@ -5,28 +5,20 @@ const {
5
5
  getMediaTypeFromContentType,
6
6
  extractTitle
7
7
  } = require('./twilioHelper');
8
- const { validateMedia } = require('../utils/mediaValidator');
9
8
  const { generatePresignedUrl } = require('../config/awsConfig');
10
9
  const { addLinkedRecord } = require('../services/airtableService');
11
10
  const { Monitoreo_ID } = require('../config/airtableConfig');
12
-
13
- const ensureLogger = (logger) => {
14
- if (!logger) return console;
15
- const fallback = {};
16
- for (const level of ['info', 'warn', 'error', 'debug']) {
17
- fallback[level] = typeof logger[level] === 'function' ? logger[level].bind(logger) : console[level].bind(console);
18
- }
19
- return fallback;
20
- };
11
+ const { validateMedia } = require('../utils/mediaValidator');
12
+ const { logger } = require('../utils/logger');
21
13
 
22
14
  const normalizeMediaType = (type) => {
23
15
  if (!type || typeof type !== 'string') return 'document';
24
16
  return type.replace(/Message$/i, '').replace(/WithCaption$/i, '').toLowerCase();
25
17
  };
26
18
 
27
- async function uploadMediaToAirtable(whatsappId, mediaUrl, log, baseID, tableName) {
19
+ async function uploadMediaToAirtable(whatsappId, mediaUrl, baseID, tableName) {
28
20
  if (!baseID) {
29
- log.debug && log.debug('[uploadMediaToAirtable] Base ID not configured; skipping Airtable upload');
21
+ logger.debug('[uploadMediaToAirtable] Base ID not configured; skipping Airtable upload');
30
22
  return;
31
23
  }
32
24
 
@@ -45,25 +37,23 @@ async function uploadMediaToAirtable(whatsappId, mediaUrl, log, baseID, tableNam
45
37
  }
46
38
  );
47
39
 
48
- log.debug && log.debug('[uploadMediaToAirtable] Successfully uploaded media to estudios table', { whatsappId });
40
+ logger.debug('[uploadMediaToAirtable] Successfully uploaded media to estudios table', { whatsappId });
49
41
  } catch (error) {
50
- log.warn && log.warn('[uploadMediaToAirtable] Failed to upload media to estudios table', { whatsappId, error: error?.message || error });
42
+ logger.warn('[uploadMediaToAirtable] Failed to upload media to estudios table', { whatsappId, error: error?.message || error });
51
43
  throw error;
52
44
  }
53
45
  }
54
46
 
55
- async function processTwilioMediaMessage(twilioMessage, logger, bucketName) {
47
+ async function processTwilioMediaMessage(twilioMessage, bucketName) {
56
48
  if (!twilioMessage) return [];
57
-
58
- const log = ensureLogger(logger);
59
49
  const numMedia = parseInt(twilioMessage.NumMedia || '0', 10);
60
50
  if (!numMedia || numMedia <= 0) {
61
- log.debug && log.debug('[TwilioMedia] No media detected in message');
51
+ logger.debug('[TwilioMedia] No media detected in message');
62
52
  return [];
63
53
  }
64
54
 
65
55
  if (!bucketName) {
66
- log.warn && log.warn('[TwilioMedia] AWS bucket not configured; skipping media processing');
56
+ logger.warn('[TwilioMedia] AWS bucket not configured; skipping media processing');
67
57
  return [];
68
58
  }
69
59
 
@@ -78,25 +68,25 @@ async function processTwilioMediaMessage(twilioMessage, logger, bucketName) {
78
68
  const contentType = twilioMessage[`MediaContentType${i}`];
79
69
 
80
70
  if (!mediaUrl || !contentType) {
81
- log.warn && log.warn('[TwilioMedia] Missing media URL or content type', { index: i });
71
+ logger.warn('[TwilioMedia] Missing media URL or content type', { index: i });
82
72
  continue;
83
73
  }
84
74
 
85
- log.info && log.info('[TwilioMedia] Processing media item', { index: i, contentType, mediaUrl });
75
+ logger.info('[TwilioMedia] Processing media item', { index: i, contentType, mediaUrl });
86
76
 
87
77
  let mediaBuffer;
88
78
  try {
89
- mediaBuffer = await downloadMediaFromTwilio(mediaUrl, log);
79
+ mediaBuffer = await downloadMediaFromTwilio(mediaUrl, logger);
90
80
  } catch (error) {
91
- log.error && log.error('[TwilioMedia] Failed to download media', { index: i, error: error?.message || error });
81
+ logger.error('[TwilioMedia] Failed to download media', { index: i, error: error?.message || error });
92
82
  continue;
93
83
  }
94
84
 
95
85
  const validationResult = validateMedia(mediaBuffer, contentType);
96
86
  if (!validationResult.valid) {
97
- log.warn && log.warn('[TwilioMedia] Media validation warning', { index: i, message: validationResult.message });
87
+ logger.warn('[TwilioMedia] Media validation warning', { index: i, message: validationResult.message });
98
88
  } else {
99
- log.debug && log.debug('[TwilioMedia] Media validation passed', { index: i, message: validationResult.message });
89
+ logger.debug('[TwilioMedia] Media validation passed', { index: i, message: validationResult.message });
100
90
  }
101
91
 
102
92
  const helperMediaType = getMediaTypeFromContentType(contentType);
@@ -119,7 +109,7 @@ async function processTwilioMediaMessage(twilioMessage, logger, bucketName) {
119
109
  derivedMediaType
120
110
  );
121
111
  } catch (error) {
122
- log.error && log.error('[TwilioMedia] Failed to upload media to S3', { index: i, error: error?.message || error });
112
+ logger.error('[TwilioMedia] Failed to upload media to S3', { index: i, error: error?.message || error });
123
113
  continue;
124
114
  }
125
115
 
@@ -146,16 +136,16 @@ async function processTwilioMediaMessage(twilioMessage, logger, bucketName) {
146
136
  item.metadata.presignedUrl = url;
147
137
 
148
138
  if (derivedMediaType !== 'sticker') {
149
- await uploadMediaToAirtable(code, url, log, Monitoreo_ID, 'estudios');
139
+ await uploadMediaToAirtable(code, url, Monitoreo_ID, 'estudios');
150
140
  }
151
141
  } catch (error) {
152
- log.warn && log.warn('[TwilioMedia] Failed to update Airtable with media reference', { index: i, error: error?.message || error });
142
+ logger.warn('[TwilioMedia] Failed to update Airtable with media reference', { index: i, error: error?.message || error });
153
143
  }
154
144
 
155
145
  mediaItems.push(item);
156
146
  }
157
147
 
158
- console.log('[TwilioMedia] Completed processing', {
148
+ logger.info('[TwilioMedia] Completed processing', {
159
149
  from: code,
160
150
  itemsStored: mediaItems.length,
161
151
  bucket: bucketName
@@ -1,4 +1,5 @@
1
1
  const moment = require('moment-timezone');
2
+ const { logger } = require('../utils/logger');
2
3
 
3
4
 
4
5
  function delay(ms) {
@@ -7,7 +8,7 @@ function delay(ms) {
7
8
 
8
9
 
9
10
  function formatCode(codeBase) {
10
- console.log(`formatCode ${codeBase}`);
11
+ logger.info(`formatCode ${codeBase}`);
11
12
 
12
13
  const [number, domain] = codeBase.split('@');
13
14
 
@@ -50,7 +51,7 @@ function calculateDelay(sendTime, timeZone) {
50
51
  const delay = sendMoment.diff(now) + randomDelay;
51
52
 
52
53
  // Log the calculated details for debugging
53
- console.log(
54
+ logger.info(
54
55
  'Scheduled Time:', sendMoment.format(),
55
56
  'Current Time:', now.format(),
56
57
  'Delay (minutes):', delay / 60000,
package/lib/index.js CHANGED
@@ -25,9 +25,9 @@ const {
25
25
  } = require('./services/preprocessingHooks');
26
26
  const {
27
27
  requestIdMiddleware,
28
- getRequestId,
29
- logger
28
+ getRequestId
30
29
  } = require('./middleware/requestId');
30
+ const { logger } = require('./utils/logger');
31
31
 
32
32
  /**
33
33
  * Main Nexus class that orchestrates all components
@@ -39,7 +39,7 @@ class Nexus {
39
39
  try {
40
40
  setDefaultInstance(this.messaging);
41
41
  } catch (err) {
42
- console.warn('[Nexus] Failed to set default messaging instance:', err?.message || err);
42
+ logger.warn('[Nexus] Failed to set default messaging instance:', err?.message || err);
43
43
  }
44
44
  this.storage = null;
45
45
  this.messageParser = null;
@@ -92,11 +92,10 @@ class Nexus {
92
92
  }
93
93
  }
94
94
  } catch (e) {
95
- console.warn('Warning: failed to auto-configure template providers:', e?.message || e);
95
+ logger.warn('Warning: failed to auto-configure template providers:', e?.message || e);
96
96
  }
97
97
 
98
-
99
- // Convenience: handle mongoUri early (before storage connect)
98
+ // Handle mongoUri early (before storage connect)
100
99
  try {
101
100
  if (options.mongoUri) {
102
101
  if (storage === 'mongo') {
@@ -104,12 +103,11 @@ class Nexus {
104
103
  storageConfig.mongoUri = options.mongoUri;
105
104
  }
106
105
  } else {
107
- // If not using MongoStorage but a URI is provided, initialize default mongoose connection
108
106
  await this.messaging.initializeMongoDB(options.mongoUri);
109
107
  }
110
108
  }
111
109
  } catch (dbErr) {
112
- console.warn('[Nexus] mongo convenience warning:', dbErr?.message || dbErr);
110
+ logger.warn('[Nexus] mongo convenience warning:', dbErr?.message || dbErr);
113
111
  }
114
112
 
115
113
  // Initialize storage if provided
@@ -119,9 +117,8 @@ class Nexus {
119
117
  if (typeof storage === 'string') {
120
118
  this.storage = createStorage(storage, storageConfig || {});
121
119
  } else if (typeof storage === 'object' && (storage.saveMessage || storage.saveInteractive)) {
122
- this.storage = storage; // external adapter instance
120
+ this.storage = storage;
123
121
  } else {
124
- // default to mongo if truthy but not recognized
125
122
  this.storage = createStorage('mongo', storageConfig || {});
126
123
  }
127
124
  if (this.storage && typeof this.storage.connect === 'function') {
@@ -129,7 +126,7 @@ class Nexus {
129
126
  }
130
127
  this.messaging.setMessageStorage(this.storage);
131
128
  } catch (e) {
132
- console.warn('Warning: storage initialization failed:', e?.message || e);
129
+ logger.warn('Warning: storage initialization failed:', e?.message || e);
133
130
  }
134
131
  }
135
132
 
@@ -150,7 +147,7 @@ class Nexus {
150
147
  llmConfigModule.setOpenAIClient(providerInstance.getClient());
151
148
  }
152
149
  } catch (err) {
153
- console.warn('[Nexus] Failed to expose OpenAI provider:', err?.message || err);
150
+ logger.warn('[Nexus] Failed to expose OpenAI provider:', err?.message || err);
154
151
  }
155
152
  }
156
153
 
@@ -171,7 +168,7 @@ class Nexus {
171
168
  runtimeConfig.set('AIRTABLE_API_KEY', options.airtable.apiKey);
172
169
  }
173
170
  } catch (cfgErr) {
174
- console.warn('[Nexus] convenience config warning:', cfgErr?.message || cfgErr);
171
+ logger.warn('[Nexus] convenience config warning:', cfgErr?.message || cfgErr);
175
172
  }
176
173
 
177
174
 
@@ -195,7 +192,7 @@ class Nexus {
195
192
  }
196
193
  }
197
194
  } catch (e) {
198
- console.warn('Warning: failed to configure assistants:', e?.message || e);
195
+ logger.warn('Warning: failed to configure assistants:', e?.message || e);
199
196
  }
200
197
 
201
198
  this.isInitialized = true;
@@ -1,6 +1,7 @@
1
1
 
2
2
  const { toTwilioContent } = require('./twilioMapper');
3
3
  const { registerFlow, getFlow, listFlows, registerInteractiveHandler, listInteractiveHandlers } = require('./registry');
4
+ const { logger } = require('../utils/logger');
4
5
 
5
6
  async function sendInteractive(nexusOrMessaging, params) {
6
7
  const { code, spec, id, variables } = params || {};
@@ -28,16 +29,6 @@ async function sendInteractive(nexusOrMessaging, params) {
28
29
  throw new Error('Interactive/flows not supported for this provider');
29
30
  }
30
31
 
31
- module.exports = {
32
- registerFlow,
33
- getFlow,
34
- listFlows,
35
- sendInteractive,
36
- registerInteractiveHandler,
37
- attachInteractiveRouter
38
- };
39
-
40
-
41
32
  function _matchInteractive(match, interactive) {
42
33
  if (!match) return true;
43
34
  if (!interactive) return false;
@@ -76,7 +67,7 @@ function attachInteractiveRouter(nexusOrMessaging) {
76
67
  await handler(messageData, messaging);
77
68
  }
78
69
  } catch (e) {
79
- console.warn('Interactive handler error:', e && e.message || e);
70
+ logger.warn('Interactive handler error:', e && e.message || e);
80
71
  }
81
72
  }
82
73
  };
@@ -84,3 +75,12 @@ function attachInteractiveRouter(nexusOrMessaging) {
84
75
  bus.on('interactive:received', handler);
85
76
  return () => bus.off && bus.off('interactive:received', handler);
86
77
  }
78
+
79
+ module.exports = {
80
+ registerFlow,
81
+ getFlow,
82
+ listFlows,
83
+ sendInteractive,
84
+ registerInteractiveHandler,
85
+ attachInteractiveRouter
86
+ };
@@ -1,41 +1,36 @@
1
1
  const { AsyncLocalStorage } = require('async_hooks');
2
2
  const { v4: uuidv4 } = require('uuid');
3
+ const { setRequestIdGetter } = require('../utils/logger');
3
4
 
4
5
  const requestContext = new AsyncLocalStorage();
5
6
 
6
7
  function getRequestId() {
7
8
  const store = requestContext.getStore();
8
- return store?.requestId || 'no-context';
9
+ return store?.requestId || null;
9
10
  }
10
11
 
12
+ setRequestIdGetter(getRequestId);
13
+
11
14
  function requestIdMiddleware(req, res, next) {
12
15
  const requestId = uuidv4().substring(0, 8);
13
16
  req.requestId = requestId;
14
17
  res.setHeader('X-Request-Id', requestId);
15
18
 
16
19
  requestContext.run({ requestId }, () => {
17
- console.log(`[${requestId}] → ${req.method} ${req.path}`);
18
-
19
20
  const startTime = Date.now();
21
+
20
22
  res.on('finish', () => {
21
23
  const duration = Date.now() - startTime;
22
- console.log(`[${requestId}] ← ${res.statusCode} ${req.method} ${req.path} (${duration}ms)`);
24
+ if (process.env.LOG_HTTP_REQUESTS === 'true') {
25
+ console.log(`[${requestId}] ${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`);
26
+ }
23
27
  });
24
28
 
25
29
  next();
26
30
  });
27
31
  }
28
32
 
29
- const logger = {
30
- log: (...args) => console.log(`[${getRequestId()}]`, ...args),
31
- error: (...args) => console.error(`[${getRequestId()}]`, ...args),
32
- warn: (...args) => console.warn(`[${getRequestId()}]`, ...args),
33
- info: (...args) => console.info(`[${getRequestId()}]`, ...args),
34
- debug: (...args) => console.debug(`[${getRequestId()}]`, ...args)
35
- };
36
-
37
33
  module.exports = {
38
34
  requestIdMiddleware,
39
- getRequestId,
40
- logger
35
+ getRequestId
41
36
  };
@@ -2,6 +2,7 @@ const mongoose = require('mongoose');
2
2
  const moment = require('moment-timezone');
3
3
  const { getRecordByFilter } = require('../services/airtableService');
4
4
  const { Monitoreo_ID } = require('../config/airtableConfig');
5
+ const { logger } = require('../utils/logger');
5
6
 
6
7
 
7
8
  const messageSchema = new mongoose.Schema({
@@ -79,7 +80,7 @@ async function getClinicalContext(whatsappId) {
79
80
  }
80
81
  return null;
81
82
  } catch (error) {
82
- console.error('Error fetching clinical context from Airtable:', error);
83
+ logger.error('Error fetching clinical context from Airtable:', error);
83
84
  return null;
84
85
  }
85
86
  }
@@ -116,9 +117,9 @@ async function insertMessage(values) {
116
117
  { upsert: true, new: true }
117
118
  );
118
119
 
119
- console.log('[MongoStorage] Message inserted or updated successfully');
120
+ logger.info('[MongoStorage] Message inserted or updated successfully');
120
121
  } catch (err) {
121
- console.error('[MongoStorage] Error inserting message:', err);
122
+ logger.error('[MongoStorage] Error inserting message:', err);
122
123
  throw err;
123
124
  }
124
125
  }
@@ -173,7 +174,7 @@ async function getContactDisplayName(contactNumber) {
173
174
  return contactNumber;
174
175
  }
175
176
  } catch (error) {
176
- console.error('[MongoStorage] Error fetching display name for ${contactNumber}:', error);
177
+ logger.error('[MongoStorage] Error fetching display name for ${contactNumber}:', error);
177
178
  return contactNumber;
178
179
  }
179
180
  }
@@ -1,5 +1,6 @@
1
1
  const { OpenAI } = require('openai');
2
2
  const { retryWithBackoff } = require('../utils/retryHelper');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  const DEFAULT_MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '10', 10);
5
6
  const PROVIDER_NAME = 'OpenAIAssistantsProvider';
@@ -274,7 +275,7 @@ class OpenAIAssistantsProvider {
274
275
  const result = await assistant.executeTool(name, args);
275
276
  outputs.push({ tool_call_id: call.id, output: typeof result === 'string' ? result : JSON.stringify(result) });
276
277
  } catch (error) {
277
- console.error('[OpenAIAssistantsProvider] Tool execution failed', error);
278
+ logger.error('[OpenAIAssistantsProvider] Tool execution failed', error);
278
279
  outputs.push({
279
280
  tool_call_id: call.id,
280
281
  output: JSON.stringify({ success: false, error: error?.message || 'Tool execution failed' })
@@ -294,19 +295,19 @@ class OpenAIAssistantsProvider {
294
295
  async checkRunStatus(assistant, thread_id, run_id, retryCount = 0, maxRetries = DEFAULT_MAX_RETRIES, actionHandled = false) {
295
296
  try {
296
297
  const run = await this.getRun({ threadId: thread_id, runId: run_id });
297
- console.log(`Status: ${run.status} ${thread_id} ${run_id} (attempt ${retryCount + 1})`);
298
+ logger.info(`Status: ${run.status} ${thread_id} ${run_id} (attempt ${retryCount + 1})`);
298
299
 
299
300
  const failedStatuses = ['failed', 'expired', 'incomplete', 'errored'];
300
301
  if (failedStatuses.includes(run.status)) {
301
- console.log(`Run failed. ${run.status} `);
302
- console.log('Error:');
303
- console.log(run);
302
+ logger.info(`Run failed. ${run.status} `);
303
+ logger.info('Error:');
304
+ logger.info(run);
304
305
  return {run, completed: false};
305
306
  } else if (run.status === 'cancelled') {
306
- console.log('cancelled');
307
+ logger.info('cancelled');
307
308
  return {run, completed: true};
308
309
  } else if (run.status === 'requires_action') {
309
- console.log('requires_action');
310
+ logger.info('requires_action');
310
311
  if (retryCount >= maxRetries) {
311
312
  return {run, completed: false};
312
313
  }
@@ -325,11 +326,11 @@ class OpenAIAssistantsProvider {
325
326
  await new Promise(resolve => setTimeout(resolve, 1000));
326
327
  return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, actionHandled);
327
328
  } else {
328
- console.log('Run completed.');
329
+ logger.info('Run completed.');
329
330
  return {run, completed: true};
330
331
  }
331
332
  } catch (error) {
332
- console.error('Error checking run status:', error);
333
+ logger.error('Error checking run status:', error);
333
334
  return {run: null, completed: false};
334
335
  }
335
336
  }