@peopl-health/nexus 3.13.2 → 3.13.3
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/core/AssistantProcessor.js +62 -17
- package/lib/core/NexusMessaging.js +4 -3
- package/lib/helpers/metricsHelper.js +62 -0
- package/lib/index.d.ts +19 -7
- package/lib/services/assistantService.js +21 -144
- package/package.json +1 -1
|
@@ -1,30 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const { sanitizeOutput } = require('../utils/formatUtils');
|
|
2
|
+
const { getThread } = require('../helpers/threadHelper');
|
|
3
|
+
const { runAssistantWithRetries } = require('../helpers/assistantHelper');
|
|
4
|
+
const { getAssistantById } = require('../services/assistantResolver');
|
|
5
|
+
|
|
4
6
|
class AssistantProcessor {
|
|
5
|
-
constructor({ mode = 'local', queueAdapter = null, sendMessage = null,
|
|
6
|
-
Object.assign(this, { mode, queueAdapter, sendMessage,
|
|
7
|
+
constructor({ mode = 'local', queueAdapter = null, sendMessage = null, preProcessMessages = null, storeRunMetrics = null }) {
|
|
8
|
+
Object.assign(this, { mode, queueAdapter, sendMessage, preProcessMessages, storeRunMetrics });
|
|
7
9
|
if (mode === 'queue' && queueAdapter) {
|
|
8
|
-
queueAdapter.process('assistant.process', (payload) => this.
|
|
10
|
+
queueAdapter.process('assistant.process', (payload) => this._processViaLocal(payload));
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
setReplyAssistant(fn) { this.replyAssistant = fn; }
|
|
13
14
|
setSendMessage(fn) { this.sendMessage = fn; }
|
|
14
15
|
|
|
16
|
+
async resolveThread(code) {
|
|
17
|
+
const thread = await getThread(code);
|
|
18
|
+
if (!thread) return null;
|
|
19
|
+
const assistant = getAssistantById(thread.getAssistantId(), thread);
|
|
20
|
+
return { thread, assistant };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async executeLLM(thread, assistant, runOptions = {}, messages = null) {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
const runResult = await runAssistantWithRetries(thread, assistant, runOptions, messages);
|
|
26
|
+
const predictionTimeMs = Date.now() - startTime;
|
|
27
|
+
|
|
28
|
+
const output = sanitizeOutput(runResult?.output);
|
|
29
|
+
const run = runResult?.run;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
output,
|
|
33
|
+
tools_executed: runResult?.tools_executed,
|
|
34
|
+
prompt: run?.prompt || null,
|
|
35
|
+
preset: run?.preset || null,
|
|
36
|
+
response_id: run?.id || null,
|
|
37
|
+
run,
|
|
38
|
+
predictionTimeMs,
|
|
39
|
+
retries: runResult?.retries || 0,
|
|
40
|
+
completed: runResult?.completed,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
15
44
|
async process({ code, body = null, runOptions = {} }) {
|
|
16
45
|
if (!code) throw new Error('code is required for assistant processing');
|
|
17
46
|
|
|
18
|
-
|
|
47
|
+
return (this.mode === 'queue')
|
|
19
48
|
? await this._processViaQueue({ code, body, runOptions })
|
|
20
|
-
: await this.
|
|
21
|
-
|
|
22
|
-
return result;
|
|
49
|
+
: await this._processViaLocal({ code, body, runOptions });
|
|
23
50
|
}
|
|
24
51
|
|
|
25
|
-
async
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
async _processViaLocal({ code, body = null, runOptions = {} }) {
|
|
53
|
+
const resolved = await this.resolveThread(code);
|
|
54
|
+
if (!resolved) return null;
|
|
55
|
+
const { thread, assistant } = resolved;
|
|
56
|
+
|
|
57
|
+
if (this.preProcessMessages) {
|
|
58
|
+
const preProcessed = await this.preProcessMessages(code, body, thread);
|
|
59
|
+
if (!preProcessed.shouldProcess) return null;
|
|
60
|
+
|
|
61
|
+
const result = await this.executeLLM(thread, assistant, runOptions, preProcessed.messages);
|
|
62
|
+
if (this.storeRunMetrics) await this.storeRunMetrics(code, thread, result, preProcessed.timings);
|
|
63
|
+
return { ...result, timings: preProcessed.timings };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = await this.executeLLM(thread, assistant, runOptions);
|
|
67
|
+
if (this.storeRunMetrics) await this.storeRunMetrics(code, thread, result);
|
|
68
|
+
return result;
|
|
28
69
|
}
|
|
29
70
|
|
|
30
71
|
async _processViaQueue({ code, body, runOptions }) {
|
|
@@ -35,8 +76,13 @@ class AssistantProcessor {
|
|
|
35
76
|
|
|
36
77
|
async processDirect({ code, runOptions = {} }) {
|
|
37
78
|
if (!code) throw new Error('code is required for direct processing');
|
|
38
|
-
|
|
39
|
-
|
|
79
|
+
|
|
80
|
+
const resolved = await this.resolveThread(code);
|
|
81
|
+
if (!resolved) return null;
|
|
82
|
+
|
|
83
|
+
const result = await this.executeLLM(resolved.thread, resolved.assistant, runOptions);
|
|
84
|
+
if (this.storeRunMetrics) await this.storeRunMetrics(code, resolved.thread, result);
|
|
85
|
+
return result;
|
|
40
86
|
}
|
|
41
87
|
|
|
42
88
|
async sendResponse(code, result) {
|
|
@@ -45,7 +91,6 @@ class AssistantProcessor {
|
|
|
45
91
|
await this.sendMessage({ code, body: result.output, processed: true, origin: 'assistant', tools_executed: result.tools_executed, prompt: result.prompt, preset: result.preset, response_id: result.response_id });
|
|
46
92
|
return result.output;
|
|
47
93
|
}
|
|
48
|
-
|
|
49
94
|
}
|
|
50
95
|
|
|
51
96
|
module.exports = { AssistantProcessor };
|
|
@@ -11,10 +11,11 @@ const { Thread } = require('../models/threadModel');
|
|
|
11
11
|
|
|
12
12
|
const { setEventBus: setStatusEventBus } = require('../helpers/messageStatusHelper');
|
|
13
13
|
const { ensureThreadExists } = require('../helpers/threadHelper');
|
|
14
|
+
const { storeRunMetrics } = require('../helpers/metricsHelper');
|
|
14
15
|
|
|
15
16
|
const { createMessagingProvider } = require('../adapters/registry');
|
|
16
17
|
|
|
17
|
-
const { addMsgAssistant,
|
|
18
|
+
const { addMsgAssistant, preProcessMessages } = require('../services/assistantService');
|
|
18
19
|
const { hasPreprocessingHandler, invokePreprocessingHandler } = require('../services/preprocessingService');
|
|
19
20
|
|
|
20
21
|
const { BatchingManager } = require('../core/BatchingManager');
|
|
@@ -76,8 +77,8 @@ class NexusMessaging {
|
|
|
76
77
|
mode: config.assistant?.mode || 'local',
|
|
77
78
|
queueAdapter: this.queueAdapter,
|
|
78
79
|
sendMessage: this.sendMessage.bind(this),
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
preProcessMessages,
|
|
81
|
+
storeRunMetrics,
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const { logger } = require('../utils/logger');
|
|
2
|
+
const { getPredictionMetrics } = require('../models/predictionMetricsModel');
|
|
3
|
+
|
|
4
|
+
const storeRunMetrics = async (code, thread, result, timings = {}) => {
|
|
5
|
+
const { output, run, predictionTimeMs, retries, completed } = result;
|
|
6
|
+
if (!output || !predictionTimeMs) return;
|
|
7
|
+
|
|
8
|
+
const usage = run?.usage || null;
|
|
9
|
+
const model = run?.model || null;
|
|
10
|
+
|
|
11
|
+
logger.info('[Assistant Reply Complete]', {
|
|
12
|
+
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
13
|
+
retries,
|
|
14
|
+
totalMs: timings.total_ms,
|
|
15
|
+
toolsExecuted: result.tools_executed?.length || 0,
|
|
16
|
+
token_usage: usage ? {
|
|
17
|
+
input_tokens: usage.input_tokens,
|
|
18
|
+
output_tokens: usage.output_tokens,
|
|
19
|
+
total_tokens: usage.total_tokens,
|
|
20
|
+
model,
|
|
21
|
+
} : undefined,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const tokenUsage = usage ? {
|
|
25
|
+
input_tokens: usage.input_tokens || 0,
|
|
26
|
+
output_tokens: usage.output_tokens || 0,
|
|
27
|
+
total_tokens: usage.total_tokens || 0,
|
|
28
|
+
model: model || undefined,
|
|
29
|
+
} : undefined;
|
|
30
|
+
|
|
31
|
+
await getPredictionMetrics().create({
|
|
32
|
+
message_id: `${code}-${Date.now()}`,
|
|
33
|
+
numero: code,
|
|
34
|
+
assistant_id: thread.getAssistantId(),
|
|
35
|
+
prediction_time_ms: predictionTimeMs,
|
|
36
|
+
retry_count: retries,
|
|
37
|
+
completed,
|
|
38
|
+
timing_breakdown: timings,
|
|
39
|
+
token_usage: tokenUsage,
|
|
40
|
+
prompt_config: run?.prompt || null,
|
|
41
|
+
response_id: run?.id || null,
|
|
42
|
+
resolved_prompt: run?.resolved_prompt || null,
|
|
43
|
+
snippet_ids: run?.snippet_ids || [],
|
|
44
|
+
tool_ids: run?.tool_ids || [],
|
|
45
|
+
preset_id: run?.preset_id || null,
|
|
46
|
+
preset_version: run?.preset_version || null,
|
|
47
|
+
preset: run?.preset || null,
|
|
48
|
+
}).catch(err => logger.error('[storeRunMetrics] Failed to store metrics', { error: err.message }));
|
|
49
|
+
|
|
50
|
+
const alertThreshold = parseInt(process.env.TOKEN_ALERT_THRESHOLD, 10);
|
|
51
|
+
if (alertThreshold && usage?.total_tokens > alertThreshold) {
|
|
52
|
+
logger.warn('[storeRunMetrics] Token usage spike detected', {
|
|
53
|
+
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
54
|
+
total_tokens: usage.total_tokens,
|
|
55
|
+
threshold: alertThreshold,
|
|
56
|
+
model,
|
|
57
|
+
assistant_id: thread.getAssistantId(),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports = { storeRunMetrics };
|
package/lib/index.d.ts
CHANGED
|
@@ -558,23 +558,35 @@ declare module '@peopl-health/nexus' {
|
|
|
558
558
|
mode?: 'local' | 'queue';
|
|
559
559
|
queueAdapter?: QueueAdapter;
|
|
560
560
|
sendMessage?: (messageData: MessageData) => Promise<any>;
|
|
561
|
-
|
|
562
|
-
|
|
561
|
+
preProcessMessages?: (code: string, body: any, thread: any) => Promise<{ shouldProcess: boolean; messages: any[] | null; timings: Record<string, any> }>;
|
|
562
|
+
storeRunMetrics?: (code: string, thread: any, result: any, timings?: Record<string, any>) => Promise<void>;
|
|
563
563
|
}
|
|
564
564
|
|
|
565
565
|
export interface ProcessInput {
|
|
566
566
|
code: string;
|
|
567
|
-
|
|
568
|
-
thread?: any;
|
|
567
|
+
body?: any;
|
|
569
568
|
runOptions?: Record<string, any>;
|
|
570
569
|
}
|
|
571
570
|
|
|
571
|
+
export interface LLMResult {
|
|
572
|
+
output: string;
|
|
573
|
+
tools_executed?: any[];
|
|
574
|
+
prompt?: string | null;
|
|
575
|
+
preset?: string | null;
|
|
576
|
+
response_id?: string | null;
|
|
577
|
+
run?: any;
|
|
578
|
+
predictionTimeMs?: number;
|
|
579
|
+
retries?: number;
|
|
580
|
+
completed?: boolean;
|
|
581
|
+
}
|
|
582
|
+
|
|
572
583
|
export class AssistantProcessor {
|
|
573
584
|
constructor(config: AssistantProcessorConfig);
|
|
574
|
-
setReplyAssistant(fn: AssistantProcessorConfig['replyAssistant']): void;
|
|
575
585
|
setSendMessage(fn: AssistantProcessorConfig['sendMessage']): void;
|
|
576
|
-
|
|
577
|
-
|
|
586
|
+
resolveThread(code: string): Promise<{ thread: any; assistant: any } | null>;
|
|
587
|
+
executeLLM(thread: any, assistant: any, runOptions?: Record<string, any>, messages?: any[]): Promise<LLMResult>;
|
|
588
|
+
process(input: ProcessInput): Promise<LLMResult | null>;
|
|
589
|
+
processDirect(input: { code: string; runOptions?: Record<string, any> }): Promise<LLMResult | null>;
|
|
578
590
|
sendResponse(code: string, result: any): Promise<string | null>;
|
|
579
591
|
}
|
|
580
592
|
}
|
|
@@ -3,14 +3,12 @@ const runtimeConfig = require('../config/runtimeConfig');
|
|
|
3
3
|
const { Historial_Clinico_ID } = require('../config/airtableConfig');
|
|
4
4
|
|
|
5
5
|
const { logger } = require('../utils/logger');
|
|
6
|
-
const { sanitizeOutput } = require('../utils/formatUtils');
|
|
7
6
|
const { withTracing } = require('../utils/tracingDecorator.js');
|
|
8
7
|
|
|
9
8
|
const { Thread } = require('../models/threadModel.js');
|
|
10
|
-
const { getPredictionMetrics } = require('../models/predictionMetricsModel');
|
|
11
9
|
const { insertMessage } = require('../models/messageModel');
|
|
12
10
|
|
|
13
|
-
const { getCurRow
|
|
11
|
+
const { getCurRow } = require('../helpers/assistantHelper.js');
|
|
14
12
|
const { getThread, switchThreadStoppedStatus, setThreadPromptId } = require('../helpers/threadHelper.js');
|
|
15
13
|
const { processThreadMessage } = require('../helpers/processHelper.js');
|
|
16
14
|
const { getLastNMessages, storeProcessedContent } = require('../helpers/messageHelper.js');
|
|
@@ -124,38 +122,31 @@ const addInstructionCore = async (code, instruction, role = 'system') => {
|
|
|
124
122
|
}
|
|
125
123
|
};
|
|
126
124
|
|
|
127
|
-
const
|
|
125
|
+
const preProcessMessagesCore = async (code, message_ = null, thread) => {
|
|
128
126
|
const timings = {};
|
|
129
|
-
const startTotal = Date.now();
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const threadStart = Date.now();
|
|
133
|
-
const thread = thread_ || await getThread(code);
|
|
134
|
-
timings.get_thread_ms = Date.now() - threadStart;
|
|
135
|
-
|
|
136
|
-
if (!thread) return null;
|
|
137
127
|
|
|
128
|
+
try {
|
|
138
129
|
const messagesStart = Date.now();
|
|
139
130
|
const beforeCheckpoint = message_?.createdAt ?? null;
|
|
140
131
|
const lastMessage = await getLastNMessages(code, 1, beforeCheckpoint, {
|
|
141
132
|
query: { from_me: false }
|
|
142
133
|
});
|
|
143
134
|
timings.get_messages_ms = Date.now() - messagesStart;
|
|
144
|
-
|
|
135
|
+
|
|
145
136
|
if (!lastMessage || lastMessage.length === 0) {
|
|
146
|
-
logger.info('[
|
|
147
|
-
return null;
|
|
137
|
+
logger.info('[preProcessMessages] No relevant data found for this assistant.');
|
|
138
|
+
return { shouldProcess: false, messages: null, timings };
|
|
148
139
|
}
|
|
149
140
|
|
|
150
141
|
const provider = createLLMProvider({ variant: runtimeConfig.get('VARIANT') });
|
|
151
|
-
logger.info(`[
|
|
142
|
+
logger.info(`[preProcessMessages] Processing ${lastMessage.length} messages in parallel`);
|
|
152
143
|
const processStart = Date.now();
|
|
153
144
|
const processResult = await processThreadMessage(code, lastMessage, provider);
|
|
154
|
-
|
|
145
|
+
|
|
155
146
|
const { results: processResults, timings: processTimings } = processResult;
|
|
156
147
|
timings.process_messages_ms = Date.now() - processStart;
|
|
157
|
-
|
|
158
|
-
logger.debug('[
|
|
148
|
+
|
|
149
|
+
logger.debug('[preProcessMessages] Process timings breakdown', { processTimings });
|
|
159
150
|
|
|
160
151
|
if (processTimings) {
|
|
161
152
|
timings.process_messages_breakdown = {
|
|
@@ -172,7 +163,7 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
172
163
|
const allTempFiles = processResults.flatMap(r => r.tempFiles || []);
|
|
173
164
|
|
|
174
165
|
await Promise.all(processResults.map(r => {
|
|
175
|
-
const processedContent = r.messages && r.messages.length > 0
|
|
166
|
+
const processedContent = r.messages && r.messages.length > 0
|
|
176
167
|
? r.messages
|
|
177
168
|
.filter(msg => msg.content.text !== r.reply?.body)
|
|
178
169
|
.map(msg => msg.content.text)
|
|
@@ -184,13 +175,13 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
184
175
|
await cleanupFiles(allTempFiles);
|
|
185
176
|
|
|
186
177
|
if (urls.length > 0) {
|
|
187
|
-
logger.info(`[
|
|
178
|
+
logger.info(`[preProcessMessages] Processing ${urls.length} URLs for PDF combination`);
|
|
188
179
|
const pdfStart = Date.now();
|
|
189
180
|
const pdfResult = await combineImagesToPDF({ code });
|
|
190
181
|
timings.pdf_combination_ms = Date.now() - pdfStart;
|
|
191
182
|
const { pdfBuffer, processedFiles } = pdfResult;
|
|
192
|
-
logger.info(`[
|
|
193
|
-
|
|
183
|
+
logger.info(`[preProcessMessages] PDF combination complete: ${processedFiles?.length || 0} files processed`);
|
|
184
|
+
|
|
194
185
|
if (pdfBuffer) {
|
|
195
186
|
const key = `${code}-${Date.now()}-combined.pdf`;
|
|
196
187
|
const bucket = runtimeConfig.get('AWS_S3_BUCKET_NAME');
|
|
@@ -198,128 +189,20 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
|
|
|
198
189
|
await AWS.uploadBufferToS3(pdfBuffer, bucket, key, 'application/pdf');
|
|
199
190
|
}
|
|
200
191
|
}
|
|
201
|
-
|
|
192
|
+
|
|
202
193
|
if (processedFiles && processedFiles.length) {
|
|
203
194
|
await cleanupFiles(processedFiles);
|
|
204
195
|
}
|
|
205
196
|
}
|
|
206
197
|
|
|
207
198
|
if (!patientMsg || thread.stopped) {
|
|
208
|
-
logger.info('[
|
|
209
|
-
return null;
|
|
199
|
+
logger.info('[preProcessMessages] Skipping AI processing', { patientMsg, stopped: thread.stopped, code });
|
|
200
|
+
return { shouldProcess: false, messages: null, timings };
|
|
210
201
|
}
|
|
211
202
|
|
|
212
|
-
|
|
213
|
-
const runStart = Date.now();
|
|
214
|
-
const runResult = await runAssistantWithRetries(thread, assistant, runOptions, lastMessage);
|
|
215
|
-
timings.run_assistant_ms = Date.now() - runStart;
|
|
216
|
-
timings.total_ms = Date.now() - startTotal;
|
|
217
|
-
|
|
218
|
-
const { output: rawOutput, completed, retries, predictionTimeMs, tools_executed } = runResult;
|
|
219
|
-
const run = runResult.run;
|
|
220
|
-
const usage = run?.usage || null;
|
|
221
|
-
const model = run?.model || null;
|
|
222
|
-
|
|
223
|
-
const output = sanitizeOutput(rawOutput);
|
|
224
|
-
if (rawOutput !== output) {
|
|
225
|
-
logger.debug('[replyAssistant] Output sanitized', {
|
|
226
|
-
originalLength: rawOutput?.length || 0,
|
|
227
|
-
sanitizedLength: output?.length || 0,
|
|
228
|
-
removedContent: rawOutput?.length ? 'brackets_removed' : 'none'
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
logger.info('[Assistant Response]', { output });
|
|
233
|
-
logger.info('[Assistant Reply Complete]', {
|
|
234
|
-
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
235
|
-
messageCount: lastMessage.length,
|
|
236
|
-
hasMedia: urls.length > 0,
|
|
237
|
-
retries,
|
|
238
|
-
totalMs: timings.total_ms,
|
|
239
|
-
toolsExecuted: tools_executed?.length || 0,
|
|
240
|
-
token_usage: usage ? {
|
|
241
|
-
input_tokens: usage.input_tokens,
|
|
242
|
-
output_tokens: usage.output_tokens,
|
|
243
|
-
total_tokens: usage.total_tokens,
|
|
244
|
-
model,
|
|
245
|
-
} : undefined,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (output && predictionTimeMs) {
|
|
249
|
-
logger.debug('[replyAssistant] Storing metrics with timing_breakdown', {
|
|
250
|
-
timing_breakdown: timings,
|
|
251
|
-
has_breakdown: !!timings.process_messages_breakdown
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
const tokenUsage = usage ? {
|
|
255
|
-
input_tokens: usage.input_tokens || 0,
|
|
256
|
-
output_tokens: usage.output_tokens || 0,
|
|
257
|
-
total_tokens: usage.total_tokens || 0,
|
|
258
|
-
model: model || undefined,
|
|
259
|
-
} : undefined;
|
|
260
|
-
|
|
261
|
-
await getPredictionMetrics().create({
|
|
262
|
-
message_id: `${code}-${Date.now()}`,
|
|
263
|
-
numero: code,
|
|
264
|
-
assistant_id: thread.getAssistantId(),
|
|
265
|
-
prediction_time_ms: predictionTimeMs,
|
|
266
|
-
retry_count: retries,
|
|
267
|
-
completed: completed,
|
|
268
|
-
timing_breakdown: timings,
|
|
269
|
-
token_usage: tokenUsage,
|
|
270
|
-
prompt_config: run?.prompt || null,
|
|
271
|
-
response_id: run?.id || null,
|
|
272
|
-
context_message_count: lastMessage?.length || null,
|
|
273
|
-
resolved_prompt: run?.resolved_prompt || null,
|
|
274
|
-
snippet_ids: run?.snippet_ids || [],
|
|
275
|
-
tool_ids: run?.tool_ids || [],
|
|
276
|
-
preset_id: run?.preset_id || null,
|
|
277
|
-
preset_version: run?.preset_version || null,
|
|
278
|
-
preset: run?.preset || null,
|
|
279
|
-
}).catch(err => logger.error('[replyAssistant] Failed to store metrics', { error: err.message }));
|
|
280
|
-
|
|
281
|
-
const alertThreshold = parseInt(process.env.TOKEN_ALERT_THRESHOLD, 10);
|
|
282
|
-
if (alertThreshold && usage?.total_tokens > alertThreshold) {
|
|
283
|
-
logger.warn('[replyAssistant] Token usage spike detected', {
|
|
284
|
-
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
285
|
-
total_tokens: usage.total_tokens,
|
|
286
|
-
threshold: alertThreshold,
|
|
287
|
-
model,
|
|
288
|
-
assistant_id: thread.getAssistantId(),
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return { output, tools_executed, prompt: run?.prompt || null, preset: run?.preset || null, response_id: run?.id || null };
|
|
294
|
-
} catch (error) {
|
|
295
|
-
logger.error('[replyAssistant] Error in reply', {
|
|
296
|
-
error: error.message,
|
|
297
|
-
stack: error.stack,
|
|
298
|
-
code: code ? `${code.substring(0, 3)}***${code.slice(-4)}` : 'unknown',
|
|
299
|
-
hasCustomThread: !!thread_,
|
|
300
|
-
hasMessage: !!message_
|
|
301
|
-
});
|
|
302
|
-
throw error;
|
|
303
|
-
}
|
|
304
|
-
};
|
|
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
|
-
};
|
|
203
|
+
return { shouldProcess: true, messages: lastMessage, timings };
|
|
321
204
|
} catch (error) {
|
|
322
|
-
logger.error('[
|
|
205
|
+
logger.error('[preProcessMessages] Error', { error: error.message, code });
|
|
323
206
|
throw error;
|
|
324
207
|
}
|
|
325
208
|
};
|
|
@@ -370,15 +253,9 @@ module.exports = {
|
|
|
370
253
|
'instruction.role': role,
|
|
371
254
|
'operation.type': 'add_instruction'
|
|
372
255
|
})),
|
|
373
|
-
|
|
374
|
-
'assistant.thread_code': code,
|
|
375
|
-
'assistant.has_message': !!message_,
|
|
376
|
-
'assistant.has_custom_thread': !!thread_,
|
|
377
|
-
'assistant.has_run_options': !!runOptions && Object.keys(runOptions).length > 0
|
|
378
|
-
})),
|
|
379
|
-
runDirect: withTracing(runDirectCore, 'run_direct', (code) => ({
|
|
256
|
+
preProcessMessages: withTracing(preProcessMessagesCore, 'pre_process_messages', (code) => ({
|
|
380
257
|
'assistant.thread_code': code,
|
|
381
|
-
'operation.type': '
|
|
258
|
+
'operation.type': 'pre_process_messages'
|
|
382
259
|
})),
|
|
383
260
|
switchAssistant: withTracing(switchAssistantCore, 'switch_assistant', (code, assistant_id) => ({
|
|
384
261
|
'assistant.thread_code': code,
|