@peopl-health/nexus 2.4.7 → 2.4.8

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 (45) 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 +9 -8
  22. package/lib/helpers/llmsHelper.js +5 -4
  23. package/lib/helpers/mediaHelper.js +3 -2
  24. package/lib/helpers/messageHelper.js +12 -11
  25. package/lib/helpers/processHelper.js +1 -1
  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 +18 -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 +20 -20
  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/sanitizer.js +0 -6
  45. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  const { getRecordByFilter } = require('../services/airtableService');
2
2
  const { Monitoreo_ID } = require('../config/airtableConfig');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  const getPatientInfoController = async (req, res) => {
5
6
  try {
@@ -15,7 +16,7 @@ const getPatientInfoController = async (req, res) => {
15
16
  const clinicalContext = records[0]['clinical-context-json'];
16
17
  res.status(200).json({ success: true, whatsappId: id, clinicalContext, patientInfo: records[0] });
17
18
  } catch (error) {
18
- console.error('Error fetching patient info:', error);
19
+ logger.error('Error fetching patient info:', error);
19
20
  res.status(500).json({ success: false, error: error.message });
20
21
  }
21
22
  };
@@ -1,5 +1,6 @@
1
1
  const { QualityMessage } = require('../models/qualityMessageModel');
2
2
  const { Message } = require('../models/messageModel');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  const addQualityVoteController = async (req, res) => {
5
6
  try {
@@ -22,7 +23,7 @@ const addQualityVoteController = async (req, res) => {
22
23
 
23
24
  res.status(201).json({ success: true, qualityVote });
24
25
  } catch (error) {
25
- console.error('Error adding quality vote:', error);
26
+ logger.error('Error adding quality vote:', error);
26
27
  res.status(500).json({ success: false, error: error.message });
27
28
  }
28
29
  };
@@ -41,7 +42,7 @@ const getQualityVotesByMessageController = async (req, res) => {
41
42
 
42
43
  res.status(200).json({ success: true, messageId: message_id, summary, votes });
43
44
  } catch (error) {
44
- console.error('Error fetching quality votes:', error);
45
+ logger.error('Error fetching quality votes:', error);
45
46
  res.status(500).json({ success: false, error: error.message });
46
47
  }
47
48
  };
@@ -52,7 +53,7 @@ const getQualityVotesByVoterController = async (req, res) => {
52
53
  const votes = await QualityMessage.find({ voter_username }).populate('message_id').sort({ createdAt: -1 });
53
54
  res.status(200).json({ success: true, voterUsername: voter_username, count: votes.length, votes });
54
55
  } catch (error) {
55
- console.error('Error fetching voter quality votes:', error);
56
+ logger.error('Error fetching voter quality votes:', error);
56
57
  res.status(500).json({ success: false, error: error.message });
57
58
  }
58
59
  };
@@ -68,7 +69,7 @@ const getQualityVoteByMessageAndVoterController = async (req, res) => {
68
69
 
69
70
  res.status(200).json({ success: true, vote });
70
71
  } catch (error) {
71
- console.error('Error fetching quality vote:', error);
72
+ logger.error('Error fetching quality vote:', error);
72
73
  res.status(500).json({ success: false, error: error.message });
73
74
  }
74
75
  };
@@ -1,3 +1,5 @@
1
+ const { logger } = require('../utils/logger');
2
+
1
3
  // Check if provider supports templates
2
4
  const checkTemplateSupport = () => {
3
5
  if (!nexusProvider) {
@@ -29,7 +31,7 @@ const configureNexusProvider = (provider) => {
29
31
  try {
30
32
  configureTemplateProvider(provider);
31
33
  } catch (err) {
32
- console.warn('[templateController] Failed to propagate provider to template structure:', err?.message || err);
34
+ logger.warn('[templateController] Failed to propagate provider to template structure:', err?.message || err);
33
35
  }
34
36
  }
35
37
  };
@@ -132,7 +134,7 @@ const createTemplate = async (req, res) => {
132
134
 
133
135
  // Make sure the dates are valid before saving
134
136
  if (isNaN(dateCreated.getTime())) {
135
- console.log('Invalid dateCreated, using current date');
137
+ logger.info('Invalid dateCreated, using current date');
136
138
  dateCreated = currentDate;
137
139
  }
138
140
 
@@ -182,8 +184,8 @@ const listTemplates = async (req, res) => {
182
184
  const { status: queryStatus, type, limit = 50, showFlows: queryShowFlows } = req.query;
183
185
  const TemplateModel = getTemplateModel();
184
186
 
185
- console.log('nexusProvider:', nexusProvider ? 'configured' : 'not configured');
186
- console.log('nexusProvider methods:', nexusProvider ? Object.keys(nexusProvider) : 'none');
187
+ logger.info('nexusProvider:', nexusProvider ? 'configured' : 'not configured');
188
+ logger.info('nexusProvider methods:', nexusProvider ? Object.keys(nexusProvider) : 'none');
187
189
 
188
190
  checkTemplateSupport();
189
191
  const twilioRawTemplates = await nexusProvider.listTemplates({ limit: parseInt(limit, 10) });
@@ -224,7 +226,7 @@ const listTemplates = async (req, res) => {
224
226
  else if (reqData.status === 'PENDING') updateFields.status = 'PENDING';
225
227
  }
226
228
  } catch (err) {
227
- console.warn(`Could not fetch approval status for template ${twilioTemplate.sid}:`, err.message);
229
+ logger.warn(`Could not fetch approval status for template ${twilioTemplate.sid}:`, err.message);
228
230
  }
229
231
 
230
232
  const onInsertFields = {
@@ -281,7 +283,7 @@ const listTemplates = async (req, res) => {
281
283
  });
282
284
 
283
285
  } catch (error) {
284
- console.error('Error in listTemplates:', error);
286
+ logger.error('Error in listTemplates:', error);
285
287
  return handleApiError(res, error, 'Failed to list templates');
286
288
  }
287
289
  };
@@ -557,7 +559,7 @@ const getPredefinedTemplates = (req, res) => {
557
559
  });
558
560
 
559
561
  } catch (error) {
560
- console.error('Error getting predefined templates:', error);
562
+ logger.error('Error getting predefined templates:', error);
561
563
  res.status(500).json({
562
564
  success: false,
563
565
  error: 'Failed to retrieve predefined templates',
@@ -584,7 +586,7 @@ const getCompleteTemplate = async (req, res) => {
584
586
  try {
585
587
  checkTemplateSupport();
586
588
  const twilioTemplate = await nexusProvider.getTemplate(sid);
587
- console.log('Fetched template from Twilio:', twilioTemplate);
589
+ logger.info('Fetched template from Twilio:', twilioTemplate);
588
590
 
589
591
  let body = '';
590
592
  let footer = '';
@@ -659,7 +661,7 @@ const getCompleteTemplate = async (req, res) => {
659
661
  template.type = type;
660
662
  }
661
663
  } catch (twilioError) {
662
- console.error('Error fetching complete template from Twilio:', twilioError);
664
+ logger.error('Error fetching complete template from Twilio:', twilioError);
663
665
  }
664
666
  }
665
667
 
@@ -1,6 +1,8 @@
1
1
  const multer = require('multer');
2
2
  const { v4: uuidv4 } = require('uuid');
3
3
  const { generatePresignedUrl, uploadBufferToS3 } = require('../config/awsConfig');
4
+ const { logger } = require('../utils/logger');
5
+
4
6
  const bucketName = process.env.AWS_S3_BUCKET_NAME;
5
7
 
6
8
  const storage = multer.memoryStorage();
@@ -66,7 +68,7 @@ const handleFileUpload = async (req, res) => {
66
68
  contentType: file.mimetype
67
69
  });
68
70
  } catch (error) {
69
- console.error('Error uploading file:', error);
71
+ logger.error('Error uploading file:', error);
70
72
  res.status(500).json({
71
73
  success: false,
72
74
  error: error.message || 'Failed to upload file'
@@ -3,6 +3,7 @@ const { addMsgAssistant, replyAssistant } = require('../services/assistantServic
3
3
  const { createProvider } = require('../adapters/registry');
4
4
  const runtimeConfig = require('../config/runtimeConfig');
5
5
  const { hasPreprocessingHandler, invokePreprocessingHandler } = require('../services/preprocessingHooks');
6
+ const { logger } = require('../utils/logger');
6
7
 
7
8
  const mongoose = require('mongoose');
8
9
  const OpenAI = require('openai');
@@ -58,9 +59,9 @@ class NexusMessaging {
58
59
  try {
59
60
  await mongoose.connect(mongoUri);
60
61
  this.mongodb = mongoose.connection;
61
- console.log('MongoDB connected successfully');
62
+ logger.info('MongoDB connected successfully');
62
63
  } catch (error) {
63
- console.error('MongoDB connection failed:', error);
64
+ logger.error('MongoDB connection failed', { error: error.message });
64
65
  throw error;
65
66
  }
66
67
  }
@@ -359,7 +360,7 @@ class NexusMessaging {
359
360
  return await this.handleFlow(messageData);
360
361
  } else {
361
362
  // For regular messages and media, use batching if enabled
362
- console.log('Batching config:', this.batchingConfig);
363
+ logger.info('Batching config:', this.batchingConfig);
363
364
  if (this.batchingConfig.enabled && chatId) {
364
365
  return await this._handleWithBatching(messageData, chatId);
365
366
  } else {
@@ -437,7 +438,7 @@ class NexusMessaging {
437
438
  }
438
439
 
439
440
  if (!from || !body) {
440
- console.warn('Unable to resolve assistant inputs from message, skipping automatic reply.');
441
+ logger.warn('Unable to resolve assistant inputs from message, skipping automatic reply.');
441
442
  return;
442
443
  }
443
444
 
@@ -452,7 +453,7 @@ class NexusMessaging {
452
453
  });
453
454
  }
454
455
  } catch (error) {
455
- console.error('Error in handleMessageWithAssistant:', error);
456
+ logger.error('Error in handleMessageWithAssistant:', error);
456
457
  }
457
458
  }
458
459
 
@@ -488,7 +489,7 @@ class NexusMessaging {
488
489
  const { from, body } = this._extractAssistantInputs(messageData);
489
490
 
490
491
  if (!from) {
491
- console.warn('Unable to resolve sender for media message, skipping automatic reply.');
492
+ logger.warn('Unable to resolve sender for media message, skipping automatic reply.');
492
493
  return;
493
494
  }
494
495
 
@@ -516,7 +517,7 @@ class NexusMessaging {
516
517
  });
517
518
  }
518
519
  } catch (error) {
519
- console.error('Error in handleMediaWithAssistant:', error);
520
+ logger.error('Error in handleMediaWithAssistant:', error);
520
521
  }
521
522
  }
522
523
 
@@ -530,16 +531,15 @@ class NexusMessaging {
530
531
 
531
532
  const bucketName = runtimeConfig.get('AWS_S3_BUCKET_NAME') || process.env.AWS_S3_BUCKET_NAME;
532
533
  if (!bucketName) {
533
- console.warn('[NexusMessaging] AWS_S3_BUCKET_NAME not configured. Skipping media persistence.');
534
+ logger.warn('[NexusMessaging] AWS_S3_BUCKET_NAME not configured. Skipping media persistence.');
534
535
  return;
535
536
  }
536
537
 
537
538
  const { processTwilioMediaMessage } = require('../helpers/twilioMediaProcessor');
538
- const { logger } = require('../utils/logger');
539
- const mediaItems = await processTwilioMediaMessage(raw, logger, bucketName);
539
+ const mediaItems = await processTwilioMediaMessage(raw, bucketName);
540
540
 
541
541
  if (!mediaItems || mediaItems.length === 0) {
542
- console.warn('[NexusMessaging] Media processing returned no items for incoming message.');
542
+ logger.warn('[NexusMessaging] Media processing returned no items for incoming message.');
543
543
  return;
544
544
  }
545
545
 
@@ -573,13 +573,13 @@ class NexusMessaging {
573
573
  values.media = mediaPayload;
574
574
 
575
575
  await insertMessage(values);
576
- console.log('[NexusMessaging] Media message stored via legacy inserter', {
576
+ logger.info('[NexusMessaging] Media message stored via legacy inserter', {
577
577
  messageId: values.message_id,
578
578
  numero: values.numero
579
579
  });
580
580
  }
581
581
  } catch (error) {
582
- console.error('[NexusMessaging] Failed to ensure media persistence:', error);
582
+ logger.error('[NexusMessaging] Failed to ensure media persistence:', error);
583
583
  }
584
584
  }
585
585
 
@@ -618,7 +618,7 @@ class NexusMessaging {
618
618
  // Clear existing timeout if there is one
619
619
  if (this.pendingResponses.has(chatId)) {
620
620
  clearTimeout(this.pendingResponses.get(chatId));
621
- console.log(`Received additional message from ${chatId}, resetting wait timer`);
621
+ logger.info(`Received additional message from ${chatId}, resetting wait timer`);
622
622
  }
623
623
 
624
624
  // Calculate wait time with random variation
@@ -631,12 +631,12 @@ class NexusMessaging {
631
631
  this.pendingResponses.delete(chatId);
632
632
  await this._handleBatchedMessages(chatId);
633
633
  } catch (error) {
634
- console.error(`Error handling batched messages for ${chatId}:`, error);
634
+ logger.error(`Error handling batched messages for ${chatId}:`, error);
635
635
  }
636
636
  }, waitTime);
637
637
 
638
638
  this.pendingResponses.set(chatId, timeoutId);
639
- console.log(`Waiting ${Math.round(waitTime/1000)} seconds for more messages from ${chatId}`);
639
+ logger.info(`Waiting ${Math.round(waitTime/1000)} seconds for more messages from ${chatId}`);
640
640
  }
641
641
 
642
642
  /**
@@ -644,7 +644,7 @@ class NexusMessaging {
644
644
  */
645
645
  async _handleBatchedMessages(chatId) {
646
646
  try {
647
- console.log(`Processing batched messages from ${chatId} (including media if any)`);
647
+ logger.info(`Processing batched messages from ${chatId} (including media if any)`);
648
648
 
649
649
  // Get assistant response
650
650
  const botResponse = await replyAssistant(chatId);
@@ -661,7 +661,7 @@ class NexusMessaging {
661
661
  this.events.emit('messages:batched', { chatId, response: botResponse });
662
662
 
663
663
  } catch (error) {
664
- console.error('Error in batched message handling:', error);
664
+ logger.error('Error in batched message handling:', error);
665
665
  }
666
666
  }
667
667
 
@@ -5,7 +5,7 @@ const { createProvider } = require('../providers/createProvider.js');
5
5
  const { withTracing } = require('../utils/tracingDecorator');
6
6
 
7
7
  const { getRecordByFilter } = require('../services/airtableService.js');
8
- const { logger } = require('../middleware/requestId');
8
+ const { logger } = require('../utils/logger');
9
9
 
10
10
  const DEFAULT_MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '30', 10);
11
11
 
@@ -28,7 +28,7 @@ async function checkIfFinished(text) {
28
28
 
29
29
  return completion.choices[0].message.content;
30
30
  } catch (error) {
31
- console.error('[checkIfFinished] Error checking run status:', error);
31
+ logger.error('[checkIfFinished] Error checking run status:', error);
32
32
  }
33
33
  }
34
34
 
@@ -78,7 +78,7 @@ const runAssistantAndWait = async ({
78
78
  let completed = false;
79
79
 
80
80
  try {
81
- logger.log('[runAssistantAndWait] RUN ID', run.id, 'THREAD ID', thread.getConversationId(), 'ASSISTANT ID', thread.getAssistantId());
81
+ logger.info('[runAssistantAndWait] Run started', { runId: run.id, threadId: thread.getConversationId(), assistantId: thread.getAssistantId() });
82
82
  ({run, completed} = await provider.checkRunStatus(assistant, thread.getConversationId(), run.id, 0, maxRetries));
83
83
  } finally {
84
84
  if (filter) {
@@ -102,7 +102,7 @@ const executeAssistantAttempt = async (thread, assistant, runConfig, attemptNumb
102
102
  runConfig
103
103
  });
104
104
 
105
- logger.log(`[executeAssistantAttempt] Attempt ${attemptNumber}: completed=${result.completed}, output=${result.output || '(empty)'}`);
105
+ logger.info(`[executeAssistantAttempt] Attempt ${attemptNumber}: completed=${result.completed}, output=${result.output || '(empty)'}`);
106
106
 
107
107
  return result;
108
108
  };
@@ -135,17 +135,16 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
135
135
  const delay = retries === 1
136
136
  ? 500
137
137
  : Math.min(1000 * Math.pow(1.5, retries - 1), 5000);
138
- logger.log(`[runAssistantWithRetries] Retry ${retries}, waiting ${delay}ms`);
138
+ logger.info(`[runAssistantWithRetries] Retry ${retries}, waiting ${delay}ms`);
139
139
  await new Promise(resolve => setTimeout(resolve, delay));
140
140
  }
141
141
  } while (retries < maxRetries && (!completed || !output));
142
142
 
143
143
  const predictionTimeMs = Date.now() - startTime;
144
144
 
145
- if (run?.last_error) logger.log('[runAssistantWithRetries] RUN LAST ERROR:', run.last_error);
146
- logger.log('[runAssistantWithRetries] RUN STATUS', completed);
147
- logger.log('[runAssistantWithRetries] OUTPUT', output);
148
- logger.log('[runAssistantWithRetries] TIMING', { predictionTimeMs, retries });
145
+ if (run?.last_error) logger.warn('[runAssistantWithRetries] Run error', { error: run.last_error });
146
+ logger.info('[runAssistantWithRetries] Run completed', { completed, outputLength: output?.length || 0 });
147
+ logger.info('[runAssistantWithRetries] TIMING', { predictionTimeMs, retries });
149
148
 
150
149
  return { run, output, completed, retries, predictionTimeMs };
151
150
  };
@@ -2,6 +2,7 @@ const { Message, insertMessage, getMessageValues } = require('../models/messageM
2
2
  const { uploadMediaToS3 } = require('./mediaHelper.js');
3
3
  const { isRecentMessage } = require('./messageHelper.js');
4
4
  const { downloadMediaMessage } = require('baileys');
5
+ const { logger } = require('../utils/logger');
5
6
 
6
7
 
7
8
  async function processMessage(message, messageType) {
@@ -19,7 +20,7 @@ async function processMessage(message, messageType) {
19
20
 
20
21
  return values;
21
22
  } catch (error) {
22
- console.log('Failed to process message: %s', error.stack);
23
+ logger.error('Failed to process message', { error: error.message, stack: error.stack });
23
24
  throw error;
24
25
  }
25
26
  }
@@ -44,7 +45,7 @@ async function processMediaMessage(message, logger, messageType, bucketName, soc
44
45
 
45
46
  return values;
46
47
  } catch (error) {
47
- console.log('Failed to process message: %s', error.stack);
48
+ logger.error('Failed to process message', { error: error.message, stack: error.stack });
48
49
  throw error;
49
50
  }
50
51
  }
@@ -128,7 +129,7 @@ async function downloadMedia(message, logger, sock) {
128
129
  );
129
130
  return buffer;
130
131
  } catch (error) {
131
- console.log('Failed to download media:', error.stack);
132
+ logger.error('Failed to download media', { error: error.message, stack: error.stack });
132
133
  throw error;
133
134
  }
134
135
  }
@@ -7,6 +7,7 @@ const sharp = require('sharp');
7
7
  const { downloadFileFromS3 } = require('../config/awsConfig.js');
8
8
  const { Message } = require('../models/messageModel.js');
9
9
  const { sanitizeFilename } = require('../utils/sanitizer.js');
10
+ const { logger } = require('../utils/logger');
10
11
 
11
12
  async function convertPdfToImages(pdfName) {
12
13
  const outputDir = path.join(__dirname, 'assets', 'tmp');
@@ -19,7 +20,7 @@ async function convertPdfToImages(pdfName) {
19
20
 
20
21
  return new Promise((resolve, reject) => {
21
22
  const args = ['-jpeg', pdfPath, outputPattern];
22
- console.log('[convertPdfToImages] Running: pdftoppm', args.join(' '));
23
+ logger.info('[convertPdfToImages] Running: pdftoppm', args.join(' '));
23
24
 
24
25
  execFile('pdftoppm', args, (error, stdout, stderr) => {
25
26
  if (error) {
@@ -71,7 +72,7 @@ async function combineImagesToPDF(config) {
71
72
  imageFiles.sort();
72
73
  }
73
74
 
74
- console.log(`Found ${imageFiles.length} image files to combine`);
75
+ logger.info(`Found ${imageFiles.length} image files to combine`);
75
76
 
76
77
  const pdfDoc = await PDFDocument.create();
77
78
  const processedFiles = [];
@@ -79,7 +80,7 @@ async function combineImagesToPDF(config) {
79
80
  for (const [index, file] of imageFiles.entries()) {
80
81
  try {
81
82
  const filePath = path.join(inputDir, file);
82
- console.log(`Processing file ${index + 1}/${imageFiles.length}: ${file}`);
83
+ logger.info(`Processing file ${index + 1}/${imageFiles.length}: ${file}`);
83
84
 
84
85
  const imageBuffer = await fs.readFile(filePath);
85
86
  const pngBuffer = await sharp(imageBuffer)
@@ -99,11 +100,11 @@ async function combineImagesToPDF(config) {
99
100
 
100
101
  processedFiles.push(filePath);
101
102
  } catch (error) {
102
- console.error(`Error processing file ${file}:`, error);
103
+ logger.error(`Error processing file ${file}:`, error);
103
104
  }
104
105
  }
105
106
 
106
- console.log('Combined PDF created successfully');
107
+ logger.info('Combined PDF created successfully');
107
108
 
108
109
  return {
109
110
  pdfBuffer: await pdfDoc.save(),
@@ -120,12 +121,12 @@ const cleanupFiles = async (files) => {
120
121
  } catch (error) {
121
122
  if (error?.code !== 'ENOENT') {
122
123
  const safeFileName = filePath ? filePath.split('/').pop().replace(/^[^-]+-[^-]+-/, 'xxx-xxx-') : 'unknown';
123
- console.warn(`[cleanupFiles] Error deleting ${safeFileName}:`, error?.message || String(error));
124
+ logger.warn(`[cleanupFiles] Error deleting ${safeFileName}:`, error?.message || String(error));
124
125
  }
125
126
  }
126
127
  }));
127
128
 
128
- console.log(`[cleanupFiles] Cleaned up ${files.length} files`);
129
+ logger.info(`[cleanupFiles] Cleaned up ${files.length} files`);
129
130
  };
130
131
 
131
132
  async function downloadMediaAndCreateFile(code, reply) {
@@ -138,7 +139,7 @@ async function downloadMediaAndCreateFile(code, reply) {
138
139
  if (!resultMedia) return [];
139
140
 
140
141
  if (!resultMedia.media || !resultMedia.media.key) {
141
- console.log('[downloadMediaAndCreateFile] No valid media found for message:', reply.message_id);
142
+ logger.info('[downloadMediaAndCreateFile] No valid media found for message:', reply.message_id);
142
143
  return [];
143
144
  }
144
145
 
@@ -1,4 +1,5 @@
1
1
  const llmConfig = require('../config/llmConfig.js');
2
+ const { logger } = require('../utils/logger');
2
3
  const fs = require('fs');
3
4
  const mime = require('mime-types');
4
5
 
@@ -7,7 +8,7 @@ async function analyzeImage(imagePath, isSticker = false) {
7
8
  try {
8
9
  const anthropicClient = llmConfig.anthropicClient;
9
10
  if (!anthropicClient || !anthropicClient.messages) {
10
- console.warn('[llmsHelper] Anthropics client not configured; skipping image analysis');
11
+ logger.warn('[llmsHelper] Anthropics client not configured; skipping image analysis');
11
12
  return {
12
13
  description: 'Image could not be analyzed - Anthropic client not configured',
13
14
  medical_analysis: 'QUALITY_INSUFFICIENT',
@@ -19,7 +20,7 @@ async function analyzeImage(imagePath, isSticker = false) {
19
20
 
20
21
  // Skip only WBMP images (unsupported format)
21
22
  if (imagePath.toLowerCase().includes('.wbmp')) {
22
- console.log('Skipping WBMP image analysis:', imagePath);
23
+ logger.info('Skipping WBMP image or sticker analysis:', imagePath);
23
24
  return {
24
25
  description: 'Unsupported image format',
25
26
  medical_analysis: 'NOT_MEDICAL',
@@ -32,7 +33,7 @@ async function analyzeImage(imagePath, isSticker = false) {
32
33
  // Check MIME type
33
34
  const mimeType = mime.lookup(imagePath) || 'image/jpeg';
34
35
  if (mimeType === 'image/vnd.wap.wbmp') {
35
- console.log('Skipping image with MIME type:', mimeType);
36
+ logger.info('Skipping image with MIME type:', mimeType);
36
37
  return {
37
38
  description: 'Unsupported image format',
38
39
  medical_analysis: 'NOT_MEDICAL',
@@ -231,7 +232,7 @@ Ejemplo 1:
231
232
  return {description: description, medical_analysis: messageAnalysisStr,
232
233
  medical_relevance: isRelevant, has_table: isTable, table_data: table};
233
234
  } catch (error) {
234
- console.error('Error analyzing image:', error);
235
+ logger.error('Error analyzing image:', error);
235
236
  throw error;
236
237
  }
237
238
  }
@@ -2,6 +2,7 @@ const path = require('path');
2
2
  const fs = require('fs');
3
3
  const AWS = require('../config/awsConfig.js');
4
4
  const { sanitizeMediaFilename } = require('../utils/sanitizer.js');
5
+ const { logger } = require('../utils/logger');
5
6
 
6
7
  async function uploadMediaToS3(buffer, messageID, titleFile, bucketName, contentType, messageType) {
7
8
  const extension = getFileExtension(contentType);
@@ -9,13 +10,13 @@ async function uploadMediaToS3(buffer, messageID, titleFile, bucketName, content
9
10
  const fileName = sanitizedTitle
10
11
  ? `${messageType}/${messageID}_${sanitizedTitle}.${extension}`
11
12
  : `${messageType}/${messageID}.${extension}`;
12
- console.log(titleFile, messageType);
13
+ logger.info(titleFile, messageType);
13
14
 
14
15
  try {
15
16
  await AWS.uploadBufferToS3(buffer, bucketName, fileName, contentType);
16
17
  return fileName;
17
18
  } catch (error) {
18
- console.error('Failed to upload media to S3:', error.stack);
19
+ logger.error('Failed to upload media to S3:', error.stack);
19
20
  throw error;
20
21
  }
21
22
  }
@@ -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
  /**
@@ -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
  }