diff --git a/docs/TECH/1CLLMARCH-FACT.md b/docs/TECH/1CLLMARCH-FACT.md index cd7fedb..24080fc 100644 --- a/docs/TECH/1CLLMARCH-FACT.md +++ b/docs/TECH/1CLLMARCH-FACT.md @@ -1261,7 +1261,34 @@ 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 completed)** +Implemented in current pass (Phase 2.40): +1. Extracted address-lane attempt bridge (`runAddressLaneAttempt`) from `assistantService` into dedicated runtime adapter: + - `assistantAddressLaneAttemptRuntimeAdapter.ts` + - introduced: + - `runAssistantAddressLaneAttemptRuntime(...)` +2. Centralized address-lane attempt handoff logic (behavior-preserving): + - organization-scoped followup context merge; + - fallback to plain lane attempt when scoped context is unavailable; + - strict propagation of analysis-date hint into `addressQueryService.tryHandle(...)`. +3. Rewired `assistantService` to consume lane-attempt runtime adapter. +4. Added focused unit tests: + - `assistantAddressLaneAttemptRuntimeAdapter.test.ts` + +Validation: +1. `npm run build` passed. +2. Targeted living/address/deep followup pack passed: + - `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 completed)** ## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards) diff --git a/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js new file mode 100644 index 0000000..a9ef3a1 --- /dev/null +++ b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runAssistantAddressLaneAttemptRuntime = runAssistantAddressLaneAttemptRuntime; +async function runAssistantAddressLaneAttemptRuntime(input) { + const followupContext = input.carryMeta?.followupContext && typeof input.carryMeta.followupContext === "object" + ? input.carryMeta.followupContext + : null; + const scopedFollowupContext = input.mergeFollowupContextWithOrganizationScope(followupContext, input.activeOrganization); + if (scopedFollowupContext) { + return input.runAddressQueryTryHandle(input.messageUsed, { + followupContext: scopedFollowupContext, + analysisDateHint: input.analysisDateHint + }); + } + return input.runAddressQueryTryHandle(input.messageUsed, { + analysisDateHint: input.analysisDateHint + }); +} diff --git a/llm_normalizer/backend/dist/services/assistantService.js b/llm_normalizer/backend/dist/services/assistantService.js index 6ce9fce..41ccc02 100644 --- a/llm_normalizer/backend/dist/services/assistantService.js +++ b/llm_normalizer/backend/dist/services/assistantService.js @@ -79,6 +79,7 @@ const assistantDeepTurnNormalizationRuntimeAdapter_1 = __importStar(require("./a const assistantDeepTurnResponseRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnResponseRuntimeAdapter")); const assistantDeepTurnRetrievalRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnRetrievalRuntimeAdapter")); const assistantAddressRuntimeAdapter_1 = __importStar(require("./assistantAddressRuntimeAdapter")); +const assistantAddressLaneAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressLaneAttemptRuntimeAdapter")); const assistantLivingChatHandlerRuntimeAdapter_1 = __importStar(require("./assistantLivingChatHandlerRuntimeAdapter")); const assistantLivingChatLlmRuntimeAdapter_1 = __importStar(require("./assistantLivingChatLlmRuntimeAdapter")); const assistantUserTurnBootstrapRuntimeAdapter_1 = __importStar(require("./assistantUserTurnBootstrapRuntimeAdapter")); @@ -4476,18 +4477,14 @@ class AssistantService { }); }; let addressRuntimeMetaForDeep = null; - const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => { - const scopedFollowupContext = mergeFollowupContextWithOrganizationScope(carryMeta?.followupContext ?? null, sessionOrganizationScope.activeOrganization); - if (scopedFollowupContext) { - return this.addressQueryService.tryHandle(messageUsed, { - followupContext: scopedFollowupContext, - analysisDateHint - }); - } - return this.addressQueryService.tryHandle(messageUsed, { - analysisDateHint - }); - }; + const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => (0, assistantAddressLaneAttemptRuntimeAdapter_1.runAssistantAddressLaneAttemptRuntime)({ + messageUsed, + carryMeta: carryMeta ?? null, + analysisDateHint: analysisDateHint ?? null, + activeOrganization: sessionOrganizationScope.activeOrganization, + mergeFollowupContextWithOrganizationScope, + runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options) + }); const addressRuntime = await (0, assistantAddressRuntimeAdapter_1.runAssistantAddressRuntime)({ featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1, sessionId, diff --git a/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts new file mode 100644 index 0000000..d9f76bd --- /dev/null +++ b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts @@ -0,0 +1,42 @@ +import type { AssistantAddressLaneLike } from "./assistantAddressLaneRuntimeAdapter"; +import type { AssistantAddressCarryoverLike } from "./assistantAddressOrchestrationRuntimeAdapter"; + +export interface RunAssistantAddressLaneAttemptRuntimeInput { + messageUsed: string; + carryMeta: AssistantAddressCarryoverLike | null; + analysisDateHint: string | null; + activeOrganization: string | null; + mergeFollowupContextWithOrganizationScope: ( + followupContext: Record | null, + organization: string | null + ) => Record | null; + runAddressQueryTryHandle: ( + messageUsed: string, + options: { + followupContext?: Record; + analysisDateHint?: string | null; + } + ) => Promise; +} + +export async function runAssistantAddressLaneAttemptRuntime( + input: RunAssistantAddressLaneAttemptRuntimeInput +): Promise { + const followupContext = + input.carryMeta?.followupContext && typeof input.carryMeta.followupContext === "object" + ? (input.carryMeta.followupContext as Record) + : null; + const scopedFollowupContext = input.mergeFollowupContextWithOrganizationScope( + followupContext, + input.activeOrganization + ); + if (scopedFollowupContext) { + return input.runAddressQueryTryHandle(input.messageUsed, { + followupContext: scopedFollowupContext, + analysisDateHint: input.analysisDateHint + }); + } + return input.runAddressQueryTryHandle(input.messageUsed, { + analysisDateHint: input.analysisDateHint + }); +} diff --git a/llm_normalizer/backend/src/services/assistantService.ts b/llm_normalizer/backend/src/services/assistantService.ts index 473d385..ab9b3cf 100644 --- a/llm_normalizer/backend/src/services/assistantService.ts +++ b/llm_normalizer/backend/src/services/assistantService.ts @@ -33,6 +33,7 @@ import * as assistantDeepTurnNormalizationRuntimeAdapter_1 from "./assistantDeep import * as assistantDeepTurnResponseRuntimeAdapter_1 from "./assistantDeepTurnResponseRuntimeAdapter"; import * as assistantDeepTurnRetrievalRuntimeAdapter_1 from "./assistantDeepTurnRetrievalRuntimeAdapter"; import * as assistantAddressRuntimeAdapter_1 from "./assistantAddressRuntimeAdapter"; +import * as assistantAddressLaneAttemptRuntimeAdapter_1 from "./assistantAddressLaneAttemptRuntimeAdapter"; import * as assistantLivingChatHandlerRuntimeAdapter_1 from "./assistantLivingChatHandlerRuntimeAdapter"; import * as assistantLivingChatLlmRuntimeAdapter_1 from "./assistantLivingChatLlmRuntimeAdapter"; import * as assistantUserTurnBootstrapRuntimeAdapter_1 from "./assistantUserTurnBootstrapRuntimeAdapter"; @@ -4431,18 +4432,14 @@ export class AssistantService { }); }; let addressRuntimeMetaForDeep = null; - const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => { - const scopedFollowupContext = mergeFollowupContextWithOrganizationScope(carryMeta?.followupContext ?? null, sessionOrganizationScope.activeOrganization); - if (scopedFollowupContext) { - return this.addressQueryService.tryHandle(messageUsed, { - followupContext: scopedFollowupContext, - analysisDateHint - }); - } - return this.addressQueryService.tryHandle(messageUsed, { - analysisDateHint - }); - }; + const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => (0, assistantAddressLaneAttemptRuntimeAdapter_1.runAssistantAddressLaneAttemptRuntime)({ + messageUsed, + carryMeta: carryMeta ?? null, + analysisDateHint: analysisDateHint ?? null, + activeOrganization: sessionOrganizationScope.activeOrganization, + mergeFollowupContextWithOrganizationScope, + runAddressQueryTryHandle: (laneMessageUsed, options) => this.addressQueryService.tryHandle(laneMessageUsed, options) + }); const addressRuntime = await (0, assistantAddressRuntimeAdapter_1.runAssistantAddressRuntime)({ featureAssistantAddressQueryV1: config_1.FEATURE_ASSISTANT_ADDRESS_QUERY_V1, sessionId, diff --git a/llm_normalizer/backend/tests/assistantAddressLaneAttemptRuntimeAdapter.test.ts b/llm_normalizer/backend/tests/assistantAddressLaneAttemptRuntimeAdapter.test.ts new file mode 100644 index 0000000..36ce31e --- /dev/null +++ b/llm_normalizer/backend/tests/assistantAddressLaneAttemptRuntimeAdapter.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; +import { runAssistantAddressLaneAttemptRuntime } from "../src/services/assistantAddressLaneAttemptRuntimeAdapter"; + +describe("assistant address lane attempt runtime adapter", () => { + it("uses scoped followup context when available", async () => { + const runAddressQueryTryHandle = vi.fn(async () => ({ + response_type: "READY" + })); + const result = await runAssistantAddressLaneAttemptRuntime({ + messageUsed: "msg", + carryMeta: { + followupContext: { + previous_intent: "docs_by_counterparty" + } + }, + analysisDateHint: "2020-07-31", + activeOrganization: "ООО Тест", + mergeFollowupContextWithOrganizationScope: () => ({ + previous_intent: "docs_by_counterparty", + active_organization: "ООО Тест" + }), + runAddressQueryTryHandle + }); + + expect(runAddressQueryTryHandle).toHaveBeenCalledWith("msg", { + followupContext: { + previous_intent: "docs_by_counterparty", + active_organization: "ООО Тест" + }, + analysisDateHint: "2020-07-31" + }); + expect(result).toEqual({ + response_type: "READY" + }); + }); + + it("falls back to plain attempt when scoped followup context is empty", async () => { + const runAddressQueryTryHandle = vi.fn(async () => null); + await runAssistantAddressLaneAttemptRuntime({ + messageUsed: "msg", + carryMeta: null, + analysisDateHint: null, + activeOrganization: null, + mergeFollowupContextWithOrganizationScope: () => null, + runAddressQueryTryHandle + }); + + expect(runAddressQueryTryHandle).toHaveBeenCalledWith("msg", { + analysisDateHint: null + }); + }); +});