From bf16309a2955d22073240f044b82af5228a094e8 Mon Sep 17 00:00:00 2001 From: dctouch Date: Sat, 11 Apr 2026 10:00:05 +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=20=202.652.68=20-=20=20=20=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=20=D0=B2=D1=85=D0=BE=D0=B4=D0=BE=D0=B2=20tur?= =?UTF-8?q?n=20attempt=20=D0=B2=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20builder?= =?UTF-8?q?:=20=20assistantTurnAttemptInputBuilder.ts=20/=20=20=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=20=D0=BD=D0=B0=20builder?= =?UTF-8?q?=20(=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20inline-=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D1=83=20payload=20=D0=B4=D0=BB=D1=8F=20address/dee?= =?UTF-8?q?p):=20=20assistantTurnAttemptRuntimeAdapter.ts=20/=20=D1=81?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=BA=D1=83=20followupContext/options=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20address=20lane=20attempt=20=D0=B2=20=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D0=B9=20builder:=20=20assistantAddressLaneAt?= =?UTF-8?q?temptQueryOptionsBuilder.ts=20/=20=20=D0=B0=D0=B4=D0=B0=D0=BF?= =?UTF-8?q?=D1=82=D0=B5=D1=80=20=D0=BD=D0=B0=20builder=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20query=20options:=20=20assistantAddressLaneAttemptRunti?= =?UTF-8?q?meAdapter.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/TECH/1CLLMARCH-FACT.md | 53 +++++++++++++++- ...ntAddressLaneAttemptQueryOptionsBuilder.js | 20 ++++++ ...sistantAddressLaneAttemptRuntimeAdapter.js | 18 ++---- .../assistantTurnAttemptInputBuilder.js | 25 ++++++++ .../assistantTurnAttemptRuntimeAdapter.js | 20 +++--- ...ntAddressLaneAttemptQueryOptionsBuilder.ts | 28 +++++++++ ...sistantAddressLaneAttemptRuntimeAdapter.ts | 25 ++++---- .../assistantTurnAttemptInputBuilder.ts | 45 +++++++++++++ .../assistantTurnAttemptRuntimeAdapter.ts | 35 ++++++----- ...ressLaneAttemptQueryOptionsBuilder.test.ts | 48 ++++++++++++++ .../assistantTurnAttemptInputBuilder.test.ts | 63 +++++++++++++++++++ 11 files changed, 324 insertions(+), 56 deletions(-) create mode 100644 llm_normalizer/backend/dist/services/assistantAddressLaneAttemptQueryOptionsBuilder.js create mode 100644 llm_normalizer/backend/dist/services/assistantTurnAttemptInputBuilder.js create mode 100644 llm_normalizer/backend/src/services/assistantAddressLaneAttemptQueryOptionsBuilder.ts create mode 100644 llm_normalizer/backend/src/services/assistantTurnAttemptInputBuilder.ts create mode 100644 llm_normalizer/backend/tests/assistantAddressLaneAttemptQueryOptionsBuilder.test.ts create mode 100644 llm_normalizer/backend/tests/assistantTurnAttemptInputBuilder.test.ts diff --git a/docs/TECH/1CLLMARCH-FACT.md b/docs/TECH/1CLLMARCH-FACT.md index f81adb1..35d0e57 100644 --- a/docs/TECH/1CLLMARCH-FACT.md +++ b/docs/TECH/1CLLMARCH-FACT.md @@ -1799,7 +1799,58 @@ Validation: - `assistantWave10SettlementCorrectiveRegression.test.ts` - `assistantLivingChatMode.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 + 2.48 + 2.49 + 2.50 + 2.51 + 2.52 + 2.53 + 2.54 + 2.55 + 2.56 + 2.57 + 2.58 + 2.59 + 2.60 + 2.61 + 2.62 + 2.63 + 2.64 completed)** +Implemented in current pass (Phase 2.65 + 2.66 + 2.67 + 2.68): +1. Added dedicated turn-attempt input builder: + - `assistantTurnAttemptInputBuilder.ts` + - introduced: + - `buildAssistantTurnAttemptAddressRuntimeInput(...)` + - `buildAssistantTurnAttemptDeepRuntimeInput(...)` +2. Rewired `assistantTurnAttemptRuntimeAdapter` to consume turn-attempt builder outputs (behavior-preserving): + - removed inline payload assembly for `runAddressAttemptRuntime(...)` and `runDeepTurnAttemptRuntime(...)`. +3. Added dedicated query-options builder for address lane attempt: + - `assistantAddressLaneAttemptQueryOptionsBuilder.ts` + - introduced: + - `resolveAssistantAddressLaneAttemptFollowupContext(...)` + - `buildAssistantAddressLaneAttemptQueryOptions(...)` +4. Rewired `assistantAddressLaneAttemptRuntimeAdapter` to consume query-options builder (behavior-preserving): + - moved followup-context extraction and options branching behind builder helpers. +5. Added focused builder tests: + - `assistantTurnAttemptInputBuilder.test.ts` + - `assistantAddressLaneAttemptQueryOptionsBuilder.test.ts` + +Validation: +1. `npm run build` passed. +2. Targeted living/address/deep followup pack passed: + - `assistantTurnAttemptInputBuilder.test.ts` + - `assistantAddressLaneAttemptQueryOptionsBuilder.test.ts` + - `assistantTurnAttemptRuntimeAdapter.test.ts` + - `assistantAddressLaneAttemptRuntimeAdapter.test.ts` + - `assistantAddressLaneAttemptInputBuilder.test.ts` + - `assistantAddressLaneResponseRuntimeInputBuilder.test.ts` + - `assistantLivingChatAttemptRuntimeInputBuilder.test.ts` + - `assistantAddressLaneResponseAttemptRuntimeAdapter.test.ts` + - `assistantLivingChatAttemptRuntimeAdapter.test.ts` + - `assistantAddressAttemptRuntimeAdapter.test.ts` + - `assistantAddressRuntimeAdapter.test.ts` + - `assistantAddressLaneResponseRuntimeAdapter.test.ts` + - `assistantLivingChatHandlerRuntimeAdapter.test.ts` + - `assistantLivingChatRuntimeAdapter.test.ts` + - `assistantDeepTurnAttemptInputBuilder.test.ts` + - `assistantDeepTurnAnalysisAttemptInputBuilder.test.ts` + - `assistantDeepTurnResponseRuntimeInputBuilder.test.ts` + - `assistantDeepTurnAttemptRuntimeAdapter.test.ts` + - `assistantDeepTurnAnalysisAttemptRuntimeAdapter.test.ts` + - `assistantDeepTurnResponseAttemptRuntimeAdapter.test.ts` + - `assistantDeepTurnAnalysisRuntimeAdapter.test.ts` + - `assistantDeepTurnResponseRuntimeAdapter.test.ts` + - `assistantDeepTurnPackagingRuntimeAdapter.test.ts` + - `assistantTurnRuntimeInputBuilder.test.ts` + - `assistantTurnRuntimeDepsAdapter.test.ts` + - `assistantOrganizationScopeRuntimeAdapter.test.ts` + - `assistantWave10SettlementCorrectiveRegression.test.ts` + - `assistantLivingChatMode.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 + 2.48 + 2.49 + 2.50 + 2.51 + 2.52 + 2.53 + 2.54 + 2.55 + 2.56 + 2.57 + 2.58 + 2.59 + 2.60 + 2.61 + 2.62 + 2.63 + 2.64 + 2.65 + 2.66 + 2.67 + 2.68 completed)** ## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards) diff --git a/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptQueryOptionsBuilder.js b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptQueryOptionsBuilder.js new file mode 100644 index 0000000..4f07598 --- /dev/null +++ b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptQueryOptionsBuilder.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.resolveAssistantAddressLaneAttemptFollowupContext = resolveAssistantAddressLaneAttemptFollowupContext; +exports.buildAssistantAddressLaneAttemptQueryOptions = buildAssistantAddressLaneAttemptQueryOptions; +function resolveAssistantAddressLaneAttemptFollowupContext(carryMeta) { + return carryMeta?.followupContext && typeof carryMeta.followupContext === "object" + ? carryMeta.followupContext + : null; +} +function buildAssistantAddressLaneAttemptQueryOptions(input) { + if (input.scopedFollowupContext) { + return { + followupContext: input.scopedFollowupContext, + analysisDateHint: input.analysisDateHint + }; + } + return { + analysisDateHint: input.analysisDateHint + }; +} diff --git a/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js index a9ef3a1..bfebe3c 100644 --- a/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantAddressLaneAttemptRuntimeAdapter.js @@ -1,18 +1,12 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.runAssistantAddressLaneAttemptRuntime = runAssistantAddressLaneAttemptRuntime; +const assistantAddressLaneAttemptQueryOptionsBuilder_1 = require("./assistantAddressLaneAttemptQueryOptionsBuilder"); async function runAssistantAddressLaneAttemptRuntime(input) { - const followupContext = input.carryMeta?.followupContext && typeof input.carryMeta.followupContext === "object" - ? input.carryMeta.followupContext - : null; + const followupContext = (0, assistantAddressLaneAttemptQueryOptionsBuilder_1.resolveAssistantAddressLaneAttemptFollowupContext)(input.carryMeta); 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 - }); + return input.runAddressQueryTryHandle(input.messageUsed, (0, assistantAddressLaneAttemptQueryOptionsBuilder_1.buildAssistantAddressLaneAttemptQueryOptions)({ + analysisDateHint: input.analysisDateHint, + scopedFollowupContext + })); } diff --git a/llm_normalizer/backend/dist/services/assistantTurnAttemptInputBuilder.js b/llm_normalizer/backend/dist/services/assistantTurnAttemptInputBuilder.js new file mode 100644 index 0000000..7d6787e --- /dev/null +++ b/llm_normalizer/backend/dist/services/assistantTurnAttemptInputBuilder.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.buildAssistantTurnAttemptAddressRuntimeInput = buildAssistantTurnAttemptAddressRuntimeInput; +exports.buildAssistantTurnAttemptDeepRuntimeInput = buildAssistantTurnAttemptDeepRuntimeInput; +function buildAssistantTurnAttemptAddressRuntimeInput(input) { + return { + payload: input.payload, + sessionId: input.userTurn.sessionId, + userMessage: input.userTurn.userMessage, + sessionItems: input.userTurn.session.items, + runtimeAnalysisContext: input.userTurn.runtimeAnalysisContext, + sessionOrganizationScope: input.sessionOrganizationScope + }; +} +function buildAssistantTurnAttemptDeepRuntimeInput(input) { + return { + payload: input.payload, + sessionId: input.userTurn.sessionId, + questionId: input.userTurn.userItem.message_id, + userMessage: input.userTurn.userMessage, + runtimeAnalysisContext: input.userTurn.runtimeAnalysisContext, + sessionInvestigationState: input.userTurn.session.investigation_state, + addressRuntimeMetaForDeep: input.addressRuntimeMetaForDeep + }; +} diff --git a/llm_normalizer/backend/dist/services/assistantTurnAttemptRuntimeAdapter.js b/llm_normalizer/backend/dist/services/assistantTurnAttemptRuntimeAdapter.js index 3158df9..e8005c7 100644 --- a/llm_normalizer/backend/dist/services/assistantTurnAttemptRuntimeAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantTurnAttemptRuntimeAdapter.js @@ -1,17 +1,15 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.runAssistantTurnAttemptRuntime = runAssistantTurnAttemptRuntime; +const assistantTurnAttemptInputBuilder_1 = require("./assistantTurnAttemptInputBuilder"); async function runAssistantTurnAttemptRuntime(input) { const userTurn = input.runUserTurnBootstrapRuntime(input.payload); const sessionOrganizationScope = input.resolveSessionOrganizationScopeContext(userTurn.userMessage, userTurn.session.items); - const addressRuntime = await input.runAddressAttemptRuntime({ + const addressRuntime = await input.runAddressAttemptRuntime((0, assistantTurnAttemptInputBuilder_1.buildAssistantTurnAttemptAddressRuntimeInput)({ payload: input.payload, - sessionId: userTurn.sessionId, - userMessage: userTurn.userMessage, - sessionItems: userTurn.session.items, - runtimeAnalysisContext: userTurn.runtimeAnalysisContext, + userTurn, sessionOrganizationScope - }); + })); const addressRuntimeMetaForDeep = addressRuntime.addressRuntimeMetaForDeep ?? null; if (addressRuntime.handled && addressRuntime.response) { return { @@ -22,15 +20,11 @@ async function runAssistantTurnAttemptRuntime(input) { sessionOrganizationScope }; } - const deepTurnRuntime = await input.runDeepTurnAttemptRuntime({ + const deepTurnRuntime = await input.runDeepTurnAttemptRuntime((0, assistantTurnAttemptInputBuilder_1.buildAssistantTurnAttemptDeepRuntimeInput)({ payload: input.payload, - sessionId: userTurn.sessionId, - questionId: userTurn.userItem.message_id, - userMessage: userTurn.userMessage, - runtimeAnalysisContext: userTurn.runtimeAnalysisContext, - sessionInvestigationState: userTurn.session.investigation_state, + userTurn, addressRuntimeMetaForDeep - }); + })); return { response: deepTurnRuntime.response, source: "deep", diff --git a/llm_normalizer/backend/src/services/assistantAddressLaneAttemptQueryOptionsBuilder.ts b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptQueryOptionsBuilder.ts new file mode 100644 index 0000000..7516da5 --- /dev/null +++ b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptQueryOptionsBuilder.ts @@ -0,0 +1,28 @@ +import type { RunAssistantAddressLaneAttemptRuntimeInput } from "./assistantAddressLaneAttemptRuntimeAdapter"; + +export function resolveAssistantAddressLaneAttemptFollowupContext( + carryMeta: RunAssistantAddressLaneAttemptRuntimeInput["carryMeta"] +): Record | null { + return carryMeta?.followupContext && typeof carryMeta.followupContext === "object" + ? (carryMeta.followupContext as Record) + : null; +} + +export interface BuildAssistantAddressLaneAttemptQueryOptionsInput { + analysisDateHint: RunAssistantAddressLaneAttemptRuntimeInput["analysisDateHint"]; + scopedFollowupContext: Record | null; +} + +export function buildAssistantAddressLaneAttemptQueryOptions( + input: BuildAssistantAddressLaneAttemptQueryOptionsInput +): Parameters[1] { + if (input.scopedFollowupContext) { + return { + followupContext: input.scopedFollowupContext, + analysisDateHint: input.analysisDateHint + }; + } + return { + analysisDateHint: input.analysisDateHint + }; +} diff --git a/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts index d9f76bd..6e255cf 100644 --- a/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantAddressLaneAttemptRuntimeAdapter.ts @@ -1,5 +1,9 @@ import type { AssistantAddressLaneLike } from "./assistantAddressLaneRuntimeAdapter"; import type { AssistantAddressCarryoverLike } from "./assistantAddressOrchestrationRuntimeAdapter"; +import { + buildAssistantAddressLaneAttemptQueryOptions, + resolveAssistantAddressLaneAttemptFollowupContext +} from "./assistantAddressLaneAttemptQueryOptionsBuilder"; export interface RunAssistantAddressLaneAttemptRuntimeInput { messageUsed: string; @@ -22,21 +26,16 @@ export interface RunAssistantAddressLaneAttemptRuntimeInput { export async function runAssistantAddressLaneAttemptRuntime( input: RunAssistantAddressLaneAttemptRuntimeInput ): Promise { - const followupContext = - input.carryMeta?.followupContext && typeof input.carryMeta.followupContext === "object" - ? (input.carryMeta.followupContext as Record) - : null; + const followupContext = resolveAssistantAddressLaneAttemptFollowupContext(input.carryMeta); 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 - }); + return input.runAddressQueryTryHandle( + input.messageUsed, + buildAssistantAddressLaneAttemptQueryOptions({ + analysisDateHint: input.analysisDateHint, + scopedFollowupContext + }) + ); } diff --git a/llm_normalizer/backend/src/services/assistantTurnAttemptInputBuilder.ts b/llm_normalizer/backend/src/services/assistantTurnAttemptInputBuilder.ts new file mode 100644 index 0000000..1d7e3d4 --- /dev/null +++ b/llm_normalizer/backend/src/services/assistantTurnAttemptInputBuilder.ts @@ -0,0 +1,45 @@ +import type { + RunAssistantTurnAttemptRuntimeAddressInput, + RunAssistantTurnAttemptRuntimeDeepInput, + AssistantSessionOrganizationScopeContext +} from "./assistantTurnAttemptRuntimeAdapter"; +import type { RunAssistantUserTurnBootstrapRuntimeOutput } from "./assistantUserTurnBootstrapRuntimeAdapter"; + +export interface BuildAssistantTurnAttemptAddressRuntimeInputInput { + payload: PayloadType; + userTurn: RunAssistantUserTurnBootstrapRuntimeOutput; + sessionOrganizationScope: AssistantSessionOrganizationScopeContext; +} + +export function buildAssistantTurnAttemptAddressRuntimeInput( + input: BuildAssistantTurnAttemptAddressRuntimeInputInput +): RunAssistantTurnAttemptRuntimeAddressInput { + return { + payload: input.payload, + sessionId: input.userTurn.sessionId, + userMessage: input.userTurn.userMessage, + sessionItems: input.userTurn.session.items, + runtimeAnalysisContext: input.userTurn.runtimeAnalysisContext, + sessionOrganizationScope: input.sessionOrganizationScope + }; +} + +export interface BuildAssistantTurnAttemptDeepRuntimeInputInput { + payload: PayloadType; + userTurn: RunAssistantUserTurnBootstrapRuntimeOutput; + addressRuntimeMetaForDeep: Record | null; +} + +export function buildAssistantTurnAttemptDeepRuntimeInput( + input: BuildAssistantTurnAttemptDeepRuntimeInputInput +): RunAssistantTurnAttemptRuntimeDeepInput { + return { + payload: input.payload, + sessionId: input.userTurn.sessionId, + questionId: input.userTurn.userItem.message_id, + userMessage: input.userTurn.userMessage, + runtimeAnalysisContext: input.userTurn.runtimeAnalysisContext, + sessionInvestigationState: input.userTurn.session.investigation_state, + addressRuntimeMetaForDeep: input.addressRuntimeMetaForDeep + }; +} diff --git a/llm_normalizer/backend/src/services/assistantTurnAttemptRuntimeAdapter.ts b/llm_normalizer/backend/src/services/assistantTurnAttemptRuntimeAdapter.ts index 0704f62..7752b13 100644 --- a/llm_normalizer/backend/src/services/assistantTurnAttemptRuntimeAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantTurnAttemptRuntimeAdapter.ts @@ -1,5 +1,9 @@ import type { RunAssistantAddressRuntimeOutput } from "./assistantAddressRuntimeAdapter"; import type { RunAssistantUserTurnBootstrapRuntimeOutput } from "./assistantUserTurnBootstrapRuntimeAdapter"; +import { + buildAssistantTurnAttemptAddressRuntimeInput, + buildAssistantTurnAttemptDeepRuntimeInput +} from "./assistantTurnAttemptInputBuilder"; export interface AssistantSessionOrganizationScopeContext { knownOrganizations: string[]; @@ -57,14 +61,13 @@ export async function runAssistantTurnAttemptRuntime { + it("extracts followup context object from carry meta", () => { + const followupContext = resolveAssistantAddressLaneAttemptFollowupContext({ + followupContext: { + previous_intent: "docs_by_counterparty" + } + } as any); + + expect(followupContext).toEqual({ + previous_intent: "docs_by_counterparty" + }); + }); + + it("builds query options with scoped followup context when present", () => { + const options = buildAssistantAddressLaneAttemptQueryOptions({ + analysisDateHint: "2020-07-31", + scopedFollowupContext: { + previous_intent: "docs_by_counterparty", + active_organization: "Org A" + } + }); + + expect(options).toEqual({ + followupContext: { + previous_intent: "docs_by_counterparty", + active_organization: "Org A" + }, + analysisDateHint: "2020-07-31" + }); + }); + + it("builds query options with only analysis date when scoped context is missing", () => { + const options = buildAssistantAddressLaneAttemptQueryOptions({ + analysisDateHint: null, + scopedFollowupContext: null + }); + + expect(options).toEqual({ + analysisDateHint: null + }); + }); +}); diff --git a/llm_normalizer/backend/tests/assistantTurnAttemptInputBuilder.test.ts b/llm_normalizer/backend/tests/assistantTurnAttemptInputBuilder.test.ts new file mode 100644 index 0000000..350c27e --- /dev/null +++ b/llm_normalizer/backend/tests/assistantTurnAttemptInputBuilder.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from "vitest"; +import { + buildAssistantTurnAttemptAddressRuntimeInput, + buildAssistantTurnAttemptDeepRuntimeInput +} from "../src/services/assistantTurnAttemptInputBuilder"; + +function buildUserTurn(overrides: Record = {}) { + 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" + }, + ...overrides + } as any; +} + +describe("assistant turn attempt input builder", () => { + it("builds address runtime input from user turn and organization scope", () => { + const runtimeInput = buildAssistantTurnAttemptAddressRuntimeInput({ + payload: { user_message: "where tail" }, + userTurn: buildUserTurn(), + sessionOrganizationScope: { + knownOrganizations: ["Org A"], + selectedOrganization: "Org A", + activeOrganization: "Org A" + } + }); + + expect(runtimeInput.sessionId).toBe("asst-1"); + expect(runtimeInput.userMessage).toBe("where tail"); + expect(runtimeInput.runtimeAnalysisContext).toEqual({ as_of_date: "2020-07-31" }); + expect(runtimeInput.sessionOrganizationScope).toEqual({ + knownOrganizations: ["Org A"], + selectedOrganization: "Org A", + activeOrganization: "Org A" + }); + }); + + it("builds deep runtime input with investigation state and address meta", () => { + const runtimeInput = buildAssistantTurnAttemptDeepRuntimeInput({ + payload: { user_message: "where tail" }, + userTurn: buildUserTurn(), + addressRuntimeMetaForDeep: { attempted: true } + }); + + expect(runtimeInput.sessionId).toBe("asst-1"); + expect(runtimeInput.questionId).toBe("msg-q1"); + expect(runtimeInput.userMessage).toBe("where tail"); + expect(runtimeInput.addressRuntimeMetaForDeep).toEqual({ attempted: true }); + expect(runtimeInput.sessionInvestigationState).toEqual({ focus: "settlements_60_62" }); + }); +});