@peopl-health/nexus 3.13.3 → 3.13.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.
@@ -4,10 +4,10 @@ const { runAssistantWithRetries } = require('../helpers/assistantHelper');
4
4
  const { getAssistantById } = require('../services/assistantResolver');
5
5
 
6
6
  class AssistantProcessor {
7
- constructor({ mode = 'local', queueAdapter = null, sendMessage = null, preProcessMessages = null, storeRunMetrics = null }) {
8
- Object.assign(this, { mode, queueAdapter, sendMessage, preProcessMessages, storeRunMetrics });
7
+ constructor({ mode = 'local', queueAdapter = null, sendMessage = null, storeRunMetrics = null }) {
8
+ Object.assign(this, { mode, queueAdapter, sendMessage, storeRunMetrics });
9
9
  if (mode === 'queue' && queueAdapter) {
10
- queueAdapter.process('assistant.process', (payload) => this._processViaLocal(payload));
10
+ queueAdapter.process('assistant.process', (payload) => this._executeLocal(payload));
11
11
  }
12
12
  }
13
13
 
@@ -41,50 +41,29 @@ class AssistantProcessor {
41
41
  };
42
42
  }
43
43
 
44
- async process({ code, body = null, runOptions = {} }) {
44
+ async process({ code, runOptions = {}, messages = null }) {
45
45
  if (!code) throw new Error('code is required for assistant processing');
46
46
 
47
47
  return (this.mode === 'queue')
48
- ? await this._processViaQueue({ code, body, runOptions })
49
- : await this._processViaLocal({ code, body, runOptions });
48
+ ? await this._executeViaQueue({ code, runOptions })
49
+ : await this._executeLocal({ code, runOptions, messages });
50
50
  }
51
51
 
52
- async _processViaLocal({ code, body = null, runOptions = {} }) {
52
+ async _executeLocal({ code, runOptions = {}, messages = null }) {
53
53
  const resolved = await this.resolveThread(code);
54
54
  if (!resolved) return null;
55
- const { thread, assistant } = resolved;
56
55
 
57
- if (this.preProcessMessages) {
58
- const preProcessed = await this.preProcessMessages(code, body, thread);
59
- if (!preProcessed.shouldProcess) return null;
60
-
61
- const result = await this.executeLLM(thread, assistant, runOptions, preProcessed.messages);
62
- if (this.storeRunMetrics) await this.storeRunMetrics(code, thread, result, preProcessed.timings);
63
- return { ...result, timings: preProcessed.timings };
64
- }
65
-
66
- const result = await this.executeLLM(thread, assistant, runOptions);
67
- if (this.storeRunMetrics) await this.storeRunMetrics(code, thread, result);
56
+ const result = await this.executeLLM(resolved.thread, resolved.assistant, runOptions, messages);
57
+ if (this.storeRunMetrics) await this.storeRunMetrics(code, resolved.thread, result);
68
58
  return result;
69
59
  }
70
60
 
71
- async _processViaQueue({ code, body, runOptions }) {
61
+ async _executeViaQueue({ code, runOptions }) {
72
62
  if (!this.queueAdapter) throw new Error('queueAdapter is required for queue mode');
73
- const jobId = await this.queueAdapter.enqueue('assistant.process', { code, body, runOptions });
63
+ const jobId = await this.queueAdapter.enqueue('assistant.process', { code, runOptions });
74
64
  return await this.queueAdapter.waitForResult(jobId, 120000);
75
65
  }
76
66
 
77
- async processDirect({ code, runOptions = {} }) {
78
- if (!code) throw new Error('code is required for direct processing');
79
-
80
- const resolved = await this.resolveThread(code);
81
- if (!resolved) return null;
82
-
83
- const result = await this.executeLLM(resolved.thread, resolved.assistant, runOptions);
84
- if (this.storeRunMetrics) await this.storeRunMetrics(code, resolved.thread, result);
85
- return result;
86
- }
87
-
88
67
  async sendResponse(code, result) {
89
68
  if (!this.sendMessage) throw new Error('sendMessage function not configured');
90
69
  if (!result?.output) return null;
@@ -77,7 +77,6 @@ class NexusMessaging {
77
77
  mode: config.assistant?.mode || 'local',
78
78
  queueAdapter: this.queueAdapter,
79
79
  sendMessage: this.sendMessage.bind(this),
80
- preProcessMessages,
81
80
  storeRunMetrics,
82
81
  });
83
82
  }
@@ -384,9 +383,22 @@ class NexusMessaging {
384
383
  async _handleWithCheckAfter(chatId) {
385
384
  await this._executeWithPipeline(chatId, 'message', 'preempt',
386
385
  async (preProcessResult, shouldContinue) => {
387
- return await this._processMessages(chatId, () =>
388
- this.assistantProcessor.process({ code: chatId, runOptions: { prePromptResult: preProcessResult } })
389
- , shouldContinue);
386
+ return await this._processMessages(chatId, async () => {
387
+ const resolved = await this.assistantProcessor.resolveThread(chatId);
388
+ if (!resolved) return null;
389
+
390
+ const preProcessed = await preProcessMessages(chatId, null, resolved.thread);
391
+ if (!preProcessed.shouldProcess) return null;
392
+
393
+ const result = await this.assistantProcessor.executeLLM(
394
+ resolved.thread, resolved.assistant,
395
+ { prePromptResult: preProcessResult },
396
+ preProcessed.messages
397
+ );
398
+
399
+ if (storeRunMetrics) await storeRunMetrics(chatId, resolved.thread, result, preProcessed.timings);
400
+ return { ...result, timings: preProcessed.timings };
401
+ }, shouldContinue);
390
402
  }
391
403
  );
392
404
  }
@@ -407,7 +419,7 @@ class NexusMessaging {
407
419
 
408
420
  const result = await this._executeWithPipeline(code, 'instruction', 'queue',
409
421
  async (preProcessResult) => {
410
- return await this.assistantProcessor.processDirect({
422
+ return await this.assistantProcessor.process({
411
423
  code,
412
424
  runOptions: {
413
425
  prePromptResult: preProcessResult,
@@ -442,7 +454,7 @@ class NexusMessaging {
442
454
 
443
455
  const result = await this._executeWithPipeline(code, 'system', 'queue',
444
456
  async (preProcessResult) => {
445
- return await this.assistantProcessor.processDirect({
457
+ return await this.assistantProcessor.process({
446
458
  code,
447
459
  runOptions: {
448
460
  prePromptResult: preProcessResult,
@@ -3,9 +3,6 @@ const fsSync = require('fs');
3
3
  const path = require('path');
4
4
  const { execFile } = require('child_process');
5
5
 
6
- const sharp = require('sharp');
7
- const { PDFDocument } = require('pdf-lib');
8
-
9
6
  const { downloadFileFromS3 } = require('../config/awsConfig.js');
10
7
 
11
8
  const { sanitizeFilename } = require('../utils/sanitizerUtils.js');
@@ -76,43 +73,6 @@ async function convertPdfToImages(pdfName, existingPdfPath = null) {
76
73
  });
77
74
  }
78
75
 
79
- async function combineImagesToPDF(config) {
80
- const { code, extensions = ['jpg', 'jpeg', 'png', 'tiff'], sortNumerically = true } = config;
81
- const inputDir = path.join(__dirname, 'assets', 'tmp');
82
- const files = await fs.readdir(inputDir);
83
-
84
- const imageFiles = files.filter(file => {
85
- const ext = path.extname(file).toLowerCase().slice(1);
86
- return extensions.includes(ext) && (!code || file.startsWith(code));
87
- });
88
-
89
- imageFiles.sort((a, b) => {
90
- if (!sortNumerically) return a.localeCompare(b);
91
- const aNum = a.match(/\d+/)?.[0];
92
- const bNum = b.match(/\d+/)?.[0];
93
- return (aNum && bNum) ? parseInt(aNum) - parseInt(bNum) : a.localeCompare(b);
94
- });
95
-
96
- const pdfDoc = await PDFDocument.create();
97
- const processedFiles = [];
98
-
99
- for (const file of imageFiles) {
100
- try {
101
- const filePath = path.join(inputDir, file);
102
- const imageBuffer = await fs.readFile(filePath);
103
- const pngBuffer = await sharp(imageBuffer).toFormat('png').toBuffer();
104
- const { width, height } = await sharp(imageBuffer).metadata();
105
- const img = await pdfDoc.embedPng(pngBuffer);
106
- pdfDoc.addPage([width, height]).drawImage(img, { x: 0, y: 0, width, height });
107
- processedFiles.push(filePath);
108
- } catch (error) {
109
- logger.error(`Error processing file ${file}`, { error: error.message });
110
- }
111
- }
112
-
113
- return { pdfBuffer: await pdfDoc.save(), processedFiles };
114
- }
115
-
116
76
  const cleanupFiles = async (files) => {
117
77
  if (!files?.length) return;
118
78
 
@@ -168,7 +128,6 @@ async function downloadMediaAndCreateFile(code, reply) {
168
128
 
169
129
  module.exports = {
170
130
  convertPdfToImages,
171
- combineImagesToPDF,
172
131
  cleanupFiles,
173
132
  downloadMediaAndCreateFile
174
133
  };
@@ -138,6 +138,15 @@ const processMediaFilesCore = async (code, reply, provider) => {
138
138
 
139
139
  if (!reply.media) return { messagesChat: [], url: null, tempFiles: [], timings };
140
140
 
141
+ if (reply.media?.metadata?.processedContent) {
142
+ return {
143
+ messagesChat: [{ type: 'text', text: reply.media.metadata.processedContent }],
144
+ url: null,
145
+ tempFiles: [],
146
+ timings
147
+ };
148
+ }
149
+
141
150
  const { result: fileNames, duration } = await withTracing(
142
151
  downloadMediaAndCreateFile, 'download_media',
143
152
  () => ({ 'media.message_id': reply.message_id, 'media.type': reply.media?.mediaType }),
package/lib/index.d.ts CHANGED
@@ -558,14 +558,13 @@ declare module '@peopl-health/nexus' {
558
558
  mode?: 'local' | 'queue';
559
559
  queueAdapter?: QueueAdapter;
560
560
  sendMessage?: (messageData: MessageData) => Promise<any>;
561
- preProcessMessages?: (code: string, body: any, thread: any) => Promise<{ shouldProcess: boolean; messages: any[] | null; timings: Record<string, any> }>;
562
561
  storeRunMetrics?: (code: string, thread: any, result: any, timings?: Record<string, any>) => Promise<void>;
563
562
  }
564
563
 
565
564
  export interface ProcessInput {
566
565
  code: string;
567
- body?: any;
568
566
  runOptions?: Record<string, any>;
567
+ messages?: any[] | null;
569
568
  }
570
569
 
571
570
  export interface LLMResult {
@@ -586,7 +585,6 @@ declare module '@peopl-health/nexus' {
586
585
  resolveThread(code: string): Promise<{ thread: any; assistant: any } | null>;
587
586
  executeLLM(thread: any, assistant: any, runOptions?: Record<string, any>, messages?: any[]): Promise<LLMResult>;
588
587
  process(input: ProcessInput): Promise<LLMResult | null>;
589
- processDirect(input: { code: string; runOptions?: Record<string, any> }): Promise<LLMResult | null>;
590
588
  sendResponse(code: string, result: any): Promise<string | null>;
591
589
  }
592
590
  }
@@ -1,4 +1,3 @@
1
- const AWS = require('../config/awsConfig.js');
2
1
  const runtimeConfig = require('../config/runtimeConfig');
3
2
  const { Historial_Clinico_ID } = require('../config/airtableConfig');
4
3
 
@@ -12,7 +11,7 @@ const { getCurRow } = require('../helpers/assistantHelper.js');
12
11
  const { getThread, switchThreadStoppedStatus, setThreadPromptId } = require('../helpers/threadHelper.js');
13
12
  const { processThreadMessage } = require('../helpers/processHelper.js');
14
13
  const { getLastNMessages, storeProcessedContent } = require('../helpers/messageHelper.js');
15
- const { combineImagesToPDF, cleanupFiles } = require('../helpers/filesHelper.js');
14
+ const { cleanupFiles } = require('../helpers/filesHelper.js');
16
15
 
17
16
  const { createLLMProvider } = require('../providers/createLLMProvider');
18
17
  const { getAssistantById } = require('./assistantResolver');
@@ -159,7 +158,6 @@ const preProcessMessagesCore = async (code, message_ = null, thread) => {
159
158
  }
160
159
 
161
160
  const patientMsg = processResults.some(r => r.isPatient);
162
- const urls = processResults.filter(r => r.url).map(r => ({ url: r.url }));
163
161
  const allTempFiles = processResults.flatMap(r => r.tempFiles || []);
164
162
 
165
163
  await Promise.all(processResults.map(r => {
@@ -174,27 +172,6 @@ const preProcessMessagesCore = async (code, message_ = null, thread) => {
174
172
 
175
173
  await cleanupFiles(allTempFiles);
176
174
 
177
- if (urls.length > 0) {
178
- logger.info(`[preProcessMessages] Processing ${urls.length} URLs for PDF combination`);
179
- const pdfStart = Date.now();
180
- const pdfResult = await combineImagesToPDF({ code });
181
- timings.pdf_combination_ms = Date.now() - pdfStart;
182
- const { pdfBuffer, processedFiles } = pdfResult;
183
- logger.info(`[preProcessMessages] PDF combination complete: ${processedFiles?.length || 0} files processed`);
184
-
185
- if (pdfBuffer) {
186
- const key = `${code}-${Date.now()}-combined.pdf`;
187
- const bucket = runtimeConfig.get('AWS_S3_BUCKET_NAME');
188
- if (bucket) {
189
- await AWS.uploadBufferToS3(pdfBuffer, bucket, key, 'application/pdf');
190
- }
191
- }
192
-
193
- if (processedFiles && processedFiles.length) {
194
- await cleanupFiles(processedFiles);
195
- }
196
- }
197
-
198
175
  if (!patientMsg || thread.stopped) {
199
176
  logger.info('[preProcessMessages] Skipping AI processing', { patientMsg, stopped: thread.stopped, code });
200
177
  return { shouldProcess: false, messages: null, timings };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.13.3",
3
+ "version": "3.13.4",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",