@peopl-health/nexus 3.0.6 → 3.1.0
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.
|
@@ -781,8 +781,8 @@ class NexusMessaging {
|
|
|
781
781
|
const lastMessage = await Message.findOne({
|
|
782
782
|
numero: chatId,
|
|
783
783
|
from_me: false,
|
|
784
|
-
|
|
785
|
-
|
|
784
|
+
message_id: { $exists: true, $ne: null, $not: /^pending-/ },
|
|
785
|
+
createdAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
|
|
786
786
|
}).sort({ createdAt: -1 });
|
|
787
787
|
|
|
788
788
|
if (!lastMessage?.message_id) {
|
|
@@ -23,65 +23,86 @@ function convertTwilioToInternalFormat(twilioMessage) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
async function downloadMediaFromTwilio(mediaUrl, logger) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
26
|
+
async function downloadMediaFromTwilio(mediaUrl, logger, maxRetries = 5) {
|
|
27
|
+
logger.info('[TwilioMedia] Starting download', {
|
|
28
|
+
url: mediaUrl,
|
|
29
|
+
maxRetries,
|
|
30
|
+
hasAccountSid: !!process.env.TWILIO_ACCOUNT_SID,
|
|
31
|
+
hasAuthToken: !!process.env.TWILIO_AUTH_TOKEN,
|
|
32
|
+
hasCredentials: !!(process.env.TWILIO_ACCOUNT_SID && process.env.TWILIO_AUTH_TOKEN)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const authorization = `Basic ${Buffer.from(
|
|
36
|
+
`${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}`
|
|
37
|
+
).toString('base64')}`;
|
|
38
|
+
|
|
39
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
logger.debug('[TwilioMedia] Download attempt', { attempt, maxRetries });
|
|
42
|
+
|
|
43
|
+
const response = await axios.get(mediaUrl, {
|
|
44
|
+
headers: {
|
|
45
|
+
'Authorization': authorization,
|
|
46
|
+
'User-Agent': 'Nexus-Media-Processor/1.0'
|
|
47
|
+
},
|
|
48
|
+
responseType: 'arraybuffer',
|
|
49
|
+
timeout: 30000
|
|
50
|
+
});
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
logger.info('[TwilioMedia] Download successful', {
|
|
53
|
+
status: response.status,
|
|
54
|
+
contentType: response.headers['content-type'],
|
|
55
|
+
contentLength: response.headers['content-length'],
|
|
56
|
+
dataSize: response.data?.length || 0,
|
|
57
|
+
attempt
|
|
58
|
+
});
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
return Buffer.from(response.data);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const is404 = error.response?.status === 404;
|
|
63
|
+
const isLastAttempt = attempt === maxRetries;
|
|
64
|
+
|
|
65
|
+
if (is404 && !isLastAttempt) {
|
|
66
|
+
const delay = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s
|
|
67
|
+
logger.info('[TwilioMedia] Media not ready, retrying after delay', {
|
|
68
|
+
attempt,
|
|
69
|
+
maxRetries,
|
|
70
|
+
delayMs: delay,
|
|
71
|
+
mediaId: mediaUrl.split('/').pop()
|
|
72
|
+
});
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if truly expired (after all retries)
|
|
78
|
+
const isMediaExpired = is404 && mediaUrl.includes('/Media/');
|
|
79
|
+
|
|
80
|
+
if (isMediaExpired && isLastAttempt) {
|
|
81
|
+
logger.info('[TwilioMedia] Media expired (24h limit), skipping download', {
|
|
82
|
+
mediaId: mediaUrl.split('/').pop(),
|
|
83
|
+
status: 404,
|
|
84
|
+
attemptsUsed: attempt
|
|
85
|
+
});
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
logger.error('[TwilioMedia] Download failed', {
|
|
90
|
+
message: error.message,
|
|
91
|
+
status: error.response?.status,
|
|
92
|
+
statusText: error.response?.statusText,
|
|
93
|
+
attempt,
|
|
94
|
+
maxRetries,
|
|
95
|
+
responseHeaders: error.response?.headers,
|
|
96
|
+
responseData: error.response?.data ? error.response.data.toString().substring(0, 500) : null,
|
|
97
|
+
url: mediaUrl,
|
|
98
|
+
hasCredentials: !!(process.env.TWILIO_ACCOUNT_SID && process.env.TWILIO_AUTH_TOKEN)
|
|
76
99
|
});
|
|
77
|
-
|
|
100
|
+
|
|
101
|
+
throw error;
|
|
78
102
|
}
|
|
79
|
-
|
|
80
|
-
throw error;
|
|
81
103
|
}
|
|
82
104
|
}
|
|
83
105
|
|
|
84
|
-
|
|
85
106
|
function getMediaTypeFromContentType(contentType) {
|
|
86
107
|
if (contentType.startsWith('image/')) return 'imageMessage';
|
|
87
108
|
if (contentType.startsWith('audio/')) return 'audioMessage';
|
|
@@ -200,7 +200,7 @@ class OpenAIResponsesProvider {
|
|
|
200
200
|
logger.info('[OpenAIResponsesProvider] Context built', {
|
|
201
201
|
conversationId,
|
|
202
202
|
assistantId,
|
|
203
|
-
context
|
|
203
|
+
lastContext: context[-1] || null
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
const filter = thread.code ? { code: thread.code, active: true } : null;
|
|
@@ -192,7 +192,10 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
192
192
|
|
|
193
193
|
await Promise.all(processResults.map(r => {
|
|
194
194
|
const processedContent = r.messages && r.messages.length > 0
|
|
195
|
-
? r.messages
|
|
195
|
+
? r.messages
|
|
196
|
+
.filter(msg => msg.content.text !== r.reply?.body)
|
|
197
|
+
.map(msg => msg.content.text)
|
|
198
|
+
.join(' ')
|
|
196
199
|
: null;
|
|
197
200
|
return updateMessageRecord(r.reply, finalThread, processedContent);
|
|
198
201
|
}));
|