@peopl-health/nexus 3.1.5 → 3.2.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.
@@ -483,6 +483,8 @@ class NexusMessaging {
483
483
  const result = await this._processMessages(from, () => replyAssistant(from, body));
484
484
  const response = typeof result === 'string' ? result : result?.output;
485
485
  const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
486
+ const prompt = typeof result === 'object' ? result?.prompt : undefined;
487
+ const response_id = typeof result === 'object' ? result?.response_id : undefined;
486
488
 
487
489
  if (response) {
488
490
  await this.sendMessage({
@@ -490,7 +492,9 @@ class NexusMessaging {
490
492
  body: response,
491
493
  processed: true,
492
494
  origin: 'assistant',
493
- tools_executed
495
+ tools_executed,
496
+ prompt,
497
+ response_id
494
498
  });
495
499
  }
496
500
  } catch (error) {
@@ -550,6 +554,8 @@ class NexusMessaging {
550
554
  const result = await this._processMessages(from, () => replyAssistant(from, fallbackMessage));
551
555
  const response = typeof result === 'string' ? result : result?.output;
552
556
  const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
557
+ const prompt = typeof result === 'object' ? result?.prompt : undefined;
558
+ const response_id = typeof result === 'object' ? result?.response_id : undefined;
553
559
 
554
560
  if (response) {
555
561
  await this.sendMessage({
@@ -557,7 +563,9 @@ class NexusMessaging {
557
563
  body: response,
558
564
  processed: true,
559
565
  origin: 'assistant',
560
- tools_executed
566
+ tools_executed,
567
+ prompt,
568
+ response_id
561
569
  });
562
570
  }
563
571
  } catch (error) {
@@ -728,6 +736,8 @@ class NexusMessaging {
728
736
 
729
737
  const botResponse = typeof result === 'string' ? result : result?.output;
730
738
  const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
739
+ const prompt = typeof result === 'object' ? result?.prompt : undefined;
740
+ const response_id = typeof result === 'object' ? result?.response_id : undefined;
731
741
 
732
742
  if (typingInterval) {
733
743
  clearInterval(typingInterval);
@@ -741,7 +751,9 @@ class NexusMessaging {
741
751
  body: botResponse,
742
752
  processed: true,
743
753
  origin: 'assistant',
744
- tools_executed
754
+ tools_executed,
755
+ prompt,
756
+ response_id
745
757
  });
746
758
  }
747
759
 
@@ -59,11 +59,17 @@ async function getLastMessages(code) {
59
59
  }
60
60
  }
61
61
 
62
- async function getLastNMessages(code, n) {
62
+ // Create a variable to use as a chackpoint
63
+ async function getLastNMessages(code, n, before=null) {
63
64
  try {
64
- const lastMessages = await Message.find({ numero: code })
65
+ const query = { numero: code };
66
+
67
+ if (before) query.createdAt = { $lte: before };
68
+
69
+ const lastMessages = await Message.find(query)
65
70
  .sort({ createdAt: -1 })
66
- .limit(n);
71
+ .limit(n)
72
+ .lean();
67
73
 
68
74
  if (!lastMessages || lastMessages.length === 0) {
69
75
  logger.info(`[getLastNMessages] No messages found for code: ${code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown'}, limit: ${n}`);
@@ -73,7 +73,9 @@ const messageSchema = new mongoose.Schema({
73
73
  errorCode: { type: String, default: null },
74
74
  errorMessage: { type: String, default: null },
75
75
  updatedAt: { type: Date, default: null }
76
- }
76
+ },
77
+ prompt: { type: Object, default: null },
78
+ response_id: { type: String, default: null }
77
79
  }, { timestamps: true });
78
80
 
79
81
  messageSchema.index({ numero: 1, createdAt: -1 });
@@ -12,6 +12,7 @@ const threadSchema = new mongoose.Schema({
12
12
  active: { type: Boolean, default: true },
13
13
  stopped: { type: Boolean, default: false },
14
14
  review: { type: Boolean, default: false },
15
+ version: { type: Number, default: null },
15
16
  nextSid: { type: [String], default: [] }
16
17
  }, { timestamps: true });
17
18
 
@@ -175,10 +175,12 @@ class OpenAIResponsesProvider {
175
175
  */
176
176
  async executeRun({ thread, assistant, tools = [], config = {} }) {
177
177
  const { conversationId, assistantId } = this._normalizeThread(thread);
178
+ const promptVersion = thread?.version || null;
178
179
 
179
180
  logger.info('[OpenAIResponsesProvider] Starting run', {
180
181
  conversationId,
181
- assistantId
182
+ assistantId,
183
+ promptVersion
182
184
  });
183
185
 
184
186
  try {
@@ -199,7 +201,7 @@ class OpenAIResponsesProvider {
199
201
  logger.info('[OpenAIResponsesProvider] Context built', {
200
202
  conversationId,
201
203
  assistantId,
202
- lastContext: context[-1] || null
204
+ context
203
205
  });
204
206
 
205
207
  const filter = thread.code ? { code: thread.code, active: true } : null;
@@ -219,6 +221,7 @@ class OpenAIResponsesProvider {
219
221
  tools,
220
222
  context,
221
223
  promptVariables,
224
+ promptVersion,
222
225
  assistant,
223
226
  ...config
224
227
  });
@@ -275,7 +278,8 @@ class OpenAIResponsesProvider {
275
278
  model,
276
279
  assistant,
277
280
  toolMetadata,
278
- promptVariables = null,
281
+ promptVersion = null,
282
+ promptVariables = null
279
283
  } = {}) {
280
284
  try {
281
285
  const id = this._ensurethreadId(threadId);
@@ -286,9 +290,9 @@ class OpenAIResponsesProvider {
286
290
 
287
291
  input = input.filter(item => item.type !== 'function_call' && item.type !== 'function_call_output');
288
292
 
289
- const promptConfig = promptVariables
290
- ? { id: assistantId, variables: promptVariables }
291
- : { id: assistantId };
293
+ const promptConfig = { id: assistantId };
294
+ if (promptVariables) promptConfig.variables = promptVariables;
295
+ if (promptVersion) promptConfig.version = String(promptVersion);
292
296
  logger.info('[OpenAIResponsesProvider] Prompt config', { promptConfig });
293
297
 
294
298
  const makeAPICall = (inputData) => retryWithBackoff(() =>
@@ -158,7 +158,9 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
158
158
  const finalThread = thread;
159
159
 
160
160
  const messagesStart = Date.now();
161
- const lastMessage = await getLastNMessages(code, 1);
161
+ const beforeCheckpoint = message_?.createdAt ?
162
+ (message_.createdAt.$date ? new Date(message_.createdAt.$date) : message_.createdAt) : null;
163
+ const lastMessage = await getLastNMessages(code, 1, beforeCheckpoint);
162
164
  timings.get_messages_ms = Date.now() - messagesStart;
163
165
 
164
166
  if (!lastMessage || lastMessage.length === 0 || lastMessage[0].from_me) {
@@ -232,6 +234,8 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
232
234
  timings.total_ms = Date.now() - startTotal;
233
235
 
234
236
  const { output: rawOutput, completed, retries, predictionTimeMs, tools_executed } = runResult;
237
+ const prompt = runResult.run?.prompt || null;
238
+ const response_id = runResult.run?.id || null;
235
239
 
236
240
  const output = sanitizeOutput(rawOutput);
237
241
  if (rawOutput !== output) {
@@ -269,7 +273,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
269
273
  }).catch(err => logger.error('[replyAssistantCore] Failed to store metrics', { error: err.message }));
270
274
  }
271
275
 
272
- return { output, tools_executed };
276
+ return { output, tools_executed, prompt, response_id };
273
277
  } catch (error) {
274
278
  logger.error('[replyAssistantCore] Error in reply', {
275
279
  error: error.message,
@@ -157,6 +157,8 @@ class MongoStorage {
157
157
  raw: messageData.raw || null,
158
158
  origin: messageData.origin || 'whatsapp_platform',
159
159
  tools_executed: messageData.tools_executed,
160
+ prompt: messageData.prompt || null,
161
+ response_id: messageData.response_id || null,
160
162
  statusInfo
161
163
  };
162
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.1.5",
3
+ "version": "3.2.0",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -1,80 +0,0 @@
1
- # Migration Guide
2
-
3
- This guide summarizes changes introduced in the current Nexus library refresh to help you migrate quickly with minimal breakage.
4
-
5
- ## TL;DR
6
- - Twilio remains first‑class; Baileys is supported for messaging but not for templates/flows (returns clear errors).
7
- - Message sends accept both `to` (preferred) and legacy `code` (normalized internally).
8
- - Templates and flows are supported through Twilio Content API; provider is auto‑injected into template controllers on `Nexus.initialize()` when Twilio is active.
9
- - New event bus + middleware model; existing handlers continue to work.
10
- - Storage adapter registry; use built‑in `mongo`/`noop` or plug your own adapter or instance.
11
- - Interactive/flows now have a provider‑agnostic API with Twilio mapping and event‑driven routing.
12
- - Assistant registry with optional override for `getAssistantById`.
13
-
14
- ## Messaging API
15
- - BREAKING (soft): Prefer `to` over `code` in `sendMessage`. Legacy `code` is still accepted and normalized internally.
16
-
17
- Before:
18
- ```js
19
- await nexus.sendMessage({ code: 'whatsapp:+521555...', message: 'Hi' });
20
- ```
21
- After (preferred):
22
- ```js
23
- await nexus.sendMessage({ code: '+521555...', message: 'Hi' });
24
- ```
25
-
26
- ## Templates & Flows (Twilio)
27
- - Twilio provider now implements content operations: `listTemplates`, `getTemplate`, `createTemplate`, `deleteTemplate`, `submitForApproval`, `checkApprovalStatus`.
28
- - On `Nexus.initialize()` with Twilio, the provider is auto‑injected into template controllers — default routes under `/api/template` work immediately.
29
- - Baileys: content/template operations are not supported; a clear error is thrown if called.
30
-
31
- ## Interactive & Flows (Provider‑agnostic)
32
- - New helper APIs:
33
- - `registerFlow(id, spec)`, `sendInteractive(nexus, { to, id | spec, variables })`.
34
- - Twilio: spec is converted to Content API payload and sent; if `spec.contentSid` is provided, it is used directly.
35
- - Baileys: currently unsupported.
36
- - Event‑driven routing:
37
- - `registerInteractiveHandler(match, handler)` and `attachInteractiveRouter(nexus)`; handlers are called when interactive messages are received.
38
-
39
- ## Middleware & Events
40
- - New event bus on `NexusMessaging`: subscribe to `*:received` and `*:handled` (message, media, interactive, command, keyword, flow).
41
- - New middleware: `nexus.getMessaging().use(type?, async (msg, nexus, next) => { ... })`.
42
- - Existing `setHandlers` and `onMessage`/`onInteractive`/etc. continue to work.
43
-
44
- ## Storage
45
- - Storage is now pluggable via a registry:
46
- - Built‑ins: `mongo` (default), `noop`.
47
- - Register your adapter: `registerStorage('src', MyStorageClass)` then `storage: 'src'`.
48
- - Or pass an instance: `storage: new MyStorageClass()`.
49
- - If the adapter has `connect()`, Nexus calls it automatically.
50
- - Controllers that rely on your Mongo models can continue to do so — no change required.
51
-
52
- ## Assistants
53
- - Register assistant classes at init:
54
- ```js
55
- await nexus.initialize({
56
- provider: 'twilio',
57
- llm: 'openai', llmConfig: { apiKey: process.env.OPENAI_API_KEY },
58
- assistants: {
59
- registry: { SUPPORT: SupportAssistantClass, SALES: SalesAssistantClass },
60
- getAssistantById: (id, thread) => null // optional override
61
- }
62
- });
63
- ```
64
- - An internal override for `getAssistantById` is supported; if provided, it is tried first, then fallback to registry.
65
-
66
- ## Utilities & Fixes
67
- - Utils index corrected to export existing files: `{ DefaultLLMProvider, MessageParser, logger }`.
68
- - Default OpenAI import fixed for CommonJS: `const OpenAI = require('openai');`.
69
- - Message model helpers unified; `LegacyMessage` references replaced with `Message` and a new exported `insertMessage`.
70
- - Types `declare module` now matches package name: `@peopl-health/nexus`.
71
-
72
- ## Routes
73
- - Built‑in route bundles remain available and importable via `setupDefaultRoutes(app)` or per‑group via `routes` + `createRouter()`.
74
-
75
- ## Testing
76
- - If your environment restricts forking, run Jest in‑band: `jest --runInBand`.
77
-
78
- ## Notes
79
- - Twilio approvals data shape varies by account/region. The provider looks up `content.links` and falls back to the documented REST path; data is normalized where possible.
80
- - Baileys: templates/flows remain unsupported; messaging and media send/receive are supported.