@peopl-health/nexus 5.9.0-dev.921 → 6.0.0-dev.611
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/clinical/AssistantProcessor.js +5 -34
- package/lib/clinical/config/divergenceConfig.js +1 -18
- package/lib/clinical/config/llmConfig.js +1 -9
- package/lib/clinical/context/ToolRuntimeContext.js +2 -1
- package/lib/clinical/helpers/divergenceHelper.js +0 -12
- package/lib/clinical/index.js +0 -2
- package/lib/clinical/memory/DefaultMemoryManager.js +9 -19
- package/lib/clinical/models/turnTraceModel.js +5 -18
- package/lib/clinical/providers/AnthropicProvider.js +29 -291
- package/lib/clinical/providers/BaseLLMProvider.js +24 -113
- package/lib/clinical/providers/GatewayProvider.js +3 -3
- package/lib/clinical/providers/OpenAIResponsesProvider.js +4 -46
- package/lib/clinical/services/divergenceService.js +5 -22
- package/lib/clinical/services/promptComposerService.js +15 -46
- package/lib/clinical/services/shadowMessageService.js +12 -28
- package/lib/clinical/services/shadowService.js +3 -12
- package/lib/clinical/stores/CtcaeCatalog.js +0 -20
- package/lib/config/airtableConfig.js +0 -3
- package/lib/core/BatchingManager.js +3 -10
- package/lib/core/NexusMessaging.js +13 -31
- package/lib/eval/EvalProvider.js +2 -3
- package/lib/fhir/config/fhirConfig.js +3 -34
- package/lib/fhir/constants/projectionSlugs.js +1 -41
- package/lib/fhir/helpers/elementHelper.js +0 -21
- package/lib/fhir/helpers/fhirHelper.js +2 -5
- package/lib/fhir/index.js +0 -24
- package/lib/fhir/models/envelopeModel.js +0 -5
- package/lib/fhir/projections/registerProjectors.js +0 -16
- package/lib/fhir/resources/Condition.js +2 -107
- package/lib/fhir/resources/Observation.js +1 -73
- package/lib/fhir/resources/Procedure.js +2 -34
- package/lib/fhir/resources/Provenance.js +2 -93
- package/lib/fhir/services/fhirService.js +2 -2
- package/lib/helpers/twilioHelper.js +1 -9
- package/lib/index.d.ts +0 -16
- package/lib/index.js +0 -9
- package/lib/models/messageModel.js +0 -6
- package/lib/routes/index.js +1 -21
- package/lib/services/airtableService.js +1 -2
- package/lib/shared/dtos/ContingencySafetyNet.js +3 -10
- package/lib/shared/dtos/DispatchedEscalation.js +8 -3
- package/lib/shared/dtos/Reminder.js +2 -2
- package/lib/storage/MongoStorage.js +0 -2
- package/package.json +1 -1
- package/lib/clinical/config/cohortConfig.js +0 -68
- package/lib/clinical/config/composerConfig.js +0 -42
- package/lib/clinical/config/subAgentsConfig.js +0 -44
- package/lib/clinical/flags/bridgeConfig.js +0 -14
- package/lib/clinical/flags/composerFlags.js +0 -14
- package/lib/clinical/helpers/clinicalFactsHelper.js +0 -108
- package/lib/clinical/helpers/clinicalMentionHelper.js +0 -68
- package/lib/clinical/helpers/cohortHelper.js +0 -22
- package/lib/clinical/helpers/gradeEstimateHelper.js +0 -29
- package/lib/clinical/helpers/resultsHelper.js +0 -34
- package/lib/clinical/helpers/safetyFlagsHelper.js +0 -10
- package/lib/clinical/services/bridgeService.js +0 -55
- package/lib/clinical/services/clinicalAirtableService.js +0 -162
- package/lib/clinical/services/clinicalExtractionService.js +0 -112
- package/lib/clinical/services/composerService.js +0 -59
- package/lib/clinical/services/dispatcherService.js +0 -137
- package/lib/clinical/services/reminderDispatchService.js +0 -36
- package/lib/clinical/services/skillService.js +0 -44
- package/lib/clinical/tools/analyzeSymptomPatternsTool.js +0 -279
- package/lib/clinical/tools/deliverPatientMessageTool.js +0 -202
- package/lib/clinical/tools/extractClinicalInfoTool.js +0 -167
- package/lib/clinical/tools/getActiveSymptomLandscapeTool.js +0 -141
- package/lib/clinical/tools/getPatientHistoryTool.js +0 -235
- package/lib/clinical/tools/getPatientRiskProfileTool.js +0 -71
- package/lib/clinical/tools/getRouterContextBundleTool.js +0 -93
- package/lib/clinical/tools/lookupCtcaeEntryTool.js +0 -35
- package/lib/clinical/tools/openClusterHypothesisTool.js +0 -160
- package/lib/clinical/tools/openConditionTool.js +0 -136
- package/lib/clinical/tools/recordClinicalImpressionTool.js +0 -251
- package/lib/clinical/tools/recordInterventionTool.js +0 -140
- package/lib/clinical/tools/registerClinicalTools.js +0 -39
- package/lib/clinical/tools/reportCrisisTool.js +0 -211
- package/lib/clinical/tools/reportMedicalEscalationTool.js +0 -187
- package/lib/clinical/tools/reportResultsReceivedTool.js +0 -153
- package/lib/clinical/tools/reportUnresolvedRequestv2Tool.js +0 -177
- package/lib/clinical/tools/schedulePatientReminderTool.js +0 -149
- package/lib/clinical/tools/setContingencyPlanTool.js +0 -176
- package/lib/clinical/tools/skillTool.js +0 -67
- package/lib/clinical/tools/submitRoutingDecisionTool.js +0 -291
- package/lib/clinical/tools/updateClusterHypothesisTool.js +0 -159
- package/lib/controllers/cohortController.js +0 -16
- package/lib/controllers/traceController.js +0 -26
- package/lib/core/workflowRunner.js +0 -35
- package/lib/fhir/config/fhirTablesConfig.js +0 -9
- package/lib/fhir/constants/severityPriority.js +0 -8
- package/lib/fhir/helpers/compositeHelper.js +0 -88
- package/lib/fhir/helpers/fhirReadHelper.js +0 -42
- package/lib/fhir/helpers/terminologyHelper.js +0 -260
- package/lib/fhir/projections/clusterProjection.js +0 -24
- package/lib/fhir/projections/contingencyProjection.js +0 -36
- package/lib/fhir/projections/dispatchedEscalationProjection.js +0 -25
- package/lib/fhir/projections/interventionProjection.js +0 -42
- package/lib/fhir/projections/proctcaeProjection.js +0 -179
- package/lib/fhir/projections/reminderProjection.js +0 -20
- package/lib/fhir/projections/riskProjection.js +0 -16
- package/lib/fhir/projections/routingProjection.js +0 -48
- package/lib/fhir/projections/symptomCaseProjection.js +0 -65
- package/lib/fhir/resources/CarePlan.js +0 -93
- package/lib/fhir/resources/ClinicalImpression.js +0 -118
- package/lib/fhir/resources/Communication.js +0 -65
- package/lib/fhir/resources/CommunicationRequest.js +0 -182
- package/lib/fhir/resources/Device.js +0 -24
- package/lib/fhir/resources/Patient.js +0 -18
- package/lib/fhir/resources/Questionnaire.js +0 -23
- package/lib/fhir/resources/QuestionnaireResponse.js +0 -29
- package/lib/fhir/resources/RequestOrchestration.js +0 -46
- package/lib/fhir/resources/RiskAssessment.js +0 -99
- package/lib/fhir/resources/Task.js +0 -106
- package/lib/fhir/resources/interventionExtensions.js +0 -28
- package/lib/fhir/services/clusterService.js +0 -77
- package/lib/fhir/services/contingencyService.js +0 -117
- package/lib/fhir/services/interventionService.js +0 -149
- package/lib/fhir/services/resultObservationService.js +0 -52
- package/lib/fhir/services/riskService.js +0 -101
- package/lib/fhir/services/routingService.js +0 -147
- package/lib/fhir/services/snapshotService.js +0 -28
- package/lib/fhir/services/symptomCaseService.js +0 -139
- package/lib/fhir/services/terminologyService.js +0 -58
- package/lib/fhir/services/triageService.js +0 -57
- package/lib/fhir/utils/normalizeUtils.js +0 -33
- package/lib/shared/dtos/GradedSymptom.js +0 -23
- package/lib/shared/dtos/TriageResponse.js +0 -35
|
@@ -2,14 +2,10 @@ const { sanitizeOutput } = require('../utils/formatUtils');
|
|
|
2
2
|
|
|
3
3
|
const { getThread } = require('../helpers/threadHelper');
|
|
4
4
|
const { runAssistantWithRetries } = require('./helpers/assistantHelper');
|
|
5
|
-
const { overlayCohortPreset } = require('./helpers/cohortHelper');
|
|
6
5
|
|
|
7
6
|
const { forkShadowTurn } = require('./services/shadowService');
|
|
8
7
|
const { diverge } = require('./services/divergenceService');
|
|
9
8
|
const { getAssistantById } = require('./services/assistantResolver');
|
|
10
|
-
const { maybeSendBridge } = require('./services/bridgeService');
|
|
11
|
-
const { getBridgePresetId } = require('./flags/bridgeConfig');
|
|
12
|
-
const { ToolRuntimeContext } = require('./context/ToolRuntimeContext');
|
|
13
9
|
|
|
14
10
|
class AssistantProcessor {
|
|
15
11
|
constructor({ mode = 'local', queueAdapter = null, sendMessage = null, phiProcessor = null, storeRunMetrics = null }) {
|
|
@@ -22,15 +18,14 @@ class AssistantProcessor {
|
|
|
22
18
|
setSendMessage(fn) { this.sendMessage = fn; }
|
|
23
19
|
|
|
24
20
|
async resolveThread(code) {
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
-
const thread = await overlayCohortPreset(stored);
|
|
21
|
+
const thread = await getThread(code);
|
|
22
|
+
if (!thread) return null;
|
|
28
23
|
const assistant = getAssistantById(thread.getAssistantId(), thread);
|
|
29
24
|
return { thread, assistant };
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
async executeLLM(thread, assistant, runOptions = {}, message = null) {
|
|
33
|
-
if (!runOptions.shadow
|
|
28
|
+
if (!runOptions.shadow) {
|
|
34
29
|
forkShadowTurn({
|
|
35
30
|
thread,
|
|
36
31
|
message,
|
|
@@ -42,33 +37,11 @@ class AssistantProcessor {
|
|
|
42
37
|
});
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
if (thread.isInCohort && !runOptions.toolRuntimeContext && runOptions.runId && thread.code) {
|
|
46
|
-
runOptions.toolRuntimeContext = new ToolRuntimeContext({ patientCode: thread.code, turnId: runOptions.runId });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
40
|
const ctx = runOptions.toolRuntimeContext || null;
|
|
50
41
|
assistant.toolRuntimeContext = ctx;
|
|
51
|
-
if (ctx && thread) thread.toolRuntimeContext = ctx;
|
|
52
|
-
|
|
53
|
-
const runConfig = { ...runOptions, phiProcessor: this.phiProcessor };
|
|
54
|
-
const isClinicalAgentTurn = thread.preset_id === getBridgePresetId();
|
|
55
|
-
if (!runOptions.shadow && isClinicalAgentTurn && message?.body && this.sendMessage) {
|
|
56
|
-
let decision = null;
|
|
57
|
-
runConfig.onFirstIteration = (args) => {
|
|
58
|
-
decision = decision || maybeSendBridge({
|
|
59
|
-
...args,
|
|
60
|
-
code: thread.code,
|
|
61
|
-
turnId: runOptions.runId || null,
|
|
62
|
-
traceId: ctx?.trace?._id ? String(ctx.trace._id) : null,
|
|
63
|
-
shouldContinue: runOptions.shouldContinue,
|
|
64
|
-
sendMessage: this.sendMessage,
|
|
65
|
-
});
|
|
66
|
-
return decision;
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
42
|
|
|
70
43
|
const startTime = Date.now();
|
|
71
|
-
const runResult = await runAssistantWithRetries(thread, assistant,
|
|
44
|
+
const runResult = await runAssistantWithRetries(thread, assistant, { ...runOptions, phiProcessor: this.phiProcessor }, message);
|
|
72
45
|
const predictionTimeMs = Date.now() - startTime;
|
|
73
46
|
|
|
74
47
|
const output = sanitizeOutput(runResult?.output);
|
|
@@ -88,8 +61,6 @@ class AssistantProcessor {
|
|
|
88
61
|
prompt: run?.prompt || null,
|
|
89
62
|
preset: run?.preset || null,
|
|
90
63
|
response_id: run?.id || null,
|
|
91
|
-
turnId: runOptions.runId || null,
|
|
92
|
-
traceId: ctx?.trace?._id ? String(ctx.trace._id) : null,
|
|
93
64
|
run,
|
|
94
65
|
predictionTimeMs,
|
|
95
66
|
retries: runResult?.retries || 0,
|
|
@@ -124,7 +95,7 @@ class AssistantProcessor {
|
|
|
124
95
|
async sendResponse(code, result) {
|
|
125
96
|
if (!this.sendMessage) throw new Error('sendMessage function not configured');
|
|
126
97
|
if (!result?.output) return null;
|
|
127
|
-
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
|
|
98
|
+
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 });
|
|
128
99
|
return result.output;
|
|
129
100
|
}
|
|
130
101
|
}
|
|
@@ -46,23 +46,7 @@ async function load() {
|
|
|
46
46
|
const stochastic = { ...STOCHASTIC_DEFAULTS, ...(parsedStochastic || {}) };
|
|
47
47
|
if (stochastic.enabled && !stochastic.presetId) throw new Error('divergence config STOCHASTIC.presetId is required when enabled');
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
if (byKey.SAFETY_FLAG_PATTERNS) {
|
|
51
|
-
const parsedPatterns = safeParseArray(byKey.SAFETY_FLAG_PATTERNS);
|
|
52
|
-
if (!parsedPatterns) throw new Error('divergence config SAFETY_FLAG_PATTERNS is not a JSON array');
|
|
53
|
-
safetyFlagPatterns = parsedPatterns.map((row) => {
|
|
54
|
-
if (!isPlainObject(row)) throw new Error('divergence config SAFETY_FLAG_PATTERNS row is not an object');
|
|
55
|
-
const { flag, pattern } = row;
|
|
56
|
-
if (!flag || !pattern) throw new Error('divergence config SAFETY_FLAG_PATTERNS row missing flag or pattern');
|
|
57
|
-
try {
|
|
58
|
-
return { flag, re: new RegExp(pattern, 'i') };
|
|
59
|
-
} catch (err) {
|
|
60
|
-
throw new Error(`divergence config SAFETY_FLAG_PATTERNS pattern for ${flag} is not a valid regex: ${err.message}`);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const config = { FIELDS: fields, ORACLE_CONNECTION: oracleConnection, STOCHASTIC: stochastic, SAFETY_FLAG_PATTERNS: safetyFlagPatterns };
|
|
49
|
+
const config = { FIELDS: fields, ORACLE_CONNECTION: oracleConnection, STOCHASTIC: stochastic };
|
|
66
50
|
cache.set(CACHE_KEY, config);
|
|
67
51
|
return config;
|
|
68
52
|
}
|
|
@@ -76,5 +60,4 @@ module.exports = {
|
|
|
76
60
|
getFields: () => get('FIELDS'),
|
|
77
61
|
getOracleConnection: () => get('ORACLE_CONNECTION'),
|
|
78
62
|
getStochastic: () => get('STOCHASTIC'),
|
|
79
|
-
getSafetyFlagPatterns: () => get('SAFETY_FLAG_PATTERNS'),
|
|
80
63
|
};
|
|
@@ -7,8 +7,7 @@ const state = {
|
|
|
7
7
|
anthropicClient: null,
|
|
8
8
|
openaiClient: null,
|
|
9
9
|
provider: null,
|
|
10
|
-
variant: null
|
|
11
|
-
gatewayProvider: null
|
|
10
|
+
variant: null
|
|
12
11
|
};
|
|
13
12
|
|
|
14
13
|
const setOpenAIProvider = (provider) => {
|
|
@@ -39,11 +38,6 @@ const configureOpenAIProvider = (config = {}) => {
|
|
|
39
38
|
return provider;
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
const requireGatewayProvider = () => {
|
|
43
|
-
if (!state.gatewayProvider) state.gatewayProvider = createLLMProvider({ variant: 'gateway' });
|
|
44
|
-
return state.gatewayProvider;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
41
|
const getAnthropicClient = () => {
|
|
48
42
|
if (state.anthropicClient) return state.anthropicClient;
|
|
49
43
|
const apiKey = runtimeConfig.get('ANTHROPIC_API_KEY');
|
|
@@ -63,7 +57,6 @@ const _resetState = () => {
|
|
|
63
57
|
state.openaiClient = null;
|
|
64
58
|
state.provider = null;
|
|
65
59
|
state.variant = null;
|
|
66
|
-
state.gatewayProvider = null;
|
|
67
60
|
};
|
|
68
61
|
|
|
69
62
|
module.exports = {
|
|
@@ -74,7 +67,6 @@ module.exports = {
|
|
|
74
67
|
setOpenAIProvider,
|
|
75
68
|
getOpenAIProvider,
|
|
76
69
|
requireOpenAIProvider,
|
|
77
|
-
requireGatewayProvider,
|
|
78
70
|
configureOpenAIProvider,
|
|
79
71
|
_resetState
|
|
80
72
|
};
|
|
@@ -7,6 +7,7 @@ class ToolRuntimeContext {
|
|
|
7
7
|
sessionId = null,
|
|
8
8
|
repos = {},
|
|
9
9
|
contextSource = null,
|
|
10
|
+
thread = null,
|
|
10
11
|
} = {}) {
|
|
11
12
|
if (typeof patientCode !== 'string' || !patientCode) {
|
|
12
13
|
throw new Error('ToolRuntimeContext requires a non-empty patientCode');
|
|
@@ -23,7 +24,7 @@ class ToolRuntimeContext {
|
|
|
23
24
|
this.sessionId = sessionId;
|
|
24
25
|
this.repos = repos;
|
|
25
26
|
this.contextSource = contextSource;
|
|
26
|
-
this.
|
|
27
|
+
this.thread = thread;
|
|
27
28
|
this.toolHistory = [];
|
|
28
29
|
this.safetyArtifact = null;
|
|
29
30
|
this.palliativeAssessment = null;
|
|
@@ -29,17 +29,6 @@ const COMPARATORS = {
|
|
|
29
29
|
set: (expected, actual) => isDeepStrictEqual(new Set(expected || []), new Set(actual || [])),
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
function oracleToolsInvoked(turn = {}, enabledTools = null) {
|
|
33
|
-
const summary = Array.isArray(turn.state_mutations_summary) ? turn.state_mutations_summary : [];
|
|
34
|
-
const allow = Array.isArray(enabledTools) ? new Set(enabledTools) : null;
|
|
35
|
-
const names = summary
|
|
36
|
-
.filter((entry) => typeof entry === 'string')
|
|
37
|
-
.map((entry) => entry.split('(')[0].trim())
|
|
38
|
-
.filter(Boolean)
|
|
39
|
-
.filter((name) => !allow || allow.has(name));
|
|
40
|
-
return [...new Set(names)];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
32
|
const OUTCOME_RULES = [
|
|
44
33
|
[({ deterministic }) => deterministic.divergences.some((d) => d.isEmergencyFlip), 'review'],
|
|
45
34
|
[({ stochastic }) => stochastic.verdict === 'fail', 'fail'],
|
|
@@ -107,6 +96,5 @@ async function classify({ expected, actual, expectedMessage, actualMessage }) {
|
|
|
107
96
|
|
|
108
97
|
module.exports = {
|
|
109
98
|
deterministicDivergence,
|
|
110
|
-
oracleToolsInvoked,
|
|
111
99
|
classify,
|
|
112
100
|
};
|
package/lib/clinical/index.js
CHANGED
|
@@ -24,7 +24,6 @@ const llmConfigModule = require('./config/llmConfig');
|
|
|
24
24
|
const { ToolRuntimeContext } = require('./context/ToolRuntimeContext');
|
|
25
25
|
|
|
26
26
|
const { getCtcaeCatalog } = require('./stores/CtcaeCatalog');
|
|
27
|
-
const { registerClinicalTools } = require('./tools/registerClinicalTools');
|
|
28
27
|
|
|
29
28
|
async function initialize() {
|
|
30
29
|
await getCtcaeCatalog().initialize();
|
|
@@ -32,7 +31,6 @@ async function initialize() {
|
|
|
32
31
|
|
|
33
32
|
module.exports = {
|
|
34
33
|
initialize,
|
|
35
|
-
registerClinicalTools,
|
|
36
34
|
createLLMProvider,
|
|
37
35
|
PROVIDER_VARIANTS,
|
|
38
36
|
OpenAIResponsesProvider,
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const { handlePendingFunctionCalls: handlePendingFunctionCallsUtil } = require('../providers/OpenAIResponsesProviderTools');
|
|
2
|
-
|
|
3
2
|
const { Follow_Up_ID } = require('../../config/airtableConfig');
|
|
4
|
-
const runtimeConfig = require('../../config/runtimeConfig');
|
|
5
3
|
|
|
6
4
|
const { logger } = require('../../utils/logger');
|
|
7
5
|
|
|
@@ -9,11 +7,11 @@ const { getMessageTools, formatMessage } = require('../../helpers/messageHelper'
|
|
|
9
7
|
|
|
10
8
|
const { getRecordByFilter } = require('../../services/airtableService');
|
|
11
9
|
const { getMessages } = require('../../services/messageService');
|
|
12
|
-
|
|
10
|
+
const runtimeConfig = require('../../config/runtimeConfig');
|
|
13
11
|
const { MemoryManager } = require('./MemoryManager');
|
|
14
12
|
|
|
15
13
|
const DEFAULT_MAX_HISTORICAL_MESSAGES = 50;
|
|
16
|
-
const
|
|
14
|
+
const ALLOWED_SYSTEM_ROLES = new Set(['system', 'developer']);
|
|
17
15
|
|
|
18
16
|
class DefaultMemoryManager extends MemoryManager {
|
|
19
17
|
constructor(options = {}) {
|
|
@@ -38,15 +36,13 @@ class DefaultMemoryManager extends MemoryManager {
|
|
|
38
36
|
|
|
39
37
|
const context = allMessages.reverse().flatMap(msg => {
|
|
40
38
|
const toolItems = getMessageTools(msg);
|
|
41
|
-
const role = this._resolveContextRole(msg);
|
|
42
|
-
const prefix = this._isInlineOperatorTurn(msg, role) ? `${OPERATOR_TURN_PREFIX} ` : '';
|
|
43
39
|
const contentItems = formatMessage(msg)
|
|
44
|
-
.filter(Boolean)
|
|
45
40
|
.map(content => ({
|
|
46
|
-
role,
|
|
47
|
-
content:
|
|
41
|
+
role: this._resolveContextRole(msg),
|
|
42
|
+
content: content,
|
|
48
43
|
type: 'message'
|
|
49
|
-
}))
|
|
44
|
+
}))
|
|
45
|
+
.filter(item => item.content);
|
|
50
46
|
return [...toolItems, ...contentItems];
|
|
51
47
|
});
|
|
52
48
|
|
|
@@ -61,19 +57,13 @@ class DefaultMemoryManager extends MemoryManager {
|
|
|
61
57
|
if (msg.origin === 'patient') return 'user';
|
|
62
58
|
if (msg.origin === 'system' && msg.raw?.role) {
|
|
63
59
|
const role = String(msg.raw.role).trim().toLowerCase();
|
|
64
|
-
if (role
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
return 'user';
|
|
60
|
+
if (ALLOWED_SYSTEM_ROLES.has(role)) return role;
|
|
61
|
+
logger.warn('[DefaultMemoryManager] Unexpected system message role, using fallback', { role: msg.raw.role });
|
|
62
|
+
return 'system';
|
|
69
63
|
}
|
|
70
64
|
return 'assistant';
|
|
71
65
|
}
|
|
72
66
|
|
|
73
|
-
_isInlineOperatorTurn(msg, role) {
|
|
74
|
-
return msg.origin === 'system' && role === 'user';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
67
|
async processResponse(response, thread) {
|
|
78
68
|
this._logActivity('Processing response', { threadCode: thread.code, responseId: response.id });
|
|
79
69
|
try {
|
|
@@ -14,7 +14,6 @@ const iterationSchema = new mongoose.Schema({
|
|
|
14
14
|
reasoning: { type: mongoose.Schema.Types.Mixed, default: null },
|
|
15
15
|
responseId: { type: String, default: null },
|
|
16
16
|
toolCallIds: { type: [String], default: [] },
|
|
17
|
-
toolNames: { type: [String], default: [] },
|
|
18
17
|
durationMs: { type: Number, default: null },
|
|
19
18
|
}, { _id: false });
|
|
20
19
|
|
|
@@ -53,9 +52,6 @@ turnTraceSchema.methods.toDecision = function toDecision() {
|
|
|
53
52
|
outbound_purpose: signals.outboundPurpose || null,
|
|
54
53
|
branch_taken: signals.branchTaken || null,
|
|
55
54
|
red_flags_matched: decision.redFlagsMatched || [],
|
|
56
|
-
tools_invoked: [...new Set(this.iterations.flatMap((it) => it.toolNames || []))],
|
|
57
|
-
escalation_justification: signals.medicalEscalation?.urgencyJustification || null,
|
|
58
|
-
model_identifier: this.iterations.at(-1)?.model || null,
|
|
59
55
|
};
|
|
60
56
|
};
|
|
61
57
|
|
|
@@ -70,26 +66,17 @@ function buildIterationRecord(response, durationMs, apiCallConfig = {}) {
|
|
|
70
66
|
const usage = response?.usage || {};
|
|
71
67
|
const inputDetails = usage.input_tokens_details || {};
|
|
72
68
|
const output = response?.output || [];
|
|
73
|
-
const content = Array.isArray(response?.content) ? response.content : [];
|
|
74
69
|
return {
|
|
75
70
|
model: apiCallConfig?.model || null,
|
|
76
71
|
temperature: apiCallConfig?.temperature ?? null,
|
|
77
|
-
maxOutputTokens: apiCallConfig?.max_output_tokens ||
|
|
72
|
+
maxOutputTokens: apiCallConfig?.max_output_tokens || null,
|
|
78
73
|
stopReason: response?.stop_reason || response?.status || null,
|
|
79
74
|
inputTokens: usage.input_tokens || 0,
|
|
80
75
|
outputTokens: usage.output_tokens || 0,
|
|
81
|
-
cacheCreationTokens: usage.cache_creation_tokens ||
|
|
82
|
-
cacheReadTokens: usage.cache_read_tokens ||
|
|
83
|
-
reasoning: output.find((item) => item.type === 'reasoning')?.summary
|
|
84
|
-
|
|
85
|
-
toolCallIds: [
|
|
86
|
-
...output.filter((item) => item.type === 'function_call').map((item) => item.call_id),
|
|
87
|
-
...content.filter((block) => block.type === 'tool_use').map((block) => block.id),
|
|
88
|
-
].filter((id) => typeof id === 'string' && id.length > 0),
|
|
89
|
-
toolNames: [
|
|
90
|
-
...output.filter((item) => item.type === 'function_call' && typeof item.call_id === 'string' && item.call_id.length > 0).map((item) => item.name),
|
|
91
|
-
...content.filter((block) => block.type === 'tool_use' && typeof block.id === 'string' && block.id.length > 0).map((block) => block.name),
|
|
92
|
-
].filter((name) => typeof name === 'string' && name.length > 0),
|
|
76
|
+
cacheCreationTokens: usage.cache_creation_tokens || inputDetails.cache_creation_tokens || 0,
|
|
77
|
+
cacheReadTokens: usage.cache_read_tokens || inputDetails.cached_tokens || 0,
|
|
78
|
+
reasoning: output.find((item) => item.type === 'reasoning')?.summary || null,
|
|
79
|
+
toolCallIds: output.filter((item) => item.type === 'function_call').map((item) => item.call_id),
|
|
93
80
|
responseId: response?.id || null,
|
|
94
81
|
durationMs,
|
|
95
82
|
};
|