@peopl-health/nexus 3.13.6 → 3.13.8
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/examples/basic-usage.js
CHANGED
|
@@ -81,7 +81,7 @@ async function startServer() {
|
|
|
81
81
|
apiVersion: process.env.META_API_VERSION || 'v21.0'
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
// Example:
|
|
84
|
+
// Example: postProcess hooks for running classifiers after each batch completes
|
|
85
85
|
async function classifySymptoms({ chatId }) {
|
|
86
86
|
console.log(`[symptomClassifier] Running for ${chatId}`);
|
|
87
87
|
// Replace with actual classifier logic: check eligibility, fetch messages, classify via LLM, persist results
|
|
@@ -94,7 +94,7 @@ async function startServer() {
|
|
|
94
94
|
console.log(`[escalationRouting] Done for ${chatId}`);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
// Example:
|
|
97
|
+
// Example: preProcess hook for per-turn context injection. Runs right
|
|
98
98
|
// before the assistant prompt is sent to the model. Use this to inject
|
|
99
99
|
// per-turn instructions, escalation acknowledgments, runtime feature flags, etc.
|
|
100
100
|
// Return { additionalInstructions, additionalMessages } to inject context,
|
|
@@ -104,7 +104,7 @@ async function startServer() {
|
|
|
104
104
|
// salience option (developer message at the end of history). To test the
|
|
105
105
|
// others, comment the active `return` and uncomment one of the alternatives.
|
|
106
106
|
async function injectPromptContext({ chatId, runId }) {
|
|
107
|
-
console.log(`[
|
|
107
|
+
console.log(`[preProcess] Running for ${chatId} (runId=${runId})`);
|
|
108
108
|
// Replace with your own lookup — e.g. query a pending escalations store,
|
|
109
109
|
// check feature flags, fetch session state, etc.
|
|
110
110
|
|
|
@@ -114,7 +114,7 @@ async function startServer() {
|
|
|
114
114
|
//return {
|
|
115
115
|
//additionalMessages: [{
|
|
116
116
|
//role: 'developer',
|
|
117
|
-
//content: 'TEST:
|
|
117
|
+
//content: 'TEST: preProcess hook is active. End your response with the marker [hook-ok].',
|
|
118
118
|
//}],
|
|
119
119
|
//};
|
|
120
120
|
|
|
@@ -155,8 +155,8 @@ async function startServer() {
|
|
|
155
155
|
enabled: true, // Enable check-after processing
|
|
156
156
|
checkDelayMs: 100, // Delay before checking for new messages (ms)
|
|
157
157
|
hooks: {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
preProcess: [injectPromptContext],
|
|
159
|
+
postProcess: [classifySymptoms, classifyEscalation],
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -2,12 +2,14 @@ const { logger } = require('../utils/logger');
|
|
|
2
2
|
|
|
3
3
|
const { handleStatusCallback, getMessageStatus } = require('../helpers/messageStatusHelper');
|
|
4
4
|
|
|
5
|
+
const { getEventBus } = require('../core/NexusMessaging');
|
|
6
|
+
|
|
5
7
|
async function messageStatusCallbackController(req, res) {
|
|
6
8
|
try {
|
|
7
9
|
const { MessageSid, MessageStatus } = req.body;
|
|
8
10
|
logger.debug('[MessageStatusController] Status callback received', { messageSid: MessageSid, status: MessageStatus });
|
|
9
|
-
|
|
10
|
-
const updated = await handleStatusCallback(req.body);
|
|
11
|
+
|
|
12
|
+
const updated = await handleStatusCallback(req.body, { eventBus: getEventBus() });
|
|
11
13
|
|
|
12
14
|
if (!updated) {
|
|
13
15
|
logger.warn('[MessageStatusController] Message not found', { messageSid: MessageSid });
|
|
@@ -9,7 +9,6 @@ const { logger } = require('../utils/logger');
|
|
|
9
9
|
const { Message, insertMessage } = require('../models/messageModel');
|
|
10
10
|
const { Thread } = require('../models/threadModel');
|
|
11
11
|
|
|
12
|
-
const { setEventBus: setStatusEventBus } = require('../helpers/messageStatusHelper');
|
|
13
12
|
const { ensureThreadExists } = require('../helpers/threadHelper');
|
|
14
13
|
const { storeRunMetrics } = require('../helpers/metricsHelper');
|
|
15
14
|
|
|
@@ -44,7 +43,6 @@ class NexusMessaging {
|
|
|
44
43
|
onFlow: null
|
|
45
44
|
};
|
|
46
45
|
this.events = new EventEmitter();
|
|
47
|
-
setStatusEventBus(this.events);
|
|
48
46
|
this.middleware = {
|
|
49
47
|
any: [],
|
|
50
48
|
message: [],
|
|
@@ -497,7 +495,6 @@ class NexusMessaging {
|
|
|
497
495
|
if (this.provider) await this.provider.disconnect();
|
|
498
496
|
if (this.queueAdapter) await this.queueAdapter.shutdown();
|
|
499
497
|
this.events.removeAllListeners();
|
|
500
|
-
setStatusEventBus(null);
|
|
501
498
|
}
|
|
502
499
|
}
|
|
503
500
|
|
|
@@ -2,13 +2,6 @@ const { logger } = require('../utils/logger');
|
|
|
2
2
|
|
|
3
3
|
const VALID_HOOKS = ['preProcess', 'postProcess'];
|
|
4
4
|
|
|
5
|
-
const LEGACY_MAP = {
|
|
6
|
-
preBatch: 'preProcess',
|
|
7
|
-
prePromptBuild: 'preProcess',
|
|
8
|
-
postBatch: 'postProcess',
|
|
9
|
-
onBatchError: 'postProcess',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
5
|
class ProcessingPipeline {
|
|
13
6
|
constructor(hooks = {}) {
|
|
14
7
|
this._hooks = {
|
|
@@ -16,8 +9,6 @@ class ProcessingPipeline {
|
|
|
16
9
|
postProcess: [],
|
|
17
10
|
};
|
|
18
11
|
|
|
19
|
-
this._hookMeta = new WeakMap();
|
|
20
|
-
|
|
21
12
|
if (hooks && typeof hooks === 'object') {
|
|
22
13
|
for (const [name, handlers] of Object.entries(hooks)) {
|
|
23
14
|
const fns = Array.isArray(handlers) ? handlers : [handlers];
|
|
@@ -29,27 +20,38 @@ class ProcessingPipeline {
|
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
addHook(name, fn) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!VALID_HOOKS.includes(resolved)) {
|
|
23
|
+
if (!VALID_HOOKS.includes(name)) {
|
|
35
24
|
throw new Error(`[ProcessingPipeline] Unknown hook: "${name}". Valid hooks: ${VALID_HOOKS.join(', ')}`);
|
|
36
25
|
}
|
|
37
26
|
if (typeof fn !== 'function') {
|
|
38
27
|
throw new Error(`[ProcessingPipeline] Hook "${name}" must be a function`);
|
|
39
28
|
}
|
|
40
29
|
|
|
41
|
-
this._hooks[
|
|
42
|
-
this._hookMeta.set(fn, { originalName: name });
|
|
30
|
+
this._hooks[name].push(fn);
|
|
43
31
|
return this;
|
|
44
32
|
}
|
|
45
33
|
|
|
46
34
|
async run(context, executeFn, shouldContinue = () => true) {
|
|
47
35
|
const ctx = { metadata: {}, ...context };
|
|
36
|
+
const preProcessCount = this._hooks.preProcess.length;
|
|
37
|
+
const postProcessCount = this._hooks.postProcess.length;
|
|
38
|
+
|
|
39
|
+
logger.info('[ProcessingPipeline] Running', { chatId: ctx.chatId, type: ctx.type, preProcessHooks: preProcessCount, postProcessHooks: postProcessCount });
|
|
48
40
|
|
|
49
41
|
try {
|
|
50
42
|
const preProcessResult = await this._runPreProcessHooks(ctx);
|
|
43
|
+
|
|
44
|
+
logger.debug('[ProcessingPipeline] preProcess complete', {
|
|
45
|
+
chatId: ctx.chatId,
|
|
46
|
+
type: ctx.type,
|
|
47
|
+
skip: preProcessResult.skip,
|
|
48
|
+
hasAdditionalInstructions: !!preProcessResult.additionalInstructions,
|
|
49
|
+
additionalMessagesCount: preProcessResult.additionalMessages.length,
|
|
50
|
+
hasToolChoice: preProcessResult.toolChoice != null,
|
|
51
|
+
});
|
|
52
|
+
|
|
51
53
|
if (preProcessResult.skip) {
|
|
52
|
-
logger.
|
|
54
|
+
logger.info('[ProcessingPipeline] Skipped by preProcess hook', { chatId: ctx.chatId, type: ctx.type });
|
|
53
55
|
this._runPostProcessHooks(ctx, null, null);
|
|
54
56
|
return null;
|
|
55
57
|
}
|
|
@@ -119,15 +121,7 @@ class ProcessingPipeline {
|
|
|
119
121
|
if (result) postContext.result = result;
|
|
120
122
|
if (error) postContext.error = error;
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
// postProcess and untagged: always.
|
|
124
|
-
const applicable = hooks.filter(fn => {
|
|
125
|
-
const meta = this._hookMeta.get(fn);
|
|
126
|
-
if (meta?.originalName === 'onBatchError') return !!error;
|
|
127
|
-
return true;
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
Promise.allSettled(applicable.map(fn => fn(postContext)))
|
|
124
|
+
Promise.allSettled(hooks.map(fn => fn(postContext)))
|
|
131
125
|
.then(results => {
|
|
132
126
|
for (const r of results) {
|
|
133
127
|
if (r.status === 'rejected') {
|
|
@@ -9,12 +9,6 @@ const { handle24HourWindowError } = require('../helpers/templateRecoveryHelper')
|
|
|
9
9
|
|
|
10
10
|
const { addLinkedRecord, updateRecordByFilter } = require('../services/airtableService');
|
|
11
11
|
|
|
12
|
-
let _eventBus = null;
|
|
13
|
-
|
|
14
|
-
function setEventBus(eventBus) {
|
|
15
|
-
_eventBus = eventBus;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
async function updateMessageStatus(messageSid, status, errorCode = null, errorMessage = null) {
|
|
19
13
|
try {
|
|
20
14
|
const updateData = {
|
|
@@ -72,7 +66,7 @@ async function updateMessageStatus(messageSid, status, errorCode = null, errorMe
|
|
|
72
66
|
}
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
async function handleStatusCallback(twilioStatusData) {
|
|
69
|
+
async function handleStatusCallback(twilioStatusData, { eventBus } = {}) {
|
|
76
70
|
const { MessageSid, MessageStatus, ErrorCode, ErrorMessage } = twilioStatusData;
|
|
77
71
|
|
|
78
72
|
if (!MessageSid || !MessageStatus) {
|
|
@@ -82,8 +76,8 @@ async function handleStatusCallback(twilioStatusData) {
|
|
|
82
76
|
|
|
83
77
|
const updated = await updateMessageStatus(MessageSid, MessageStatus.toLowerCase(), ErrorCode, ErrorMessage);
|
|
84
78
|
|
|
85
|
-
if (updated &&
|
|
86
|
-
safeEmit(
|
|
79
|
+
if (updated && eventBus) {
|
|
80
|
+
safeEmit(eventBus, 'message:status', createEvent('message:status', {
|
|
87
81
|
messageId: updated.message_id,
|
|
88
82
|
to: updated.numero,
|
|
89
83
|
status: MessageStatus.toLowerCase(),
|
|
@@ -120,6 +114,5 @@ async function getMessageStatus(messageSid) {
|
|
|
120
114
|
module.exports = {
|
|
121
115
|
updateMessageStatus,
|
|
122
116
|
handleStatusCallback,
|
|
123
|
-
getMessageStatus
|
|
124
|
-
setEventBus
|
|
117
|
+
getMessageStatus
|
|
125
118
|
};
|