@peopl-health/nexus 3.1.3 → 3.1.4
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/lib/assistants/BaseAssistant.js +6 -3
- package/lib/core/NexusMessaging.js +2 -1
- package/lib/helpers/filesHelper.js +1 -1
- package/lib/helpers/messageHelper.js +43 -19
- package/lib/helpers/processHelper.js +3 -3
- package/lib/memory/DefaultMemoryManager.js +8 -8
- package/lib/memory/MemoryManager.js +1 -1
- package/lib/providers/OpenAIResponsesProvider.js +0 -1
- package/lib/services/assistantServiceCore.js +12 -2
- package/package.json +1 -1
|
@@ -198,9 +198,12 @@ class BaseAssistant {
|
|
|
198
198
|
// Messages with from_me: true are assistant messages, from_me: false are user messages
|
|
199
199
|
const formattedMessages = messagesInOrder
|
|
200
200
|
.filter(message => message && message.timestamp && message.body && message.body.trim() !== '')
|
|
201
|
-
.
|
|
202
|
-
const
|
|
203
|
-
return
|
|
201
|
+
.flatMap(message => {
|
|
202
|
+
const formattedTexts = formatMessage(message);
|
|
203
|
+
return formattedTexts.map(text => ({
|
|
204
|
+
role: message.from_me ? 'assistant' : 'user',
|
|
205
|
+
content: text
|
|
206
|
+
}));
|
|
204
207
|
})
|
|
205
208
|
.filter(message => message !== null);
|
|
206
209
|
|
|
@@ -732,6 +732,7 @@ class NexusMessaging {
|
|
|
732
732
|
if (typingInterval) {
|
|
733
733
|
clearInterval(typingInterval);
|
|
734
734
|
typingInterval = null;
|
|
735
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
735
736
|
}
|
|
736
737
|
|
|
737
738
|
if (botResponse) {
|
|
@@ -770,7 +771,7 @@ class NexusMessaging {
|
|
|
770
771
|
}
|
|
771
772
|
|
|
772
773
|
/**
|
|
773
|
-
* Central processing pipeline - handles
|
|
774
|
+
* Central processing pipeline - handles assistant processing, preprocessing, and status updates
|
|
774
775
|
*/
|
|
775
776
|
async _processMessages(chatId, processingFn) {
|
|
776
777
|
const unprocessedMessages = await Message.find({
|
|
@@ -171,7 +171,7 @@ async function downloadMediaAndCreateFile(code, reply) {
|
|
|
171
171
|
if (!resultMedia) return [];
|
|
172
172
|
|
|
173
173
|
if (!resultMedia.media || !resultMedia.media.key) {
|
|
174
|
-
logger.info('[downloadMediaAndCreateFile] No valid media found for message:', reply.message_id);
|
|
174
|
+
logger.info('[downloadMediaAndCreateFile] No valid media found for message:', { messageId: reply.message_id });
|
|
175
175
|
return [];
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -2,31 +2,34 @@ const moment = require('moment-timezone');
|
|
|
2
2
|
const { Message } = require('../models/messageModel.js');
|
|
3
3
|
const { logger } = require('../utils/logger');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Store processed media content (transcriptions, image analysis) before assistant runs
|
|
7
|
+
*/
|
|
8
|
+
const storeProcessedContent = async (reply, thread, processedContent) => {
|
|
9
|
+
if (!processedContent || !reply.media) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
const threadId = thread.getConversationId();
|
|
7
14
|
|
|
8
|
-
const updateData = {
|
|
15
|
+
const updateData = {
|
|
9
16
|
assistant_id: thread.getAssistantId(),
|
|
10
17
|
thread_id: threadId,
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
if (processedContent && reply.media) {
|
|
15
|
-
updateData.media = {
|
|
18
|
+
media: {
|
|
16
19
|
...reply.media,
|
|
17
20
|
metadata: {
|
|
18
21
|
...(reply.media.metadata || {}),
|
|
19
|
-
|
|
22
|
+
processedContent
|
|
20
23
|
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
23
26
|
|
|
24
27
|
await Message.updateOne(
|
|
25
28
|
{ message_id: reply.message_id, timestamp: reply.timestamp },
|
|
26
29
|
{ $set: updateData }
|
|
27
30
|
);
|
|
28
31
|
|
|
29
|
-
logger.info(`[
|
|
32
|
+
logger.info(`[storeProcessedContent] Media content stored - ID: ${reply.message_id}`);
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
|
|
@@ -79,13 +82,13 @@ async function getLastNMessages(code, n) {
|
|
|
79
82
|
function formatMessage(reply) {
|
|
80
83
|
try {
|
|
81
84
|
if (!reply.createdAt) {
|
|
82
|
-
return
|
|
85
|
+
return [];
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
const msgDate = new Date(reply.createdAt);
|
|
86
89
|
if (isNaN(msgDate.getTime())) {
|
|
87
90
|
logger.warn(`[formatMessage] Invalid timestamp for message ID: ${reply.message_id}`);
|
|
88
|
-
return reply.body;
|
|
91
|
+
return reply.body ? [`[Invalid timestamp] ${reply.body}`] : [];
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
const mexicoCityTime = moment(msgDate)
|
|
@@ -93,15 +96,36 @@ function formatMessage(reply) {
|
|
|
93
96
|
.locale('es')
|
|
94
97
|
.format('dddd, D [de] MMMM [de] YYYY [a las] h:mm A');
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
const messages = [];
|
|
100
|
+
|
|
101
|
+
const generateMediaCode = (messageId, mediaType) => {
|
|
102
|
+
if (!messageId || !mediaType) return null;
|
|
103
|
+
const shortId = messageId.slice(-6);
|
|
104
|
+
return `${mediaType.toUpperCase()}-${shortId}`;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (reply.body && reply.body.trim()) {
|
|
108
|
+
const mediaCode = reply.media?.mediaType ?
|
|
109
|
+
generateMediaCode(reply.message_id, reply.media.mediaType) : null;
|
|
110
|
+
const mediaIndicator = mediaCode ? `[${mediaCode}] ` : '';
|
|
111
|
+
messages.push(`[${mexicoCityTime}] ${mediaIndicator}${reply.body}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (reply.media?.metadata?.processedContent &&
|
|
115
|
+
reply.media.metadata.processedContent !== reply.body) {
|
|
116
|
+
const mediaCode = generateMediaCode(reply.message_id, reply.media.mediaType);
|
|
117
|
+
const processingType = reply.media.mediaType === 'audio' ? 'TRANSCRIPTION' :
|
|
118
|
+
reply.media.mediaType === 'image' ? 'ANALYSIS' :
|
|
119
|
+
reply.media.mediaType === 'document' ? 'DOCUMENT_ANALYSIS' :
|
|
120
|
+
'PROCESSED';
|
|
121
|
+
const indicator = mediaCode ? `[${mediaCode} ${processingType}]` : `[${processingType}]`;
|
|
122
|
+
messages.push(`[${mexicoCityTime}] ${indicator} ${reply.media.metadata.processedContent}`);
|
|
99
123
|
}
|
|
100
124
|
|
|
101
|
-
return
|
|
125
|
+
return messages.length > 0 ? messages : [];
|
|
102
126
|
} catch (error) {
|
|
103
127
|
logger.error(`[formatMessage] Error for message ID: ${reply.message_id}:`, error?.message || String(error));
|
|
104
|
-
return
|
|
128
|
+
return [];
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
@@ -117,7 +141,7 @@ async function isRecentMessage(chatId) {
|
|
|
117
141
|
}
|
|
118
142
|
|
|
119
143
|
module.exports = {
|
|
120
|
-
|
|
144
|
+
storeProcessedContent,
|
|
121
145
|
getLastMessages,
|
|
122
146
|
getLastNMessages,
|
|
123
147
|
formatMessage,
|
|
@@ -299,8 +299,8 @@ const processThreadMessageCore = async (code, replies, provider) => {
|
|
|
299
299
|
const textMessages = processTextMessage(reply);
|
|
300
300
|
const mediaResult = await processMediaFiles(code, reply, provider);
|
|
301
301
|
|
|
302
|
-
const { messagesChat: mediaMessages, url, tempFiles
|
|
303
|
-
tempFiles
|
|
302
|
+
const { messagesChat: mediaMessages, url, tempFiles, timings: mediaTimings } = mediaResult;
|
|
303
|
+
console.log('[processThreadMessageCore] tempFiles', tempFiles);
|
|
304
304
|
|
|
305
305
|
if (mediaTimings) {
|
|
306
306
|
timings.download_ms += mediaTimings.download_ms || 0;
|
|
@@ -321,7 +321,7 @@ const processThreadMessageCore = async (code, replies, provider) => {
|
|
|
321
321
|
hasMedia: !!reply.media,
|
|
322
322
|
hasUrl: !!url
|
|
323
323
|
});
|
|
324
|
-
|
|
324
|
+
await cleanupFiles(tempFiles);
|
|
325
325
|
return { isPatient, url, messages, reply, tempFiles };
|
|
326
326
|
} catch (error) {
|
|
327
327
|
logger.error('processThreadMessage', error, {
|
|
@@ -11,7 +11,7 @@ class DefaultMemoryManager extends MemoryManager {
|
|
|
11
11
|
this.maxHistoricalMessages = parseInt(process.env.MAX_HISTORICAL_MESSAGES || '50', 10);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
async buildContext({ thread,
|
|
14
|
+
async buildContext({ thread, config = {} }) {
|
|
15
15
|
this._logActivity('Building context', { threadCode: thread.code });
|
|
16
16
|
|
|
17
17
|
try {
|
|
@@ -22,14 +22,14 @@ class DefaultMemoryManager extends MemoryManager {
|
|
|
22
22
|
return additionalMessages;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const messageContext = allMessages.reverse().
|
|
26
|
-
const
|
|
27
|
-
return {
|
|
25
|
+
const messageContext = allMessages.reverse().flatMap(msg => {
|
|
26
|
+
const formattedContents = formatMessage(msg);
|
|
27
|
+
return formattedContents.map(content => ({
|
|
28
28
|
role: msg.origin === 'patient' ? 'user' : 'assistant',
|
|
29
|
-
content:
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
|
|
29
|
+
content: content || msg.body || msg.content || ''
|
|
30
|
+
}));
|
|
31
|
+
}).filter(msg => msg.content);
|
|
32
|
+
|
|
33
33
|
return [...additionalMessages, ...messageContext];
|
|
34
34
|
} catch (error) {
|
|
35
35
|
logger.error('[DefaultMemoryManager] Context building failed', {
|
|
@@ -10,7 +10,7 @@ const { Historial_Clinico_ID } = require('../config/airtableConfig');
|
|
|
10
10
|
const { getCurRow, runAssistantWithRetries } = require('../helpers/assistantHelper.js');
|
|
11
11
|
const { getThread } = require('../helpers/threadHelper.js');
|
|
12
12
|
const { processThreadMessage } = require('../helpers/processHelper.js');
|
|
13
|
-
const { getLastNMessages,
|
|
13
|
+
const { getLastNMessages, storeProcessedContent } = require('../helpers/messageHelper.js');
|
|
14
14
|
const { combineImagesToPDF, cleanupFiles } = require('../helpers/filesHelper.js');
|
|
15
15
|
const { getAssistantById } = require('./assistantResolver');
|
|
16
16
|
|
|
@@ -190,6 +190,16 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
190
190
|
const urls = processResults.filter(r => r.url).map(r => ({ url: r.url }));
|
|
191
191
|
const allTempFiles = processResults.flatMap(r => r.tempFiles || []);
|
|
192
192
|
|
|
193
|
+
await Promise.all(processResults.map(r => {
|
|
194
|
+
const processedContent = r.messages && r.messages.length > 0
|
|
195
|
+
? r.messages
|
|
196
|
+
.filter(msg => msg.content.text !== r.reply?.body)
|
|
197
|
+
.map(msg => msg.content.text)
|
|
198
|
+
.join(' ')
|
|
199
|
+
: null;
|
|
200
|
+
return r.reply ? storeProcessedContent(r.reply, finalThread, processedContent) : null;
|
|
201
|
+
}).filter(Boolean));
|
|
202
|
+
|
|
193
203
|
await cleanupFiles(allTempFiles);
|
|
194
204
|
|
|
195
205
|
if (urls.length > 0) {
|
|
@@ -209,7 +219,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
209
219
|
}
|
|
210
220
|
|
|
211
221
|
if (processedFiles && processedFiles.length) {
|
|
212
|
-
cleanupFiles(processedFiles);
|
|
222
|
+
await cleanupFiles(processedFiles);
|
|
213
223
|
}
|
|
214
224
|
}
|
|
215
225
|
|