@peopl-health/nexus 2.5.2 → 2.5.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.
@@ -3,8 +3,8 @@ const { Config_ID } = require('../config/airtableConfig');
3
3
  const { Thread } = require('../models/threadModel');
4
4
 
5
5
  const { getRecordByFilter } = require('../services/airtableService');
6
- const { createAssistant, addMsgAssistant, addInsAssistant } = require('../services/assistantService');
7
- const { getThreadInfo, switchAssistant } = require('../services/assistantService');
6
+ const { createAssistant, addMsgAssistant, addInsAssistant, switchAssistant } = require('../services/assistantService');
7
+ const { getThreadInfo } = require('../helpers/threadHelper');
8
8
  const { sendMessage } = require('../core/NexusMessaging');
9
9
  const { logger } = require('../utils/logger');
10
10
 
@@ -16,7 +16,7 @@ const activeAssistantController = async (req, res) => {
16
16
  await Thread.updateOne({ code }, { $set: { active: !!active } });
17
17
  return res.status(200).send({ message: 'Active assistant' });
18
18
  } catch (error) {
19
- logger.error(error);
19
+ logger.error('[activeAssistantController] Error', { error: error.message, code });
20
20
  return res.status(500).send({ message: 'Failed to active assistant', error });
21
21
  }
22
22
  };
@@ -28,11 +28,11 @@ const addInsAssistantController = async (req, res) => {
28
28
  const variant = process.env.VARIANT || 'assistants';
29
29
  const role = variant === 'responses' ? 'developer' : 'user';
30
30
 
31
- const ans = await addInsAssistant(code, instruction, role);
32
- if (ans) await sendMessage({code, body: ans, fileType: 'text', origin: 'assistant'});
33
- return res.status(200).send({ message: 'Add instruction to the assistant' });
31
+ const assistantReply = await addInsAssistant(code, instruction, role);
32
+ if (assistantReply) await sendMessage({code, body: assistantReply, fileType: 'text', origin: 'assistant'});
33
+ return res.status(200).send({ message: 'Instruction added to assistant', success: true });
34
34
  } catch (error) {
35
- logger.error(error);
35
+ logger.error('[addInsAssistantController] Error', { error: error.message, code });
36
36
  res.status(500).send({ message: 'Failed to add instruction to assistant', error });
37
37
  }
38
38
  };
@@ -41,11 +41,11 @@ const addMsgAssistantController = async (req, res) => {
41
41
  const { code, messages, role = 'user', reply = false } = req.body;
42
42
 
43
43
  try {
44
- const ans = await addMsgAssistant(code, messages, role, reply);
45
- if (ans) await sendMessage({code, body: ans, fileType: 'text', origin: 'assistant'});
46
- return res.status(200).send({ message: 'Add message to the assistant' });
44
+ const assistantReply = await addMsgAssistant(code, messages, role, reply);
45
+ if (assistantReply) await sendMessage({code, body: assistantReply, fileType: 'text', origin: 'assistant'});
46
+ return res.status(200).send({ message: 'Message added to assistant', success: true });
47
47
  } catch (error) {
48
- logger.error(error);
48
+ logger.error('[addMsgAssistantController] Error', { error: error.message, code, role });
49
49
  res.status(500).send({ message: 'Failed to add message assistant', error });
50
50
  }
51
51
  };
@@ -59,18 +59,18 @@ const createAssistantController = async (req, res) => {
59
59
  messages = Array.isArray(messages) ? messages : [];
60
60
 
61
61
  try {
62
- logger.info('[createAssistantController] codes', codes);
62
+ logger.info('[createAssistantController] codes', { codes });
63
63
  for (const code of codes) {
64
64
  await createAssistant(code, assistant_id, [...instrucciones, ...messages], force);
65
- logger.info('[createAssistantController] messages', messages);
65
+ logger.info('[createAssistantController] messages', { messages });
66
66
  for (const message of messages) {
67
- logger.info('[createAssistantController] message', message);
67
+ logger.info('[createAssistantController] message', { message });
68
68
  await sendMessage({code, body: message, fileType: 'text'});
69
69
  }
70
70
  }
71
71
  res.status(200).send({ message: 'Create the assistant' });
72
72
  } catch (error) {
73
- logger.error(error);
73
+ logger.error('[createAssistantController] Error creating assistant', { error: error.message, stack: error.stack });
74
74
  res.status(500).send({ message: 'Failed to create assistant', error });
75
75
  }
76
76
  };
@@ -82,7 +82,7 @@ const getInfoAssistantController = async (req, res) => {
82
82
  let threadInfo = await getThreadInfo(code);
83
83
  return res.status(200).send({ message: 'Send assistant info' , threadInfo});
84
84
  } catch (error) {
85
- logger.error(error);
85
+ logger.error('[getInfoAssistantController] Error', { error: error.message, code });
86
86
  res.status(500).send({ message: 'Failed to receive assistant info', error });
87
87
  }
88
88
  };
@@ -97,7 +97,7 @@ const listAssistantController = async (req, res) => {
97
97
  const assistants = await getRecordByFilter(Config_ID, tableName, `status="${airtableStatus}"`);
98
98
  return res.status(200).send({ message: 'List assistants' , assistants});
99
99
  } catch (error) {
100
- logger.error(error);
100
+ logger.error('[listAssistantController] Error', { error: error.message });
101
101
  res.status(500).send({ message: 'Failed to list assistants', error });
102
102
  }
103
103
  };
@@ -109,7 +109,7 @@ const switchAssistantController = async (req, res) => {
109
109
  await switchAssistant(code, assistant_id);
110
110
  return res.status(200).send({ message: 'Switch assistant' });
111
111
  } catch (error) {
112
- logger.error(error);
112
+ logger.error('[switchAssistantController] Error', { error: error.message, code, assistant_id });
113
113
  res.status(500).send({ message: 'Failed to switch assistant', error });
114
114
  }
115
115
  };
@@ -121,7 +121,7 @@ const stopAssistantController = async (req, res) => {
121
121
  await Thread.updateOne({ code }, { $set: { stopped: !!stop } });
122
122
  return res.status(200).send({ message: 'Stop assistant' });
123
123
  } catch (error) {
124
- logger.error(error);
124
+ logger.error('[stopAssistantController] Error', { error: error.message, code, stop });
125
125
  return res.status(500).send({ message: 'Failed to stop assistant', error });
126
126
  }
127
127
  };
@@ -354,18 +354,6 @@ class NexusMessaging {
354
354
 
355
355
  const chatId = messageData.from || messageData.From;
356
356
 
357
- // Send initial typing indicator with delay
358
- if (chatId && this.provider && typeof this.provider.sendTypingIndicator === 'function') {
359
- const messageId = messageData.id || messageData.MessageSid || messageData.message_id;
360
- if (messageId) {
361
- setTimeout(() => {
362
- this.provider.sendTypingIndicator(messageId).catch(err =>
363
- logger.debug('[processIncomingMessage] Typing indicator failed', { error: err.message })
364
- );
365
- }, 3000);
366
- }
367
- }
368
-
369
357
  if (chatId && hasPreprocessingHandler()) {
370
358
  const stop = await invokePreprocessingHandler({
371
359
  code: chatId,
package/lib/index.js CHANGED
@@ -14,7 +14,7 @@ const {
14
14
  registerAssistant,
15
15
  overrideGetAssistantById,
16
16
  configureAssistants: setAssistantsConfig
17
- } = require('./services/assistantService');
17
+ } = require('./services/assistantResolver');
18
18
  const { TwilioProvider } = require('./adapters/TwilioProvider');
19
19
  const { BaileysProvider } = require('./adapters/BaileysProvider');
20
20
  const { BaseAssistant } = require('./assistants/BaseAssistant');
@@ -0,0 +1,105 @@
1
+ const llmConfig = require('../config/llmConfig');
2
+ const { BaseAssistant } = require('../assistants/BaseAssistant');
3
+
4
+ let assistantConfig = null;
5
+ let assistantRegistry = {};
6
+ let customGetAssistantById = null;
7
+
8
+ const configureAssistants = (config) => {
9
+ if (!config) {
10
+ throw new Error('Assistant configuration is required');
11
+ }
12
+ assistantConfig = config;
13
+ };
14
+
15
+ const registerAssistant = (assistantId, definition) => {
16
+ if (!assistantId || typeof assistantId !== 'string') {
17
+ throw new Error('registerAssistant requires a string assistantId');
18
+ }
19
+
20
+ if (typeof definition === 'function') {
21
+ assistantRegistry[assistantId] = definition;
22
+ return definition;
23
+ }
24
+
25
+ if (definition && typeof definition === 'object') {
26
+ const {
27
+ extends: ParentClass = BaseAssistant,
28
+ create,
29
+ tools = [],
30
+ setup
31
+ } = definition;
32
+
33
+ class ConfiguredAssistant extends ParentClass {
34
+ constructor(options = {}) {
35
+ const provider = options.provider || llmConfig.getOpenAIProvider({ instantiate: false });
36
+ const sharedClient = options.client
37
+ || provider?.getClient?.()
38
+ || llmConfig.openaiClient
39
+ || null;
40
+
41
+ super({
42
+ ...options,
43
+ assistantId,
44
+ client: sharedClient,
45
+ provider,
46
+ tools: [...tools, ...(options.tools || [])]
47
+ });
48
+
49
+ if (typeof setup === 'function') {
50
+ setup.call(this, {
51
+ assistantId,
52
+ thread: this.thread,
53
+ options
54
+ });
55
+ }
56
+ }
57
+ }
58
+
59
+ if (typeof create === 'function') {
60
+ ConfiguredAssistant.prototype.create = async function(code, context) {
61
+ return await create.call(this, code, context);
62
+ };
63
+ }
64
+
65
+ assistantRegistry[assistantId] = ConfiguredAssistant;
66
+ return ConfiguredAssistant;
67
+ }
68
+
69
+ throw new Error('registerAssistant expects a class/function or configuration object');
70
+ };
71
+
72
+ const overrideGetAssistantById = (resolverFn) => {
73
+ if (typeof resolverFn === 'function') {
74
+ customGetAssistantById = resolverFn;
75
+ }
76
+ };
77
+
78
+ const getAssistantById = (assistant_id, thread) => {
79
+ if (customGetAssistantById) {
80
+ return customGetAssistantById(assistant_id, thread);
81
+ }
82
+
83
+ if (assistantRegistry[assistant_id]) {
84
+ const AssistantClass = assistantRegistry[assistant_id];
85
+ return new AssistantClass({ thread });
86
+ }
87
+
88
+ if (assistantConfig && assistantConfig[assistant_id]) {
89
+ const config = assistantConfig[assistant_id];
90
+ return new BaseAssistant({
91
+ ...config,
92
+ assistantId: assistant_id,
93
+ thread
94
+ });
95
+ }
96
+
97
+ throw new Error(`Assistant with ID "${assistant_id}" not found`);
98
+ };
99
+
100
+ module.exports = {
101
+ getAssistantById,
102
+ configureAssistants,
103
+ registerAssistant,
104
+ overrideGetAssistantById
105
+ };
@@ -1,6 +1,5 @@
1
1
  const { withTracing } = require('../utils/tracingDecorator.js');
2
- const llmConfig = require('../config/llmConfig');
3
- const { BaseAssistant } = require('../assistants/BaseAssistant');
2
+ const { runAssistantAndWait } = require('../helpers/assistantHelper');
4
3
  const {
5
4
  createAssistantCore,
6
5
  addMsgAssistantCore,
@@ -8,105 +7,15 @@ const {
8
7
  replyAssistantCore,
9
8
  switchAssistantCore
10
9
  } = require('./assistantServiceCore');
11
-
12
- let assistantConfig = null;
13
- let assistantRegistry = {};
14
- let customGetAssistantById = null;
15
-
16
- const configureAssistants = (config) => {
17
- if (!config) {
18
- throw new Error('Assistant configuration is required');
19
- }
20
- assistantConfig = config;
21
- };
22
-
23
- const registerAssistant = (assistantId, definition) => {
24
- if (!assistantId || typeof assistantId !== 'string') {
25
- throw new Error('registerAssistant requires a string assistantId');
26
- }
27
-
28
- if (typeof definition === 'function') {
29
- assistantRegistry[assistantId] = definition;
30
- return definition;
31
- }
32
-
33
- if (definition && typeof definition === 'object') {
34
- const {
35
- extends: ParentClass = BaseAssistant,
36
- create,
37
- tools = [],
38
- setup
39
- } = definition;
40
-
41
- class ConfiguredAssistant extends ParentClass {
42
- constructor(options = {}) {
43
- const provider = options.provider || llmConfig.getOpenAIProvider({ instantiate: false });
44
- const sharedClient = options.client
45
- || provider?.getClient?.()
46
- || llmConfig.openaiClient
47
- || null;
48
-
49
- super({
50
- ...options,
51
- assistantId,
52
- client: sharedClient,
53
- provider,
54
- tools: [...tools, ...(options.tools || [])]
55
- });
56
-
57
- if (typeof setup === 'function') {
58
- setup.call(this, {
59
- assistantId,
60
- thread: this.thread,
61
- options
62
- });
63
- }
64
- }
65
- }
66
-
67
- if (typeof create === 'function') {
68
- ConfiguredAssistant.prototype.create = async function(code, context) {
69
- return await create.call(this, code, context);
70
- };
71
- }
72
-
73
- assistantRegistry[assistantId] = ConfiguredAssistant;
74
- return ConfiguredAssistant;
75
- }
76
-
77
- throw new Error('registerAssistant expects a class/function or configuration object');
78
- };
79
-
80
- const overrideGetAssistantById = (resolverFn) => {
81
- if (typeof resolverFn === 'function') {
82
- customGetAssistantById = resolverFn;
83
- }
84
- };
85
-
86
- const getAssistantById = (assistant_id, thread) => {
87
- if (customGetAssistantById) {
88
- return customGetAssistantById(assistant_id, thread);
89
- }
90
-
91
- if (assistantRegistry[assistant_id]) {
92
- const AssistantClass = assistantRegistry[assistant_id];
93
- return new AssistantClass({ thread });
94
- }
95
-
96
- if (assistantConfig && assistantConfig[assistant_id]) {
97
- const config = assistantConfig[assistant_id];
98
- return new BaseAssistant({
99
- ...config,
100
- assistantId: assistant_id,
101
- thread
102
- });
103
- }
104
-
105
- throw new Error(`Assistant with ID "${assistant_id}" not found`);
106
- };
10
+ const {
11
+ getAssistantById,
12
+ configureAssistants,
13
+ registerAssistant,
14
+ overrideGetAssistantById
15
+ } = require('./assistantResolver');
107
16
 
108
17
  const createAssistant = withTracing(
109
- (code, assistant_id) => createAssistantCore(code, assistant_id, getAssistantById),
18
+ createAssistantCore,
110
19
  'create_assistant',
111
20
  (code, assistant_id) => ({
112
21
  'assistant.thread_code': code,
@@ -138,7 +47,7 @@ const addInsAssistant = withTracing(
138
47
  );
139
48
 
140
49
  const replyAssistant = withTracing(
141
- (code, message_, thread_, runOptions) => replyAssistantCore(code, message_, thread_, runOptions, getAssistantById),
50
+ replyAssistantCore,
142
51
  'assistant_reply',
143
52
  (code, message_, thread_, runOptions) => ({
144
53
  'assistant.thread_code': code,
@@ -159,13 +68,14 @@ const switchAssistant = withTracing(
159
68
  );
160
69
 
161
70
  module.exports = {
162
- getAssistantById,
163
71
  createAssistant,
164
72
  replyAssistant,
165
73
  addMsgAssistant,
166
74
  addInsAssistant,
167
75
  switchAssistant,
76
+ getAssistantById,
168
77
  configureAssistants,
169
78
  registerAssistant,
170
- overrideGetAssistantById
79
+ overrideGetAssistantById,
80
+ runAssistantAndWait
171
81
  };
@@ -5,6 +5,7 @@ const { createProvider } = require('../providers/createProvider');
5
5
  const { Thread } = require('../models/threadModel.js');
6
6
  const { PredictionMetrics } = require('../models/predictionMetricsModel');
7
7
  const { insertMessage } = require('../models/messageModel');
8
+ const { Historial_Clinico_ID } = require('../config/airtableConfig');
8
9
 
9
10
  const { getCurRow, runAssistantWithRetries } = require('../helpers/assistantHelper.js');
10
11
  const { getThread } = require('../helpers/threadHelper.js');
@@ -12,26 +13,27 @@ const { processThreadMessage } = require('../helpers/processHelper.js');
12
13
  const { getLastMessages, updateMessageRecord } = require('../helpers/messageHelper.js');
13
14
  const { withThreadRecovery } = require('../helpers/threadRecoveryHelper.js');
14
15
  const { combineImagesToPDF, cleanupFiles } = require('../helpers/filesHelper.js');
16
+ const { getAssistantById } = require('./assistantResolver');
15
17
  const { logger } = require('../utils/logger');
16
18
 
17
- const createAssistantCore = async (code, assistant_id, getAssistantById) => {
19
+ const createAssistantCore = async (code, assistant_id) => {
18
20
  const thread = await getThread(code);
19
21
  if (!thread) return null;
20
22
 
21
23
  const assistant = getAssistantById(assistant_id, thread);
22
- const curRow = await getCurRow(code);
24
+ const curRow = await getCurRow(Historial_Clinico_ID, code);
23
25
  const context = { curRow };
24
26
 
25
27
  try {
26
28
  await assistant.create(code, context);
27
29
  return { success: true, assistant_id };
28
30
  } catch (error) {
29
- logger.error('[createAssistantCore] Error:', error);
31
+ logger.error('[createAssistantCore] Error creating assistant', { error: error.message, assistant_id, code });
30
32
  return { success: false, error: error.message };
31
33
  }
32
34
  };
33
35
 
34
- const addMsgAssistantCore = async (code, message, role = 'user') => {
36
+ const addMsgAssistantCore = async (code, message, role = 'user', reply = false) => {
35
37
  const thread = await getThread(code);
36
38
  if (!thread) return null;
37
39
 
@@ -41,10 +43,17 @@ const addMsgAssistantCore = async (code, message, role = 'user') => {
41
43
  try {
42
44
  await provider.addMessage({ threadId, messages: [{ role, content: message }] });
43
45
  await insertMessage({ code, message, role });
44
- return { success: true };
46
+
47
+ if (reply) {
48
+ const assistant = getAssistantById(thread.getAssistantId(), thread);
49
+ const runResult = await runAssistantWithRetries(thread, assistant, {});
50
+ return runResult?.output || null;
51
+ }
52
+
53
+ return null;
45
54
  } catch (error) {
46
- logger.error('[addMsgAssistantCore] Error:', error);
47
- return { success: false, error: error.message };
55
+ logger.error('[addMsgAssistantCore] Error adding message', { error: error.message, code, role });
56
+ return null;
48
57
  }
49
58
  };
50
59
 
@@ -57,14 +66,16 @@ const addInstructionCore = async (code, instruction, role = 'user') => {
57
66
 
58
67
  try {
59
68
  await provider.addMessage({ threadId, messages: [{ role, content: instruction }] });
60
- return { success: true };
69
+ const assistant = getAssistantById(thread.getAssistantId(), thread);
70
+ const runResult = await runAssistantWithRetries(thread, assistant, {});
71
+ return runResult?.output || null;
61
72
  } catch (error) {
62
- logger.error('[addInstructionCore] Error:', error);
63
- return { success: false, error: error.message };
73
+ logger.error('[addInstructionCore] Error adding instruction', { error: error.message, code, role });
74
+ return null;
64
75
  }
65
76
  };
66
77
 
67
- const replyAssistantCore = async (code, message_ = null, thread_ = null, runOptions = {}, getAssistantById) => {
78
+ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOptions = {}) => {
68
79
  const timings = {};
69
80
  const startTotal = Date.now();
70
81
 
@@ -175,12 +186,18 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
175
186
  retry_count: retries,
176
187
  completed: completed,
177
188
  timing_breakdown: timings
178
- }).catch(err => logger.error('[replyAssistantCore] Failed to store metrics:', err));
189
+ }).catch(err => logger.error('[replyAssistantCore] Failed to store metrics', { error: err.message }));
179
190
  }
180
191
 
181
192
  return { output, tools_executed };
182
193
  } catch (error) {
183
- logger.error('[replyAssistantCore] Error:', { error: error.message });
194
+ logger.error('[replyAssistantCore] Error in reply', {
195
+ error: error.message,
196
+ stack: error.stack,
197
+ code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
198
+ hasCustomThread: !!thread_,
199
+ hasMessage: !!message_
200
+ });
184
201
  throw error;
185
202
  }
186
203
  };
@@ -189,7 +206,8 @@ const switchAssistantCore = async (code, assistant_id) => {
189
206
  try {
190
207
  const thread = await Thread.findOne({ code });
191
208
  if (!thread) {
192
- return null;
209
+ logger.warn('[switchAssistantCore] Thread not found', { code, assistant_id });
210
+ return { success: false, error: 'Thread not found' };
193
211
  }
194
212
 
195
213
  const updateFields = {
@@ -199,10 +217,16 @@ const switchAssistantCore = async (code, assistant_id) => {
199
217
  };
200
218
 
201
219
  await Thread.updateOne({ code }, { $set: updateFields });
220
+ logger.info('[switchAssistantCore] Assistant switched', { code, assistant_id });
202
221
  return { success: true, assistant_id };
203
222
  } catch (error) {
204
- logger.info(error);
205
- return null;
223
+ logger.error('[switchAssistantCore] Error switching assistant', {
224
+ error: error.message,
225
+ stack: error.stack,
226
+ code,
227
+ assistant_id
228
+ });
229
+ return { success: false, error: error.message };
206
230
  }
207
231
  };
208
232
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",