@peopl-health/nexus 2.4.2 → 2.4.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/adapters/TwilioProvider.js +3 -2
- package/lib/assistants/BaseAssistant.js +1 -1
- package/lib/helpers/assistantHelper.js +5 -1
- package/lib/helpers/filesHelper.js +6 -6
- package/lib/models/predictionMetricsModel.js +20 -0
- package/lib/services/assistantService.js +15 -1
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const { MessageProvider } = require('../core/MessageProvider');
|
|
2
2
|
const axios = require('axios');
|
|
3
3
|
const runtimeConfig = require('../config/runtimeConfig');
|
|
4
|
-
const { uploadMediaToS3,
|
|
4
|
+
const { uploadMediaToS3, getFileExtension } = require('../helpers/mediaHelper');
|
|
5
|
+
const { sanitizeMediaFilename } = require('../utils/sanitizer');
|
|
5
6
|
const { generatePresignedUrl } = require('../config/awsConfig');
|
|
6
7
|
const { validateMedia, getMediaType } = require('../utils/mediaValidator');
|
|
7
8
|
const { v4: uuidv4 } = require('uuid');
|
|
@@ -299,7 +300,7 @@ class TwilioProvider extends MessageProvider {
|
|
|
299
300
|
})();
|
|
300
301
|
|
|
301
302
|
const baseName = existingFileName ? existingFileName.split('.').slice(0, -1).join('.') : `${mediaType}_${Date.now()}`;
|
|
302
|
-
const sanitizedBase =
|
|
303
|
+
const sanitizedBase = sanitizeMediaFilename(baseName) || `${mediaType}_${Date.now()}`;
|
|
303
304
|
const extension = getFileExtension(contentType) || 'bin';
|
|
304
305
|
const uploadId = uuidv4();
|
|
305
306
|
const key = await uploadMediaToS3(buffer, uploadId, sanitizedBase, bucketName, contentType, mediaType);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const llmConfig = require('../config/llmConfig');
|
|
2
2
|
const { Thread } = require('../models/threadModel');
|
|
3
3
|
const { Message } = require('../models/messageModel');
|
|
4
|
-
const { formatMessage } = require('../helpers/
|
|
4
|
+
const { formatMessage } = require('../helpers/messageHelper');
|
|
5
5
|
const { createProvider } = require('../providers/createProvider');
|
|
6
6
|
|
|
7
7
|
const DEFAULT_MAX_HISTORICAL_MESSAGES = parseInt(process.env.MAX_HISTORICAL_MESSAGES || '50', 10);
|
|
@@ -111,6 +111,7 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
|
|
|
111
111
|
assistant.setReplies(patientReply);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
const startTime = Date.now();
|
|
114
115
|
let run, output, completed;
|
|
115
116
|
let retries = 0;
|
|
116
117
|
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
@@ -131,11 +132,14 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
|
|
|
131
132
|
if (retries < maxRetries) await new Promise(resolve => setTimeout(resolve, 2000));
|
|
132
133
|
} while (retries < maxRetries && (!completed || !output));
|
|
133
134
|
|
|
135
|
+
const predictionTimeMs = Date.now() - startTime;
|
|
136
|
+
|
|
134
137
|
if (run?.last_error) console.log('[runAssistantWithRetries] RUN LAST ERROR:', run.last_error);
|
|
135
138
|
console.log('[runAssistantWithRetries] RUN STATUS', completed);
|
|
136
139
|
console.log('[runAssistantWithRetries] OUTPUT', output);
|
|
140
|
+
console.log('[runAssistantWithRetries] TIMING', { predictionTimeMs, retries });
|
|
137
141
|
|
|
138
|
-
return { run, output, completed, retries };
|
|
142
|
+
return { run, output, completed, retries, predictionTimeMs };
|
|
139
143
|
};
|
|
140
144
|
|
|
141
145
|
module.exports = {
|
|
@@ -15,7 +15,7 @@ async function convertPdfToImages(pdfName) {
|
|
|
15
15
|
const pdfPath = path.join(outputDir, `${sanitizedName}.pdf`);
|
|
16
16
|
const outputPattern = path.join(outputDir, sanitizedName);
|
|
17
17
|
|
|
18
|
-
await fs.
|
|
18
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
19
19
|
|
|
20
20
|
return new Promise((resolve, reject) => {
|
|
21
21
|
const args = ['-jpeg', pdfPath, outputPattern];
|
|
@@ -49,7 +49,7 @@ async function combineImagesToPDF(config) {
|
|
|
49
49
|
} = config;
|
|
50
50
|
|
|
51
51
|
const inputDir = path.join(__dirname, 'assets', 'tmp');
|
|
52
|
-
const files = await fs.
|
|
52
|
+
const files = await fs.readdir(inputDir);
|
|
53
53
|
|
|
54
54
|
const imageFiles = files.filter(file => {
|
|
55
55
|
const ext = path.extname(file).toLowerCase().substring(1);
|
|
@@ -81,7 +81,7 @@ async function combineImagesToPDF(config) {
|
|
|
81
81
|
const filePath = path.join(inputDir, file);
|
|
82
82
|
console.log(`Processing file ${index + 1}/${imageFiles.length}: ${file}`);
|
|
83
83
|
|
|
84
|
-
const imageBuffer = await fs.
|
|
84
|
+
const imageBuffer = await fs.readFile(filePath);
|
|
85
85
|
const pngBuffer = await sharp(imageBuffer)
|
|
86
86
|
.toFormat('png')
|
|
87
87
|
.toBuffer();
|
|
@@ -116,7 +116,7 @@ const cleanupFiles = async (files) => {
|
|
|
116
116
|
|
|
117
117
|
await Promise.all(files.map(async (filePath) => {
|
|
118
118
|
try {
|
|
119
|
-
await fs.
|
|
119
|
+
await fs.unlink(filePath);
|
|
120
120
|
} catch (error) {
|
|
121
121
|
if (error?.code !== 'ENOENT') {
|
|
122
122
|
const safeFileName = filePath ? filePath.split('/').pop().replace(/^[^-]+-[^-]+-/, 'xxx-xxx-') : 'unknown';
|
|
@@ -154,7 +154,7 @@ async function downloadMediaAndCreateFile(code, reply) {
|
|
|
154
154
|
const sourceFile = `${sanitizedCode}-${sanitizedSubType}-${sanitizedFileName}`;
|
|
155
155
|
const downloadPath = path.join(__dirname, 'assets', 'tmp', sourceFile);
|
|
156
156
|
|
|
157
|
-
await fs.
|
|
157
|
+
await fs.mkdir(path.dirname(downloadPath), { recursive: true });
|
|
158
158
|
await downloadFileFromS3(bucketName, key, downloadPath);
|
|
159
159
|
|
|
160
160
|
const { name: baseName } = path.parse(sourceFile);
|
|
@@ -163,7 +163,7 @@ async function downloadMediaAndCreateFile(code, reply) {
|
|
|
163
163
|
: [downloadPath];
|
|
164
164
|
|
|
165
165
|
if (subType === 'document' || subType === 'application') {
|
|
166
|
-
await fs.
|
|
166
|
+
await fs.unlink(downloadPath);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
return fileNames;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const predictionMetricsSchema = new mongoose.Schema({
|
|
4
|
+
message_id: { type: String, required: true, index: true },
|
|
5
|
+
numero: { type: String, required: true, index: true },
|
|
6
|
+
assistant_id: { type: String, required: true, index: true },
|
|
7
|
+
thread_id: { type: String, required: true },
|
|
8
|
+
prediction_time_ms: { type: Number, required: true },
|
|
9
|
+
retry_count: { type: Number, required: true, default: 1 },
|
|
10
|
+
completed: { type: Boolean, default: true },
|
|
11
|
+
error: { type: String, default: null }
|
|
12
|
+
}, { timestamps: true });
|
|
13
|
+
|
|
14
|
+
predictionMetricsSchema.index({ createdAt: -1 });
|
|
15
|
+
predictionMetricsSchema.index({ assistant_id: 1, createdAt: -1 });
|
|
16
|
+
predictionMetricsSchema.index({ numero: 1, createdAt: -1 });
|
|
17
|
+
|
|
18
|
+
const PredictionMetrics = mongoose.model('PredictionMetrics', predictionMetricsSchema);
|
|
19
|
+
|
|
20
|
+
module.exports = { PredictionMetrics, predictionMetricsSchema };
|
|
@@ -6,6 +6,7 @@ const { BaseAssistant } = require('../assistants/BaseAssistant');
|
|
|
6
6
|
const { createProvider } = require('../providers/createProvider');
|
|
7
7
|
|
|
8
8
|
const { Thread } = require('../models/threadModel.js');
|
|
9
|
+
const { PredictionMetrics } = require('../models/predictionMetricsModel');
|
|
9
10
|
|
|
10
11
|
const { getCurRow } = require('../helpers/assistantHelper.js');
|
|
11
12
|
const { runAssistantAndWait, runAssistantWithRetries } = require('../helpers/assistantHelper.js');
|
|
@@ -316,7 +317,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
316
317
|
if (!patientMsg || thread.stopped) return null;
|
|
317
318
|
|
|
318
319
|
const assistant = getAssistantById(thread.getAssistantId(), thread);
|
|
319
|
-
const { run, output, completed, retries } = await withTracing(
|
|
320
|
+
const { run, output, completed, retries, predictionTimeMs } = await withTracing(
|
|
320
321
|
runAssistantWithRetries,
|
|
321
322
|
'run_assistant_with_retries',
|
|
322
323
|
(thread, assistant, runConfig, patientReply) => ({
|
|
@@ -326,6 +327,19 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
326
327
|
})
|
|
327
328
|
)(thread, assistant, runOptions, patientReply);
|
|
328
329
|
|
|
330
|
+
// Store prediction metrics
|
|
331
|
+
if (output && predictionTimeMs) {
|
|
332
|
+
await PredictionMetrics.create({
|
|
333
|
+
message_id: `${code}-${Date.now()}`,
|
|
334
|
+
numero: code,
|
|
335
|
+
assistant_id: thread.getAssistantId(),
|
|
336
|
+
thread_id: thread.getConversationId(),
|
|
337
|
+
prediction_time_ms: predictionTimeMs,
|
|
338
|
+
retry_count: retries,
|
|
339
|
+
completed: completed
|
|
340
|
+
}).catch(err => console.error('[replyAssistantCore] Failed to store metrics:', err));
|
|
341
|
+
}
|
|
342
|
+
|
|
329
343
|
return output;
|
|
330
344
|
};
|
|
331
345
|
|