ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.47: вынесен верхний orchestration-скелет handleMessage (bootstrap -> address attempt -> deep attempt) в единый turn runtime adapter без изменения поведения.
This commit is contained in:
parent
5f4e898c7c
commit
90d529f79b
|
|
@ -1468,7 +1468,40 @@ Validation:
|
||||||
- `assistantDeepTurnPackagingRuntimeAdapter.test.ts`
|
- `assistantDeepTurnPackagingRuntimeAdapter.test.ts`
|
||||||
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
||||||
|
|
||||||
Status: **In progress (Phase 2.1 + 2.2 + 2.3 + 2.4 + 2.5 + 2.6 + 2.7 + 2.8 + 2.9 + 2.10 + 2.11 + 2.12 + 2.13 + 2.14 + 2.15 + 2.16 + 2.17 + 2.18 + 2.19 + 2.20 + 2.21 + 2.22 + 2.23 + 2.24 + 2.25 + 2.26 + 2.27 + 2.28 + 2.29 + 2.30 + 2.31 + 2.32 + 2.33 + 2.34 + 2.35 + 2.36 + 2.37 + 2.38 + 2.39 + 2.40 + 2.41 + 2.42 + 2.43 + 2.44 + 2.45 + 2.46 completed)**
|
Implemented in current pass (Phase 2.47):
|
||||||
|
1. Extracted top-level assistant turn orchestration (`bootstrap -> address attempt -> deep attempt`) into dedicated runtime adapter:
|
||||||
|
- `assistantTurnAttemptRuntimeAdapter.ts`
|
||||||
|
- introduced:
|
||||||
|
- `runAssistantTurnAttemptRuntime(...)`
|
||||||
|
2. Rewired `assistantService.handleMessage` to use single top-level turn runtime boundary (behavior-preserving):
|
||||||
|
- user-turn bootstrap remains delegated to `assistantUserTurnBootstrapRuntime`;
|
||||||
|
- address and deep attempt runtimes remain unchanged, but orchestration/early-return logic moved out of service body.
|
||||||
|
3. Added focused unit tests:
|
||||||
|
- `assistantTurnAttemptRuntimeAdapter.test.ts`
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
1. `npm run build` passed.
|
||||||
|
2. Targeted living/address/deep followup pack passed:
|
||||||
|
- `assistantTurnAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnResponseAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAnalysisAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnAnalysisRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneResponseAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneAttemptRuntimeAdapter.test.ts`
|
||||||
|
- `assistantUserTurnBootstrapRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatLlmRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatHandlerRuntimeAdapter.test.ts`
|
||||||
|
- `assistantLivingChatRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressRuntimeAdapter.test.ts`
|
||||||
|
- `assistantAddressLaneResponseRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnResponseRuntimeAdapter.test.ts`
|
||||||
|
- `assistantDeepTurnPackagingRuntimeAdapter.test.ts`
|
||||||
|
- `assistantWave10SettlementCorrectiveRegression.test.ts`
|
||||||
|
|
||||||
|
Status: **In progress (Phase 2.1 + 2.2 + 2.3 + 2.4 + 2.5 + 2.6 + 2.7 + 2.8 + 2.9 + 2.10 + 2.11 + 2.12 + 2.13 + 2.14 + 2.15 + 2.16 + 2.17 + 2.18 + 2.19 + 2.20 + 2.21 + 2.22 + 2.23 + 2.24 + 2.25 + 2.26 + 2.27 + 2.28 + 2.29 + 2.30 + 2.31 + 2.32 + 2.33 + 2.34 + 2.35 + 2.36 + 2.37 + 2.38 + 2.39 + 2.40 + 2.41 + 2.42 + 2.43 + 2.44 + 2.45 + 2.46 + 2.47 completed)**
|
||||||
|
|
||||||
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ const assistantCanon_1 = __importStar(require("./assistantCanon"));
|
||||||
const assistantAddressAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressAttemptRuntimeAdapter"));
|
const assistantAddressAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressAttemptRuntimeAdapter"));
|
||||||
const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding"));
|
const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding"));
|
||||||
const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAttemptRuntimeAdapter"));
|
const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAttemptRuntimeAdapter"));
|
||||||
|
const assistantTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantTurnAttemptRuntimeAdapter"));
|
||||||
const assistantUserTurnBootstrapRuntimeAdapter_1 = __importStar(require("./assistantUserTurnBootstrapRuntimeAdapter"));
|
const assistantUserTurnBootstrapRuntimeAdapter_1 = __importStar(require("./assistantUserTurnBootstrapRuntimeAdapter"));
|
||||||
const assistantQueryPlanning_1 = __importStar(require("./assistantQueryPlanning"));
|
const assistantQueryPlanning_1 = __importStar(require("./assistantQueryPlanning"));
|
||||||
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
||||||
|
|
@ -4369,126 +4370,125 @@ class AssistantService {
|
||||||
return this.sessions.getSession(sessionId);
|
return this.sessions.getSession(sessionId);
|
||||||
}
|
}
|
||||||
async handleMessage(payload) {
|
async handleMessage(payload) {
|
||||||
const { session, sessionId, userMessage, runtimeAnalysisContext, userItem } = (0, assistantUserTurnBootstrapRuntimeAdapter_1.runAssistantUserTurnBootstrapRuntime)({
|
const turnRuntime = await (0, assistantTurnAttemptRuntimeAdapter_1.runAssistantTurnAttemptRuntime)({
|
||||||
payload,
|
payload,
|
||||||
ensureSession: (targetSessionId) => this.sessions.ensureSession(targetSessionId),
|
runUserTurnBootstrapRuntime: (runtimePayload) => (0, assistantUserTurnBootstrapRuntimeAdapter_1.runAssistantUserTurnBootstrapRuntime)({
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
payload: runtimePayload,
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
ensureSession: (targetSessionId) => this.sessions.ensureSession(targetSessionId),
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
compactWhitespace,
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
repairAddressMojibake,
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
resolveRuntimeAnalysisContext,
|
compactWhitespace,
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
repairAddressMojibake,
|
||||||
nowIso: () => new Date().toISOString()
|
resolveRuntimeAnalysisContext,
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
nowIso: () => new Date().toISOString()
|
||||||
|
}),
|
||||||
|
resolveSessionOrganizationScopeContext: (runtimeUserMessage, sessionItems) => resolveSessionOrganizationScopeContext(runtimeUserMessage, sessionItems),
|
||||||
|
runAddressAttemptRuntime: async (runtimeInput) => (0, assistantAddressAttemptRuntimeAdapter_1.runAssistantAddressAttemptRuntime)({
|
||||||
|
featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1,
|
||||||
|
sessionId: runtimeInput.sessionId,
|
||||||
|
userMessage: runtimeInput.userMessage,
|
||||||
|
sessionItems: runtimeInput.sessionItems,
|
||||||
|
payload: runtimeInput.payload,
|
||||||
|
sessionScope: {
|
||||||
|
knownOrganizations: runtimeInput.sessionOrganizationScope.knownOrganizations,
|
||||||
|
selectedOrganization: runtimeInput.sessionOrganizationScope.selectedOrganization,
|
||||||
|
activeOrganization: runtimeInput.sessionOrganizationScope.activeOrganization
|
||||||
|
},
|
||||||
|
featureAddressLlmPredecomposeV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1,
|
||||||
|
runAddressLlmPreDecompose: async () => runAddressLlmPreDecompose(this.normalizerService, runtimeInput.payload, runtimeInput.userMessage),
|
||||||
|
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
||||||
|
sanitizeAddressMessageForFallback,
|
||||||
|
toNonEmptyString,
|
||||||
|
resolveAddressFollowupCarryoverContext,
|
||||||
|
resolveAssistantOrchestrationDecision,
|
||||||
|
buildAddressDialogContinuationContractV2,
|
||||||
|
runtimeAnalysisContextAsOfDate: runtimeInput.runtimeAnalysisContext.as_of_date,
|
||||||
|
compactWhitespace,
|
||||||
|
mergeFollowupContextWithOrganizationScope,
|
||||||
|
runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options),
|
||||||
|
isRetryableAddressLimitedResult,
|
||||||
|
mergeKnownOrganizations,
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal,
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||||
|
hasDestructiveDataActionSignal,
|
||||||
|
hasDangerOrCoercionSignal,
|
||||||
|
hasOperationalAdminActionRequestSignal,
|
||||||
|
hasOrganizationFactLookupSignal,
|
||||||
|
hasOrganizationFactFollowupSignal,
|
||||||
|
shouldEmitOrganizationSelectionReply,
|
||||||
|
hasAssistantCapabilityQuestionSignal,
|
||||||
|
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
||||||
|
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
||||||
|
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
||||||
|
buildAssistantSafetyRefusalReply,
|
||||||
|
buildAssistantDataScopeContractReply,
|
||||||
|
buildAssistantOrganizationFactBoundaryReply,
|
||||||
|
buildAssistantDataScopeSelectionReply,
|
||||||
|
buildAssistantOperationalBoundaryReply,
|
||||||
|
buildAssistantCapabilityContractReply,
|
||||||
|
chatClient: this.chatClient,
|
||||||
|
loadAssistantCanonExcerpt: assistantCanon_1.loadAssistantCanonExcerpt,
|
||||||
|
sanitizeOutgoingAssistantText,
|
||||||
|
defaultModel: config_1.DEFAULT_MODEL,
|
||||||
|
defaultBaseUrl: config_1.DEFAULT_OPENAI_BASE_URL,
|
||||||
|
defaultApiKey: process.env.OPENAI_API_KEY ?? "",
|
||||||
|
buildAddressDebugPayload,
|
||||||
|
buildAddressFollowupOffer,
|
||||||
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
|
cloneConversation: (items) => cloneItems(items),
|
||||||
|
logEvent: (runtimePayload) => (0, log_1.logJson)(runtimePayload),
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
nowIso: () => new Date().toISOString()
|
||||||
|
}),
|
||||||
|
runDeepTurnAttemptRuntime: async (runtimeInput) => (0, assistantDeepTurnAttemptRuntimeAdapter_1.runAssistantDeepTurnAttemptRuntime)({
|
||||||
|
sessionId: runtimeInput.sessionId,
|
||||||
|
questionId: runtimeInput.questionId,
|
||||||
|
userMessage: runtimeInput.userMessage,
|
||||||
|
payload: runtimeInput.payload,
|
||||||
|
runtimeAnalysisContext: runtimeInput.runtimeAnalysisContext,
|
||||||
|
sessionInvestigationState: runtimeInput.sessionInvestigationState,
|
||||||
|
addressRuntimeMetaForDeep: runtimeInput.addressRuntimeMetaForDeep,
|
||||||
|
featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1,
|
||||||
|
featureStateFollowupBindingV1: config_1.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1,
|
||||||
|
featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11,
|
||||||
|
featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11,
|
||||||
|
featureProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1,
|
||||||
|
featureLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1,
|
||||||
|
buildFollowupStateBinding,
|
||||||
|
normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload),
|
||||||
|
resolveBusinessScopeAlignment,
|
||||||
|
inferP0DomainFromMessage,
|
||||||
|
resolveBusinessScopeFromLiveContext,
|
||||||
|
extractRequirements,
|
||||||
|
toExecutionPlan,
|
||||||
|
enforceRbpLiveRoutePlan,
|
||||||
|
enforceFaLiveRoutePlan,
|
||||||
|
executeRouteRuntime: (route, fragmentText, options) => this.dataLayer.executeRouteRuntime(route, fragmentText, options),
|
||||||
|
mapNoRouteReason,
|
||||||
|
buildSkippedResult,
|
||||||
|
evaluateCoverage,
|
||||||
|
checkGrounding,
|
||||||
|
collectRbpLiveRouteAudit,
|
||||||
|
collectFaLiveRouteAudit,
|
||||||
|
hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload),
|
||||||
|
extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload),
|
||||||
|
buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary),
|
||||||
|
extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload),
|
||||||
|
sanitizeReply: (value, fallback) => sanitizeOutgoingAssistantText(value, fallback),
|
||||||
|
persistInvestigationState: (targetSessionId, snapshot) => this.sessions.setInvestigationState(targetSessionId, snapshot),
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
|
cloneConversation: (items) => cloneItems(items),
|
||||||
|
logEvent: (runtimePayload) => (0, log_1.logJson)(runtimePayload)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
return turnRuntime.response;
|
||||||
const addressRuntime = await (0, assistantAddressAttemptRuntimeAdapter_1.runAssistantAddressAttemptRuntime)({
|
|
||||||
featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1,
|
|
||||||
sessionId,
|
|
||||||
userMessage,
|
|
||||||
sessionItems: session.items,
|
|
||||||
payload,
|
|
||||||
sessionScope: {
|
|
||||||
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
|
||||||
selectedOrganization: sessionOrganizationScope.selectedOrganization,
|
|
||||||
activeOrganization: sessionOrganizationScope.activeOrganization
|
|
||||||
},
|
|
||||||
featureAddressLlmPredecomposeV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1,
|
|
||||||
runAddressLlmPreDecompose: async () => runAddressLlmPreDecompose(this.normalizerService, payload, userMessage),
|
|
||||||
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
|
||||||
sanitizeAddressMessageForFallback,
|
|
||||||
toNonEmptyString,
|
|
||||||
resolveAddressFollowupCarryoverContext,
|
|
||||||
resolveAssistantOrchestrationDecision,
|
|
||||||
buildAddressDialogContinuationContractV2,
|
|
||||||
runtimeAnalysisContextAsOfDate: runtimeAnalysisContext.as_of_date,
|
|
||||||
compactWhitespace,
|
|
||||||
mergeFollowupContextWithOrganizationScope,
|
|
||||||
runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options),
|
|
||||||
isRetryableAddressLimitedResult,
|
|
||||||
mergeKnownOrganizations,
|
|
||||||
hasAssistantDataScopeMetaQuestionSignal,
|
|
||||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
|
||||||
hasDestructiveDataActionSignal,
|
|
||||||
hasDangerOrCoercionSignal,
|
|
||||||
hasOperationalAdminActionRequestSignal,
|
|
||||||
hasOrganizationFactLookupSignal,
|
|
||||||
hasOrganizationFactFollowupSignal,
|
|
||||||
shouldEmitOrganizationSelectionReply,
|
|
||||||
hasAssistantCapabilityQuestionSignal,
|
|
||||||
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
|
||||||
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
|
||||||
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
|
||||||
buildAssistantSafetyRefusalReply,
|
|
||||||
buildAssistantDataScopeContractReply,
|
|
||||||
buildAssistantOrganizationFactBoundaryReply,
|
|
||||||
buildAssistantDataScopeSelectionReply,
|
|
||||||
buildAssistantOperationalBoundaryReply,
|
|
||||||
buildAssistantCapabilityContractReply,
|
|
||||||
chatClient: this.chatClient,
|
|
||||||
loadAssistantCanonExcerpt: assistantCanon_1.loadAssistantCanonExcerpt,
|
|
||||||
sanitizeOutgoingAssistantText,
|
|
||||||
defaultModel: config_1.DEFAULT_MODEL,
|
|
||||||
defaultBaseUrl: config_1.DEFAULT_OPENAI_BASE_URL,
|
|
||||||
defaultApiKey: process.env.OPENAI_API_KEY ?? "",
|
|
||||||
buildAddressDebugPayload,
|
|
||||||
buildAddressFollowupOffer,
|
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
|
||||||
cloneConversation: (items) => cloneItems(items),
|
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload),
|
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
|
||||||
nowIso: () => new Date().toISOString()
|
|
||||||
});
|
|
||||||
const addressRuntimeMetaForDeep = addressRuntime.addressRuntimeMetaForDeep;
|
|
||||||
if (addressRuntime.handled && addressRuntime.response) {
|
|
||||||
return addressRuntime.response;
|
|
||||||
}
|
|
||||||
const deepTurnRuntime = await (0, assistantDeepTurnAttemptRuntimeAdapter_1.runAssistantDeepTurnAttemptRuntime)({
|
|
||||||
sessionId,
|
|
||||||
questionId: userItem.message_id,
|
|
||||||
userMessage,
|
|
||||||
payload,
|
|
||||||
runtimeAnalysisContext,
|
|
||||||
sessionInvestigationState: session.investigation_state,
|
|
||||||
addressRuntimeMetaForDeep,
|
|
||||||
featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1,
|
|
||||||
featureStateFollowupBindingV1: config_1.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1,
|
|
||||||
featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11,
|
|
||||||
featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11,
|
|
||||||
featureProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1,
|
|
||||||
featureLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1,
|
|
||||||
buildFollowupStateBinding,
|
|
||||||
normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload),
|
|
||||||
resolveBusinessScopeAlignment,
|
|
||||||
inferP0DomainFromMessage,
|
|
||||||
resolveBusinessScopeFromLiveContext,
|
|
||||||
extractRequirements,
|
|
||||||
toExecutionPlan,
|
|
||||||
enforceRbpLiveRoutePlan,
|
|
||||||
enforceFaLiveRoutePlan,
|
|
||||||
executeRouteRuntime: (route, fragmentText, options) => this.dataLayer.executeRouteRuntime(route, fragmentText, options),
|
|
||||||
mapNoRouteReason,
|
|
||||||
buildSkippedResult,
|
|
||||||
evaluateCoverage,
|
|
||||||
checkGrounding,
|
|
||||||
collectRbpLiveRouteAudit,
|
|
||||||
collectFaLiveRouteAudit,
|
|
||||||
hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload),
|
|
||||||
extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload),
|
|
||||||
buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary),
|
|
||||||
extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload),
|
|
||||||
sanitizeReply: (value, fallback) => sanitizeOutgoingAssistantText(value, fallback),
|
|
||||||
persistInvestigationState: (targetSessionId, snapshot) => this.sessions.setInvestigationState(targetSessionId, snapshot),
|
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
|
||||||
cloneConversation: (items) => cloneItems(items),
|
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload)
|
|
||||||
});
|
|
||||||
return deepTurnRuntime.response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.AssistantService = AssistantService;
|
exports.AssistantService = AssistantService;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.runAssistantTurnAttemptRuntime = runAssistantTurnAttemptRuntime;
|
||||||
|
async function runAssistantTurnAttemptRuntime(input) {
|
||||||
|
const userTurn = input.runUserTurnBootstrapRuntime(input.payload);
|
||||||
|
const sessionOrganizationScope = input.resolveSessionOrganizationScopeContext(userTurn.userMessage, userTurn.session.items);
|
||||||
|
const addressRuntime = await input.runAddressAttemptRuntime({
|
||||||
|
payload: input.payload,
|
||||||
|
sessionId: userTurn.sessionId,
|
||||||
|
userMessage: userTurn.userMessage,
|
||||||
|
sessionItems: userTurn.session.items,
|
||||||
|
runtimeAnalysisContext: userTurn.runtimeAnalysisContext,
|
||||||
|
sessionOrganizationScope
|
||||||
|
});
|
||||||
|
const addressRuntimeMetaForDeep = addressRuntime.addressRuntimeMetaForDeep ?? null;
|
||||||
|
if (addressRuntime.handled && addressRuntime.response) {
|
||||||
|
return {
|
||||||
|
response: addressRuntime.response,
|
||||||
|
source: "address",
|
||||||
|
addressRuntimeMetaForDeep,
|
||||||
|
userTurn,
|
||||||
|
sessionOrganizationScope
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const deepTurnRuntime = await input.runDeepTurnAttemptRuntime({
|
||||||
|
payload: input.payload,
|
||||||
|
sessionId: userTurn.sessionId,
|
||||||
|
questionId: userTurn.userItem.message_id,
|
||||||
|
userMessage: userTurn.userMessage,
|
||||||
|
runtimeAnalysisContext: userTurn.runtimeAnalysisContext,
|
||||||
|
sessionInvestigationState: userTurn.session.investigation_state,
|
||||||
|
addressRuntimeMetaForDeep
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
response: deepTurnRuntime.response,
|
||||||
|
source: "deep",
|
||||||
|
addressRuntimeMetaForDeep,
|
||||||
|
userTurn,
|
||||||
|
sessionOrganizationScope
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ import * as assistantCanon_1 from "./assistantCanon";
|
||||||
import * as assistantAddressAttemptRuntimeAdapter_1 from "./assistantAddressAttemptRuntimeAdapter";
|
import * as assistantAddressAttemptRuntimeAdapter_1 from "./assistantAddressAttemptRuntimeAdapter";
|
||||||
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
||||||
import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAttemptRuntimeAdapter";
|
import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAttemptRuntimeAdapter";
|
||||||
|
import * as assistantTurnAttemptRuntimeAdapter_1 from "./assistantTurnAttemptRuntimeAdapter";
|
||||||
import * as assistantUserTurnBootstrapRuntimeAdapter_1 from "./assistantUserTurnBootstrapRuntimeAdapter";
|
import * as assistantUserTurnBootstrapRuntimeAdapter_1 from "./assistantUserTurnBootstrapRuntimeAdapter";
|
||||||
import * as assistantQueryPlanning_1 from "./assistantQueryPlanning";
|
import * as assistantQueryPlanning_1 from "./assistantQueryPlanning";
|
||||||
import iconv from "iconv-lite";
|
import iconv from "iconv-lite";
|
||||||
|
|
@ -4324,125 +4325,124 @@ export class AssistantService {
|
||||||
return this.sessions.getSession(sessionId);
|
return this.sessions.getSession(sessionId);
|
||||||
}
|
}
|
||||||
async handleMessage(payload) {
|
async handleMessage(payload) {
|
||||||
const { session, sessionId, userMessage, runtimeAnalysisContext, userItem } = (0, assistantUserTurnBootstrapRuntimeAdapter_1.runAssistantUserTurnBootstrapRuntime)({
|
const turnRuntime = await (0, assistantTurnAttemptRuntimeAdapter_1.runAssistantTurnAttemptRuntime)({
|
||||||
payload,
|
payload,
|
||||||
ensureSession: (targetSessionId) => this.sessions.ensureSession(targetSessionId),
|
runUserTurnBootstrapRuntime: (runtimePayload) => (0, assistantUserTurnBootstrapRuntimeAdapter_1.runAssistantUserTurnBootstrapRuntime)({
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
payload: runtimePayload,
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
ensureSession: (targetSessionId) => this.sessions.ensureSession(targetSessionId),
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
compactWhitespace,
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
repairAddressMojibake,
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
resolveRuntimeAnalysisContext,
|
compactWhitespace,
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
repairAddressMojibake,
|
||||||
nowIso: () => new Date().toISOString()
|
resolveRuntimeAnalysisContext,
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
nowIso: () => new Date().toISOString()
|
||||||
|
}),
|
||||||
|
resolveSessionOrganizationScopeContext: (runtimeUserMessage, sessionItems) => resolveSessionOrganizationScopeContext(runtimeUserMessage, sessionItems),
|
||||||
|
runAddressAttemptRuntime: async (runtimeInput) => (0, assistantAddressAttemptRuntimeAdapter_1.runAssistantAddressAttemptRuntime)({
|
||||||
|
featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1,
|
||||||
|
sessionId: runtimeInput.sessionId,
|
||||||
|
userMessage: runtimeInput.userMessage,
|
||||||
|
sessionItems: runtimeInput.sessionItems,
|
||||||
|
payload: runtimeInput.payload,
|
||||||
|
sessionScope: {
|
||||||
|
knownOrganizations: runtimeInput.sessionOrganizationScope.knownOrganizations,
|
||||||
|
selectedOrganization: runtimeInput.sessionOrganizationScope.selectedOrganization,
|
||||||
|
activeOrganization: runtimeInput.sessionOrganizationScope.activeOrganization
|
||||||
|
},
|
||||||
|
featureAddressLlmPredecomposeV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1,
|
||||||
|
runAddressLlmPreDecompose: async () => runAddressLlmPreDecompose(this.normalizerService, runtimeInput.payload, runtimeInput.userMessage),
|
||||||
|
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
||||||
|
sanitizeAddressMessageForFallback,
|
||||||
|
toNonEmptyString,
|
||||||
|
resolveAddressFollowupCarryoverContext,
|
||||||
|
resolveAssistantOrchestrationDecision,
|
||||||
|
buildAddressDialogContinuationContractV2,
|
||||||
|
runtimeAnalysisContextAsOfDate: runtimeInput.runtimeAnalysisContext.as_of_date,
|
||||||
|
compactWhitespace,
|
||||||
|
mergeFollowupContextWithOrganizationScope,
|
||||||
|
runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options),
|
||||||
|
isRetryableAddressLimitedResult,
|
||||||
|
mergeKnownOrganizations,
|
||||||
|
hasAssistantDataScopeMetaQuestionSignal,
|
||||||
|
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||||
|
hasDestructiveDataActionSignal,
|
||||||
|
hasDangerOrCoercionSignal,
|
||||||
|
hasOperationalAdminActionRequestSignal,
|
||||||
|
hasOrganizationFactLookupSignal,
|
||||||
|
hasOrganizationFactFollowupSignal,
|
||||||
|
shouldEmitOrganizationSelectionReply,
|
||||||
|
hasAssistantCapabilityQuestionSignal,
|
||||||
|
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
||||||
|
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
||||||
|
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
||||||
|
buildAssistantSafetyRefusalReply,
|
||||||
|
buildAssistantDataScopeContractReply,
|
||||||
|
buildAssistantOrganizationFactBoundaryReply,
|
||||||
|
buildAssistantDataScopeSelectionReply,
|
||||||
|
buildAssistantOperationalBoundaryReply,
|
||||||
|
buildAssistantCapabilityContractReply,
|
||||||
|
chatClient: this.chatClient,
|
||||||
|
loadAssistantCanonExcerpt: assistantCanon_1.loadAssistantCanonExcerpt,
|
||||||
|
sanitizeOutgoingAssistantText,
|
||||||
|
defaultModel: config_1.DEFAULT_MODEL,
|
||||||
|
defaultBaseUrl: config_1.DEFAULT_OPENAI_BASE_URL,
|
||||||
|
defaultApiKey: process.env.OPENAI_API_KEY ?? "",
|
||||||
|
buildAddressDebugPayload,
|
||||||
|
buildAddressFollowupOffer,
|
||||||
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
|
cloneConversation: (items) => cloneItems(items),
|
||||||
|
logEvent: (runtimePayload) => (0, log_1.logJson)(runtimePayload),
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
nowIso: () => new Date().toISOString()
|
||||||
|
}),
|
||||||
|
runDeepTurnAttemptRuntime: async (runtimeInput) => (0, assistantDeepTurnAttemptRuntimeAdapter_1.runAssistantDeepTurnAttemptRuntime)({
|
||||||
|
sessionId: runtimeInput.sessionId,
|
||||||
|
questionId: runtimeInput.questionId,
|
||||||
|
userMessage: runtimeInput.userMessage,
|
||||||
|
payload: runtimeInput.payload,
|
||||||
|
runtimeAnalysisContext: runtimeInput.runtimeAnalysisContext,
|
||||||
|
sessionInvestigationState: runtimeInput.sessionInvestigationState,
|
||||||
|
addressRuntimeMetaForDeep: runtimeInput.addressRuntimeMetaForDeep,
|
||||||
|
featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1,
|
||||||
|
featureStateFollowupBindingV1: config_1.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1,
|
||||||
|
featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11,
|
||||||
|
featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11,
|
||||||
|
featureProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1,
|
||||||
|
featureLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1,
|
||||||
|
buildFollowupStateBinding,
|
||||||
|
normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload),
|
||||||
|
resolveBusinessScopeAlignment,
|
||||||
|
inferP0DomainFromMessage,
|
||||||
|
resolveBusinessScopeFromLiveContext,
|
||||||
|
extractRequirements,
|
||||||
|
toExecutionPlan,
|
||||||
|
enforceRbpLiveRoutePlan,
|
||||||
|
enforceFaLiveRoutePlan,
|
||||||
|
executeRouteRuntime: (route, fragmentText, options) => this.dataLayer.executeRouteRuntime(route, fragmentText, options),
|
||||||
|
mapNoRouteReason,
|
||||||
|
buildSkippedResult,
|
||||||
|
evaluateCoverage,
|
||||||
|
checkGrounding,
|
||||||
|
collectRbpLiveRouteAudit,
|
||||||
|
collectFaLiveRouteAudit,
|
||||||
|
hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload),
|
||||||
|
extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload),
|
||||||
|
buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary),
|
||||||
|
extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload),
|
||||||
|
sanitizeReply: (value, fallback) => sanitizeOutgoingAssistantText(value, fallback),
|
||||||
|
persistInvestigationState: (targetSessionId, snapshot) => this.sessions.setInvestigationState(targetSessionId, snapshot),
|
||||||
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
||||||
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
|
cloneConversation: (items) => cloneItems(items),
|
||||||
|
logEvent: (runtimePayload) => (0, log_1.logJson)(runtimePayload)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
return turnRuntime.response;
|
||||||
const addressRuntime = await (0, assistantAddressAttemptRuntimeAdapter_1.runAssistantAddressAttemptRuntime)({
|
|
||||||
featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1,
|
|
||||||
sessionId,
|
|
||||||
userMessage,
|
|
||||||
sessionItems: session.items,
|
|
||||||
payload,
|
|
||||||
sessionScope: {
|
|
||||||
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
|
||||||
selectedOrganization: sessionOrganizationScope.selectedOrganization,
|
|
||||||
activeOrganization: sessionOrganizationScope.activeOrganization
|
|
||||||
},
|
|
||||||
featureAddressLlmPredecomposeV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1,
|
|
||||||
runAddressLlmPreDecompose: async () => runAddressLlmPreDecompose(this.normalizerService, payload, userMessage),
|
|
||||||
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
|
||||||
sanitizeAddressMessageForFallback,
|
|
||||||
toNonEmptyString,
|
|
||||||
resolveAddressFollowupCarryoverContext,
|
|
||||||
resolveAssistantOrchestrationDecision,
|
|
||||||
buildAddressDialogContinuationContractV2,
|
|
||||||
runtimeAnalysisContextAsOfDate: runtimeAnalysisContext.as_of_date,
|
|
||||||
compactWhitespace,
|
|
||||||
mergeFollowupContextWithOrganizationScope,
|
|
||||||
runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options),
|
|
||||||
isRetryableAddressLimitedResult,
|
|
||||||
mergeKnownOrganizations,
|
|
||||||
hasAssistantDataScopeMetaQuestionSignal,
|
|
||||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
|
||||||
hasDestructiveDataActionSignal,
|
|
||||||
hasDangerOrCoercionSignal,
|
|
||||||
hasOperationalAdminActionRequestSignal,
|
|
||||||
hasOrganizationFactLookupSignal,
|
|
||||||
hasOrganizationFactFollowupSignal,
|
|
||||||
shouldEmitOrganizationSelectionReply,
|
|
||||||
hasAssistantCapabilityQuestionSignal,
|
|
||||||
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
|
||||||
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
|
||||||
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
|
||||||
buildAssistantSafetyRefusalReply,
|
|
||||||
buildAssistantDataScopeContractReply,
|
|
||||||
buildAssistantOrganizationFactBoundaryReply,
|
|
||||||
buildAssistantDataScopeSelectionReply,
|
|
||||||
buildAssistantOperationalBoundaryReply,
|
|
||||||
buildAssistantCapabilityContractReply,
|
|
||||||
chatClient: this.chatClient,
|
|
||||||
loadAssistantCanonExcerpt: assistantCanon_1.loadAssistantCanonExcerpt,
|
|
||||||
sanitizeOutgoingAssistantText,
|
|
||||||
defaultModel: config_1.DEFAULT_MODEL,
|
|
||||||
defaultBaseUrl: config_1.DEFAULT_OPENAI_BASE_URL,
|
|
||||||
defaultApiKey: process.env.OPENAI_API_KEY ?? "",
|
|
||||||
buildAddressDebugPayload,
|
|
||||||
buildAddressFollowupOffer,
|
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
|
||||||
cloneConversation: (items) => cloneItems(items),
|
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload),
|
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
|
||||||
nowIso: () => new Date().toISOString()
|
|
||||||
});
|
|
||||||
const addressRuntimeMetaForDeep = addressRuntime.addressRuntimeMetaForDeep;
|
|
||||||
if (addressRuntime.handled && addressRuntime.response) {
|
|
||||||
return addressRuntime.response;
|
|
||||||
}
|
|
||||||
const deepTurnRuntime = await (0, assistantDeepTurnAttemptRuntimeAdapter_1.runAssistantDeepTurnAttemptRuntime)({
|
|
||||||
sessionId,
|
|
||||||
questionId: userItem.message_id,
|
|
||||||
userMessage,
|
|
||||||
payload,
|
|
||||||
runtimeAnalysisContext,
|
|
||||||
sessionInvestigationState: session.investigation_state,
|
|
||||||
addressRuntimeMetaForDeep,
|
|
||||||
featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1,
|
|
||||||
featureStateFollowupBindingV1: config_1.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1,
|
|
||||||
featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11,
|
|
||||||
featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11,
|
|
||||||
featureProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1,
|
|
||||||
featureLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1,
|
|
||||||
buildFollowupStateBinding,
|
|
||||||
normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload),
|
|
||||||
resolveBusinessScopeAlignment,
|
|
||||||
inferP0DomainFromMessage,
|
|
||||||
resolveBusinessScopeFromLiveContext,
|
|
||||||
extractRequirements,
|
|
||||||
toExecutionPlan,
|
|
||||||
enforceRbpLiveRoutePlan,
|
|
||||||
enforceFaLiveRoutePlan,
|
|
||||||
executeRouteRuntime: (route, fragmentText, options) => this.dataLayer.executeRouteRuntime(route, fragmentText, options),
|
|
||||||
mapNoRouteReason,
|
|
||||||
buildSkippedResult,
|
|
||||||
evaluateCoverage,
|
|
||||||
checkGrounding,
|
|
||||||
collectRbpLiveRouteAudit,
|
|
||||||
collectFaLiveRouteAudit,
|
|
||||||
hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload),
|
|
||||||
extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload),
|
|
||||||
buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary),
|
|
||||||
extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload),
|
|
||||||
sanitizeReply: (value, fallback) => sanitizeOutgoingAssistantText(value, fallback),
|
|
||||||
persistInvestigationState: (targetSessionId, snapshot) => this.sessions.setInvestigationState(targetSessionId, snapshot),
|
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`,
|
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
|
||||||
cloneConversation: (items) => cloneItems(items),
|
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload)
|
|
||||||
});
|
|
||||||
return deepTurnRuntime.response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import type { RunAssistantAddressRuntimeOutput } from "./assistantAddressRuntimeAdapter";
|
||||||
|
import type { RunAssistantUserTurnBootstrapRuntimeOutput } from "./assistantUserTurnBootstrapRuntimeAdapter";
|
||||||
|
|
||||||
|
export interface AssistantSessionOrganizationScopeContext {
|
||||||
|
knownOrganizations: string[];
|
||||||
|
selectedOrganization: string | null;
|
||||||
|
activeOrganization: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunAssistantTurnAttemptRuntimeAddressInput<PayloadType = unknown> {
|
||||||
|
payload: PayloadType;
|
||||||
|
sessionId: string;
|
||||||
|
userMessage: string;
|
||||||
|
sessionItems: unknown[];
|
||||||
|
runtimeAnalysisContext: { as_of_date: string | null };
|
||||||
|
sessionOrganizationScope: AssistantSessionOrganizationScopeContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunAssistantTurnAttemptRuntimeDeepInput<PayloadType = unknown> {
|
||||||
|
payload: PayloadType;
|
||||||
|
sessionId: string;
|
||||||
|
questionId: string;
|
||||||
|
userMessage: string;
|
||||||
|
runtimeAnalysisContext: unknown;
|
||||||
|
sessionInvestigationState: unknown;
|
||||||
|
addressRuntimeMetaForDeep: Record<string, unknown> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunAssistantTurnAttemptRuntimeInput<ResponseType = unknown, PayloadType = unknown> {
|
||||||
|
payload: PayloadType;
|
||||||
|
runUserTurnBootstrapRuntime: (payload: PayloadType) => RunAssistantUserTurnBootstrapRuntimeOutput;
|
||||||
|
resolveSessionOrganizationScopeContext: (
|
||||||
|
userMessage: string,
|
||||||
|
sessionItems: unknown[]
|
||||||
|
) => AssistantSessionOrganizationScopeContext;
|
||||||
|
runAddressAttemptRuntime: (
|
||||||
|
input: RunAssistantTurnAttemptRuntimeAddressInput<PayloadType>
|
||||||
|
) => Promise<RunAssistantAddressRuntimeOutput<ResponseType>>;
|
||||||
|
runDeepTurnAttemptRuntime: (
|
||||||
|
input: RunAssistantTurnAttemptRuntimeDeepInput<PayloadType>
|
||||||
|
) => Promise<{ response: ResponseType }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunAssistantTurnAttemptRuntimeOutput<ResponseType = unknown> {
|
||||||
|
response: ResponseType;
|
||||||
|
source: "address" | "deep";
|
||||||
|
addressRuntimeMetaForDeep: Record<string, unknown> | null;
|
||||||
|
userTurn: RunAssistantUserTurnBootstrapRuntimeOutput;
|
||||||
|
sessionOrganizationScope: AssistantSessionOrganizationScopeContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runAssistantTurnAttemptRuntime<ResponseType = unknown, PayloadType = unknown>(
|
||||||
|
input: RunAssistantTurnAttemptRuntimeInput<ResponseType, PayloadType>
|
||||||
|
): Promise<RunAssistantTurnAttemptRuntimeOutput<ResponseType>> {
|
||||||
|
const userTurn = input.runUserTurnBootstrapRuntime(input.payload);
|
||||||
|
const sessionOrganizationScope = input.resolveSessionOrganizationScopeContext(
|
||||||
|
userTurn.userMessage,
|
||||||
|
userTurn.session.items
|
||||||
|
);
|
||||||
|
const addressRuntime = await input.runAddressAttemptRuntime({
|
||||||
|
payload: input.payload,
|
||||||
|
sessionId: userTurn.sessionId,
|
||||||
|
userMessage: userTurn.userMessage,
|
||||||
|
sessionItems: userTurn.session.items,
|
||||||
|
runtimeAnalysisContext: userTurn.runtimeAnalysisContext,
|
||||||
|
sessionOrganizationScope
|
||||||
|
});
|
||||||
|
|
||||||
|
const addressRuntimeMetaForDeep = addressRuntime.addressRuntimeMetaForDeep ?? null;
|
||||||
|
if (addressRuntime.handled && addressRuntime.response) {
|
||||||
|
return {
|
||||||
|
response: addressRuntime.response,
|
||||||
|
source: "address",
|
||||||
|
addressRuntimeMetaForDeep,
|
||||||
|
userTurn,
|
||||||
|
sessionOrganizationScope
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const deepTurnRuntime = await input.runDeepTurnAttemptRuntime({
|
||||||
|
payload: input.payload,
|
||||||
|
sessionId: userTurn.sessionId,
|
||||||
|
questionId: userTurn.userItem.message_id,
|
||||||
|
userMessage: userTurn.userMessage,
|
||||||
|
runtimeAnalysisContext: userTurn.runtimeAnalysisContext,
|
||||||
|
sessionInvestigationState: userTurn.session.investigation_state,
|
||||||
|
addressRuntimeMetaForDeep
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
response: deepTurnRuntime.response,
|
||||||
|
source: "deep",
|
||||||
|
addressRuntimeMetaForDeep,
|
||||||
|
userTurn,
|
||||||
|
sessionOrganizationScope
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { runAssistantTurnAttemptRuntime } from "../src/services/assistantTurnAttemptRuntimeAdapter";
|
||||||
|
|
||||||
|
function buildUserTurn(overrides: Record<string, unknown> = {}) {
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
session_id: "asst-1",
|
||||||
|
updated_at: "2026-04-11T00:00:00.000Z",
|
||||||
|
items: [{ role: "user", text: "msg" }],
|
||||||
|
investigation_state: { focus: "settlements_60_62" }
|
||||||
|
},
|
||||||
|
sessionId: "asst-1",
|
||||||
|
userMessageRaw: "where tail",
|
||||||
|
userMessage: "where tail",
|
||||||
|
runtimeAnalysisContext: {
|
||||||
|
as_of_date: "2020-07-31"
|
||||||
|
},
|
||||||
|
userItem: {
|
||||||
|
message_id: "msg-q1",
|
||||||
|
session_id: "asst-1",
|
||||||
|
role: "user",
|
||||||
|
text: "where tail",
|
||||||
|
reply_type: null,
|
||||||
|
created_at: "2026-04-11T00:00:00.000Z",
|
||||||
|
trace_id: null,
|
||||||
|
debug: null
|
||||||
|
},
|
||||||
|
...overrides
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("assistant turn attempt runtime adapter", () => {
|
||||||
|
it("returns address response and skips deep runtime when address lane handled the turn", async () => {
|
||||||
|
const runUserTurnBootstrapRuntime = vi.fn(() => buildUserTurn());
|
||||||
|
const resolveSessionOrganizationScopeContext = vi.fn(() => ({
|
||||||
|
knownOrganizations: ["Org A"],
|
||||||
|
selectedOrganization: "Org A",
|
||||||
|
activeOrganization: "Org A"
|
||||||
|
}));
|
||||||
|
const runAddressAttemptRuntime = vi.fn(async () => ({
|
||||||
|
handled: true,
|
||||||
|
response: { lane: "address" },
|
||||||
|
addressRuntimeMetaForDeep: { source: "address_runtime" }
|
||||||
|
}));
|
||||||
|
const runDeepTurnAttemptRuntime = vi.fn(async () => ({
|
||||||
|
response: { lane: "deep" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
const runtime = await runAssistantTurnAttemptRuntime({
|
||||||
|
payload: { user_message: "where tail" },
|
||||||
|
runUserTurnBootstrapRuntime,
|
||||||
|
resolveSessionOrganizationScopeContext,
|
||||||
|
runAddressAttemptRuntime,
|
||||||
|
runDeepTurnAttemptRuntime
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(runtime.response).toEqual({ lane: "address" });
|
||||||
|
expect(runtime.source).toBe("address");
|
||||||
|
expect(runtime.addressRuntimeMetaForDeep).toEqual({ source: "address_runtime" });
|
||||||
|
expect(runAddressAttemptRuntime).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
sessionId: "asst-1",
|
||||||
|
userMessage: "where tail",
|
||||||
|
runtimeAnalysisContext: { as_of_date: "2020-07-31" }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(runDeepTurnAttemptRuntime).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls through to deep runtime when address lane does not return final response", async () => {
|
||||||
|
const runUserTurnBootstrapRuntime = vi.fn(() => buildUserTurn());
|
||||||
|
const resolveSessionOrganizationScopeContext = vi.fn(() => ({
|
||||||
|
knownOrganizations: [],
|
||||||
|
selectedOrganization: null,
|
||||||
|
activeOrganization: null
|
||||||
|
}));
|
||||||
|
const runAddressAttemptRuntime = vi.fn(async () => ({
|
||||||
|
handled: false,
|
||||||
|
response: null,
|
||||||
|
addressRuntimeMetaForDeep: { attempted: true }
|
||||||
|
}));
|
||||||
|
const runDeepTurnAttemptRuntime = vi.fn(async () => ({
|
||||||
|
response: { lane: "deep", ok: true }
|
||||||
|
}));
|
||||||
|
|
||||||
|
const runtime = await runAssistantTurnAttemptRuntime({
|
||||||
|
payload: { user_message: "where tail" },
|
||||||
|
runUserTurnBootstrapRuntime,
|
||||||
|
resolveSessionOrganizationScopeContext,
|
||||||
|
runAddressAttemptRuntime,
|
||||||
|
runDeepTurnAttemptRuntime
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(runtime.response).toEqual({ lane: "deep", ok: true });
|
||||||
|
expect(runtime.source).toBe("deep");
|
||||||
|
expect(runDeepTurnAttemptRuntime).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
sessionId: "asst-1",
|
||||||
|
questionId: "msg-q1",
|
||||||
|
userMessage: "where tail",
|
||||||
|
addressRuntimeMetaForDeep: { attempted: true },
|
||||||
|
sessionInvestigationState: { focus: "settlements_60_62" }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue