@peopl-health/nexus 5.68.8 → 5.68.9
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/config/cohortConfig.js +1 -1
- package/lib/clinical/context/ToolRuntimeContext.js +5 -2
- package/lib/clinical/flags/flagSnapshot.js +59 -0
- package/lib/clinical/providers/AnthropicProvider.js +1 -1
- package/lib/clinical/providers/BaseLLMProvider.js +8 -2
- package/lib/clinical/services/promptComposerService.js +4 -1
- package/package.json +1 -1
|
@@ -56,6 +56,7 @@ class ToolRuntimeContext {
|
|
|
56
56
|
this.skillActivationIteration = new Map();
|
|
57
57
|
this.currentIteration = null;
|
|
58
58
|
this.toolHistory = [];
|
|
59
|
+
this.toolRejections = [];
|
|
59
60
|
this.safetyArtifact = null;
|
|
60
61
|
this.palliativeAssessment = null;
|
|
61
62
|
this.careplan = null;
|
|
@@ -93,9 +94,11 @@ class ToolRuntimeContext {
|
|
|
93
94
|
this.trace.addStep({ iteration, ...toolData });
|
|
94
95
|
if (toolData.success) this.toolHistory.push(toolData);
|
|
95
96
|
if (!toolData.success || isToolFailure(toolData.tool_output)) {
|
|
97
|
+
const code = refusalCode(toolData.tool_output);
|
|
98
|
+
this.toolRejections.push({ tool: toolData.tool_name, code, iteration: iteration ?? null });
|
|
96
99
|
logger.warn('[toolRuntime] tool call rejected', {
|
|
97
100
|
tool: toolData.tool_name,
|
|
98
|
-
reason:
|
|
101
|
+
reason: code,
|
|
99
102
|
iteration: iteration ?? null,
|
|
100
103
|
turnId: this.turnId || null,
|
|
101
104
|
});
|
|
@@ -108,7 +111,7 @@ class ToolRuntimeContext {
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
async store({ outbound = null, completed = false, predictionTimeMs = null, error = null } = {}) {
|
|
111
|
-
this.trace.setSignals({ activatedSkills: [...this.activatedSkills].sort() });
|
|
114
|
+
this.trace.setSignals({ activatedSkills: [...this.activatedSkills].sort(), toolRejections: [...this.toolRejections] });
|
|
112
115
|
this.trace.setOutbound(outbound || this.trace.outbound || null);
|
|
113
116
|
const hasError = error !== null && error !== undefined;
|
|
114
117
|
const isSuperseded = hasError ? false : !this.isLive();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { isCohortEnabled } = require('../config/cohortConfig');
|
|
2
|
+
const { isBridgeEnabled } = require('./bridgeConfig');
|
|
3
|
+
const { contextReadLive, contextMirrorLive, contextVerifierLive } = require('./clinicalContextFlags');
|
|
4
|
+
const { clinicalPatternFloorLive } = require('./clinicalPatternFloorFlags');
|
|
5
|
+
const { isComposerEnabled, isAgentSelfRenderEnabled } = require('./composerFlags');
|
|
6
|
+
const { isContingencyCancelOnReplyLive, isArmOnOpenCeilingLive, isContingencyRuleBlockLive } = require('./contingencyFlags');
|
|
7
|
+
const { escalationAssessmentLive, escalationUpdateModeLive } = require('./escalationFlags');
|
|
8
|
+
const { inboundRecoveryLive, coldInboundMode } = require('./inboundRecoveryFlags');
|
|
9
|
+
const { isLlmAuditEnabled } = require('./llmAuditConfig');
|
|
10
|
+
const { isLlmMonitorEnabled } = require('./llmMonitorConfig');
|
|
11
|
+
const { patternConsultLive } = require('./patternConsultFlags');
|
|
12
|
+
const { recommendationRetryLive } = require('./recommendationRecoveryFlags');
|
|
13
|
+
const { isShadowEnabled } = require('./shadowConfig');
|
|
14
|
+
const { supportLightFromChatLive } = require('./supportLightFlags');
|
|
15
|
+
const { triageGateFloorLive } = require('./triageGateFloorFlags');
|
|
16
|
+
const { triageRecommendationLive, rankComparatorLive } = require('./triageRecommendationFlags');
|
|
17
|
+
const { isConversationalDeclarationLive, isConversationalDraftReuseLive, isDeclarationModelCheckLive } = require('./turnEfficiencyFlags');
|
|
18
|
+
|
|
19
|
+
const FLAG_READERS = {
|
|
20
|
+
cohort: isCohortEnabled,
|
|
21
|
+
bridge: isBridgeEnabled,
|
|
22
|
+
contextRead: contextReadLive,
|
|
23
|
+
contextMirror: contextMirrorLive,
|
|
24
|
+
contextVerifier: contextVerifierLive,
|
|
25
|
+
clinicalPatternFloor: clinicalPatternFloorLive,
|
|
26
|
+
composer: isComposerEnabled,
|
|
27
|
+
agentSelfRender: isAgentSelfRenderEnabled,
|
|
28
|
+
contingencyCancelOnReply: isContingencyCancelOnReplyLive,
|
|
29
|
+
armOnOpenCeiling: isArmOnOpenCeilingLive,
|
|
30
|
+
contingencyRuleBlock: isContingencyRuleBlockLive,
|
|
31
|
+
escalationAssessment: escalationAssessmentLive,
|
|
32
|
+
escalationUpdateMode: escalationUpdateModeLive,
|
|
33
|
+
inboundRecovery: inboundRecoveryLive,
|
|
34
|
+
coldInboundMode,
|
|
35
|
+
llmAudit: isLlmAuditEnabled,
|
|
36
|
+
llmMonitor: isLlmMonitorEnabled,
|
|
37
|
+
patternConsult: patternConsultLive,
|
|
38
|
+
recommendationRetry: recommendationRetryLive,
|
|
39
|
+
shadow: isShadowEnabled,
|
|
40
|
+
supportLightFromChat: supportLightFromChatLive,
|
|
41
|
+
triageGateFloor: triageGateFloorLive,
|
|
42
|
+
triageRecommendation: triageRecommendationLive,
|
|
43
|
+
rankComparator: rankComparatorLive,
|
|
44
|
+
conversationalDeclaration: isConversationalDeclarationLive,
|
|
45
|
+
conversationalDraftReuse: isConversationalDraftReuseLive,
|
|
46
|
+
declarationModelCheck: isDeclarationModelCheckLive,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function snapshotClinicalFlags() {
|
|
50
|
+
return Object.fromEntries(Object.entries(FLAG_READERS).map(([name, read]) => {
|
|
51
|
+
try {
|
|
52
|
+
return [name, read()];
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return [name, null];
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { snapshotClinicalFlags };
|
|
@@ -240,7 +240,7 @@ class AnthropicProvider extends BaseLLMProvider {
|
|
|
240
240
|
if (lastUserTurn?.role === 'user') {
|
|
241
241
|
deliveryNudges++;
|
|
242
242
|
previousRoundTools = [];
|
|
243
|
-
trace?.setSignals?.({ deliveryNudges });
|
|
243
|
+
trace?.setSignals?.({ deliveryNudges, nudgeKind: canDecide ? 'outreach' : 'delivery' });
|
|
244
244
|
logger.warn('[AnthropicProvider] Turn will not close — nudging', { tool: canDecide ? OUTREACH_DECISION_TOOL : 'DeliverPatientMessage', deliveryNudges, hasEnded, isBudgetSpent });
|
|
245
245
|
const turnContent = Array.isArray(lastUserTurn.content)
|
|
246
246
|
? lastUserTurn.content
|
|
@@ -10,6 +10,7 @@ const { resolveSkillCatalog } = require('../services/skillService');
|
|
|
10
10
|
const { RISK_HEADLINE_MARKER } = require('../services/riskHeadlineService');
|
|
11
11
|
const { resolveClinicalContext } = require('../services/clinicalContextReadService');
|
|
12
12
|
const { isContingencyRuleBlockLive } = require('../flags/contingencyFlags');
|
|
13
|
+
const { snapshotClinicalFlags } = require('../flags/flagSnapshot');
|
|
13
14
|
const { TURN_KIND } = require('../context/turnContext');
|
|
14
15
|
const { extractPatientMessage } = require('../../helpers/patientMessageHelper');
|
|
15
16
|
const runtimeConfig = require('../../config/runtimeConfig');
|
|
@@ -102,7 +103,7 @@ class BaseLLMProvider {
|
|
|
102
103
|
presetId: resolvedPresetId, presetVersion: resolvedPresetVersion, modelConfig,
|
|
103
104
|
} = await composePrompt({ presetId, presetVersion, promptId: assistantId, variables: promptVariables });
|
|
104
105
|
|
|
105
|
-
const { toolIds, filtered, descriptions: toolDescriptions } = await resolveTools({
|
|
106
|
+
const { toolIds, filtered, descriptions: toolDescriptions, unregisteredToolIds = [] } = await resolveTools({
|
|
106
107
|
promptId: resolvedPromptId || assistantId, assistant, presetToolIds,
|
|
107
108
|
});
|
|
108
109
|
const requestedAdditionalToolIds = Array.isArray(prePromptResult?.additionalToolIds)
|
|
@@ -200,7 +201,12 @@ class BaseLLMProvider {
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
trace?.setPrompt({ resolvedPresetId, resolvedPresetVersion });
|
|
203
|
-
trace?.setSignals?.({
|
|
204
|
+
trace?.setSignals?.({
|
|
205
|
+
offeredToolIds: effectiveToolIds,
|
|
206
|
+
enabledSkills,
|
|
207
|
+
flags: snapshotClinicalFlags(),
|
|
208
|
+
...(unregisteredToolIds.length > 0 && { unregisteredToolIds }),
|
|
209
|
+
});
|
|
204
210
|
|
|
205
211
|
const { response, toolsExecuted, retries, usage } = await this._invokeModel({
|
|
206
212
|
instructions: instructions || devContent || '',
|
|
@@ -191,6 +191,7 @@ async function fetchToolsByRecordIds(recordIds) {
|
|
|
191
191
|
return tools;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
const BLANK_TOOL_ID = '(blank tool_id)';
|
|
194
195
|
const warnedUnlinked = new Set();
|
|
195
196
|
|
|
196
197
|
function warnUnlinkedRegisteredTools(linkedToolIds, promptId) {
|
|
@@ -228,6 +229,7 @@ async function resolveTools({ promptId, assistant, presetToolIds = null, status
|
|
|
228
229
|
const activeTools = mappedTools.filter(t => !t.status || t.status === 'prod' || (!isProd && t.status === 'dev'));
|
|
229
230
|
|
|
230
231
|
const validToolIds = [];
|
|
232
|
+
const unregisteredToolIds = [];
|
|
231
233
|
const descriptions = {};
|
|
232
234
|
|
|
233
235
|
for (const tool of activeTools) {
|
|
@@ -237,6 +239,7 @@ async function resolveTools({ promptId, assistant, presetToolIds = null, status
|
|
|
237
239
|
// An Airtable column retyped away from long text would otherwise reach the model as [object Object].
|
|
238
240
|
if (typeof tool.description === 'string' && tool.description) descriptions[id] = tool.description;
|
|
239
241
|
} else {
|
|
242
|
+
unregisteredToolIds.push(id || BLANK_TOOL_ID);
|
|
240
243
|
logger.error('[promptComposer] Tool mapped in Airtable but not registered in code', { toolId: id, promptId });
|
|
241
244
|
}
|
|
242
245
|
}
|
|
@@ -252,7 +255,7 @@ async function resolveTools({ promptId, assistant, presetToolIds = null, status
|
|
|
252
255
|
|
|
253
256
|
logger.debug('[promptComposer] Tools resolved', { promptId, mapped: activeTools.length, valid: validToolIds.length, filtered: true });
|
|
254
257
|
|
|
255
|
-
return { toolIds: validToolIds, filtered: true, descriptions };
|
|
258
|
+
return { toolIds: validToolIds, filtered: true, descriptions, unregisteredToolIds };
|
|
256
259
|
}
|
|
257
260
|
|
|
258
261
|
function clearCache() {
|