@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
|
-
|
|
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 =
|
|
670
|
+
let typingInterval = existingTypingInterval;
|
|
669
671
|
|
|
670
672
|
try {
|
|
671
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
},
|