@peopl-health/nexus 2.4.6 → 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 +24 -9
  23. package/lib/helpers/mediaHelper.js +3 -2
  24. package/lib/helpers/messageHelper.js +12 -11
  25. package/lib/helpers/processHelper.js +24 -10
  26. package/lib/helpers/qrHelper.js +2 -1
  27. package/lib/helpers/twilioMediaProcessor.js +45 -35
  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 +39 -6
  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
@@ -5,6 +5,7 @@ const {
5
5
  handleRequiresAction: handleRequiresActionUtil,
6
6
  transformToolsForResponsesAPI: transformToolsForResponsesAPIUtil
7
7
  } = require('./OpenAIResponsesProviderTools');
8
+ const { logger } = require('../utils/logger');
8
9
 
9
10
  const CONVERSATION_PREFIX = 'conv_';
10
11
  const RESPONSE_PREFIX = 'resp_';
@@ -81,7 +82,7 @@ class OpenAIResponsesProvider {
81
82
  : messages;
82
83
 
83
84
  if (messages.length > DEFAULT_MAX_HISTORICAL_MESSAGES) {
84
- console.warn(`[OpenAIResponsesProvider] Capped ${messages.length} → ${DEFAULT_MAX_HISTORICAL_MESSAGES} messages`);
85
+ logger.warn(`[OpenAIResponsesProvider] Capped ${messages.length} → ${DEFAULT_MAX_HISTORICAL_MESSAGES} messages`);
85
86
  }
86
87
 
87
88
  const allItems = this._conversationItems(messagesToProcess);
@@ -108,7 +109,7 @@ class OpenAIResponsesProvider {
108
109
  const totalBatches = Math.ceil(remainingItems.length / MAX_ITEMS_PER_BATCH);
109
110
 
110
111
  // Concise batch summary
111
- console.log(`[OpenAIResponsesProvider] Batching: ${initialItems.length} (create) + ${remainingItems.length} (${totalBatches} batches) = ${totalItems} total`);
112
+ logger.info(`[OpenAIResponsesProvider] Batching: ${initialItems.length} (create) + ${remainingItems.length} (${totalBatches} batches) = ${totalItems} total`);
112
113
 
113
114
  const payload = this._cleanObject({
114
115
  metadata,
@@ -125,7 +126,7 @@ class OpenAIResponsesProvider {
125
126
  return await this._post('/conversations', payload);
126
127
  });
127
128
  } catch (error) {
128
- console.error('[OpenAIResponsesProvider] Failed to create conversation:', error?.message || error);
129
+ logger.error('[OpenAIResponsesProvider] Failed to create conversation:', error?.message || error);
129
130
  throw error;
130
131
  }
131
132
 
@@ -134,7 +135,7 @@ class OpenAIResponsesProvider {
134
135
  try {
135
136
  await this._addItemsInBatches(conversation.id, remainingItems);
136
137
  } catch (error) {
137
- console.error('[OpenAIResponsesProvider] Failed to add remaining messages. Conversation created with partial history:', error?.message || error);
138
+ logger.error('[OpenAIResponsesProvider] Failed to add remaining messages. Conversation created with partial history:', error?.message || error);
138
139
  }
139
140
  }
140
141
 
@@ -168,12 +169,12 @@ class OpenAIResponsesProvider {
168
169
  return await this._post(`/conversations/${id}/items`, { items: batchPayload });
169
170
  });
170
171
  } catch (error) {
171
- console.error(`[OpenAIResponsesProvider] Batch ${batchNumber}/${totalBatches} failed:`, error?.message || error);
172
+ logger.error(`[OpenAIResponsesProvider] Batch ${batchNumber}/${totalBatches} failed:`, error?.message || error);
172
173
  throw error;
173
174
  }
174
175
  }
175
176
 
176
- console.log(`[OpenAIResponsesProvider] Successfully added ${items.length} messages in ${totalBatches} batches`);
177
+ logger.info(`[OpenAIResponsesProvider] Successfully added ${items.length} messages in ${totalBatches} batches`);
177
178
  }
178
179
 
179
180
  /**
@@ -235,7 +236,7 @@ class OpenAIResponsesProvider {
235
236
  if (items.length === 0) return;
236
237
 
237
238
  if (deleteAll) {
238
- console.log(`[OpenAIResponsesProvider] Deleting all ${items.length} items from conversation`);
239
+ logger.info(`[OpenAIResponsesProvider] Deleting all ${items.length} items from conversation`);
239
240
  for (const item of items) {
240
241
  await this.conversations.items.delete(item.id, {conversation_id: id});
241
242
  }
@@ -249,12 +250,12 @@ class OpenAIResponsesProvider {
249
250
  const hasOutput = functionOutputs.some(output => output.call_id === functionCall.call_id);
250
251
 
251
252
  if (!hasOutput) {
252
- console.log(`[OpenAIResponsesProvider] Deleting orphaned function_call: ${functionCall.id} (${functionCall.call_id})`);
253
+ logger.info(`[OpenAIResponsesProvider] Deleting orphaned function_call: ${functionCall.id} (${functionCall.call_id})`);
253
254
  await this.conversations.items.delete(functionCall.id, {conversation_id: id});
254
255
  }
255
256
  }
256
257
  } catch (error) {
257
- console.warn('[OpenAIResponsesProvider] Failed to cleanup conversation:', error?.message);
258
+ logger.warn('[OpenAIResponsesProvider] Failed to cleanup conversation:', error?.message);
258
259
  }
259
260
  }
260
261
 
@@ -288,7 +289,7 @@ class OpenAIResponsesProvider {
288
289
  toolOutputs = pendingOutputs;
289
290
  }
290
291
  } catch (error) {
291
- console.warn('[OpenAIResponsesProvider] Error checking for pending function calls:', error?.message);
292
+ logger.warn('[OpenAIResponsesProvider] Error checking for pending function calls:', error?.message);
292
293
  }
293
294
  }
294
295
 
@@ -324,7 +325,7 @@ class OpenAIResponsesProvider {
324
325
  object: response.object || 'response',
325
326
  };
326
327
  } catch (error) {
327
- console.error('[OpenAIResponsesProvider] Error running conversation:', error);
328
+ logger.error('[OpenAIResponsesProvider] Error running conversation:', error);
328
329
  throw error;
329
330
  }
330
331
  }
@@ -419,7 +420,7 @@ class OpenAIResponsesProvider {
419
420
  async checkRunStatus(assistant, thread_id, run_id, retryCount = 0, maxRetries = DEFAULT_MAX_RETRIES, actionHandled = false) {
420
421
  try {
421
422
  let run = await this.getRun({ threadId: thread_id, runId: run_id });
422
- console.log(`Status: ${run.status} ${thread_id} ${run_id} (attempt ${retryCount + 1})`);
423
+ logger.info(`Status: ${run.status} ${thread_id} ${run_id} (attempt ${retryCount + 1})`);
423
424
 
424
425
  if (run.status === 'completed') {
425
426
  return {run, completed: true};
@@ -432,12 +433,12 @@ class OpenAIResponsesProvider {
432
433
  const needsFunctionCall = run.output?.some(item => item.type === 'function_call');
433
434
  if (needsFunctionCall && !actionHandled) {
434
435
  if (retryCount >= maxRetries) {
435
- console.warn('[OpenAIResponsesProvider] Max retries reached while handling function calls');
436
+ logger.warn('[OpenAIResponsesProvider] Max retries reached while handling function calls');
436
437
  return {run, completed: false};
437
438
  }
438
439
 
439
440
  const outputs = await handleRequiresActionUtil(assistant, run);
440
- console.log('[OpenAIResponsesProvider] Function call outputs:', outputs);
441
+ logger.info('[OpenAIResponsesProvider] Function call outputs:', outputs);
441
442
 
442
443
  if (outputs.length > 0) {
443
444
  try {
@@ -451,7 +452,7 @@ class OpenAIResponsesProvider {
451
452
 
452
453
  return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, true);
453
454
  } catch (submitError) {
454
- console.error('[OpenAIResponsesProvider] Error submitting tool outputs:', submitError);
455
+ logger.error('[OpenAIResponsesProvider] Error submitting tool outputs:', submitError);
455
456
  if (retryCount < maxRetries) {
456
457
  await new Promise(resolve => setTimeout(resolve, 2000));
457
458
  return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, false);
@@ -459,7 +460,7 @@ class OpenAIResponsesProvider {
459
460
  return {run, completed: false};
460
461
  }
461
462
  } else {
462
- console.warn('[OpenAIResponsesProvider] Function calls detected but no outputs generated');
463
+ logger.warn('[OpenAIResponsesProvider] Function calls detected but no outputs generated');
463
464
  return {run, completed: false};
464
465
  }
465
466
  }
@@ -471,7 +472,7 @@ class OpenAIResponsesProvider {
471
472
 
472
473
  return {run, completed: false};
473
474
  } catch (error) {
474
- console.error('[OpenAIResponsesProvider] Error checking run status:', error);
475
+ logger.error('[OpenAIResponsesProvider] Error checking run status:', error);
475
476
  return {run: null, completed: false};
476
477
  }
477
478
  }
@@ -1,6 +1,4 @@
1
- /**
2
- * Tool and function call handling utilities for OpenAIResponsesProvider
3
- */
1
+ const { logger } = require('../utils/logger');
4
2
 
5
3
  /**
6
4
  * Execute a function call and return the output format
@@ -19,7 +17,7 @@ async function executeFunctionCall(assistant, call) {
19
17
  output: typeof result === 'string' ? result : JSON.stringify(result)
20
18
  };
21
19
  } catch (error) {
22
- console.error('[OpenAIResponsesProvider] Tool execution failed', error);
20
+ logger.error('[OpenAIResponsesProvider] Tool execution failed', error);
23
21
  return {
24
22
  type: 'function_call_output',
25
23
  call_id: call.call_id,
@@ -46,7 +44,7 @@ async function handlePendingFunctionCalls(assistant, conversationItems) {
46
44
  return [];
47
45
  }
48
46
 
49
- console.log(`[OpenAIResponsesProvider] Found ${orphanedCalls.length} pending function calls, handling them...`);
47
+ logger.info(`[OpenAIResponsesProvider] Found ${orphanedCalls.length} pending function calls, handling them...`);
50
48
  const outputs = [];
51
49
  for (const call of orphanedCalls) {
52
50
  outputs.push(await executeFunctionCall(assistant, call));
@@ -1,5 +1,6 @@
1
1
  const { OpenAIAssistantsProvider } = require('./OpenAIAssistantsProvider');
2
2
  const { OpenAIResponsesProvider } = require('./OpenAIResponsesProvider');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  const PROVIDER_VARIANTS = {
5
6
  assistants: OpenAIAssistantsProvider,
@@ -10,7 +11,7 @@ const PROVIDER_VARIANTS = {
10
11
  * Returns the appropriate OpenAI provider implementation for the requested variant.
11
12
  */
12
13
  function createProvider(config = {}) {
13
- console.log('Creating OpenAI provider with config:', config);
14
+ logger.debug('Creating OpenAI provider', { variant: config.variant || 'assistants' });
14
15
  const variant = (config.variant || config.providerVariant || config.llmVariant || 'assistants')
15
16
  .toString()
16
17
  .toLowerCase();
@@ -1,4 +1,5 @@
1
1
  const { airtable } = require('../config/airtableConfig');
2
+ const { logger } = require('../utils/logger');
2
3
 
3
4
 
4
5
  async function addRecord(baseID, tableName, fields) {
@@ -6,10 +7,10 @@ async function addRecord(baseID, tableName, fields) {
6
7
  if (!airtable) throw new Error('Airtable not configured. Set AIRTABLE_API_KEY');
7
8
  const base = airtable.base(baseID);
8
9
  const record = await base(tableName).create(fields);
9
- console.log('Record added at', tableName);
10
+ logger.info('Record added at', tableName);
10
11
  return record;
11
12
  } catch (error) {
12
- console.error('Error adding record:', error);
13
+ logger.error('Error adding record:', error);
13
14
  throw error;
14
15
  }
15
16
  }
@@ -30,7 +31,7 @@ async function getRecords(baseID, tableName) {
30
31
  });
31
32
  return records;
32
33
  } catch (error) {
33
- console.error('Error fetching records:', error);
34
+ logger.error('Error fetching records:', error);
34
35
  }
35
36
  }
36
37
 
@@ -51,7 +52,7 @@ async function getRecordByFilter(baseID, tableName, filter, view = 'Grid view')
51
52
  });
52
53
  return records;
53
54
  } catch (error) {
54
- console.error(`Error fetching records by ${filter}:`, error);
55
+ logger.error(`Error fetching records by ${filter}:`, error);
55
56
  }
56
57
  }
57
58
 
@@ -74,7 +75,38 @@ async function updateRecordByFilter(baseID, tableName, filter, updateFields) {
74
75
 
75
76
  return updatedRecords;
76
77
  } catch (error) {
77
- console.error(`Error updating records by ${filter}:`, error);
78
+ logger.error(`Error updating records by ${filter}:`, error);
79
+ }
80
+ }
81
+
82
+
83
+ async function addLinkedRecord(baseID, targetTable, fields, linkConfig) {
84
+ if (!baseID) {
85
+ throw new Error('[addLinkedRecord] Base ID is required');
86
+ }
87
+
88
+ try {
89
+ if (linkConfig) {
90
+ const { referenceTable, referenceFilter, linkFieldName } = linkConfig;
91
+
92
+ const base = airtable.base(baseID);
93
+ const referenceRecords = await base(referenceTable).select({
94
+ filterByFormula: referenceFilter
95
+ }).firstPage();
96
+
97
+ if (linkFieldName && referenceRecords && referenceRecords.length > 0) {
98
+ const recordId = referenceRecords[0].id;
99
+ if (recordId) {
100
+ fields[linkFieldName] = [recordId];
101
+ }
102
+ }
103
+ }
104
+
105
+ const record = await addRecord(baseID, targetTable, [{ fields }]);
106
+ return record;
107
+ } catch (error) {
108
+ console.error('[addLinkedRecord] Error adding linked record:', error);
109
+ throw error;
78
110
  }
79
111
  }
80
112
 
@@ -82,5 +114,6 @@ module.exports = {
82
114
  addRecord,
83
115
  getRecords,
84
116
  getRecordByFilter,
85
- updateRecordByFilter
117
+ updateRecordByFilter,
118
+ addLinkedRecord
86
119
  };
@@ -15,7 +15,7 @@ const { withTracing } = require('../utils/tracingDecorator.js');
15
15
  const { processThreadMessage } = require('../helpers/processHelper.js');
16
16
  const { getLastMessages, updateMessageRecord } = require('../helpers/messageHelper.js');
17
17
  const { combineImagesToPDF, cleanupFiles } = require('../helpers/filesHelper.js');
18
- const { logger } = require('../middleware/requestId');
18
+ const { logger } = require('../utils/logger');
19
19
 
20
20
  const DEFAULT_MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '30', 10);
21
21
 
@@ -136,9 +136,9 @@ const getAssistantById = (assistant_id, thread) => {
136
136
 
137
137
  const createAssistant = async (code, assistant_id, messages=[], force=false) => {
138
138
  const findThread = await Thread.findOne({ code: code });
139
- console.log('[createAssistant] findThread', findThread);
139
+ logger.info('[createAssistant] findThread', findThread);
140
140
  if (findThread && findThread.getConversationId()) {
141
- console.log('[createAssistant] Thread already exists');
141
+ logger.info('[createAssistant] Thread already exists');
142
142
  const updateFields = { active: true, stopped: false };
143
143
  Thread.setAssistantId(updateFields, assistant_id);
144
144
  await Thread.updateOne({ code: code }, { $set: updateFields });
@@ -146,7 +146,7 @@ const createAssistant = async (code, assistant_id, messages=[], force=false) =>
146
146
  }
147
147
 
148
148
  const curRow = await getCurRow(Historial_Clinico_ID, code);
149
- console.log('[createAssistant] curRow', curRow[0]);
149
+ logger.info('[createAssistant] curRow', curRow[0]);
150
150
  const nombre = curRow?.[0]?.['name'] || null;
151
151
  const patientId = curRow?.[0]?.['record_id'] || null;
152
152
 
@@ -175,7 +175,7 @@ const createAssistant = async (code, assistant_id, messages=[], force=false) =>
175
175
  const condition = { $or: [{ code: code }] };
176
176
  const options = { new: true, upsert: true };
177
177
  const updatedThread = await Thread.findOneAndUpdate(condition, {run_id: null, ...thread}, options);
178
- console.log('[createAssistant] Updated thread:', updatedThread);
178
+ logger.info('[createAssistant] Updated thread:', updatedThread);
179
179
 
180
180
  // Delete previous thread
181
181
  if (force) {
@@ -188,12 +188,12 @@ const createAssistant = async (code, assistant_id, messages=[], force=false) =>
188
188
  const addMsgAssistant = async (code, inMessages, role = 'user', reply = false) => {
189
189
  try {
190
190
  const thread = await Thread.findOne({ code: code });
191
- console.log(thread);
191
+ logger.info(thread);
192
192
  if (thread === null) return null;
193
193
 
194
194
  const provider = createProvider({ variant: process.env.VARIANT || 'assistants' });
195
195
  for (const message of inMessages) {
196
- console.log(message);
196
+ logger.info(message);
197
197
  await provider.addMessage({
198
198
  threadId: thread.getConversationId(),
199
199
  role: role,
@@ -209,17 +209,17 @@ const addMsgAssistant = async (code, inMessages, role = 'user', reply = false) =
209
209
  const assistant = getAssistantById(thread.getAssistantId(), thread);
210
210
  do {
211
211
  ({ output, completed } = await runAssistantAndWait({ thread, assistant }));
212
- console.log(`Attempt ${retries + 1}: completed=${completed}, output=${output || '(empty)'}`);
212
+ logger.info(`Attempt ${retries + 1}: completed=${completed}, output=${output || '(empty)'}`);
213
213
 
214
214
  if (completed && output) break;
215
215
  if (retries < maxRetries) await new Promise(resolve => setTimeout(resolve, 2000));
216
216
  retries++;
217
217
  } while (retries <= maxRetries && (!completed || !output));
218
218
 
219
- console.log('THE ANS IS', output);
219
+ logger.info('THE ANS IS', output);
220
220
  return output;
221
221
  } catch (error) {
222
- console.log(error);
222
+ logger.info(error);
223
223
  return null;
224
224
  }
225
225
  };
@@ -254,7 +254,7 @@ const addInstructionCore = async (code, instruction, role = 'user') => {
254
254
  null // no patientReply for instructions
255
255
  );
256
256
 
257
- console.log('RUN RESPONSE', output);
257
+ logger.info('RUN RESPONSE', output);
258
258
  return output;
259
259
  };
260
260
 
@@ -290,14 +290,14 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
290
290
  timings.getMessages = Date.now() - timings.getMessages;
291
291
 
292
292
  if (!patientReply) {
293
- logger.log('[replyAssistantCore] No relevant data found for this assistant.');
293
+ logger.info('[replyAssistantCore] No relevant data found for this assistant.');
294
294
  return null;
295
295
  }
296
296
 
297
297
  const provider = createProvider({ variant: process.env.VARIANT || 'assistants' });
298
298
 
299
299
  timings.processMessages = Date.now();
300
- logger.log(`[replyAssistantCore] Processing ${patientReply.length} messages in parallel`);
300
+ logger.info(`[replyAssistantCore] Processing ${patientReply.length} messages in parallel`);
301
301
 
302
302
  const processResults = await processThreadMessage(code, patientReply, provider);
303
303
 
@@ -308,7 +308,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
308
308
 
309
309
  if (allMessagesToAdd.length > 0) {
310
310
  const threadId = thread.getConversationId();
311
- logger.log(`[replyAssistantCore] Adding ${allMessagesToAdd.length} messages to thread in batch`);
311
+ logger.info(`[replyAssistantCore] Adding ${allMessagesToAdd.length} messages to thread in batch`);
312
312
  await provider.addMessage({ threadId, messages: allMessagesToAdd });
313
313
  }
314
314
 
@@ -319,10 +319,10 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
319
319
 
320
320
  if (urls.length > 0) {
321
321
  timings.pdfCombination = Date.now();
322
- logger.log(`[replyAssistantCore] Processing ${urls.length} URLs for PDF combination`);
322
+ logger.info(`[replyAssistantCore] Processing ${urls.length} URLs for PDF combination`);
323
323
  const { pdfBuffer, processedFiles } = await combineImagesToPDF({ code });
324
324
  timings.pdfCombination = Date.now() - timings.pdfCombination;
325
- logger.log(`[replyAssistantCore] PDF combination complete: ${processedFiles?.length || 0} files processed`);
325
+ logger.info(`[replyAssistantCore] PDF combination complete: ${processedFiles?.length || 0} files processed`);
326
326
 
327
327
  if (pdfBuffer) {
328
328
  const key = `${code}-${Date.now()}-combined.pdf`;
@@ -353,7 +353,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
353
353
  timings.runAssistant = Date.now() - timings.runAssistant;
354
354
  timings.total = Date.now() - startTotal;
355
355
 
356
- logger.log('[Performance Breakdown]', {
356
+ logger.info('[Performance Breakdown]', {
357
357
  code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
358
358
  messageCount: patientReply.length,
359
359
  hasMedia: urls.length > 0,
@@ -378,7 +378,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
378
378
  run_assistant_ms: timings.runAssistant,
379
379
  total_ms: timings.total
380
380
  }
381
- }).catch(err => console.error('[replyAssistantCore] Failed to store metrics:', err));
381
+ }).catch(err => logger.error('[replyAssistantCore] Failed to store metrics:', err));
382
382
  }
383
383
 
384
384
  return output;
@@ -398,7 +398,7 @@ const replyAssistant = withTracing(
398
398
  const switchAssistant = async (code, assistant_id) => {
399
399
  try {
400
400
  const thread = await Thread.findOne({ code: code });
401
- console.log('Inside thread', thread);
401
+ logger.info('Inside thread', thread);
402
402
  if (thread === null) return;
403
403
 
404
404
  const variant = process.env.VARIANT || 'assistants';
@@ -412,7 +412,7 @@ const switchAssistant = async (code, assistant_id) => {
412
412
 
413
413
  await Thread.updateOne({ code }, { $set: updateFields });
414
414
  } catch (error) {
415
- console.log(error);
415
+ logger.info(error);
416
416
  return null;
417
417
  }
418
418
  };
@@ -1,6 +1,7 @@
1
1
  const { Message } = require('../models/messageModel');
2
2
  const { Historial_Clinico_ID } = require('../config/airtableConfig');
3
3
  const { getRecordByFilter } = require('./airtableService');
4
+ const { logger } = require('../utils/logger');
4
5
 
5
6
 
6
7
  const fetchConversationData = async (filter, skip, limit) => {
@@ -16,11 +17,11 @@ const fetchConversationData = async (filter, skip, limit) => {
16
17
  { read: { $exists: false } }
17
18
  ]
18
19
  };
19
- console.log('Applying unread filter');
20
+ logger.info('Applying unread filter');
20
21
  break;
21
22
 
22
23
  case 'no-response':
23
- console.log('Applying no-response filter');
24
+ logger.info('Applying no-response filter');
24
25
  break;
25
26
 
26
27
  case 'recent': {
@@ -30,18 +31,18 @@ const fetchConversationData = async (filter, skip, limit) => {
30
31
  is_group: false,
31
32
  createdAt: { $gt: yesterday }
32
33
  };
33
- console.log('Applying recent filter (last 24 hours)');
34
+ logger.info('Applying recent filter (last 24 hours)');
34
35
  break;
35
36
  }
36
37
 
37
38
  case 'all':
38
39
  default:
39
40
  filterConditions = { is_group: false };
40
- console.log('Applying all conversations filter');
41
+ logger.info('Applying all conversations filter');
41
42
  break;
42
43
  }
43
44
 
44
- console.log('Executing aggregation pipeline...');
45
+ logger.info('Executing aggregation pipeline...');
45
46
  const aggregationStartTime = Date.now();
46
47
 
47
48
  let aggregationPipeline = [
@@ -77,7 +78,7 @@ const fetchConversationData = async (filter, skip, limit) => {
77
78
  const conversations = await Message.aggregate(aggregationPipeline);
78
79
 
79
80
  const aggregationTime = Date.now() - aggregationStartTime;
80
- console.log(`Aggregation completed in ${aggregationTime}ms, found ${conversations.length} conversations`);
81
+ logger.info(`Aggregation completed in ${aggregationTime}ms, found ${conversations.length} conversations`);
81
82
 
82
83
  // Fetch names from Airtable and WhatsApp
83
84
  const phoneNumbers = conversations.map(conv => conv._id).filter(Boolean);
@@ -89,7 +90,7 @@ const fetchConversationData = async (filter, skip, limit) => {
89
90
  map[patient.whatsapp_id] = patient.name;
90
91
  return map;
91
92
  }, {});
92
- console.log(`Found ${Object.keys(airtableNameMap).length} names in Airtable`);
93
+ logger.info(`Found ${Object.keys(airtableNameMap).length} names in Airtable`);
93
94
 
94
95
  const contactNames = await Message.aggregate([
95
96
  { $match: { is_group: false, from_me: false } },
@@ -110,7 +111,7 @@ const fetchConversationData = async (filter, skip, limit) => {
110
111
  }, {...airtableNameMap}) || airtableNameMap || {};
111
112
 
112
113
  // Fetch unread counts
113
- console.log('Fetching unread counts using Message.aggregate');
114
+ logger.info('Fetching unread counts using Message.aggregate');
114
115
  const unreadCounts = await Message.aggregate([
115
116
  {
116
117
  $match: {
@@ -134,8 +135,8 @@ const fetchConversationData = async (filter, skip, limit) => {
134
135
  }
135
136
  return map;
136
137
  }, {}) || {};
137
- console.log('unreadMap', JSON.stringify(unreadMap));
138
- console.log('Number of conversations found:', conversations?.length || 0);
138
+ logger.info('unreadMap', JSON.stringify(unreadMap));
139
+ logger.info('Number of conversations found:', conversations?.length || 0);
139
140
 
140
141
  // Calculate total count for pagination
141
142
  let totalFilterConditions = { is_group: false };
@@ -193,14 +194,14 @@ const fetchConversationData = async (filter, skip, limit) => {
193
194
  * Processes conversations to prepare them for the response
194
195
  */
195
196
  const processConversations = async (conversations, nameMap, unreadMap) => {
196
- console.log('Processing conversations for response...');
197
+ logger.info('Processing conversations for response...');
197
198
 
198
199
  let processedConversations = [];
199
200
  try {
200
201
  processedConversations = (conversations || []).map((conv, index) => {
201
202
  try {
202
203
  if (!conv || !conv.latestMessage) {
203
- console.warn(`Conversation ${index} missing latestMessage:`, conv?._id || 'unknown');
204
+ logger.warn(`Conversation ${index} missing latestMessage:`, conv?._id || 'unknown');
204
205
  return {
205
206
  phoneNumber: conv?._id || 'unknown',
206
207
  name: 'Unknown',
@@ -245,7 +246,7 @@ const processConversations = async (conversations, nameMap, unreadMap) => {
245
246
  lastMessageFromMe: conv?.latestMessage?.from_me || false
246
247
  };
247
248
  } catch (convError) {
248
- console.error(`Error processing conversation ${index}:`, convError);
249
+ logger.error(`Error processing conversation ${index}:`, convError);
249
250
  return {
250
251
  phoneNumber: conv?._id || `error_${index}`,
251
252
  name: 'Error Processing',
@@ -260,11 +261,10 @@ const processConversations = async (conversations, nameMap, unreadMap) => {
260
261
  }
261
262
  });
262
263
 
263
- console.log(`Successfully processed ${processedConversations.length} conversations`);
264
+ logger.info(`Successfully processed ${processedConversations.length} conversations`);
264
265
 
265
266
  } catch (mappingError) {
266
- console.error('Error in conversation mapping:', mappingError);
267
- // Return empty conversations if mapping fails
267
+ logger.error('Error in conversation mapping:', mappingError);
268
268
  processedConversations = [];
269
269
  }
270
270
 
@@ -1,3 +1,5 @@
1
+ const { logger } = require('../utils/logger');
2
+
1
3
  let preprocessingHandler = null;
2
4
 
3
5
  const setPreprocessingHandler = (handler) => {
@@ -16,7 +18,7 @@ const invokePreprocessingHandler = async (payload) => {
16
18
  const result = await preprocessingHandler(payload);
17
19
  return Boolean(result);
18
20
  } catch (error) {
19
- console.warn('[PreprocessingHooks] Handler threw an error:', error?.message || error);
21
+ logger.warn('[PreprocessingHooks] Handler threw an error:', error?.message || error);
20
22
  return false;
21
23
  }
22
24
  };
@@ -1,5 +1,6 @@
1
1
  const mongoose = require('mongoose');
2
2
  const runtimeConfig = require('../config/runtimeConfig');
3
+ const { logger } = require('../utils/logger');
3
4
 
4
5
  /**
5
6
  * MongoDB storage interface for messages and interactions
@@ -34,7 +35,7 @@ class MongoStorage {
34
35
  useNewUrlParser: true,
35
36
  useUnifiedTopology: true
36
37
  });
37
- console.log('MongoDB connected successfully');
38
+ logger.info('MongoDB connected successfully');
38
39
  } catch (error) {
39
40
  throw new Error(`MongoDB connection failed: ${error.message}`);
40
41
  }
@@ -46,10 +47,10 @@ class MongoStorage {
46
47
  const values = this.buildMessageValues(enrichedMessage);
47
48
  const { insertMessage } = require('../models/messageModel');
48
49
  await insertMessage(values);
49
- console.log('[MongoStorage] Message stored');
50
+ logger.info('[MongoStorage] Message stored');
50
51
  return values;
51
52
  } catch (error) {
52
- console.error('Error saving message:', error);
53
+ logger.error('Error saving message:', error);
53
54
  throw error;
54
55
  }
55
56
  }
@@ -64,23 +65,22 @@ class MongoStorage {
64
65
  return messageData;
65
66
  }
66
67
 
67
- console.log('[MongoStorage] Detected Twilio media message', {
68
+ logger.info('[MongoStorage] Detected Twilio media message', {
68
69
  from: rawMessage.From,
69
70
  numMedia
70
71
  });
71
72
 
72
73
  const bucketName = runtimeConfig.get('AWS_S3_BUCKET_NAME') || process.env.AWS_S3_BUCKET_NAME;
73
74
  if (!bucketName) {
74
- console.warn('[MongoStorage] AWS_S3_BUCKET_NAME not configured. Skipping media upload.');
75
+ logger.warn('[MongoStorage] AWS_S3_BUCKET_NAME not configured. Skipping media upload.');
75
76
  return messageData;
76
77
  }
77
78
 
78
79
  const { processTwilioMediaMessage } = require('../helpers/twilioMediaProcessor');
79
- const { logger } = require('../utils/logger');
80
80
 
81
- const mediaItems = await processTwilioMediaMessage(rawMessage, logger, bucketName);
81
+ const mediaItems = await processTwilioMediaMessage(rawMessage, bucketName);
82
82
  if (!mediaItems || mediaItems.length === 0) {
83
- console.warn('[MongoStorage] Media processing returned no items');
83
+ logger.warn('[MongoStorage] Media processing returned no items');
84
84
  return messageData;
85
85
  }
86
86
 
@@ -91,7 +91,7 @@ class MongoStorage {
91
91
 
92
92
  rawMessage.__nexusMediaProcessed = true;
93
93
 
94
- console.log('[MongoStorage] Media processed successfully', {
94
+ logger.info('[MongoStorage] Media processed successfully', {
95
95
  primaryType: mediaPayload.mediaType,
96
96
  mediaCount: mediaItems.length,
97
97
  s3Key: mediaPayload.key
@@ -107,7 +107,7 @@ class MongoStorage {
107
107
  caption: primary.caption || messageData.caption
108
108
  };
109
109
  } catch (error) {
110
- console.error('[MongoStorage] Failed to enrich Twilio media message:', error);
110
+ logger.error('[MongoStorage] Failed to enrich Twilio media message:', error);
111
111
  return messageData;
112
112
  }
113
113
  }
@@ -197,7 +197,7 @@ class MongoStorage {
197
197
  .sort({ createdAt: -1 })
198
198
  .limit(limit);
199
199
  } catch (error) {
200
- console.error('Error getting messages:', error);
200
+ logger.error('Error getting messages:', error);
201
201
  throw error;
202
202
  }
203
203
  }
@@ -206,7 +206,7 @@ class MongoStorage {
206
206
  try {
207
207
  return await this.schemas.Thread.findOne({ code, active: true });
208
208
  } catch (error) {
209
- console.error('Error getting thread:', error);
209
+ logger.error('Error getting thread:', error);
210
210
  throw error;
211
211
  }
212
212
  }
@@ -217,7 +217,7 @@ class MongoStorage {
217
217
  await thread.save();
218
218
  return thread;
219
219
  } catch (error) {
220
- console.error('Error creating thread:', error);
220
+ logger.error('Error creating thread:', error);
221
221
  throw error;
222
222
  }
223
223
  }
@@ -230,7 +230,7 @@ class MongoStorage {
230
230
  { new: true }
231
231
  );
232
232
  } catch (error) {
233
- console.error('Error updating thread:', error);
233
+ logger.error('Error updating thread:', error);
234
234
  throw error;
235
235
  }
236
236
  }