@peopl-health/nexus 3.0.2 → 3.0.3

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.
@@ -348,6 +348,7 @@ class NexusMessaging {
348
348
  if (this.messageStorage) {
349
349
  await this.messageStorage.saveMessage({
350
350
  ...messageData,
351
+ messageId: messageData.id,
351
352
  timestamp: new Date(),
352
353
  fromMe: false,
353
354
  origin: 'patient'
@@ -651,27 +652,29 @@ class NexusMessaging {
651
652
  * Handle message with check-after strategy - process immediately, check for new messages after
652
653
  */
653
654
  async _handleWithCheckAfter(chatId) {
654
- // If already processing this chat, just return (message is saved, will be picked up)
655
+ const typingInterval = await this._startTypingRefresh(chatId);
656
+
655
657
  if (this.processingLocks.has(chatId)) {
656
658
  logger.info(`[CheckAfter] Already processing ${chatId}, new message will be included`);
657
659
  return;
658
660
  }
659
661
 
660
- await this._processWithLock(chatId);
662
+ await this._processWithLock(chatId, typingInterval);
661
663
  }
662
664
 
663
665
  /**
664
666
  * Process messages with per-chat lock and check-after logic
665
667
  */
666
- async _processWithLock(chatId) {
668
+ async _processWithLock(chatId, existingTypingInterval = null) {
667
669
  this.processingLocks.set(chatId, true);
668
- let typingInterval = null;
670
+ let typingInterval = existingTypingInterval;
669
671
 
670
672
  try {
671
- typingInterval = await this._startTypingRefresh(chatId);
673
+ if (!typingInterval) {
674
+ typingInterval = await this._startTypingRefresh(chatId);
675
+ }
672
676
  logger.info(`[CheckAfter] Processing messages for ${chatId}`);
673
677
 
674
- // Process with assistant
675
678
  const result = await replyAssistant(chatId);
676
679
  const botResponse = typeof result === 'string' ? result : result?.output;
677
680
  const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
@@ -688,9 +691,7 @@ class NexusMessaging {
688
691
 
689
692
  if (hasNewMessages) {
690
693
  logger.info(`[CheckAfter] New messages detected for ${chatId}, discarding response and reprocessing`);
691
- if (typingInterval) clearInterval(typingInterval);
692
- // Recursively process with new messages
693
- return await this._processWithLock(chatId);
694
+ return await this._processWithLock(chatId, typingInterval);
694
695
  }
695
696
 
696
697
  // No new messages - send response and mark processed
@@ -80,6 +80,12 @@ messageSchema.index({ numero: 1, createdAt: -1 });
80
80
  messageSchema.index({ numero: 1, processed: 1, origin: 1 }, { name: 'numero_processed_origin_idx' });
81
81
  messageSchema.index({ numero: 1, createdAt: -1, processed: 1 }, { name: 'numero_created_processed_idx' });
82
82
 
83
+ // Indexes for conversation aggregation queries
84
+ messageSchema.index({ group_id: 1, createdAt: 1 }, { name: 'conversation_sort_idx' });
85
+ messageSchema.index({ group_id: 1, from_me: 1, read: 1 }, { name: 'unread_filter_idx' });
86
+ messageSchema.index({ group_id: 1, numero: 1, createdAt: -1 }, { name: 'conversation_lookup_idx' });
87
+ messageSchema.index({ createdAt: -1 }, { name: 'global_sort_idx' });
88
+
83
89
  messageSchema.pre('save', function (next) {
84
90
  if (this.timestamp) {
85
91
  this.timestamp = moment.tz(this.timestamp, 'America/Mexico_City').toDate();
@@ -287,10 +287,7 @@ class OpenAIResponsesProvider {
287
287
  const makeAPICall = (inputData) => retryWithBackoff(() =>
288
288
  this.client.responses.create({
289
289
  prompt: promptConfig,
290
- model: model || this.defaults.responseModel,
291
- instructions: additionalInstructions || instructions,
292
290
  input: inputData,
293
- metadata, top_p: topP, temperature, max_output_tokens: maxOutputTokens,
294
291
  truncation: truncationStrategy,
295
292
  }), { providerName: PROVIDER_NAME });
296
293
 
@@ -1,5 +1,5 @@
1
1
  const { ConversationManager } = require('./ConversationManager');
2
- const { getLastNMessages } = require('../helpers/messageHelper');
2
+ const { getLastNMessages, formatMessage } = require('../helpers/messageHelper');
3
3
  const { handlePendingFunctionCalls: handlePendingFunctionCallsUtil } = require('../providers/OpenAIResponsesProviderTools');
4
4
  const { getRecordByFilter } = require('./airtableService');
5
5
  const { Follow_Up_ID } = require('../config/airtableConfig');
@@ -23,10 +23,13 @@ class DefaultConversationManager extends ConversationManager {
23
23
  return additionalMessages;
24
24
  }
25
25
 
26
- const messageContext = allMessages.reverse().map(msg => ({
27
- role: msg.origin === 'patient' ? 'user' : 'assistant',
28
- content: msg.body || msg.content || ''
29
- }));
26
+ const messageContext = allMessages.reverse().map(msg => {
27
+ const formattedContent = formatMessage(msg);
28
+ return {
29
+ role: msg.origin === 'patient' ? 'user' : 'assistant',
30
+ content: formattedContent || msg.body || msg.content || ''
31
+ };
32
+ });
30
33
 
31
34
  return [...additionalMessages, ...messageContext];
32
35
  } catch (error) {
@@ -41,22 +41,23 @@ const fetchConversationData = async (filter, skip, limit) => {
41
41
  },
42
42
  { $project: { threadInfo: 0 } },
43
43
  ...(filter === 'pending-review' ? [{ $match: { $or: [{ review: false }, { review: null }] } }] : []),
44
- { $sort: { 'latestMessage.createdAt': -1, 'latestMessage.timestamp': -1 } },
44
+ { $sort: { 'latestMessage.createdAt': -1 } },
45
45
  { $skip: skip },
46
46
  { $limit: limit }
47
47
  ];
48
48
 
49
49
  const startTime = Date.now();
50
50
  const [conversations, contactNames, unreadCounts, totalResult] = await Promise.all([
51
- Message.aggregate(pipeline),
52
- Message.aggregate([{ $match: { ...baseMatch, from_me: false } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', name: { $first: '$nombre_whatsapp' } } }]),
53
- Message.aggregate([{ $match: { ...baseMatch, ...unreadMatch } }, { $group: { _id: '$numero', unreadCount: { $sum: 1 } } }]),
51
+ Message.aggregate(pipeline, { allowDiskUse: true }),
52
+ Message.aggregate([{ $match: { ...baseMatch, from_me: false } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', name: { $first: '$nombre_whatsapp' } } }], { allowDiskUse: true }),
53
+ Message.aggregate([{ $match: { ...baseMatch, ...unreadMatch } }, { $group: { _id: '$numero', unreadCount: { $sum: 1 } } }], { allowDiskUse: true }),
54
54
  Message.aggregate(
55
55
  filter === 'no-response'
56
- ? [{ $match: baseMatch }, { $project: { numero: 1, from_me: 1, createdAt: 1, timestamp: 1 } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', latestMessage: { $first: '$$ROOT' } } }, { $match: { 'latestMessage.from_me': false } }, { $count: 'total' }]
56
+ ? [{ $match: baseMatch }, { $project: { numero: 1, from_me: 1, createdAt: 1 } }, { $sort: { createdAt: -1 } }, { $group: { _id: '$numero', latestMessage: { $first: '$$ROOT' } } }, { $match: { 'latestMessage.from_me': false } }, { $count: 'total' }]
57
57
  : filter === 'pending-review'
58
58
  ? [{ $match: baseMatch }, { $group: { _id: '$numero' } }, { $lookup: { from: 'threads', localField: '_id', foreignField: 'code', as: 'threadInfo' } }, { $addFields: { review: { $arrayElemAt: ['$threadInfo.review', 0] } } }, { $match: { $or: [{ review: false }, { review: null }] } }, { $count: 'total' }]
59
- : [{ $match: filterConditions }, { $group: { _id: '$numero' } }, { $count: 'total' }]
59
+ : [{ $match: filterConditions }, { $group: { _id: '$numero' } }, { $count: 'total' }],
60
+ { allowDiskUse: true }
60
61
  )
61
62
  ]);
62
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -105,7 +105,7 @@
105
105
  "peerDependencies": {
106
106
  "@anthropic-ai/sdk": "^0.32.0",
107
107
  "baileys": "^6.4.0",
108
- "express": "4.22.1",
108
+ "express": "^4.22.1",
109
109
  "openai": "6.7.0",
110
110
  "twilio": "5.6.0"
111
111
  },