@peopl-health/nexus 2.4.10 → 2.4.11
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/lib/assistants/BaseAssistant.js +4 -1
- package/lib/core/NexusMessaging.js +16 -6
- package/lib/helpers/assistantHelper.js +16 -11
- package/lib/helpers/messageHelper.js +5 -0
- package/lib/models/messageModel.js +10 -0
- package/lib/providers/OpenAIResponsesProvider.js +25 -16
- package/lib/providers/OpenAIResponsesProviderTools.js +55 -21
- package/lib/services/assistantService.js +4 -3
- package/lib/storage/MongoStorage.js +2 -1
- package/lib/utils/messageParser.js +5 -2
- package/package.json +1 -1
|
@@ -197,7 +197,10 @@ class BaseAssistant {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
try {
|
|
200
|
-
const lastMessages = await Message.find({
|
|
200
|
+
const lastMessages = await Message.find({
|
|
201
|
+
numero: whatsappId,
|
|
202
|
+
interactive_type: { $ne: 'flow' }
|
|
203
|
+
})
|
|
201
204
|
.sort({ createdAt: -1 })
|
|
202
205
|
.limit(DEFAULT_MAX_HISTORICAL_MESSAGES);
|
|
203
206
|
|
|
@@ -442,14 +442,17 @@ class NexusMessaging {
|
|
|
442
442
|
return;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
const
|
|
445
|
+
const result = await replyAssistant(from, body);
|
|
446
|
+
const response = typeof result === 'string' ? result : result?.output;
|
|
447
|
+
const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
|
|
446
448
|
|
|
447
449
|
if (response) {
|
|
448
450
|
await this.sendMessage({
|
|
449
451
|
code: from,
|
|
450
452
|
body: response,
|
|
451
453
|
processed: true,
|
|
452
|
-
origin: 'assistant'
|
|
454
|
+
origin: 'assistant',
|
|
455
|
+
tools_executed
|
|
453
456
|
});
|
|
454
457
|
}
|
|
455
458
|
} catch (error) {
|
|
@@ -506,14 +509,17 @@ class NexusMessaging {
|
|
|
506
509
|
? body
|
|
507
510
|
: `Media received (${mediaDescriptor || 'attachment'})`;
|
|
508
511
|
|
|
509
|
-
const
|
|
512
|
+
const result = await replyAssistant(from, fallbackMessage);
|
|
513
|
+
const response = typeof result === 'string' ? result : result?.output;
|
|
514
|
+
const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
|
|
510
515
|
|
|
511
516
|
if (response) {
|
|
512
517
|
await this.sendMessage({
|
|
513
518
|
code: from,
|
|
514
519
|
body: response,
|
|
515
520
|
processed: true,
|
|
516
|
-
origin: 'assistant'
|
|
521
|
+
origin: 'assistant',
|
|
522
|
+
tools_executed
|
|
517
523
|
});
|
|
518
524
|
}
|
|
519
525
|
} catch (error) {
|
|
@@ -647,13 +653,17 @@ class NexusMessaging {
|
|
|
647
653
|
logger.info(`Processing batched messages from ${chatId} (including media if any)`);
|
|
648
654
|
|
|
649
655
|
// Get assistant response
|
|
650
|
-
const
|
|
656
|
+
const result = await replyAssistant(chatId);
|
|
657
|
+
const botResponse = typeof result === 'string' ? result : result?.output;
|
|
658
|
+
const tools_executed = typeof result === 'object' ? result?.tools_executed : undefined;
|
|
659
|
+
|
|
651
660
|
if (botResponse) {
|
|
652
661
|
await this.sendMessage({
|
|
653
662
|
code: chatId,
|
|
654
663
|
body: botResponse,
|
|
655
664
|
processed: true,
|
|
656
|
-
origin: 'assistant'
|
|
665
|
+
origin: 'assistant',
|
|
666
|
+
tools_executed
|
|
657
667
|
});
|
|
658
668
|
}
|
|
659
669
|
|
|
@@ -59,12 +59,13 @@ const runAssistantAndWait = async ({
|
|
|
59
59
|
const variant = provider.getVariant ? provider.getVariant() : (process.env.VARIANT || 'assistants');
|
|
60
60
|
const tools = assistant.getToolSchemas ? assistant.getToolSchemas() : (configTools || []);
|
|
61
61
|
|
|
62
|
-
const runConfigWithAssistant = variant === 'responses'
|
|
63
|
-
? { ...conversationConfig, assistant }
|
|
64
|
-
: conversationConfig;
|
|
65
|
-
|
|
66
62
|
return await withThreadRecovery(
|
|
67
63
|
async (currentThread = thread) => {
|
|
64
|
+
const toolMetadata = { numero: currentThread.code, assistant_id: currentThread.getAssistantId() };
|
|
65
|
+
const runConfigWithAssistant = variant === 'responses'
|
|
66
|
+
? { ...conversationConfig, assistant, toolMetadata }
|
|
67
|
+
: conversationConfig;
|
|
68
|
+
|
|
68
69
|
let run = await provider.runConversation({
|
|
69
70
|
threadId: currentThread.getConversationId(),
|
|
70
71
|
assistantId: currentThread.getAssistantId(),
|
|
@@ -79,10 +80,14 @@ const runAssistantAndWait = async ({
|
|
|
79
80
|
|
|
80
81
|
const maxRetries = polling?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
81
82
|
let completed = false;
|
|
83
|
+
let tools_executed = run.tools_executed || [];
|
|
82
84
|
|
|
83
85
|
try {
|
|
84
86
|
logger.info('[runAssistantAndWait] Run started', { runId: run.id, threadId: currentThread.getConversationId(), assistantId: currentThread.getAssistantId() });
|
|
85
|
-
|
|
87
|
+
const result = await provider.checkRunStatus(assistant, currentThread.getConversationId(), run.id, 0, maxRetries, false, toolMetadata);
|
|
88
|
+
run = result.run;
|
|
89
|
+
completed = result.completed;
|
|
90
|
+
tools_executed = [...tools_executed, ...(result.tools_executed || [])];
|
|
86
91
|
} finally {
|
|
87
92
|
if (filter) {
|
|
88
93
|
await Thread.updateOne(filter, { $set: { run_id: null } });
|
|
@@ -90,12 +95,12 @@ const runAssistantAndWait = async ({
|
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
if (!completed) {
|
|
93
|
-
return { run: run, completed: false, output: '' };
|
|
98
|
+
return { run: run, completed: false, output: '', tools_executed };
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
const output = await provider.getRunText({ threadId: currentThread.getConversationId(), runId: run.id, fallback: '' });
|
|
97
102
|
|
|
98
|
-
return { completed: true, output };
|
|
103
|
+
return { completed: true, output, tools_executed };
|
|
99
104
|
},
|
|
100
105
|
thread,
|
|
101
106
|
variant
|
|
@@ -120,13 +125,13 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
|
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
const startTime = Date.now();
|
|
123
|
-
let run, output, completed;
|
|
128
|
+
let run, output, completed, tools_executed;
|
|
124
129
|
let retries = 0;
|
|
125
130
|
const maxRetries = DEFAULT_MAX_RETRIES;
|
|
126
131
|
|
|
127
132
|
do {
|
|
128
133
|
retries++;
|
|
129
|
-
({ run, output, completed } = await withTracing(
|
|
134
|
+
({ run, output, completed, tools_executed } = await withTracing(
|
|
130
135
|
executeAssistantAttempt,
|
|
131
136
|
'assistant_attempt',
|
|
132
137
|
(thread, assistant, runConfig, attemptNumber) => ({
|
|
@@ -150,10 +155,10 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
|
|
|
150
155
|
const predictionTimeMs = Date.now() - startTime;
|
|
151
156
|
|
|
152
157
|
if (run?.last_error) logger.warn('[runAssistantWithRetries] Run error', { error: run.last_error });
|
|
153
|
-
logger.info('[runAssistantWithRetries] Run completed', { completed, outputLength: output?.length || 0 });
|
|
158
|
+
logger.info('[runAssistantWithRetries] Run completed', { completed, outputLength: output?.length || 0, toolsExecuted: tools_executed?.length || 0 });
|
|
154
159
|
logger.info('[runAssistantWithRetries] TIMING', { predictionTimeMs, retries });
|
|
155
160
|
|
|
156
|
-
return { run, output, completed, retries, predictionTimeMs };
|
|
161
|
+
return { run, output, completed, retries, predictionTimeMs, tools_executed };
|
|
157
162
|
};
|
|
158
163
|
|
|
159
164
|
module.exports = {
|
|
@@ -5,6 +5,11 @@ const { logger } = require('../utils/logger');
|
|
|
5
5
|
const addMessageToThread = async (reply, messagesChat, provider, thread) => {
|
|
6
6
|
const threadId = thread.getConversationId();
|
|
7
7
|
|
|
8
|
+
if (reply.interactive_type === 'flow') {
|
|
9
|
+
logger.info(`[addMessageToThread] Skipping flow message (UI only) - ID: ${reply.message_id}`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
if (reply.origin === 'whatsapp_platform') {
|
|
9
14
|
await provider.addMessage({
|
|
10
15
|
threadId,
|
|
@@ -32,6 +32,15 @@ const messageSchema = new mongoose.Schema({
|
|
|
32
32
|
type: String,
|
|
33
33
|
enum: ['whatsapp_platform', 'assistant', 'patient'],
|
|
34
34
|
default: 'whatsapp_platform' },
|
|
35
|
+
tools_executed: [{
|
|
36
|
+
tool_name: { type: String, required: true },
|
|
37
|
+
tool_arguments: { type: Object, default: null },
|
|
38
|
+
tool_output: { type: Object, default: null },
|
|
39
|
+
execution_time_ms: { type: Number, default: null },
|
|
40
|
+
success: { type: Boolean, default: true },
|
|
41
|
+
call_id: { type: String, default: null },
|
|
42
|
+
executed_at: { type: Date, default: Date.now }
|
|
43
|
+
}],
|
|
35
44
|
media: {
|
|
36
45
|
contentType: { type: String, default: null },
|
|
37
46
|
bucketName: { type: String, default: null },
|
|
@@ -108,6 +117,7 @@ async function insertMessage(values) {
|
|
|
108
117
|
content_sid: values.content_sid || null,
|
|
109
118
|
clinical_context: clinical_context,
|
|
110
119
|
origin: values.origin,
|
|
120
|
+
tools_executed: values.tools_executed || [],
|
|
111
121
|
raw: values.raw || null
|
|
112
122
|
};
|
|
113
123
|
|
|
@@ -280,19 +280,23 @@ class OpenAIResponsesProvider {
|
|
|
280
280
|
tools = [],
|
|
281
281
|
model,
|
|
282
282
|
assistant,
|
|
283
|
+
toolMetadata,
|
|
283
284
|
} = {}) {
|
|
284
285
|
try {
|
|
285
286
|
const id = this._ensurethreadId(threadId);
|
|
286
287
|
const messages = this._responseInput(additionalMessages) || [];
|
|
287
288
|
|
|
288
|
-
|
|
289
|
+
const execMetadata = toolMetadata || { thread_id: id, assistant_id: assistantId };
|
|
290
|
+
let toolsExecuted = [];
|
|
291
|
+
|
|
289
292
|
if (assistant && toolOutputs.length === 0) {
|
|
290
293
|
try {
|
|
291
294
|
const conversationMessages = await this.listMessages({ threadId: id, order: 'desc', limit: 50 });
|
|
292
295
|
const items = conversationMessages?.data || [];
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
toolOutputs =
|
|
296
|
+
const result = await handlePendingFunctionCallsUtil(assistant, items, execMetadata);
|
|
297
|
+
if (result.outputs && result.outputs.length > 0) {
|
|
298
|
+
toolOutputs = result.outputs;
|
|
299
|
+
toolsExecuted = result.toolsExecuted || [];
|
|
296
300
|
}
|
|
297
301
|
} catch (error) {
|
|
298
302
|
logger.warn('[OpenAIResponsesProvider] Error checking for pending function calls:', error?.message);
|
|
@@ -329,6 +333,7 @@ class OpenAIResponsesProvider {
|
|
|
329
333
|
thread_id: id,
|
|
330
334
|
assistant_id: assistantId,
|
|
331
335
|
object: response.object || 'response',
|
|
336
|
+
tools_executed: toolsExecuted,
|
|
332
337
|
};
|
|
333
338
|
} catch (error) {
|
|
334
339
|
logger.error('[OpenAIResponsesProvider] Error running conversation:', error);
|
|
@@ -423,27 +428,31 @@ class OpenAIResponsesProvider {
|
|
|
423
428
|
return await handleRequiresActionUtil(assistant, run);
|
|
424
429
|
}
|
|
425
430
|
|
|
426
|
-
async checkRunStatus(assistant, thread_id, run_id, retryCount = 0, maxRetries = DEFAULT_MAX_RETRIES, actionHandled = false) {
|
|
431
|
+
async checkRunStatus(assistant, thread_id, run_id, retryCount = 0, maxRetries = DEFAULT_MAX_RETRIES, actionHandled = false, toolMetadata = {}, accumulatedTools = []) {
|
|
427
432
|
try {
|
|
428
433
|
let run = await this.getRun({ threadId: thread_id, runId: run_id });
|
|
429
434
|
logger.info(`Status: ${run.status} ${thread_id} ${run_id} (attempt ${retryCount + 1})`);
|
|
430
435
|
|
|
431
436
|
if (run.status === 'completed') {
|
|
432
|
-
return {run, completed: true};
|
|
437
|
+
return {run, completed: true, tools_executed: accumulatedTools};
|
|
433
438
|
}
|
|
434
439
|
|
|
435
440
|
if (run.status === 'failed' || run.status === 'cancelled' || run.status === 'expired') {
|
|
436
|
-
return {run, completed: false};
|
|
441
|
+
return {run, completed: false, tools_executed: accumulatedTools};
|
|
437
442
|
}
|
|
438
443
|
|
|
439
444
|
const needsFunctionCall = run.output?.some(item => item.type === 'function_call');
|
|
440
445
|
if (needsFunctionCall && !actionHandled) {
|
|
441
446
|
if (retryCount >= maxRetries) {
|
|
442
447
|
logger.warn('[OpenAIResponsesProvider] Max retries reached while handling function calls');
|
|
443
|
-
return {run, completed: false};
|
|
448
|
+
return {run, completed: false, tools_executed: accumulatedTools};
|
|
444
449
|
}
|
|
445
450
|
|
|
446
|
-
const
|
|
451
|
+
const execMetadata = { ...toolMetadata, thread_id, run_id };
|
|
452
|
+
const result = await handleRequiresActionUtil(assistant, run, execMetadata);
|
|
453
|
+
const outputs = result.outputs || [];
|
|
454
|
+
const toolsExecuted = result.toolsExecuted || [];
|
|
455
|
+
|
|
447
456
|
logger.info('[OpenAIResponsesProvider] Function call outputs:', outputs);
|
|
448
457
|
|
|
449
458
|
if (outputs.length > 0) {
|
|
@@ -456,30 +465,30 @@ class OpenAIResponsesProvider {
|
|
|
456
465
|
|
|
457
466
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
458
467
|
|
|
459
|
-
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, true);
|
|
468
|
+
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, true, toolMetadata, [...accumulatedTools, ...toolsExecuted]);
|
|
460
469
|
} catch (submitError) {
|
|
461
470
|
logger.error('[OpenAIResponsesProvider] Error submitting tool outputs:', submitError);
|
|
462
471
|
if (retryCount < maxRetries) {
|
|
463
472
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
464
|
-
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, false);
|
|
473
|
+
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, false, toolMetadata, accumulatedTools);
|
|
465
474
|
}
|
|
466
|
-
return {run, completed: false};
|
|
475
|
+
return {run, completed: false, tools_executed: accumulatedTools};
|
|
467
476
|
}
|
|
468
477
|
} else {
|
|
469
478
|
logger.warn('[OpenAIResponsesProvider] Function calls detected but no outputs generated');
|
|
470
|
-
return {run, completed: false};
|
|
479
|
+
return {run, completed: false, tools_executed: accumulatedTools};
|
|
471
480
|
}
|
|
472
481
|
}
|
|
473
482
|
|
|
474
483
|
if (retryCount < maxRetries) {
|
|
475
484
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
476
|
-
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, actionHandled);
|
|
485
|
+
return this.checkRunStatus(assistant, thread_id, run_id, retryCount + 1, maxRetries, actionHandled, toolMetadata, accumulatedTools);
|
|
477
486
|
}
|
|
478
487
|
|
|
479
|
-
return {run, completed: false};
|
|
488
|
+
return {run, completed: false, tools_executed: accumulatedTools};
|
|
480
489
|
} catch (error) {
|
|
481
490
|
logger.error('[OpenAIResponsesProvider] Error checking run status:', error);
|
|
482
|
-
return {run: null, completed: false};
|
|
491
|
+
return {run: null, completed: false, tools_executed: accumulatedTools};
|
|
483
492
|
}
|
|
484
493
|
}
|
|
485
494
|
|
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
const { logger } = require('../utils/logger');
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* @param {Object} assistant - The assistant instance with executeTool method
|
|
6
|
-
* @param {Object} call - The function call object with name, arguments, and call_id
|
|
7
|
-
* @returns {Promise<Object>} Function call output in Responses API format
|
|
8
|
-
*/
|
|
9
|
-
async function executeFunctionCall(assistant, call) {
|
|
3
|
+
async function executeFunctionCall(assistant, call, metadata = {}) {
|
|
4
|
+
const startTime = Date.now();
|
|
10
5
|
try {
|
|
11
6
|
const name = call.name;
|
|
12
7
|
const args = call.arguments ? JSON.parse(call.arguments) : {};
|
|
13
8
|
const result = await assistant.executeTool(name, args);
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
const executionTime = Date.now() - startTime;
|
|
10
|
+
|
|
11
|
+
const toolData = {
|
|
12
|
+
tool_name: name,
|
|
13
|
+
tool_arguments: args,
|
|
14
|
+
tool_output: result,
|
|
15
|
+
execution_time_ms: executionTime,
|
|
16
|
+
success: true,
|
|
16
17
|
call_id: call.call_id,
|
|
17
|
-
|
|
18
|
+
executed_at: new Date()
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
functionOutput: {
|
|
23
|
+
type: 'function_call_output',
|
|
24
|
+
call_id: call.call_id,
|
|
25
|
+
output: typeof result === 'string' ? result : JSON.stringify(result)
|
|
26
|
+
},
|
|
27
|
+
toolData
|
|
18
28
|
};
|
|
19
29
|
} catch (error) {
|
|
30
|
+
const executionTime = Date.now() - startTime;
|
|
31
|
+
|
|
32
|
+
const toolData = {
|
|
33
|
+
tool_name: call.name,
|
|
34
|
+
tool_arguments: call.arguments ? JSON.parse(call.arguments) : {},
|
|
35
|
+
tool_output: { error: error?.message || 'Tool execution failed' },
|
|
36
|
+
execution_time_ms: executionTime,
|
|
37
|
+
success: false,
|
|
38
|
+
call_id: call.call_id,
|
|
39
|
+
executed_at: new Date()
|
|
40
|
+
};
|
|
41
|
+
|
|
20
42
|
logger.error('[OpenAIResponsesProvider] Tool execution failed', error);
|
|
21
43
|
return {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
44
|
+
functionOutput: {
|
|
45
|
+
type: 'function_call_output',
|
|
46
|
+
call_id: call.call_id,
|
|
47
|
+
output: JSON.stringify({ success: false, error: error?.message || 'Tool execution failed' })
|
|
48
|
+
},
|
|
49
|
+
toolData
|
|
25
50
|
};
|
|
26
51
|
}
|
|
27
52
|
}
|
|
@@ -32,7 +57,7 @@ async function executeFunctionCall(assistant, call) {
|
|
|
32
57
|
* @param {Array} conversationItems - Array of conversation items
|
|
33
58
|
* @returns {Promise<Array>} Array of function call outputs
|
|
34
59
|
*/
|
|
35
|
-
async function handlePendingFunctionCalls(assistant, conversationItems) {
|
|
60
|
+
async function handlePendingFunctionCalls(assistant, conversationItems, metadata = {}) {
|
|
36
61
|
const pendingFunctionCalls = conversationItems.filter(item => item.type === 'function_call');
|
|
37
62
|
const functionOutputs = conversationItems.filter(item => item.type === 'function_call_output');
|
|
38
63
|
|
|
@@ -41,15 +66,20 @@ async function handlePendingFunctionCalls(assistant, conversationItems) {
|
|
|
41
66
|
);
|
|
42
67
|
|
|
43
68
|
if (orphanedCalls.length === 0) {
|
|
44
|
-
return [];
|
|
69
|
+
return { outputs: [], toolsExecuted: [] };
|
|
45
70
|
}
|
|
46
71
|
|
|
47
72
|
logger.info(`[OpenAIResponsesProvider] Found ${orphanedCalls.length} pending function calls, handling them...`);
|
|
48
73
|
const outputs = [];
|
|
74
|
+
const toolsExecuted = [];
|
|
75
|
+
|
|
49
76
|
for (const call of orphanedCalls) {
|
|
50
|
-
|
|
77
|
+
const result = await executeFunctionCall(assistant, call, metadata);
|
|
78
|
+
outputs.push(result.functionOutput);
|
|
79
|
+
toolsExecuted.push(result.toolData);
|
|
51
80
|
}
|
|
52
|
-
|
|
81
|
+
|
|
82
|
+
return { outputs, toolsExecuted };
|
|
53
83
|
}
|
|
54
84
|
|
|
55
85
|
/**
|
|
@@ -58,18 +88,22 @@ async function handlePendingFunctionCalls(assistant, conversationItems) {
|
|
|
58
88
|
* @param {Object} run - The run object with output array
|
|
59
89
|
* @returns {Promise<Array>} Array of function call outputs
|
|
60
90
|
*/
|
|
61
|
-
async function handleRequiresAction(assistant, run) {
|
|
91
|
+
async function handleRequiresAction(assistant, run, metadata = {}) {
|
|
62
92
|
const functionCalls = run.output?.filter(item => item.type === 'function_call') || [];
|
|
63
93
|
if (functionCalls.length === 0) {
|
|
64
|
-
return [];
|
|
94
|
+
return { outputs: [], toolsExecuted: [] };
|
|
65
95
|
}
|
|
66
96
|
|
|
67
97
|
const outputs = [];
|
|
98
|
+
const toolsExecuted = [];
|
|
99
|
+
|
|
68
100
|
for (const call of functionCalls) {
|
|
69
|
-
|
|
101
|
+
const result = await executeFunctionCall(assistant, call, metadata);
|
|
102
|
+
outputs.push(result.functionOutput);
|
|
103
|
+
toolsExecuted.push(result.toolData);
|
|
70
104
|
}
|
|
71
105
|
|
|
72
|
-
return outputs;
|
|
106
|
+
return { outputs, toolsExecuted };
|
|
73
107
|
}
|
|
74
108
|
|
|
75
109
|
/**
|
|
@@ -389,14 +389,15 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
389
389
|
timings.run_assistant_ms = runAssistantMs;
|
|
390
390
|
timings.total_ms = Date.now() - startTotal;
|
|
391
391
|
|
|
392
|
-
const { run, output, completed, retries, predictionTimeMs } = runResult;
|
|
392
|
+
const { run, output, completed, retries, predictionTimeMs, tools_executed } = runResult;
|
|
393
393
|
|
|
394
394
|
logger.info('[Assistant Reply Complete]', {
|
|
395
395
|
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
396
396
|
messageCount: patientReply.length,
|
|
397
397
|
hasMedia: urls.length > 0,
|
|
398
398
|
retries,
|
|
399
|
-
totalMs: timings.total_ms
|
|
399
|
+
totalMs: timings.total_ms,
|
|
400
|
+
toolsExecuted: tools_executed?.length || 0
|
|
400
401
|
});
|
|
401
402
|
|
|
402
403
|
if (output && predictionTimeMs) {
|
|
@@ -412,7 +413,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
412
413
|
}).catch(err => logger.error('[replyAssistantCore] Failed to store metrics:', err));
|
|
413
414
|
}
|
|
414
415
|
|
|
415
|
-
return output;
|
|
416
|
+
return { output, tools_executed };
|
|
416
417
|
};
|
|
417
418
|
|
|
418
419
|
const replyAssistant = withTracing(
|
|
@@ -186,7 +186,8 @@ class MongoStorage {
|
|
|
186
186
|
content_sid: messageData.contentSid || null,
|
|
187
187
|
template_variables: messageData.variables ? JSON.stringify(messageData.variables) : null,
|
|
188
188
|
raw: messageData.raw || null,
|
|
189
|
-
origin
|
|
189
|
+
origin,
|
|
190
|
+
tools_executed: messageData.tools_executed || []
|
|
190
191
|
};
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -198,8 +198,11 @@ class MessageParser {
|
|
|
198
198
|
return interactive.title || interactive.description || '[List item selected]';
|
|
199
199
|
|
|
200
200
|
case 'flow':
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
if (interactive.data) {
|
|
202
|
+
const flowData = typeof interactive.data === 'string' ? interactive.data : JSON.stringify(interactive.data, null, 2);
|
|
203
|
+
return `Flow Response:\n${flowData}`;
|
|
204
|
+
}
|
|
205
|
+
return '[Flow response]';
|
|
203
206
|
|
|
204
207
|
default:
|
|
205
208
|
return '[Interactive message]';
|