@peopl-health/nexus 3.13.1 → 3.13.2

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.
@@ -5,9 +5,9 @@ const { logger } = require('../utils/logger');
5
5
  const { getThreadInfo, switchThreadStoppedStatus } = require('../helpers/threadHelper');
6
6
 
7
7
  const { getRecordByFilter } = require('../services/airtableService');
8
- const { createAssistant, addMsgAssistant, addInsAssistant, switchAssistant } = require('../services/assistantService');
8
+ const { createAssistant, addMsgAssistant, switchAssistant } = require('../services/assistantService');
9
9
 
10
- const { sendMessage } = require('../core/NexusMessaging');
10
+ const { sendMessage, processInstruction, processSystemMessage } = require('../core/NexusMessaging');
11
11
 
12
12
  const _updateThreadFlag = async (req, res, field, successMsg, errorMsg) => {
13
13
  const { code } = req.body;
@@ -36,8 +36,7 @@ const addInsAssistantController = async (req, res) => {
36
36
  if (!code) return res.status(400).json({ success: false, error: 'Code is required' });
37
37
 
38
38
  try {
39
- const reply = await addInsAssistant(code, instruction, 'developer');
40
- if (reply) await sendMessage({ code, body: reply, fileType: 'text', origin: 'assistant' });
39
+ await processInstruction(code, instruction, 'developer');
41
40
  return res.status(200).json({ success: true, message: 'Instruction added to assistant' });
42
41
  } catch (error) {
43
42
  logger.error('[AssistantController] Add instruction error', { error: error.message, code });
@@ -50,8 +49,11 @@ const addMsgAssistantController = async (req, res) => {
50
49
  if (!code) return res.status(400).json({ success: false, error: 'Code is required' });
51
50
 
52
51
  try {
53
- const assistantReply = await addMsgAssistant(code, messages, role, reply);
54
- if (assistantReply) await sendMessage({ code, body: assistantReply, fileType: 'text', origin: 'assistant' });
52
+ if (reply) {
53
+ await processSystemMessage(code, messages, role);
54
+ } else {
55
+ await addMsgAssistant(code, messages, role);
56
+ }
55
57
  return res.status(200).json({ success: true, message: 'Message added to assistant' });
56
58
  } catch (error) {
57
59
  logger.error('[AssistantController] Add message error', { error: error.message, code, role });
@@ -2,8 +2,8 @@
2
2
  * Handles assistant message processing with local or queue modes.
3
3
  */
4
4
  class AssistantProcessor {
5
- constructor({ mode = 'local', queueAdapter = null, sendMessage = null, replyAssistant = null }) {
6
- Object.assign(this, { mode, queueAdapter, sendMessage, replyAssistant });
5
+ constructor({ mode = 'local', queueAdapter = null, sendMessage = null, replyAssistant = null, runDirect = null }) {
6
+ Object.assign(this, { mode, queueAdapter, sendMessage, replyAssistant, runDirect });
7
7
  if (mode === 'queue' && queueAdapter) {
8
8
  queueAdapter.process('assistant.process', (payload) => this._processLocal(payload));
9
9
  }
@@ -33,6 +33,12 @@ class AssistantProcessor {
33
33
  return await this.queueAdapter.waitForResult(jobId, 120000);
34
34
  }
35
35
 
36
+ async processDirect({ code, runOptions = {} }) {
37
+ if (!code) throw new Error('code is required for direct processing');
38
+ if (!this.runDirect) throw new Error('runDirect function not configured');
39
+ return await this.runDirect(code, runOptions);
40
+ }
41
+
36
42
  async sendResponse(code, result) {
37
43
  if (!this.sendMessage) throw new Error('sendMessage function not configured');
38
44
  if (!result?.output) return null;
@@ -6,7 +6,7 @@ const { connect } = require('../config/mongoConfig');
6
6
 
7
7
  const { logger } = require('../utils/logger');
8
8
 
9
- const { Message } = require('../models/messageModel');
9
+ const { Message, insertMessage } = require('../models/messageModel');
10
10
  const { Thread } = require('../models/threadModel');
11
11
 
12
12
  const { setEventBus: setStatusEventBus } = require('../helpers/messageStatusHelper');
@@ -14,7 +14,7 @@ const { ensureThreadExists } = require('../helpers/threadHelper');
14
14
 
15
15
  const { createMessagingProvider } = require('../adapters/registry');
16
16
 
17
- const { addMsgAssistant, replyAssistant } = require('../services/assistantService');
17
+ const { addMsgAssistant, replyAssistant, runDirect } = require('../services/assistantService');
18
18
  const { hasPreprocessingHandler, invokePreprocessingHandler } = require('../services/preprocessingService');
19
19
 
20
20
  const { BatchingManager } = require('../core/BatchingManager');
@@ -76,7 +76,8 @@ class NexusMessaging {
76
76
  mode: config.assistant?.mode || 'local',
77
77
  queueAdapter: this.queueAdapter,
78
78
  sendMessage: this.sendMessage.bind(this),
79
- replyAssistant
79
+ replyAssistant,
80
+ runDirect
80
81
  });
81
82
  }
82
83
 
@@ -350,29 +351,112 @@ class NexusMessaging {
350
351
  }
351
352
 
352
353
  /*
353
- * MESSAGE BATCHING
354
+ * PROCESSING PIPELINE
354
355
  */
355
356
 
356
- async _handleWithCheckAfter(chatId) {
357
+ async _executeWithPipeline(chatId, type, mode, executeFn) {
358
+ let capturedResult = null;
359
+
357
360
  const processingFn = async (runId) => {
358
361
  const shouldContinue = () => this.batchingManager.isActiveRun(chatId, runId);
359
-
360
- return await this.pipeline.run(
361
- { chatId, runId, type: 'message' },
362
- async (preProcessResult) => {
363
- return await this._processMessages(chatId, () =>
364
- this.assistantProcessor.process({ code: chatId, runOptions: { runId, prePromptResult: preProcessResult } })
365
- , shouldContinue);
366
- },
362
+ capturedResult = await this.pipeline.run(
363
+ { chatId, runId, type },
364
+ executeFn,
367
365
  shouldContinue
368
366
  );
367
+ return capturedResult;
369
368
  };
370
369
 
371
370
  const sendResponseFn = async (result) => {
372
371
  await this.assistantProcessor.sendResponse(chatId, result);
373
372
  };
374
373
 
375
- await this.batchingManager.handleBatchedProcessing(chatId, processingFn, sendResponseFn);
374
+ if (mode === 'queue') {
375
+ await this.batchingManager.enqueueProcessing(chatId, processingFn, sendResponseFn);
376
+ } else {
377
+ await this.batchingManager.handleBatchedProcessing(chatId, processingFn, sendResponseFn);
378
+ }
379
+
380
+ return capturedResult;
381
+ }
382
+
383
+ async _handleWithCheckAfter(chatId) {
384
+ await this._executeWithPipeline(chatId, 'message', 'preempt',
385
+ async (preProcessResult, shouldContinue) => {
386
+ return await this._processMessages(chatId, () =>
387
+ this.assistantProcessor.process({ code: chatId, runOptions: { prePromptResult: preProcessResult } })
388
+ , shouldContinue);
389
+ }
390
+ );
391
+ }
392
+
393
+ async processInstruction(code, instruction, role = 'developer') {
394
+ const assistantId = await this._getThreadAssistantId(code);
395
+ await insertMessage({
396
+ nombre_whatsapp: 'Instruction',
397
+ numero: code,
398
+ body: instruction,
399
+ message_id: `instruction_${Date.now()}_${Math.random().toString(36).substring(7)}`,
400
+ from_me: true,
401
+ processed: true,
402
+ origin: 'instruction',
403
+ assistant_id: assistantId,
404
+ raw: { role }
405
+ });
406
+
407
+ const result = await this._executeWithPipeline(code, 'instruction', 'queue',
408
+ async (preProcessResult) => {
409
+ return await this.assistantProcessor.processDirect({
410
+ code,
411
+ runOptions: {
412
+ prePromptResult: preProcessResult,
413
+ additionalInstructions: instruction,
414
+ additionalMessages: [{ role, content: instruction }],
415
+ toolChoice: 'none',
416
+ }
417
+ });
418
+ }
419
+ );
420
+
421
+ return result?.output || null;
422
+ }
423
+
424
+ async processSystemMessage(code, messages, role = 'system') {
425
+ const normalizedMessages = Array.isArray(messages) ? messages : [messages];
426
+ const assistantId = await this._getThreadAssistantId(code);
427
+
428
+ for (let i = 0; i < normalizedMessages.length; i++) {
429
+ await insertMessage({
430
+ nombre_whatsapp: 'System',
431
+ numero: code,
432
+ body: normalizedMessages[i],
433
+ message_id: `system_${Date.now()}_${i}_${Math.random().toString(36).substring(7)}`,
434
+ from_me: true,
435
+ processed: true,
436
+ origin: 'system',
437
+ assistant_id: assistantId,
438
+ raw: { role }
439
+ });
440
+ }
441
+
442
+ const result = await this._executeWithPipeline(code, 'system', 'queue',
443
+ async (preProcessResult) => {
444
+ return await this.assistantProcessor.processDirect({
445
+ code,
446
+ runOptions: {
447
+ prePromptResult: preProcessResult,
448
+ toolChoice: 'none',
449
+ }
450
+ });
451
+ }
452
+ );
453
+
454
+ return result?.output || null;
455
+ }
456
+
457
+ async _getThreadAssistantId(code) {
458
+ const thread = await Thread.findOne({ code }).select('assistant_id prompt_id').lean();
459
+ return thread?.prompt_id || thread?.assistant_id || null;
376
460
  }
377
461
 
378
462
  async _processMessages(chatId, processingFn, shouldFinalize = () => true) {
@@ -450,6 +534,14 @@ const sendScheduledMessage = async (scheduledMessage) => {
450
534
  return await requireDefaultInstance().sendScheduledMessage(scheduledMessage);
451
535
  };
452
536
 
537
+ const processInstruction = async (code, instruction, role) => {
538
+ return await requireDefaultInstance().processInstruction(code, instruction, role);
539
+ };
540
+
541
+ const processSystemMessage = async (code, messages, role) => {
542
+ return await requireDefaultInstance().processSystemMessage(code, messages, role);
543
+ };
544
+
453
545
  const getEventBus = () => getDefaultInstance()?.getEventBus();
454
546
 
455
547
  const _resetDefaultInstance = () => { defaultInstance = null; };
@@ -458,8 +550,11 @@ module.exports = {
458
550
  NexusMessaging,
459
551
  sendMessage,
460
552
  sendScheduledMessage,
553
+ processInstruction,
554
+ processSystemMessage,
461
555
  setDefaultInstance,
462
556
  getDefaultInstance,
557
+ requireDefaultInstance,
463
558
  getProvider,
464
559
  requireProvider,
465
560
  getEventBus,
package/lib/index.d.ts CHANGED
@@ -246,6 +246,8 @@ declare module '@peopl-health/nexus' {
246
246
  getBatchingManager(): BatchingManager;
247
247
  getPipeline(): ProcessingPipeline;
248
248
  getAssistantProcessor(): AssistantProcessor;
249
+ processInstruction(code: string, instruction: string, role?: string): Promise<string | null>;
250
+ processSystemMessage(code: string, messages: string | string[], role?: string): Promise<string | null>;
249
251
  isConnected(): boolean;
250
252
  disconnect(): Promise<void>;
251
253
  }
@@ -557,6 +559,7 @@ declare module '@peopl-health/nexus' {
557
559
  queueAdapter?: QueueAdapter;
558
560
  sendMessage?: (messageData: MessageData) => Promise<any>;
559
561
  replyAssistant?: (code: string, body?: string, thread?: any, options?: any) => Promise<any>;
562
+ runDirect?: (code: string, runOptions?: Record<string, any>) => Promise<any>;
560
563
  }
561
564
 
562
565
  export interface ProcessInput {
@@ -571,6 +574,7 @@ declare module '@peopl-health/nexus' {
571
574
  setReplyAssistant(fn: AssistantProcessorConfig['replyAssistant']): void;
572
575
  setSendMessage(fn: AssistantProcessorConfig['sendMessage']): void;
573
576
  process(input: ProcessInput): Promise<{ output: string; tools_executed?: any[]; prompt?: string; response_id?: string } | null>;
577
+ processDirect(input: { code: string; runOptions?: Record<string, any> }): Promise<{ output: string; tools_executed?: any[]; prompt?: string; response_id?: string } | null>;
574
578
  sendResponse(code: string, result: any): Promise<string | null>;
575
579
  }
576
580
  }
@@ -66,40 +66,34 @@ const createAssistantCore = async (code, assistant_id, _messages = [], force = f
66
66
  }
67
67
  };
68
68
 
69
- const addMsgAssistantCore = async (code, inMessages, role = 'system', reply = false, skipSystemMessage = false) => {
69
+ const addMsgAssistantCore = async (code, inMessages, role = 'system') => {
70
70
  const thread = await getThread(code);
71
71
  if (!thread) return null;
72
72
 
73
73
  try {
74
74
  const messages = Array.isArray(inMessages) ? inMessages : [inMessages];
75
75
 
76
- if (!skipSystemMessage) {
77
- for (let i = 0; i < messages.length; i++) {
78
- const message = messages[i];
79
- try {
80
- const message_id = `system_${Date.now()}_${i}_${Math.random().toString(36).substring(7)}`;
81
- await insertMessage({
82
- nombre_whatsapp: 'System',
83
- numero: code,
84
- body: message,
85
- message_id: message_id,
86
- from_me: true,
87
- processed: true,
88
- origin: 'system',
89
- assistant_id: thread.getAssistantId(),
90
- raw: { role: role }
91
- });
92
- } catch (err) {
93
- logger.error('[addMsgAssistant] Error saving system message', { err });
94
- }
76
+ for (let i = 0; i < messages.length; i++) {
77
+ const message = messages[i];
78
+ try {
79
+ const message_id = `system_${Date.now()}_${i}_${Math.random().toString(36).substring(7)}`;
80
+ await insertMessage({
81
+ nombre_whatsapp: 'System',
82
+ numero: code,
83
+ body: message,
84
+ message_id: message_id,
85
+ from_me: true,
86
+ processed: true,
87
+ origin: 'system',
88
+ assistant_id: thread.getAssistantId(),
89
+ raw: { role: role }
90
+ });
91
+ } catch (err) {
92
+ logger.error('[addMsgAssistant] Error saving system message', { err });
95
93
  }
96
94
  }
97
95
 
98
- if (!reply) return null;
99
-
100
- const assistant = getAssistantById(thread.getAssistantId(), thread);
101
- const runResult = await runAssistantWithRetries(thread, assistant, { toolChoice: 'none' });
102
- return runResult?.output || null;
96
+ return null;
103
97
  } catch (error) {
104
98
  logger.error('[addMsgAssistant] Error adding message', { error: error.message, code, role });
105
99
  return null;
@@ -111,35 +105,19 @@ const addInstructionCore = async (code, instruction, role = 'system') => {
111
105
  if (!thread) return null;
112
106
 
113
107
  try {
114
- const assistant = getAssistantById(thread.getAssistantId(), thread);
115
-
116
- try {
117
- const message_id = `instruction_${Date.now()}_${Math.random().toString(36).substring(7)}`;
118
- await insertMessage({
119
- nombre_whatsapp: 'Instruction',
120
- numero: code,
121
- body: instruction,
122
- message_id: message_id,
123
- from_me: true,
124
- processed: true,
125
- origin: 'instruction',
126
- assistant_id: thread.getAssistantId(),
127
- raw: { role: role }
128
- });
129
- } catch (err) {
130
- logger.error('[addInstruction] Error saving instruction message', { err });
131
- }
132
-
133
- const runResult = await runAssistantWithRetries(thread, assistant, {
134
- additionalInstructions: instruction,
135
- additionalMessages: [
136
- { role: role, content: instruction }
137
- ],
138
- toolChoice: 'none'
108
+ const message_id = `instruction_${Date.now()}_${Math.random().toString(36).substring(7)}`;
109
+ await insertMessage({
110
+ nombre_whatsapp: 'Instruction',
111
+ numero: code,
112
+ body: instruction,
113
+ message_id: message_id,
114
+ from_me: true,
115
+ processed: true,
116
+ origin: 'instruction',
117
+ assistant_id: thread.getAssistantId(),
118
+ raw: { role: role }
139
119
  });
140
-
141
- logger.info('[addInstruction] Run response', { output: runResult?.output });
142
- return runResult?.output || null;
120
+ return null;
143
121
  } catch (error) {
144
122
  logger.error('[addInstruction] Error adding instruction', { error: error.message, code, role });
145
123
  return null;
@@ -325,6 +303,27 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
325
303
  }
326
304
  };
327
305
 
306
+ const runDirectCore = async (code, runOptions = {}) => {
307
+ const thread = await getThread(code);
308
+ if (!thread) return null;
309
+
310
+ try {
311
+ const assistant = getAssistantById(thread.getAssistantId(), thread);
312
+ const runResult = await runAssistantWithRetries(thread, assistant, runOptions);
313
+ const output = sanitizeOutput(runResult?.output);
314
+ return {
315
+ output,
316
+ tools_executed: runResult?.tools_executed,
317
+ prompt: runResult?.run?.prompt || null,
318
+ preset: runResult?.run?.preset || null,
319
+ response_id: runResult?.run?.id || null
320
+ };
321
+ } catch (error) {
322
+ logger.error('[runDirect] Error', { error: error.message, code });
323
+ throw error;
324
+ }
325
+ };
326
+
328
327
  const switchAssistantCore = async (code, assistant_id) => {
329
328
  try {
330
329
  const thread = await Thread.findOne({ code });
@@ -377,6 +376,10 @@ module.exports = {
377
376
  'assistant.has_custom_thread': !!thread_,
378
377
  'assistant.has_run_options': !!runOptions && Object.keys(runOptions).length > 0
379
378
  })),
379
+ runDirect: withTracing(runDirectCore, 'run_direct', (code) => ({
380
+ 'assistant.thread_code': code,
381
+ 'operation.type': 'run_direct'
382
+ })),
380
383
  switchAssistant: withTracing(switchAssistantCore, 'switch_assistant', (code, assistant_id) => ({
381
384
  'assistant.thread_code': code,
382
385
  'assistant.new_id': assistant_id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.13.1",
3
+ "version": "3.13.2",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",