From 5790e25b68a95ac09531aa9a22a11b28434ae02a Mon Sep 17 00:00:00 2001 From: dctouch Date: Fri, 10 Apr 2026 23:55:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=93=D0=9B=D0=9E=D0=91=D0=90=D0=9B=D0=AC?= =?UTF-8?q?=D0=9D=D0=AB=D0=99=20=D0=A0=D0=95=D0=A4=D0=90=D0=9A=D0=A2=D0=9E?= =?UTF-8?q?=D0=A0=D0=98=D0=9D=D0=93=20=D0=90=D0=A0=D0=A5=D0=98=D0=A2=D0=95?= =?UTF-8?q?=D0=9A=D0=A2=D0=A3=D0=A0=D0=AB=20-=20=D0=A0=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=8D=D1=82=D0=B0?= =?UTF-8?q?=D0=BF=D0=BE=D0=B2=202.45=20-=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=B4?= =?UTF-8?q?=D0=B8=D0=BD=D0=B5=D0=BD=D1=8B=20=D1=82=D1=80=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D1=81?= =?UTF-8?q?=D0=BB=D0=BE=D1=8F=20(NormalizationAttempt=20+=20AnalysisAttemp?= =?UTF-8?q?t=20+=20ResponseAttempt)=20=D0=B2=20=D0=BE=D0=B4=D0=B8=D0=BD=20?= =?UTF-8?q?assistantDeepTurnAttemptRuntimeAdapter,=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=20=D1=87=D0=B5=D0=B3=D0=BE=20=D0=B2=20handleMessag?= =?UTF-8?q?e=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB=D1=81=D1=8F=20=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D0=BD=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D0=B2=D0=BC?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BE=20=D1=82=D1=80=D1=91=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/TECH/1CLLMARCH-FACT.md | 34 ++- .../assistantDeepTurnAttemptRuntimeAdapter.js | 88 +++++++ .../backend/dist/services/assistantService.js | 52 +---- .../assistantDeepTurnAttemptRuntimeAdapter.ts | 174 ++++++++++++++ .../backend/src/services/assistantService.ts | 52 +---- ...stantDeepTurnAttemptRuntimeAdapter.test.ts | 221 ++++++++++++++++++ 6 files changed, 540 insertions(+), 81 deletions(-) create mode 100644 llm_normalizer/backend/dist/services/assistantDeepTurnAttemptRuntimeAdapter.js create mode 100644 llm_normalizer/backend/src/services/assistantDeepTurnAttemptRuntimeAdapter.ts create mode 100644 llm_normalizer/backend/tests/assistantDeepTurnAttemptRuntimeAdapter.test.ts diff --git a/docs/TECH/1CLLMARCH-FACT.md b/docs/TECH/1CLLMARCH-FACT.md index 46f316f..0292dc3 100644 --- a/docs/TECH/1CLLMARCH-FACT.md +++ b/docs/TECH/1CLLMARCH-FACT.md @@ -1405,7 +1405,39 @@ Validation: - `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 completed)** +Implemented in current pass (Phase 2.45): +1. Extracted full deep-turn chain (`normalization -> analysis -> response`) from `assistantService` into dedicated runtime adapter: + - `assistantDeepTurnAttemptRuntimeAdapter.ts` + - introduced: + - `runAssistantDeepTurnAttemptRuntime(...)` +2. Centralized deep-turn orchestration handoff (behavior-preserving): + - unified composition over existing attempt adapters (`Normalization`, `Analysis`, `Response`); + - preserved followup binding, runtime context propagation, response finalization hooks and investigation-state persistence callbacks. +3. Rewired `assistantService` to consume a single deep-turn runtime adapter call. +4. Added focused unit tests: + - `assistantDeepTurnAttemptRuntimeAdapter.test.ts` + +Validation: +1. `npm run build` passed. +2. Targeted living/address/deep followup pack passed: + - `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 completed)** ## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards) diff --git a/llm_normalizer/backend/dist/services/assistantDeepTurnAttemptRuntimeAdapter.js b/llm_normalizer/backend/dist/services/assistantDeepTurnAttemptRuntimeAdapter.js new file mode 100644 index 0000000..fd2cdbf --- /dev/null +++ b/llm_normalizer/backend/dist/services/assistantDeepTurnAttemptRuntimeAdapter.js @@ -0,0 +1,88 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runAssistantDeepTurnAttemptRuntime = runAssistantDeepTurnAttemptRuntime; +const assistantDeepTurnNormalizationRuntimeAdapter_1 = require("./assistantDeepTurnNormalizationRuntimeAdapter"); +const assistantDeepTurnAnalysisAttemptRuntimeAdapter_1 = require("./assistantDeepTurnAnalysisAttemptRuntimeAdapter"); +const assistantDeepTurnResponseAttemptRuntimeAdapter_1 = require("./assistantDeepTurnResponseAttemptRuntimeAdapter"); +async function runAssistantDeepTurnAttemptRuntime(input) { + const runDeepTurnNormalizationRuntimeSafe = input.runDeepTurnNormalizationRuntime ?? assistantDeepTurnNormalizationRuntimeAdapter_1.buildAssistantDeepTurnNormalizationRuntime; + const runDeepTurnAnalysisAttemptRuntimeSafe = input.runDeepTurnAnalysisAttemptRuntime ?? assistantDeepTurnAnalysisAttemptRuntimeAdapter_1.runAssistantDeepTurnAnalysisAttemptRuntime; + const runDeepTurnResponseAttemptRuntimeSafe = input.runDeepTurnResponseAttemptRuntime ?? assistantDeepTurnResponseAttemptRuntimeAdapter_1.runAssistantDeepTurnResponseAttemptRuntime; + const normalizationRuntime = await runDeepTurnNormalizationRuntimeSafe({ + userMessage: input.userMessage, + payload: input.payload, + featureInvestigationStateV1: input.featureInvestigationStateV1, + featureStateFollowupBindingV1: input.featureStateFollowupBindingV1, + sessionInvestigationState: input.sessionInvestigationState, + buildFollowupStateBinding: input.buildFollowupStateBinding, + normalize: input.normalize + }); + const followupBinding = normalizationRuntime.followupBinding; + const normalized = normalizationRuntime.normalized; + const deepTurnAnalysisRuntime = await runDeepTurnAnalysisAttemptRuntimeSafe({ + userMessage: input.userMessage, + normalizedPayload: normalized.normalized, + routeSummary: normalized.route_hint_summary, + runtimeAnalysisContext: input.runtimeAnalysisContext, + followupUsage: followupBinding.usage, + investigationState: input.sessionInvestigationState, + featureAnswerPolicyV11: input.featureAnswerPolicyV11, + featureProblemCentricAnswerV1: input.featureProblemCentricAnswerV1, + featureLifecycleAnswerV1: input.featureLifecycleAnswerV1, + resolveBusinessScopeAlignment: input.resolveBusinessScopeAlignment, + inferP0DomainFromMessage: input.inferP0DomainFromMessage, + resolveBusinessScopeFromLiveContext: input.resolveBusinessScopeFromLiveContext, + extractRequirements: input.extractRequirements, + toExecutionPlan: input.toExecutionPlan, + enforceRbpLiveRoutePlan: input.enforceRbpLiveRoutePlan, + enforceFaLiveRoutePlan: input.enforceFaLiveRoutePlan, + executeRouteRuntime: input.executeRouteRuntime, + mapNoRouteReason: input.mapNoRouteReason, + buildSkippedResult: input.buildSkippedResult, + evaluateCoverage: input.evaluateCoverage, + checkGrounding: input.checkGrounding, + collectRbpLiveRouteAudit: input.collectRbpLiveRouteAudit, + collectFaLiveRouteAudit: input.collectFaLiveRouteAudit, + hasExplicitPeriodAnchor: input.hasExplicitPeriodAnchor + }); + const deepTurnResponseRuntime = runDeepTurnResponseAttemptRuntimeSafe({ + featureInvestigationStateV1: input.featureInvestigationStateV1, + featureContractsV11: input.featureContractsV11, + featureAnswerPolicyV11: input.featureAnswerPolicyV11, + sessionId: input.sessionId, + questionId: input.questionId, + userMessage: input.userMessage, + normalized: { + trace_id: normalized.trace_id, + prompt_version: normalized.prompt_version, + schema_version: normalized.schema_version, + normalized: normalized.normalized + }, + normalizedQuestion: followupBinding.normalizedQuestion, + deepTurnAnalysisRuntime, + runtimeAnalysisContext: input.runtimeAnalysisContext, + followupStateUsage: followupBinding.usage, + followupApplied: Boolean(followupBinding.usage?.applied), + previousInvestigationState: input.sessionInvestigationState, + addressRuntimeMetaForDeep: input.addressRuntimeMetaForDeep, + extractDroppedIntentSegments: input.extractDroppedIntentSegments, + buildDebugRoutes: input.buildDebugRoutes, + extractExecutionState: input.extractExecutionState, + sanitizeReply: input.sanitizeReply, + persistInvestigationState: input.persistInvestigationState, + messageIdFactory: input.messageIdFactory, + appendItem: input.appendItem, + getSession: input.getSession, + persistSession: input.persistSession, + cloneConversation: input.cloneConversation, + logEvent: input.logEvent, + runPackagingRuntime: input.runPackagingRuntime, + runFinalizeDeepTurn: input.runFinalizeDeepTurn + }); + return { + response: deepTurnResponseRuntime.response, + debug: deepTurnResponseRuntime.debug, + normalizationRuntime, + deepTurnAnalysisRuntime + }; +} diff --git a/llm_normalizer/backend/dist/services/assistantService.js b/llm_normalizer/backend/dist/services/assistantService.js index 982bb77..332c166 100644 --- a/llm_normalizer/backend/dist/services/assistantService.js +++ b/llm_normalizer/backend/dist/services/assistantService.js @@ -64,9 +64,7 @@ const capabilitiesRegistry_1 = __importStar(require("./capabilitiesRegistry")); const assistantCanon_1 = __importStar(require("./assistantCanon")); const assistantAddressLaneResponseAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressLaneResponseAttemptRuntimeAdapter")); const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding")); -const assistantDeepTurnAnalysisAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAnalysisAttemptRuntimeAdapter")); -const assistantDeepTurnNormalizationRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnNormalizationRuntimeAdapter")); -const assistantDeepTurnResponseAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnResponseAttemptRuntimeAdapter")); +const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAttemptRuntimeAdapter")); const assistantAddressRuntimeAdapter_1 = __importStar(require("./assistantAddressRuntimeAdapter")); const assistantAddressLaneAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressLaneAttemptRuntimeAdapter")); const assistantLivingChatAttemptRuntimeAdapter_1 = __importStar(require("./assistantLivingChatAttemptRuntimeAdapter")); @@ -4493,27 +4491,22 @@ class AssistantService { if (addressRuntime.handled && addressRuntime.response) { return addressRuntime.response; } - const normalizationRuntime = await (0, assistantDeepTurnNormalizationRuntimeAdapter_1.buildAssistantDeepTurnNormalizationRuntime)({ + 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, - sessionInvestigationState: session.investigation_state, - buildFollowupStateBinding, - normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload) - }); - const followupBinding = normalizationRuntime.followupBinding; - const normalized = normalizationRuntime.normalized; - const deepTurnAnalysisRuntime = await (0, assistantDeepTurnAnalysisAttemptRuntimeAdapter_1.runAssistantDeepTurnAnalysisAttemptRuntime)({ - userMessage, - normalizedPayload: normalized.normalized, - routeSummary: normalized.route_hint_summary, - runtimeAnalysisContext, - followupUsage: followupBinding.usage, - investigationState: session.investigation_state, + 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, @@ -4528,28 +4521,7 @@ class AssistantService { checkGrounding, collectRbpLiveRouteAudit, collectFaLiveRouteAudit, - hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload) - }); - const deepTurnResponseRuntime = (0, assistantDeepTurnResponseAttemptRuntimeAdapter_1.runAssistantDeepTurnResponseAttemptRuntime)({ - featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1, - featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11, - featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11, - sessionId, - questionId: userItem.message_id, - userMessage, - normalized: { - trace_id: normalized.trace_id, - prompt_version: normalized.prompt_version, - schema_version: normalized.schema_version, - normalized: normalized.normalized - }, - normalizedQuestion: followupBinding.normalizedQuestion, - deepTurnAnalysisRuntime, - runtimeAnalysisContext, - followupStateUsage: followupBinding.usage, - followupApplied: Boolean(followupBinding.usage?.applied), - previousInvestigationState: session.investigation_state, - addressRuntimeMetaForDeep, + hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload), extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload), buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary), extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload), @@ -4562,7 +4534,7 @@ class AssistantService { cloneConversation: (items) => cloneItems(items), logEvent: (payload) => (0, log_1.logJson)(payload) }); - return deepTurnResponseRuntime.response; + return deepTurnRuntime.response; } } exports.AssistantService = AssistantService; diff --git a/llm_normalizer/backend/src/services/assistantDeepTurnAttemptRuntimeAdapter.ts b/llm_normalizer/backend/src/services/assistantDeepTurnAttemptRuntimeAdapter.ts new file mode 100644 index 0000000..672103b --- /dev/null +++ b/llm_normalizer/backend/src/services/assistantDeepTurnAttemptRuntimeAdapter.ts @@ -0,0 +1,174 @@ +import type { AssistantMessageResponsePayload } from "../types/assistant"; +import type { InvestigationStateWithProblemUnits } from "../types/stage2ProblemUnits"; +import { + buildAssistantDeepTurnNormalizationRuntime, + type BuildAssistantDeepTurnNormalizationRuntimeInput, + type BuildAssistantDeepTurnNormalizationRuntimeOutput +} from "./assistantDeepTurnNormalizationRuntimeAdapter"; +import { + runAssistantDeepTurnAnalysisAttemptRuntime, + type RunAssistantDeepTurnAnalysisAttemptRuntimeInput +} from "./assistantDeepTurnAnalysisAttemptRuntimeAdapter"; +import { + runAssistantDeepTurnResponseAttemptRuntime, + type RunAssistantDeepTurnResponseAttemptRuntimeInput +} from "./assistantDeepTurnResponseAttemptRuntimeAdapter"; +import type { RunAssistantDeepTurnAnalysisRuntimeOutput } from "./assistantDeepTurnAnalysisRuntimeAdapter"; + +export interface RunAssistantDeepTurnAttemptRuntimeInput { + sessionId: string; + questionId: string; + userMessage: string; + payload: BuildAssistantDeepTurnNormalizationRuntimeInput["payload"]; + runtimeAnalysisContext: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["runtimeAnalysisContext"]; + sessionInvestigationState: InvestigationStateWithProblemUnits | null | undefined; + addressRuntimeMetaForDeep: unknown; + featureInvestigationStateV1: boolean; + featureStateFollowupBindingV1: boolean; + featureContractsV11: boolean; + featureAnswerPolicyV11: boolean; + featureProblemCentricAnswerV1: boolean; + featureLifecycleAnswerV1: boolean; + buildFollowupStateBinding: BuildAssistantDeepTurnNormalizationRuntimeInput["buildFollowupStateBinding"]; + normalize: BuildAssistantDeepTurnNormalizationRuntimeInput["normalize"]; + resolveBusinessScopeAlignment: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["resolveBusinessScopeAlignment"]; + inferP0DomainFromMessage: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["inferP0DomainFromMessage"]; + resolveBusinessScopeFromLiveContext: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["resolveBusinessScopeFromLiveContext"]; + extractRequirements: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["extractRequirements"]; + toExecutionPlan: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["toExecutionPlan"]; + enforceRbpLiveRoutePlan: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["enforceRbpLiveRoutePlan"]; + enforceFaLiveRoutePlan: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["enforceFaLiveRoutePlan"]; + executeRouteRuntime: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["executeRouteRuntime"]; + mapNoRouteReason: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["mapNoRouteReason"]; + buildSkippedResult: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["buildSkippedResult"]; + evaluateCoverage: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["evaluateCoverage"]; + checkGrounding: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["checkGrounding"]; + collectRbpLiveRouteAudit: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["collectRbpLiveRouteAudit"]; + collectFaLiveRouteAudit: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["collectFaLiveRouteAudit"]; + hasExplicitPeriodAnchor: RunAssistantDeepTurnAnalysisAttemptRuntimeInput["hasExplicitPeriodAnchor"]; + extractDroppedIntentSegments: RunAssistantDeepTurnResponseAttemptRuntimeInput["extractDroppedIntentSegments"]; + buildDebugRoutes: RunAssistantDeepTurnResponseAttemptRuntimeInput["buildDebugRoutes"]; + extractExecutionState: RunAssistantDeepTurnResponseAttemptRuntimeInput["extractExecutionState"]; + sanitizeReply: RunAssistantDeepTurnResponseAttemptRuntimeInput["sanitizeReply"]; + persistInvestigationState: RunAssistantDeepTurnResponseAttemptRuntimeInput["persistInvestigationState"]; + messageIdFactory: RunAssistantDeepTurnResponseAttemptRuntimeInput["messageIdFactory"]; + appendItem: RunAssistantDeepTurnResponseAttemptRuntimeInput["appendItem"]; + getSession: RunAssistantDeepTurnResponseAttemptRuntimeInput["getSession"]; + persistSession: RunAssistantDeepTurnResponseAttemptRuntimeInput["persistSession"]; + cloneConversation: RunAssistantDeepTurnResponseAttemptRuntimeInput["cloneConversation"]; + logEvent: RunAssistantDeepTurnResponseAttemptRuntimeInput["logEvent"]; + runPackagingRuntime?: RunAssistantDeepTurnResponseAttemptRuntimeInput["runPackagingRuntime"]; + runFinalizeDeepTurn?: RunAssistantDeepTurnResponseAttemptRuntimeInput["runFinalizeDeepTurn"]; + runDeepTurnNormalizationRuntime?: ( + input: BuildAssistantDeepTurnNormalizationRuntimeInput + ) => Promise; + runDeepTurnAnalysisAttemptRuntime?: ( + input: RunAssistantDeepTurnAnalysisAttemptRuntimeInput + ) => Promise; + runDeepTurnResponseAttemptRuntime?: ( + input: RunAssistantDeepTurnResponseAttemptRuntimeInput + ) => { + response: ResponseType; + debug: Record; + }; +} + +export interface RunAssistantDeepTurnAttemptRuntimeOutput { + response: ResponseType; + debug: Record; + normalizationRuntime: BuildAssistantDeepTurnNormalizationRuntimeOutput; + deepTurnAnalysisRuntime: RunAssistantDeepTurnAnalysisRuntimeOutput; +} + +export async function runAssistantDeepTurnAttemptRuntime( + input: RunAssistantDeepTurnAttemptRuntimeInput +): Promise> { + const runDeepTurnNormalizationRuntimeSafe = + input.runDeepTurnNormalizationRuntime ?? buildAssistantDeepTurnNormalizationRuntime; + const runDeepTurnAnalysisAttemptRuntimeSafe = + input.runDeepTurnAnalysisAttemptRuntime ?? runAssistantDeepTurnAnalysisAttemptRuntime; + const runDeepTurnResponseAttemptRuntimeSafe = + input.runDeepTurnResponseAttemptRuntime ?? runAssistantDeepTurnResponseAttemptRuntime; + + const normalizationRuntime = await runDeepTurnNormalizationRuntimeSafe({ + userMessage: input.userMessage, + payload: input.payload, + featureInvestigationStateV1: input.featureInvestigationStateV1, + featureStateFollowupBindingV1: input.featureStateFollowupBindingV1, + sessionInvestigationState: input.sessionInvestigationState, + buildFollowupStateBinding: input.buildFollowupStateBinding, + normalize: input.normalize + }); + + const followupBinding = normalizationRuntime.followupBinding; + const normalized = normalizationRuntime.normalized; + + const deepTurnAnalysisRuntime = await runDeepTurnAnalysisAttemptRuntimeSafe({ + userMessage: input.userMessage, + normalizedPayload: normalized.normalized, + routeSummary: normalized.route_hint_summary, + runtimeAnalysisContext: input.runtimeAnalysisContext, + followupUsage: followupBinding.usage, + investigationState: input.sessionInvestigationState, + featureAnswerPolicyV11: input.featureAnswerPolicyV11, + featureProblemCentricAnswerV1: input.featureProblemCentricAnswerV1, + featureLifecycleAnswerV1: input.featureLifecycleAnswerV1, + resolveBusinessScopeAlignment: input.resolveBusinessScopeAlignment, + inferP0DomainFromMessage: input.inferP0DomainFromMessage, + resolveBusinessScopeFromLiveContext: input.resolveBusinessScopeFromLiveContext, + extractRequirements: input.extractRequirements, + toExecutionPlan: input.toExecutionPlan, + enforceRbpLiveRoutePlan: input.enforceRbpLiveRoutePlan, + enforceFaLiveRoutePlan: input.enforceFaLiveRoutePlan, + executeRouteRuntime: input.executeRouteRuntime, + mapNoRouteReason: input.mapNoRouteReason, + buildSkippedResult: input.buildSkippedResult, + evaluateCoverage: input.evaluateCoverage, + checkGrounding: input.checkGrounding, + collectRbpLiveRouteAudit: input.collectRbpLiveRouteAudit, + collectFaLiveRouteAudit: input.collectFaLiveRouteAudit, + hasExplicitPeriodAnchor: input.hasExplicitPeriodAnchor + }); + + const deepTurnResponseRuntime = runDeepTurnResponseAttemptRuntimeSafe({ + featureInvestigationStateV1: input.featureInvestigationStateV1, + featureContractsV11: input.featureContractsV11, + featureAnswerPolicyV11: input.featureAnswerPolicyV11, + sessionId: input.sessionId, + questionId: input.questionId, + userMessage: input.userMessage, + normalized: { + trace_id: normalized.trace_id, + prompt_version: normalized.prompt_version, + schema_version: normalized.schema_version, + normalized: normalized.normalized + }, + normalizedQuestion: followupBinding.normalizedQuestion, + deepTurnAnalysisRuntime, + runtimeAnalysisContext: input.runtimeAnalysisContext, + followupStateUsage: followupBinding.usage, + followupApplied: Boolean((followupBinding.usage as { applied?: unknown } | null)?.applied), + previousInvestigationState: input.sessionInvestigationState, + addressRuntimeMetaForDeep: input.addressRuntimeMetaForDeep, + extractDroppedIntentSegments: input.extractDroppedIntentSegments, + buildDebugRoutes: input.buildDebugRoutes, + extractExecutionState: input.extractExecutionState, + sanitizeReply: input.sanitizeReply, + persistInvestigationState: input.persistInvestigationState, + messageIdFactory: input.messageIdFactory, + appendItem: input.appendItem, + getSession: input.getSession, + persistSession: input.persistSession, + cloneConversation: input.cloneConversation, + logEvent: input.logEvent, + runPackagingRuntime: input.runPackagingRuntime, + runFinalizeDeepTurn: input.runFinalizeDeepTurn + }); + + return { + response: deepTurnResponseRuntime.response, + debug: deepTurnResponseRuntime.debug, + normalizationRuntime, + deepTurnAnalysisRuntime + }; +} diff --git a/llm_normalizer/backend/src/services/assistantService.ts b/llm_normalizer/backend/src/services/assistantService.ts index 27a0487..4ce7d38 100644 --- a/llm_normalizer/backend/src/services/assistantService.ts +++ b/llm_normalizer/backend/src/services/assistantService.ts @@ -18,9 +18,7 @@ import * as capabilitiesRegistry_1 from "./capabilitiesRegistry"; import * as assistantCanon_1 from "./assistantCanon"; import * as assistantAddressLaneResponseAttemptRuntimeAdapter_1 from "./assistantAddressLaneResponseAttemptRuntimeAdapter"; import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding"; -import * as assistantDeepTurnAnalysisAttemptRuntimeAdapter_1 from "./assistantDeepTurnAnalysisAttemptRuntimeAdapter"; -import * as assistantDeepTurnNormalizationRuntimeAdapter_1 from "./assistantDeepTurnNormalizationRuntimeAdapter"; -import * as assistantDeepTurnResponseAttemptRuntimeAdapter_1 from "./assistantDeepTurnResponseAttemptRuntimeAdapter"; +import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAttemptRuntimeAdapter"; import * as assistantAddressRuntimeAdapter_1 from "./assistantAddressRuntimeAdapter"; import * as assistantAddressLaneAttemptRuntimeAdapter_1 from "./assistantAddressLaneAttemptRuntimeAdapter"; import * as assistantLivingChatAttemptRuntimeAdapter_1 from "./assistantLivingChatAttemptRuntimeAdapter"; @@ -4448,27 +4446,22 @@ export class AssistantService { if (addressRuntime.handled && addressRuntime.response) { return addressRuntime.response; } - const normalizationRuntime = await (0, assistantDeepTurnNormalizationRuntimeAdapter_1.buildAssistantDeepTurnNormalizationRuntime)({ + 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, - sessionInvestigationState: session.investigation_state, - buildFollowupStateBinding, - normalize: (normalizePayload) => this.normalizerService.normalize(normalizePayload) - }); - const followupBinding = normalizationRuntime.followupBinding; - const normalized = normalizationRuntime.normalized; - const deepTurnAnalysisRuntime = await (0, assistantDeepTurnAnalysisAttemptRuntimeAdapter_1.runAssistantDeepTurnAnalysisAttemptRuntime)({ - userMessage, - normalizedPayload: normalized.normalized, - routeSummary: normalized.route_hint_summary, - runtimeAnalysisContext, - followupUsage: followupBinding.usage, - investigationState: session.investigation_state, + 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, @@ -4483,28 +4476,7 @@ export class AssistantService { checkGrounding, collectRbpLiveRouteAudit, collectFaLiveRouteAudit, - hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload) - }); - const deepTurnResponseRuntime = (0, assistantDeepTurnResponseAttemptRuntimeAdapter_1.runAssistantDeepTurnResponseAttemptRuntime)({ - featureInvestigationStateV1: config_1.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1, - featureContractsV11: config_1.FEATURE_ASSISTANT_CONTRACTS_V11, - featureAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11, - sessionId, - questionId: userItem.message_id, - userMessage, - normalized: { - trace_id: normalized.trace_id, - prompt_version: normalized.prompt_version, - schema_version: normalized.schema_version, - normalized: normalized.normalized - }, - normalizedQuestion: followupBinding.normalizedQuestion, - deepTurnAnalysisRuntime, - runtimeAnalysisContext, - followupStateUsage: followupBinding.usage, - followupApplied: Boolean(followupBinding.usage?.applied), - previousInvestigationState: session.investigation_state, - addressRuntimeMetaForDeep, + hasExplicitPeriodAnchor: (normalizedPayload) => hasExplicitPeriodAnchorFromNormalized(normalizedPayload), extractDroppedIntentSegments: (normalizedPayload) => extractDiscardedIntentSegments(normalizedPayload), buildDebugRoutes: (routeSummary) => toDebugRoutes(routeSummary), extractExecutionState: (normalizedPayload) => extractExecutionState(normalizedPayload), @@ -4517,6 +4489,6 @@ export class AssistantService { cloneConversation: (items) => cloneItems(items), logEvent: (payload) => (0, log_1.logJson)(payload) }); - return deepTurnResponseRuntime.response; + return deepTurnRuntime.response; } } diff --git a/llm_normalizer/backend/tests/assistantDeepTurnAttemptRuntimeAdapter.test.ts b/llm_normalizer/backend/tests/assistantDeepTurnAttemptRuntimeAdapter.test.ts new file mode 100644 index 0000000..0e23dfe --- /dev/null +++ b/llm_normalizer/backend/tests/assistantDeepTurnAttemptRuntimeAdapter.test.ts @@ -0,0 +1,221 @@ +import { describe, expect, it, vi } from "vitest"; +import { runAssistantDeepTurnAttemptRuntime } from "../src/services/assistantDeepTurnAttemptRuntimeAdapter"; + +function buildInput(overrides: Record = {}) { + return { + sessionId: "asst-1", + questionId: "msg-q1", + userMessage: "почему не закрыт долг", + payload: { + llmProvider: "openai", + apiKey: "key", + model: "gpt-5", + baseUrl: "http://localhost", + temperature: 0.2, + maxOutputTokens: 400, + context: {} + }, + runtimeAnalysisContext: { + active: true, + as_of_date: "2020-07-31", + period_from: null, + period_to: null, + source: "analysis_context" + }, + sessionInvestigationState: null, + addressRuntimeMetaForDeep: null, + featureInvestigationStateV1: true, + featureStateFollowupBindingV1: true, + featureContractsV11: true, + featureAnswerPolicyV11: true, + featureProblemCentricAnswerV1: true, + featureLifecycleAnswerV1: true, + buildFollowupStateBinding: () => ({ + normalizedQuestion: "normalized-question", + mergedContext: {}, + usage: null + }), + normalize: async () => + ({ + trace_id: "trace-1", + prompt_version: "normalizer_v2_0_2", + schema_version: "normalized_query_v2_0_2", + normalized: { fragments: [] }, + route_hint_summary: { mode: "deterministic_v2", decisions: [] } + }) as any, + resolveBusinessScopeAlignment: ({ routeSummary }) => ({ + route_summary_resolved: routeSummary + }), + inferP0DomainFromMessage: () => null, + resolveBusinessScopeFromLiveContext: ({ current }) => current, + extractRequirements: () => ({ requirements: [], byFragment: new Map() }), + toExecutionPlan: () => [], + enforceRbpLiveRoutePlan: ({ executionPlan }) => ({ executionPlan, audit: {} }), + enforceFaLiveRoutePlan: ({ executionPlan }) => ({ executionPlan, audit: {} }), + executeRouteRuntime: async () => ({ + status: "ok", + result_type: "summary", + items: [], + summary: {}, + evidence: [], + limitations: [] + }), + mapNoRouteReason: () => "no_route", + buildSkippedResult: () => ({}) as any, + evaluateCoverage: () => ({ requirements: [], coverage: {} }), + checkGrounding: () => ({ status: "no_grounded_answer", reasons: [] }), + collectRbpLiveRouteAudit: () => ({}), + collectFaLiveRouteAudit: () => ({}), + hasExplicitPeriodAnchor: () => false, + extractDroppedIntentSegments: () => [], + buildDebugRoutes: () => [], + extractExecutionState: () => [], + sanitizeReply: (value: string) => value, + persistInvestigationState: () => {}, + messageIdFactory: () => "msg-a1", + appendItem: () => {}, + getSession: () => ({ session_id: "asst-1", items: [] }), + persistSession: () => {}, + cloneConversation: () => [], + logEvent: () => {}, + ...overrides + } as any; +} + +describe("assistant deep turn attempt runtime adapter", () => { + it("orchestrates normalization, analysis and response attempts in order", async () => { + const callOrder: string[] = []; + const normalizationRuntime = { + followupBinding: { + normalizedQuestion: "NQ", + mergedContext: {}, + usage: { applied: true } + }, + normalizePayload: { userQuestion: "NQ" }, + normalized: { + trace_id: "trace-1", + prompt_version: "normalizer_v2_0_2", + schema_version: "normalized_query_v2_0_2", + normalized: { fragments: [] }, + route_hint_summary: { mode: "deterministic_v2", decisions: [] } + } + } as any; + const analysisRuntime = { + resolvedRouteSummary: { mode: "deterministic_v2", decisions: [] }, + requirementExtraction: { requirements: [], byFragment: new Map() }, + coverageEvaluation: { requirements: [], coverage: {} }, + groundingCheck: { status: "no_grounded_answer", reasons: [] }, + executionPlan: [], + retrievalCalls: [], + retrievalResultsRaw: [], + retrievalResults: [], + questionTypeClass: "single_fact_lookup", + companyAnchors: {}, + businessScopeResolution: {}, + temporalGuard: {}, + polarityGuardResult: { audit: {} }, + claimAnchorAudit: { claim_type: "unknown" }, + targetedEvidenceResult: { audit: {} }, + evidenceGateResult: { audit: {} }, + rbpLiveRouteAudit: {}, + faLiveRouteAudit: {}, + groundedAnswerEligibilityGuard: {}, + composition: { reply_type: "partial_coverage" } + } as any; + + const runDeepTurnNormalizationRuntime = vi.fn(async () => { + callOrder.push("normalization"); + return normalizationRuntime; + }); + const runDeepTurnAnalysisAttemptRuntime = vi.fn(async (input) => { + callOrder.push("analysis"); + expect(input.normalizedPayload).toEqual(normalizationRuntime.normalized.normalized); + expect(input.routeSummary).toEqual(normalizationRuntime.normalized.route_hint_summary); + expect(input.followupUsage).toEqual({ applied: true }); + return analysisRuntime; + }); + const runDeepTurnResponseAttemptRuntime = vi.fn((input) => { + callOrder.push("response"); + expect(input.normalizedQuestion).toBe("NQ"); + expect(input.deepTurnAnalysisRuntime).toBe(analysisRuntime); + expect(input.followupApplied).toBe(true); + return { + response: { ok: true }, + debug: { trace_id: "trace-1" } + }; + }); + + const runtime = await runAssistantDeepTurnAttemptRuntime( + buildInput({ + runDeepTurnNormalizationRuntime, + runDeepTurnAnalysisAttemptRuntime, + runDeepTurnResponseAttemptRuntime + }) + ); + + expect(callOrder).toEqual(["normalization", "analysis", "response"]); + expect(runtime.response).toEqual({ ok: true }); + expect(runtime.debug).toEqual({ trace_id: "trace-1" }); + }); + + it("computes followupApplied=false when followup usage is absent", async () => { + const runDeepTurnNormalizationRuntime = vi.fn(async () => ({ + followupBinding: { + normalizedQuestion: "NQ", + mergedContext: {}, + usage: null + }, + normalizePayload: {}, + normalized: { + trace_id: "trace-1", + prompt_version: "normalizer_v2_0_2", + schema_version: "normalized_query_v2_0_2", + normalized: { fragments: [] }, + route_hint_summary: null + } + })); + const runDeepTurnResponseAttemptRuntime = vi.fn(() => ({ + response: { ok: true }, + debug: {} + })); + const investigationState = { session_id: "asst-1" } as any; + + await runAssistantDeepTurnAttemptRuntime( + buildInput({ + sessionInvestigationState: investigationState, + runDeepTurnNormalizationRuntime, + runDeepTurnAnalysisAttemptRuntime: async () => + ({ + resolvedRouteSummary: null, + requirementExtraction: { requirements: [], byFragment: new Map() }, + coverageEvaluation: { requirements: [], coverage: {} }, + groundingCheck: { status: "no_grounded_answer", reasons: [] }, + executionPlan: [], + retrievalCalls: [], + retrievalResultsRaw: [], + retrievalResults: [], + questionTypeClass: "single_fact_lookup", + companyAnchors: {}, + businessScopeResolution: {}, + temporalGuard: {}, + polarityGuardResult: { audit: {} }, + claimAnchorAudit: { claim_type: "unknown" }, + targetedEvidenceResult: { audit: {} }, + evidenceGateResult: { audit: {} }, + rbpLiveRouteAudit: {}, + faLiveRouteAudit: {}, + groundedAnswerEligibilityGuard: {}, + composition: { reply_type: "partial_coverage" } + }) as any, + runDeepTurnResponseAttemptRuntime + }) + ); + + expect(runDeepTurnResponseAttemptRuntime).toHaveBeenCalledWith( + expect.objectContaining({ + followupApplied: false, + previousInvestigationState: investigationState + }) + ); + }); +});