@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.
- package/examples/assistants/DoctorScheduleAssistant.js +3 -2
- package/lib/adapters/BaileysProvider.js +5 -4
- package/lib/adapters/TwilioProvider.js +20 -19
- package/lib/assistants/BaseAssistant.js +7 -6
- package/lib/config/awsConfig.js +14 -12
- package/lib/config/llmConfig.js +3 -2
- package/lib/config/mongoAuthConfig.js +5 -5
- package/lib/controllers/assistantController.js +12 -11
- package/lib/controllers/bugReportController.js +6 -5
- package/lib/controllers/conversationController.js +72 -71
- package/lib/controllers/interactionController.js +7 -6
- package/lib/controllers/mediaController.js +15 -13
- package/lib/controllers/messageController.js +7 -6
- package/lib/controllers/patientController.js +2 -1
- package/lib/controllers/qualityMessageController.js +5 -4
- package/lib/controllers/templateController.js +11 -9
- package/lib/controllers/uploadController.js +3 -1
- package/lib/core/NexusMessaging.js +18 -18
- package/lib/helpers/assistantHelper.js +8 -9
- package/lib/helpers/baileysHelper.js +4 -3
- package/lib/helpers/filesHelper.js +9 -8
- package/lib/helpers/llmsHelper.js +24 -9
- package/lib/helpers/mediaHelper.js +3 -2
- package/lib/helpers/messageHelper.js +12 -11
- package/lib/helpers/processHelper.js +24 -10
- package/lib/helpers/qrHelper.js +2 -1
- package/lib/helpers/twilioMediaProcessor.js +45 -35
- package/lib/helpers/whatsappHelper.js +3 -2
- package/lib/index.js +11 -14
- package/lib/interactive/index.js +11 -11
- package/lib/middleware/requestId.js +9 -14
- package/lib/models/messageModel.js +5 -4
- package/lib/providers/OpenAIAssistantsProvider.js +10 -9
- package/lib/providers/OpenAIResponsesProvider.js +18 -17
- package/lib/providers/OpenAIResponsesProviderTools.js +3 -5
- package/lib/providers/createProvider.js +2 -1
- package/lib/services/airtableService.js +39 -6
- package/lib/services/assistantService.js +20 -20
- package/lib/services/conversationService.js +16 -16
- package/lib/services/preprocessingHooks.js +3 -1
- package/lib/storage/MongoStorage.js +14 -14
- package/lib/utils/errorHandler.js +3 -1
- package/lib/utils/logger.js +35 -3
- package/lib/utils/sanitizer.js +0 -6
- package/package.json +1 -1
|
@@ -5,12 +5,13 @@ const { Thread } = require('../models/threadModel');
|
|
|
5
5
|
const llmConfig = require('../config/llmConfig');
|
|
6
6
|
const { Historial_Clinico_ID } = require('../config/airtableConfig');
|
|
7
7
|
const { getRecordByFilter } = require('../services/airtableService');
|
|
8
|
+
const { logger } = require('../utils/logger');
|
|
8
9
|
|
|
9
10
|
const Message = mongoose.models.Message;
|
|
10
11
|
|
|
11
12
|
const getConversationController = async (req, res) => {
|
|
12
13
|
const startTime = Date.now();
|
|
13
|
-
|
|
14
|
+
logger.info('Starting getConversationController at', new Date().toISOString());
|
|
14
15
|
|
|
15
16
|
try {
|
|
16
17
|
// Parse pagination parameters
|
|
@@ -19,10 +20,10 @@ const getConversationController = async (req, res) => {
|
|
|
19
20
|
const skip = (page - 1) * limit;
|
|
20
21
|
const filter = req.query.filter || 'all';
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
logger.info(`Pagination: page ${page}, limit ${limit}, skip ${skip}, filter: ${filter}`);
|
|
23
24
|
|
|
24
25
|
if (!Message) {
|
|
25
|
-
|
|
26
|
+
logger.info('Message model not found, returning empty conversations list');
|
|
26
27
|
return res.status(200).json({
|
|
27
28
|
success: true,
|
|
28
29
|
conversations: [],
|
|
@@ -31,10 +32,10 @@ const getConversationController = async (req, res) => {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const messageCount = await Message.countDocuments({});
|
|
34
|
-
|
|
35
|
+
logger.info('Total message count:', messageCount);
|
|
35
36
|
|
|
36
37
|
if (messageCount === 0) {
|
|
37
|
-
|
|
38
|
+
logger.info('No messages found in database, returning empty conversations list');
|
|
38
39
|
return res.status(200).json({
|
|
39
40
|
success: true,
|
|
40
41
|
conversations: [],
|
|
@@ -46,7 +47,7 @@ const getConversationController = async (req, res) => {
|
|
|
46
47
|
const { conversations, total, nameMap, unreadMap } = await fetchConversationData(filter, skip, limit);
|
|
47
48
|
|
|
48
49
|
if (!conversations || conversations.length === 0) {
|
|
49
|
-
|
|
50
|
+
logger.info('No conversations found, returning empty list');
|
|
50
51
|
return res.status(200).json({
|
|
51
52
|
success: true,
|
|
52
53
|
conversations: [],
|
|
@@ -60,9 +61,9 @@ const getConversationController = async (req, res) => {
|
|
|
60
61
|
const totalPages = Math.ceil(total / limit);
|
|
61
62
|
const totalTime = Date.now() - startTime;
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
logger.info('Number of conversations found:', conversations?.length || 0);
|
|
65
|
+
logger.info(`Total controller execution time: ${totalTime}ms`);
|
|
66
|
+
logger.info(`Filter: ${filter}, Pagination: ${conversations.length} of ${total} conversations (page ${page}/${totalPages})`);
|
|
66
67
|
|
|
67
68
|
res.status(200).json({
|
|
68
69
|
success: true,
|
|
@@ -77,11 +78,11 @@ const getConversationController = async (req, res) => {
|
|
|
77
78
|
hasPrev: page > 1
|
|
78
79
|
}
|
|
79
80
|
});
|
|
80
|
-
|
|
81
|
+
logger.info('Response sent successfully!');
|
|
81
82
|
|
|
82
83
|
} catch (error) {
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
logger.error('Error fetching conversations:', error);
|
|
85
|
+
logger.error('Error stack:', error.stack);
|
|
85
86
|
res.status(500).json({
|
|
86
87
|
success: false,
|
|
87
88
|
error: error.message || 'Failed to fetch conversations'
|
|
@@ -90,10 +91,10 @@ const getConversationController = async (req, res) => {
|
|
|
90
91
|
};
|
|
91
92
|
|
|
92
93
|
const getConversationMessagesController = async (req, res) => {
|
|
93
|
-
|
|
94
|
+
logger.info('Starting getConversationMessagesController at', new Date().toISOString());
|
|
94
95
|
try {
|
|
95
96
|
const { phoneNumber } = req.params;
|
|
96
|
-
|
|
97
|
+
logger.info('Requested conversation for phone number:', phoneNumber);
|
|
97
98
|
|
|
98
99
|
if (!phoneNumber) {
|
|
99
100
|
return res.status(400).json({
|
|
@@ -110,20 +111,20 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
110
111
|
try {
|
|
111
112
|
query.createdAt = { $lt: new Date(before) };
|
|
112
113
|
} catch (parseError) {
|
|
113
|
-
|
|
114
|
+
logger.warn('Invalid date format for before parameter:', before, parseError);
|
|
114
115
|
query.createdAt = { $lt: before };
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
logger.info('Fetching conversation messages with query:', JSON.stringify(query));
|
|
120
|
+
logger.info('Using limit:', limit);
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
logger.info('About to execute Message.find with query:', JSON.stringify(query));
|
|
122
123
|
let messages = [];
|
|
123
124
|
|
|
124
125
|
try {
|
|
125
126
|
const countResult = await Message.countDocuments(query);
|
|
126
|
-
|
|
127
|
+
logger.info(`Found ${countResult} messages matching query criteria`);
|
|
127
128
|
|
|
128
129
|
if (countResult > 0) {
|
|
129
130
|
messages = await Message.find(query)
|
|
@@ -131,22 +132,22 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
131
132
|
.limit(limit)
|
|
132
133
|
.lean();
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
logger.info(`Retrieved ${messages.length} messages, inspecting for potential issues...`);
|
|
135
136
|
|
|
136
137
|
const problematicMessages = messages.filter(msg => {
|
|
137
138
|
if (!msg || !msg.numero || !msg.createdAt) {
|
|
138
|
-
|
|
139
|
+
logger.warn('Found message missing required fields:', msg?._id || 'unknown');
|
|
139
140
|
return true;
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
if (msg.is_media === true) {
|
|
143
144
|
if (!msg.media || typeof msg.media !== 'object') {
|
|
144
|
-
|
|
145
|
+
logger.warn('Found media message with invalid media data:', msg._id);
|
|
145
146
|
return true;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
if (msg.media && (typeof msg.media.data === 'function' || msg.media.data instanceof Buffer)) {
|
|
149
|
-
|
|
150
|
+
logger.warn('Found media message with Buffer data that might cause serialization issues:', msg._id);
|
|
150
151
|
return true;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
@@ -155,25 +156,25 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
155
156
|
});
|
|
156
157
|
|
|
157
158
|
if (problematicMessages.length > 0) {
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
logger.warn(`Found ${problematicMessages.length} potentially problematic messages`);
|
|
160
|
+
logger.info('First problematic message IDs:', problematicMessages.slice(0, 3).map(m => m?._id || 'unknown'));
|
|
160
161
|
}
|
|
161
162
|
} else {
|
|
162
|
-
|
|
163
|
+
logger.info('No messages found for this query');
|
|
163
164
|
}
|
|
164
165
|
} catch (err) {
|
|
165
|
-
|
|
166
|
+
logger.error('Database query error in message retrieval:', err);
|
|
166
167
|
messages = [];
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
|
|
170
|
+
logger.info(`Retrieved a total of ${messages?.length || 0} messages for phone number: ${phoneNumber}`);
|
|
170
171
|
|
|
171
172
|
const sanitizedMessages = (messages || []).map(msg => {
|
|
172
173
|
try {
|
|
173
174
|
JSON.stringify(msg);
|
|
174
175
|
return msg;
|
|
175
176
|
} catch (serializationError) {
|
|
176
|
-
|
|
177
|
+
logger.error(`Found non-serializable message with ID ${msg._id}:`, serializationError);
|
|
177
178
|
return {
|
|
178
179
|
_id: msg._id?.toString() || 'unknown',
|
|
179
180
|
numero: msg.numero || phoneNumber,
|
|
@@ -187,16 +188,16 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
187
188
|
});
|
|
188
189
|
|
|
189
190
|
const reversedMessages = sanitizedMessages.reverse();
|
|
190
|
-
|
|
191
|
+
logger.info(`Sending ${reversedMessages.length} sanitized messages in response`);
|
|
191
192
|
|
|
192
193
|
res.status(200).json({
|
|
193
194
|
success: true,
|
|
194
195
|
phoneNumber,
|
|
195
196
|
messages: reversedMessages
|
|
196
197
|
});
|
|
197
|
-
|
|
198
|
+
logger.info('Successfully sent conversation messages response');
|
|
198
199
|
} catch (error) {
|
|
199
|
-
|
|
200
|
+
logger.error(`Error fetching conversation for ${req.params?.phoneNumber || 'unknown'}:`, error);
|
|
200
201
|
res.status(500).json({
|
|
201
202
|
success: false,
|
|
202
203
|
error: error.message || 'Failed to fetch conversation'
|
|
@@ -205,10 +206,10 @@ const getConversationMessagesController = async (req, res) => {
|
|
|
205
206
|
};
|
|
206
207
|
|
|
207
208
|
const getConversationReplyController = async (req, res) => {
|
|
208
|
-
|
|
209
|
+
logger.info('Starting getConversationReplyController at', new Date().toISOString());
|
|
209
210
|
try {
|
|
210
211
|
const { phoneNumber, message, mediaData, contentSid, variables } = req.body;
|
|
211
|
-
|
|
212
|
+
logger.info('Reply request params:', {
|
|
212
213
|
phoneNumber,
|
|
213
214
|
hasMessage: !!message,
|
|
214
215
|
hasMediaData: !!mediaData,
|
|
@@ -226,7 +227,7 @@ const getConversationReplyController = async (req, res) => {
|
|
|
226
227
|
const formattedPhoneNumber = phoneNumber?.startsWith('whatsapp:')
|
|
227
228
|
? phoneNumber
|
|
228
229
|
: `whatsapp:${phoneNumber}`;
|
|
229
|
-
|
|
230
|
+
logger.info('Formatted phone number:', formattedPhoneNumber);
|
|
230
231
|
|
|
231
232
|
const messageData = {
|
|
232
233
|
code: formattedPhoneNumber,
|
|
@@ -235,11 +236,11 @@ const getConversationReplyController = async (req, res) => {
|
|
|
235
236
|
|
|
236
237
|
// Handle template message (contentSid provided)
|
|
237
238
|
if (contentSid) {
|
|
238
|
-
|
|
239
|
+
logger.info('Processing template message with contentSid:', contentSid);
|
|
239
240
|
messageData.contentSid = contentSid;
|
|
240
241
|
|
|
241
242
|
if (variables && Object.keys(variables).length > 0) {
|
|
242
|
-
|
|
243
|
+
logger.info('Template variables:', JSON.stringify(variables));
|
|
243
244
|
messageData.variables = variables;
|
|
244
245
|
}
|
|
245
246
|
|
|
@@ -255,7 +256,7 @@ const getConversationReplyController = async (req, res) => {
|
|
|
255
256
|
}
|
|
256
257
|
// Handle media message
|
|
257
258
|
else if (mediaData && mediaData.fileUrl) {
|
|
258
|
-
|
|
259
|
+
logger.info('Processing media data:', { fileType: mediaData.fileType, hasFileUrl: !!mediaData.fileUrl, fileName: mediaData.fileName });
|
|
259
260
|
messageData.fileUrl = mediaData.fileUrl;
|
|
260
261
|
messageData.fileType = mediaData.fileType || 'image';
|
|
261
262
|
|
|
@@ -272,19 +273,19 @@ const getConversationReplyController = async (req, res) => {
|
|
|
272
273
|
messageData.body = message;
|
|
273
274
|
}
|
|
274
275
|
|
|
275
|
-
|
|
276
|
+
logger.info('Sending message with data:', JSON.stringify(messageData));
|
|
276
277
|
await sendMessage(messageData);
|
|
277
|
-
|
|
278
|
+
logger.info('Message sent successfully');
|
|
278
279
|
|
|
279
280
|
res.status(200).json({
|
|
280
281
|
success: true,
|
|
281
282
|
message: 'Reply sent successfully'
|
|
282
283
|
});
|
|
283
284
|
} catch (error) {
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
logger.error('Error sending reply:', error);
|
|
286
|
+
logger.info('Request body:', JSON.stringify(req.body || {}));
|
|
286
287
|
const errorMsg = error.message || 'Failed to send reply';
|
|
287
|
-
|
|
288
|
+
logger.error('Responding with error:', errorMsg);
|
|
288
289
|
res.status(500).json({
|
|
289
290
|
success: false,
|
|
290
291
|
error: errorMsg
|
|
@@ -306,7 +307,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
306
307
|
const maxLimit = 100;
|
|
307
308
|
const parsedLimit = Math.min(parseInt(limit) || 50, maxLimit);
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
logger.info(`Searching conversations for query: "${query}"`);
|
|
310
311
|
const searchStartTime = Date.now();
|
|
311
312
|
|
|
312
313
|
const escapedQuery = query.replace(/\+/g, '\\+');
|
|
@@ -341,7 +342,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
341
342
|
]);
|
|
342
343
|
|
|
343
344
|
const searchTime = Date.now() - searchStartTime;
|
|
344
|
-
|
|
345
|
+
logger.info(`Search completed in ${searchTime}ms, found ${conversations.length} conversations`);
|
|
345
346
|
|
|
346
347
|
// Fetch names from Airtable and WhatsApp (same logic as fetchConversationData)
|
|
347
348
|
const phoneNumbers = conversations.map(conv => conv._id).filter(Boolean);
|
|
@@ -362,7 +363,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
362
363
|
batch.map(p => `{whatsapp_id} = "${p}"`).join(', ') +
|
|
363
364
|
')';
|
|
364
365
|
return getRecordByFilter(Historial_Clinico_ID, 'estado_general', formula).catch(error => {
|
|
365
|
-
|
|
366
|
+
logger.error('Error fetching Airtable batch for search:', error);
|
|
366
367
|
return [];
|
|
367
368
|
});
|
|
368
369
|
});
|
|
@@ -379,9 +380,9 @@ const searchConversationsController = async (req, res) => {
|
|
|
379
380
|
}
|
|
380
381
|
}
|
|
381
382
|
});
|
|
382
|
-
|
|
383
|
+
logger.info(`Found ${Object.keys(airtableNameMap).length} names in Airtable for search results (${batches.length} batches)`);
|
|
383
384
|
} catch (error) {
|
|
384
|
-
|
|
385
|
+
logger.error('Error fetching names from Airtable for search, falling back to nombre_whatsapp:', error);
|
|
385
386
|
}
|
|
386
387
|
}
|
|
387
388
|
|
|
@@ -446,7 +447,7 @@ const searchConversationsController = async (req, res) => {
|
|
|
446
447
|
});
|
|
447
448
|
|
|
448
449
|
} catch (error) {
|
|
449
|
-
|
|
450
|
+
logger.error('Error searching conversations:', error);
|
|
450
451
|
res.status(500).json({
|
|
451
452
|
success: false,
|
|
452
453
|
error: error.message || 'Failed to search conversations'
|
|
@@ -479,7 +480,7 @@ const getConversationsByNameController = async (req, res) => {
|
|
|
479
480
|
}))
|
|
480
481
|
});
|
|
481
482
|
} catch (error) {
|
|
482
|
-
|
|
483
|
+
logger.error('Error fetching conversations by name:', error);
|
|
483
484
|
res.status(500).json({
|
|
484
485
|
success: false,
|
|
485
486
|
error: error.message || 'Failed to fetch conversations by name'
|
|
@@ -525,7 +526,7 @@ const getNewMessagesController = async (req, res) => {
|
|
|
525
526
|
messages: messages
|
|
526
527
|
});
|
|
527
528
|
} catch (error) {
|
|
528
|
-
|
|
529
|
+
logger.error(`Error fetching new messages for ${req.params.phoneNumber}:`, error);
|
|
529
530
|
res.status(500).json({
|
|
530
531
|
success: false,
|
|
531
532
|
error: error.message || 'Failed to fetch new messages'
|
|
@@ -534,7 +535,7 @@ const getNewMessagesController = async (req, res) => {
|
|
|
534
535
|
};
|
|
535
536
|
|
|
536
537
|
const markMessagesAsReadController = async (req, res) => {
|
|
537
|
-
|
|
538
|
+
logger.info('Starting markMessagesAsReadController at', new Date().toISOString());
|
|
538
539
|
try {
|
|
539
540
|
const { phoneNumber } = req.params;
|
|
540
541
|
|
|
@@ -545,7 +546,7 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
545
546
|
});
|
|
546
547
|
}
|
|
547
548
|
|
|
548
|
-
|
|
549
|
+
logger.info('Marking messages as read for phone number:', phoneNumber);
|
|
549
550
|
const result = await Message.updateMany(
|
|
550
551
|
{
|
|
551
552
|
numero: phoneNumber,
|
|
@@ -564,8 +565,8 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
564
565
|
modifiedCount: result.nModified || result.modifiedCount
|
|
565
566
|
});
|
|
566
567
|
} catch (error) {
|
|
567
|
-
|
|
568
|
-
|
|
568
|
+
logger.error(`Error marking messages as read for ${req.params?.phoneNumber || 'unknown'}:`, error);
|
|
569
|
+
logger.error('Error stack:', error.stack);
|
|
569
570
|
res.status(500).json({
|
|
570
571
|
success: false,
|
|
571
572
|
error: error.message || 'Failed to mark messages as read'
|
|
@@ -574,7 +575,7 @@ const markMessagesAsReadController = async (req, res) => {
|
|
|
574
575
|
};
|
|
575
576
|
|
|
576
577
|
const sendTemplateToNewNumberController = async (req, res) => {
|
|
577
|
-
|
|
578
|
+
logger.info('Starting sendTemplateToNewNumberController at', new Date().toISOString());
|
|
578
579
|
try {
|
|
579
580
|
const { phoneNumber, templateId, variables } = req.body;
|
|
580
581
|
|
|
@@ -598,7 +599,7 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
598
599
|
: `whatsapp:${phoneNumber}`;
|
|
599
600
|
|
|
600
601
|
// Log template details for debugging
|
|
601
|
-
|
|
602
|
+
logger.info('Sending template to new number with details:', {
|
|
602
603
|
phoneNumber: formattedPhoneNumber,
|
|
603
604
|
templateId,
|
|
604
605
|
hasVariables: variables && Object.keys(variables).length > 0,
|
|
@@ -612,7 +613,7 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
612
613
|
|
|
613
614
|
if (variables && Object.keys(variables).length > 0) {
|
|
614
615
|
messageData.variables = variables;
|
|
615
|
-
|
|
616
|
+
logger.info('Template variables:', JSON.stringify(variables));
|
|
616
617
|
}
|
|
617
618
|
|
|
618
619
|
const message = await sendMessage(messageData);
|
|
@@ -623,7 +624,7 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
623
624
|
messageId: message.sid
|
|
624
625
|
});
|
|
625
626
|
} catch (error) {
|
|
626
|
-
|
|
627
|
+
logger.error('Error sending template to new number:', error);
|
|
627
628
|
res.status(500).json({
|
|
628
629
|
success: false,
|
|
629
630
|
error: error.message || 'Failed to send template to new number'
|
|
@@ -632,14 +633,14 @@ const sendTemplateToNewNumberController = async (req, res) => {
|
|
|
632
633
|
};
|
|
633
634
|
|
|
634
635
|
const getOpenAIThreadMessagesController = async (req, res) => {
|
|
635
|
-
|
|
636
|
+
logger.info('Starting getOpenAIThreadMessagesController at', new Date().toISOString());
|
|
636
637
|
try {
|
|
637
638
|
const { phoneNumber } = req.params;
|
|
638
639
|
const { limit = 50, order = 'desc', runId } = req.query;
|
|
639
640
|
const variant = process.env.VARIANT || 'assistants';
|
|
640
641
|
|
|
641
|
-
|
|
642
|
-
|
|
642
|
+
logger.info('Fetching OpenAI thread messages for:', phoneNumber);
|
|
643
|
+
logger.info('Variant:', variant, 'Limit:', limit, 'Order:', order);
|
|
643
644
|
|
|
644
645
|
if (!phoneNumber) {
|
|
645
646
|
return res.status(400).json({
|
|
@@ -654,22 +655,22 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
654
655
|
}).sort({ createdAt: -1 });
|
|
655
656
|
|
|
656
657
|
if (!thread) {
|
|
657
|
-
|
|
658
|
+
logger.info('No active OpenAI thread found for:', phoneNumber);
|
|
658
659
|
return res.status(404).json({
|
|
659
660
|
success: false,
|
|
660
661
|
error: 'No active OpenAI thread found for this phone number'
|
|
661
662
|
});
|
|
662
663
|
}
|
|
663
664
|
|
|
664
|
-
const conversationId = thread.
|
|
665
|
-
|
|
665
|
+
const conversationId = thread.conversation_id;
|
|
666
|
+
logger.info('Thread found - Conversation ID:', conversationId);
|
|
666
667
|
|
|
667
668
|
const provider = llmConfig.getOpenAIProvider({ instantiate: true, variant });
|
|
668
669
|
if (!provider) {
|
|
669
670
|
throw new Error('OpenAI provider not initialized');
|
|
670
671
|
}
|
|
671
672
|
|
|
672
|
-
|
|
673
|
+
logger.info('Using provider variant:', provider.getVariant());
|
|
673
674
|
|
|
674
675
|
const queryParams = {
|
|
675
676
|
threadId: conversationId,
|
|
@@ -679,13 +680,13 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
679
680
|
|
|
680
681
|
if (variant === 'assistants' && runId) {
|
|
681
682
|
queryParams.runId = runId;
|
|
682
|
-
|
|
683
|
+
logger.info('Including runId:', runId);
|
|
683
684
|
}
|
|
684
685
|
|
|
685
|
-
|
|
686
|
+
logger.info('Calling listMessages with params:', queryParams);
|
|
686
687
|
const messages = await provider.listMessages(queryParams);
|
|
687
688
|
|
|
688
|
-
|
|
689
|
+
logger.info(`Retrieved ${messages?.data?.length || 0} messages from OpenAI`);
|
|
689
690
|
|
|
690
691
|
res.status(200).json({
|
|
691
692
|
success: true,
|
|
@@ -702,8 +703,8 @@ const getOpenAIThreadMessagesController = async (req, res) => {
|
|
|
702
703
|
});
|
|
703
704
|
|
|
704
705
|
} catch (error) {
|
|
705
|
-
|
|
706
|
-
|
|
706
|
+
logger.error('Error fetching OpenAI thread messages:', error);
|
|
707
|
+
logger.error('Error stack:', error.stack);
|
|
707
708
|
res.status(500).json({
|
|
708
709
|
success: false,
|
|
709
710
|
error: error.message || 'Failed to fetch OpenAI thread messages'
|
|
@@ -3,6 +3,7 @@ const { Message } = require('../models/messageModel');
|
|
|
3
3
|
const { INTERACTION_QUALITY_VALUES } = require('../config/interactionConfig');
|
|
4
4
|
const { addRecord, getRecordByFilter } = require('../services/airtableService');
|
|
5
5
|
const { Logging_ID } = require('../config/airtableConfig');
|
|
6
|
+
const { logger } = require('../utils/logger');
|
|
6
7
|
|
|
7
8
|
async function logInteractionToAirtable(messageIds, whatsapp_id, voter_username, description) {
|
|
8
9
|
try {
|
|
@@ -21,7 +22,7 @@ async function logInteractionToAirtable(messageIds, whatsapp_id, voter_username,
|
|
|
21
22
|
patientId = patientRecords[0].record_logging_id;
|
|
22
23
|
}
|
|
23
24
|
} catch (err) {
|
|
24
|
-
|
|
25
|
+
logger.warn('Could not find patient in estado_general:', err.message);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const airtableData = {
|
|
@@ -32,9 +33,9 @@ async function logInteractionToAirtable(messageIds, whatsapp_id, voter_username,
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
await addRecord(Logging_ID, 'interactions', airtableData);
|
|
35
|
-
|
|
36
|
+
logger.info('Interaction logged to Airtable successfully');
|
|
36
37
|
} catch (error) {
|
|
37
|
-
|
|
38
|
+
logger.error('Error logging interaction to Airtable:', error);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -52,12 +53,12 @@ const addInteractionController = async (req, res) => {
|
|
|
52
53
|
const interaction = await Interaction.create({ messages, whatsapp_id, voter_username, quality, description });
|
|
53
54
|
|
|
54
55
|
logInteractionToAirtable(messages, whatsapp_id, voter_username, description).catch(err =>
|
|
55
|
-
|
|
56
|
+
logger.error('Background Airtable logging failed:', err)
|
|
56
57
|
);
|
|
57
58
|
|
|
58
59
|
res.status(201).json({ success: true, interaction });
|
|
59
60
|
} catch (error) {
|
|
60
|
-
|
|
61
|
+
logger.error('Error adding interaction:', error);
|
|
61
62
|
res.status(500).json({ success: false, error: error.message });
|
|
62
63
|
}
|
|
63
64
|
};
|
|
@@ -68,7 +69,7 @@ const getInteractionsByWhatsappIdController = async (req, res) => {
|
|
|
68
69
|
const interactions = await Interaction.find({ whatsapp_id }).populate('messages').sort({ createdAt: -1 });
|
|
69
70
|
res.status(200).json({ success: true, whatsappId: whatsapp_id, count: interactions.length, interactions });
|
|
70
71
|
} catch (error) {
|
|
71
|
-
|
|
72
|
+
logger.error('Error fetching interactions:', error);
|
|
72
73
|
res.status(500).json({ success: false, error: error.message });
|
|
73
74
|
}
|
|
74
75
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const { s3, downloadFileFromS3 } = require('../config/awsConfig');
|
|
2
|
+
const { logger } = require('../utils/logger');
|
|
3
|
+
|
|
2
4
|
const bucketName = process.env.AWS_S3_BUCKET_NAME;
|
|
3
5
|
|
|
4
6
|
|
|
@@ -6,11 +8,11 @@ const getMediaController = async (req, res) => {
|
|
|
6
8
|
try {
|
|
7
9
|
const { key } = req.params;
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
logger.info(`[MediaController] Received request for media file with key: ${key}`);
|
|
12
|
+
logger.info(`[MediaController] Original URL path: ${req.originalUrl}`);
|
|
11
13
|
|
|
12
14
|
if (!key || key.trim() === '') {
|
|
13
|
-
|
|
15
|
+
logger.error('[MediaController] Invalid key provided:', key);
|
|
14
16
|
return res.status(400).json({
|
|
15
17
|
success: false,
|
|
16
18
|
error: 'Invalid media key provided'
|
|
@@ -27,17 +29,17 @@ const getMediaController = async (req, res) => {
|
|
|
27
29
|
|
|
28
30
|
let mediaKey = key;
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
logger.info(`[MediaController] Final S3 key to fetch: ${mediaKey}`);
|
|
31
33
|
|
|
32
34
|
const fileData = await downloadFileFromS3(bucketName, mediaKey);
|
|
33
35
|
|
|
34
36
|
if (!fileData || !fileData.Body) {
|
|
35
|
-
|
|
37
|
+
logger.error(`[MediaController] Media not found in S3: ${key}`);
|
|
36
38
|
|
|
37
39
|
if (s3) {
|
|
38
40
|
try {
|
|
39
41
|
const prefix = key.split('/') [0];
|
|
40
|
-
|
|
42
|
+
logger.info(`[MediaController] Checking S3 for objects with prefix: ${prefix}/`);
|
|
41
43
|
s3.listObjectsV2({
|
|
42
44
|
Bucket: bucketName,
|
|
43
45
|
Prefix: prefix + '/',
|
|
@@ -45,22 +47,22 @@ const getMediaController = async (req, res) => {
|
|
|
45
47
|
}).promise()
|
|
46
48
|
.then(listData => {
|
|
47
49
|
if (listData.Contents && listData.Contents.length > 0) {
|
|
48
|
-
|
|
50
|
+
logger.info(`[MediaController] Found ${listData.Contents.length} objects with similar prefix:`);
|
|
49
51
|
listData.Contents.forEach(item => {
|
|
50
|
-
|
|
52
|
+
logger.info(`[MediaController] - ${item.Key} (${item.Size} bytes)`);
|
|
51
53
|
if (item.Key.includes(key.split('/').pop().substring(0, 10))) {
|
|
52
|
-
|
|
54
|
+
logger.info(`[MediaController] !!! POTENTIAL MATCH: ${item.Key}`);
|
|
53
55
|
}
|
|
54
56
|
});
|
|
55
57
|
} else {
|
|
56
|
-
|
|
58
|
+
logger.info(`[MediaController] No objects found with prefix: ${prefix}/`);
|
|
57
59
|
}
|
|
58
60
|
})
|
|
59
61
|
.catch(listErr => {
|
|
60
|
-
|
|
62
|
+
logger.error(`[MediaController] Error listing objects: ${listErr.message}`);
|
|
61
63
|
});
|
|
62
64
|
} catch (listErr) {
|
|
63
|
-
|
|
65
|
+
logger.error(`[MediaController] Error setting up bucket listing: ${listErr.message}`);
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -94,7 +96,7 @@ const getMediaController = async (req, res) => {
|
|
|
94
96
|
|
|
95
97
|
res.send(fileData.Body);
|
|
96
98
|
} catch (error) {
|
|
97
|
-
|
|
99
|
+
logger.error('Error serving media:', error);
|
|
98
100
|
res.status(500).json({
|
|
99
101
|
success: false,
|
|
100
102
|
error: 'Failed to retrieve media'
|
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
} = require('../core/NexusMessaging');
|
|
7
7
|
const { getRecordByFilter: defaultGetRecordByFilter } = require('../services/airtableService');
|
|
8
8
|
const runtimeConfig = require('../config/runtimeConfig');
|
|
9
|
+
const { logger } = require('../utils/logger');
|
|
9
10
|
const moment = require('moment-timezone');
|
|
10
11
|
|
|
11
12
|
const dependencies = {
|
|
@@ -131,7 +132,7 @@ const sendMessageController = async (req, res) => {
|
|
|
131
132
|
messageId: pickMessageId(result, scheduledRecord)
|
|
132
133
|
});
|
|
133
134
|
} catch (err) {
|
|
134
|
-
|
|
135
|
+
logger.error('Error scheduling individual message:', err);
|
|
135
136
|
res.status(500).json({ status: false, error: err.message });
|
|
136
137
|
}
|
|
137
138
|
};
|
|
@@ -196,7 +197,7 @@ const sendBulkMessageController = async (req, res) => {
|
|
|
196
197
|
})
|
|
197
198
|
);
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
logger.info(`Send bulk of ${numSend} messages`);
|
|
200
201
|
|
|
201
202
|
const messageIds = sentMessages
|
|
202
203
|
.map(({ result, scheduled }) => pickMessageId(result, scheduled))
|
|
@@ -208,7 +209,7 @@ const sendBulkMessageController = async (req, res) => {
|
|
|
208
209
|
messageIds
|
|
209
210
|
});
|
|
210
211
|
} catch (err) {
|
|
211
|
-
|
|
212
|
+
logger.error('Error sending bulk messages:', err);
|
|
212
213
|
res.status(500).send(err.message);
|
|
213
214
|
}
|
|
214
215
|
};
|
|
@@ -307,7 +308,7 @@ const sendBulkMessageAirtableController = async (req, res) => {
|
|
|
307
308
|
.map(({ result, scheduled }) => pickMessageId(result, scheduled))
|
|
308
309
|
.filter((id) => id !== null);
|
|
309
310
|
|
|
310
|
-
|
|
311
|
+
logger.info(`Iterate over ${(rows || []).length} rows`);
|
|
311
312
|
|
|
312
313
|
res.status(200).json({
|
|
313
314
|
status: true,
|
|
@@ -315,7 +316,7 @@ const sendBulkMessageAirtableController = async (req, res) => {
|
|
|
315
316
|
messageIds
|
|
316
317
|
});
|
|
317
318
|
} catch (err) {
|
|
318
|
-
|
|
319
|
+
logger.error('Error sending Airtable bulk messages:', err);
|
|
319
320
|
res.status(500).send(err.message);
|
|
320
321
|
}
|
|
321
322
|
};
|
|
@@ -343,7 +344,7 @@ const getLastInteractionController = async (req, res) => {
|
|
|
343
344
|
|
|
344
345
|
return res.status(200).send({ message: 'Last interaction retrieved successfully.', lastMessage, minutes });
|
|
345
346
|
} catch (error) {
|
|
346
|
-
|
|
347
|
+
logger.error(error);
|
|
347
348
|
return res.status(500).send({ message: 'Failed to retrieve the last interaction.', error });
|
|
348
349
|
}
|
|
349
350
|
};
|