ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.42: вынос finalizeAddressLaneResponse из assistantService в отдельный attempt-bridge (как для living chat), для уменьшения монолита без изменения поведения.
This commit is contained in:
parent
fbf2d6a19a
commit
875f3bfbcd
|
|
@ -1316,7 +1316,35 @@ 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 completed)**
|
Implemented in current pass (Phase 2.42):
|
||||||
|
1. Extracted address-lane response attempt bridge (`finalizeAddressLaneResponse`) from `assistantService` into dedicated runtime adapter:
|
||||||
|
- `assistantAddressLaneResponseAttemptRuntimeAdapter.ts`
|
||||||
|
- introduced:
|
||||||
|
- `runAssistantAddressLaneResponseAttemptRuntime(...)`
|
||||||
|
2. Centralized address-lane response handoff logic (behavior-preserving):
|
||||||
|
- delegated response runtime invocation (`runAssistantAddressLaneResponseRuntime(...)`);
|
||||||
|
- preserved followup-offer/debug payload and session finalization contract wiring.
|
||||||
|
3. Rewired `assistantService` to consume address-lane response attempt runtime adapter.
|
||||||
|
4. Added focused unit tests:
|
||||||
|
- `assistantAddressLaneResponseAttemptRuntimeAdapter.test.ts`
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
1. `npm run build` passed.
|
||||||
|
2. Targeted living/address/deep followup pack passed:
|
||||||
|
- `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 completed)**
|
||||||
|
|
||||||
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
## Stage 3 (P2): Hybrid Semantic Layer (LLM + Deterministic Guards)
|
||||||
|
|
||||||
|
|
|
||||||
29
llm_normalizer/backend/dist/services/assistantAddressLaneResponseAttemptRuntimeAdapter.js
vendored
Normal file
29
llm_normalizer/backend/dist/services/assistantAddressLaneResponseAttemptRuntimeAdapter.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.runAssistantAddressLaneResponseAttemptRuntime = runAssistantAddressLaneResponseAttemptRuntime;
|
||||||
|
const assistantAddressLaneResponseRuntimeAdapter_1 = require("./assistantAddressLaneResponseRuntimeAdapter");
|
||||||
|
function runAssistantAddressLaneResponseAttemptRuntime(input) {
|
||||||
|
const runAddressLaneResponseRuntimeSafe = input.runAddressLaneResponseRuntime ?? assistantAddressLaneResponseRuntimeAdapter_1.runAssistantAddressLaneResponseRuntime;
|
||||||
|
const runtime = runAddressLaneResponseRuntimeSafe({
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
effectiveAddressUserMessage: input.effectiveAddressUserMessage,
|
||||||
|
addressLane: input.addressLane,
|
||||||
|
carryoverMeta: input.carryoverMeta,
|
||||||
|
llmPreDecomposeMeta: input.llmPreDecomposeMeta,
|
||||||
|
knownOrganizations: input.knownOrganizations,
|
||||||
|
activeOrganization: input.activeOrganization,
|
||||||
|
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
||||||
|
buildAddressDebugPayload: input.buildAddressDebugPayload,
|
||||||
|
buildAddressFollowupOffer: input.buildAddressFollowupOffer,
|
||||||
|
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||||
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
|
appendItem: input.appendItem,
|
||||||
|
getSession: input.getSession,
|
||||||
|
persistSession: input.persistSession,
|
||||||
|
cloneConversation: input.cloneConversation,
|
||||||
|
logEvent: input.logEvent,
|
||||||
|
messageIdFactory: input.messageIdFactory
|
||||||
|
});
|
||||||
|
return runtime.response;
|
||||||
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ const openaiResponsesClient_1 = __importStar(require("./openaiResponsesClient"))
|
||||||
const addressMcpClient_1 = __importStar(require("./addressMcpClient"));
|
const addressMcpClient_1 = __importStar(require("./addressMcpClient"));
|
||||||
const capabilitiesRegistry_1 = __importStar(require("./capabilitiesRegistry"));
|
const capabilitiesRegistry_1 = __importStar(require("./capabilitiesRegistry"));
|
||||||
const assistantCanon_1 = __importStar(require("./assistantCanon"));
|
const assistantCanon_1 = __importStar(require("./assistantCanon"));
|
||||||
const assistantAddressLaneResponseRuntimeAdapter_1 = __importStar(require("./assistantAddressLaneResponseRuntimeAdapter"));
|
const assistantAddressLaneResponseAttemptRuntimeAdapter_1 = __importStar(require("./assistantAddressLaneResponseAttemptRuntimeAdapter"));
|
||||||
const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding"));
|
const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding"));
|
||||||
const assistantDeepTurnAnalysisRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAnalysisRuntimeAdapter"));
|
const assistantDeepTurnAnalysisRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAnalysisRuntimeAdapter"));
|
||||||
const assistantDeepTurnCompositionRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnCompositionRuntimeAdapter"));
|
const assistantDeepTurnCompositionRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnCompositionRuntimeAdapter"));
|
||||||
|
|
@ -4398,30 +4398,27 @@ class AssistantService {
|
||||||
nowIso: () => new Date().toISOString()
|
nowIso: () => new Date().toISOString()
|
||||||
});
|
});
|
||||||
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
||||||
const finalizeAddressLaneResponse = (addressLane, effectiveAddressUserMessage, carryoverMeta = null, llmPreDecomposeMeta = null) => {
|
const finalizeAddressLaneResponse = (addressLane, effectiveAddressUserMessage, carryoverMeta = null, llmPreDecomposeMeta = null) => (0, assistantAddressLaneResponseAttemptRuntimeAdapter_1.runAssistantAddressLaneResponseAttemptRuntime)({
|
||||||
const runtime = (0, assistantAddressLaneResponseRuntimeAdapter_1.runAssistantAddressLaneResponseRuntime)({
|
sessionId,
|
||||||
sessionId,
|
userMessage,
|
||||||
userMessage,
|
effectiveAddressUserMessage,
|
||||||
effectiveAddressUserMessage,
|
addressLane,
|
||||||
addressLane,
|
carryoverMeta,
|
||||||
carryoverMeta,
|
llmPreDecomposeMeta,
|
||||||
llmPreDecomposeMeta,
|
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
||||||
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
activeOrganization: sessionOrganizationScope.activeOrganization,
|
||||||
activeOrganization: sessionOrganizationScope.activeOrganization,
|
sanitizeOutgoingAssistantText,
|
||||||
sanitizeOutgoingAssistantText,
|
buildAddressDebugPayload,
|
||||||
buildAddressDebugPayload,
|
buildAddressFollowupOffer,
|
||||||
buildAddressFollowupOffer,
|
mergeKnownOrganizations,
|
||||||
mergeKnownOrganizations,
|
toNonEmptyString,
|
||||||
toNonEmptyString,
|
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
||||||
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
|
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
||||||
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
|
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
||||||
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
|
cloneConversation: (items) => cloneItems(items),
|
||||||
cloneConversation: (items) => cloneItems(items),
|
logEvent: (payload) => (0, log_1.logJson)(payload),
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload),
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`
|
});
|
||||||
});
|
|
||||||
return runtime.response;
|
|
||||||
};
|
|
||||||
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => (0, assistantLivingChatAttemptRuntimeAdapter_1.runAssistantLivingChatAttemptRuntime)({
|
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => (0, assistantLivingChatAttemptRuntimeAdapter_1.runAssistantLivingChatAttemptRuntime)({
|
||||||
sessionId,
|
sessionId,
|
||||||
userMessage,
|
userMessage,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import type { AssistantMessageResponsePayload } from "../types/assistant";
|
||||||
|
import {
|
||||||
|
runAssistantAddressLaneResponseRuntime,
|
||||||
|
type RunAssistantAddressLaneResponseRuntimeInput,
|
||||||
|
type RunAssistantAddressLaneResponseRuntimeOutput
|
||||||
|
} from "./assistantAddressLaneResponseRuntimeAdapter";
|
||||||
|
|
||||||
|
export interface RunAssistantAddressLaneResponseAttemptRuntimeInput<
|
||||||
|
ResponseType = AssistantMessageResponsePayload
|
||||||
|
> extends RunAssistantAddressLaneResponseRuntimeInput<ResponseType> {
|
||||||
|
runAddressLaneResponseRuntime?: (
|
||||||
|
input: RunAssistantAddressLaneResponseRuntimeInput<ResponseType>
|
||||||
|
) => RunAssistantAddressLaneResponseRuntimeOutput<ResponseType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runAssistantAddressLaneResponseAttemptRuntime<
|
||||||
|
ResponseType = AssistantMessageResponsePayload
|
||||||
|
>(
|
||||||
|
input: RunAssistantAddressLaneResponseAttemptRuntimeInput<ResponseType>
|
||||||
|
): ResponseType {
|
||||||
|
const runAddressLaneResponseRuntimeSafe =
|
||||||
|
input.runAddressLaneResponseRuntime ?? runAssistantAddressLaneResponseRuntime;
|
||||||
|
const runtime = runAddressLaneResponseRuntimeSafe({
|
||||||
|
sessionId: input.sessionId,
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
effectiveAddressUserMessage: input.effectiveAddressUserMessage,
|
||||||
|
addressLane: input.addressLane,
|
||||||
|
carryoverMeta: input.carryoverMeta,
|
||||||
|
llmPreDecomposeMeta: input.llmPreDecomposeMeta,
|
||||||
|
knownOrganizations: input.knownOrganizations,
|
||||||
|
activeOrganization: input.activeOrganization,
|
||||||
|
sanitizeOutgoingAssistantText: input.sanitizeOutgoingAssistantText,
|
||||||
|
buildAddressDebugPayload: input.buildAddressDebugPayload,
|
||||||
|
buildAddressFollowupOffer: input.buildAddressFollowupOffer,
|
||||||
|
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||||
|
toNonEmptyString: input.toNonEmptyString,
|
||||||
|
appendItem: input.appendItem,
|
||||||
|
getSession: input.getSession,
|
||||||
|
persistSession: input.persistSession,
|
||||||
|
cloneConversation: input.cloneConversation,
|
||||||
|
logEvent: input.logEvent,
|
||||||
|
messageIdFactory: input.messageIdFactory
|
||||||
|
});
|
||||||
|
return runtime.response;
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ import * as openaiResponsesClient_1 from "./openaiResponsesClient";
|
||||||
import * as addressMcpClient_1 from "./addressMcpClient";
|
import * as addressMcpClient_1 from "./addressMcpClient";
|
||||||
import * as capabilitiesRegistry_1 from "./capabilitiesRegistry";
|
import * as capabilitiesRegistry_1 from "./capabilitiesRegistry";
|
||||||
import * as assistantCanon_1 from "./assistantCanon";
|
import * as assistantCanon_1 from "./assistantCanon";
|
||||||
import * as assistantAddressLaneResponseRuntimeAdapter_1 from "./assistantAddressLaneResponseRuntimeAdapter";
|
import * as assistantAddressLaneResponseAttemptRuntimeAdapter_1 from "./assistantAddressLaneResponseAttemptRuntimeAdapter";
|
||||||
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
||||||
import * as assistantDeepTurnAnalysisRuntimeAdapter_1 from "./assistantDeepTurnAnalysisRuntimeAdapter";
|
import * as assistantDeepTurnAnalysisRuntimeAdapter_1 from "./assistantDeepTurnAnalysisRuntimeAdapter";
|
||||||
import * as assistantDeepTurnCompositionRuntimeAdapter_1 from "./assistantDeepTurnCompositionRuntimeAdapter";
|
import * as assistantDeepTurnCompositionRuntimeAdapter_1 from "./assistantDeepTurnCompositionRuntimeAdapter";
|
||||||
|
|
@ -4353,8 +4353,7 @@ export class AssistantService {
|
||||||
nowIso: () => new Date().toISOString()
|
nowIso: () => new Date().toISOString()
|
||||||
});
|
});
|
||||||
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
const sessionOrganizationScope = resolveSessionOrganizationScopeContext(userMessage, session.items);
|
||||||
const finalizeAddressLaneResponse = (addressLane, effectiveAddressUserMessage, carryoverMeta = null, llmPreDecomposeMeta = null) => {
|
const finalizeAddressLaneResponse = (addressLane, effectiveAddressUserMessage, carryoverMeta = null, llmPreDecomposeMeta = null) => (0, assistantAddressLaneResponseAttemptRuntimeAdapter_1.runAssistantAddressLaneResponseAttemptRuntime)({
|
||||||
const runtime = (0, assistantAddressLaneResponseRuntimeAdapter_1.runAssistantAddressLaneResponseRuntime)({
|
|
||||||
sessionId,
|
sessionId,
|
||||||
userMessage,
|
userMessage,
|
||||||
effectiveAddressUserMessage,
|
effectiveAddressUserMessage,
|
||||||
|
|
@ -4375,8 +4374,6 @@ export class AssistantService {
|
||||||
logEvent: (payload) => (0, log_1.logJson)(payload),
|
logEvent: (payload) => (0, log_1.logJson)(payload),
|
||||||
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`
|
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`
|
||||||
});
|
});
|
||||||
return runtime.response;
|
|
||||||
};
|
|
||||||
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => (0, assistantLivingChatAttemptRuntimeAdapter_1.runAssistantLivingChatAttemptRuntime)({
|
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => (0, assistantLivingChatAttemptRuntimeAdapter_1.runAssistantLivingChatAttemptRuntime)({
|
||||||
sessionId,
|
sessionId,
|
||||||
userMessage,
|
userMessage,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { runAssistantAddressLaneResponseAttemptRuntime } from "../src/services/assistantAddressLaneResponseAttemptRuntimeAdapter";
|
||||||
|
|
||||||
|
function buildInput(overrides: Record<string, unknown> = {}) {
|
||||||
|
return {
|
||||||
|
sessionId: "asst-1",
|
||||||
|
userMessage: "где хвост по оплате",
|
||||||
|
effectiveAddressUserMessage: "где хвост по оплате",
|
||||||
|
addressLane: {
|
||||||
|
reply_text: "address reply",
|
||||||
|
reply_type: "factual_with_explanation",
|
||||||
|
debug: { extracted_filters: {} }
|
||||||
|
},
|
||||||
|
carryoverMeta: null,
|
||||||
|
llmPreDecomposeMeta: null,
|
||||||
|
knownOrganizations: [],
|
||||||
|
activeOrganization: null,
|
||||||
|
sanitizeOutgoingAssistantText: (value: unknown, fallback = "") => {
|
||||||
|
const text = String(value ?? "").trim();
|
||||||
|
return text || fallback;
|
||||||
|
},
|
||||||
|
buildAddressDebugPayload: () => ({}),
|
||||||
|
buildAddressFollowupOffer: () => null,
|
||||||
|
mergeKnownOrganizations: (value: string[]) => value,
|
||||||
|
toNonEmptyString: (value: unknown) => (typeof value === "string" && value.trim() ? value.trim() : null),
|
||||||
|
appendItem: () => {},
|
||||||
|
getSession: () => ({
|
||||||
|
session_id: "asst-1",
|
||||||
|
updated_at: "",
|
||||||
|
items: [],
|
||||||
|
investigation_state: null
|
||||||
|
}),
|
||||||
|
persistSession: () => {},
|
||||||
|
cloneConversation: (items: unknown[]) => items,
|
||||||
|
logEvent: () => {},
|
||||||
|
messageIdFactory: () => "msg-1",
|
||||||
|
...overrides
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("assistant address lane response attempt runtime adapter", () => {
|
||||||
|
it("returns delegated runtime response", () => {
|
||||||
|
const runAddressLaneResponseRuntime = vi.fn(() => ({
|
||||||
|
response: { ok: true, lane: "address" },
|
||||||
|
debug: { marker: "v1" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
const response = runAssistantAddressLaneResponseAttemptRuntime(
|
||||||
|
buildInput({ runAddressLaneResponseRuntime })
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response).toEqual({ ok: true, lane: "address" });
|
||||||
|
expect(runAddressLaneResponseRuntime).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
sessionId: "asst-1",
|
||||||
|
userMessage: "где хвост по оплате"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("forwards carryover and llm predecompose metadata", () => {
|
||||||
|
const carryoverMeta = { previousReplyType: "partial_coverage" };
|
||||||
|
const llmPreDecomposeMeta = { mode: "supported", confidence: "high" };
|
||||||
|
const runAddressLaneResponseRuntime = vi.fn(() => ({
|
||||||
|
response: { ok: true },
|
||||||
|
debug: {}
|
||||||
|
}));
|
||||||
|
|
||||||
|
runAssistantAddressLaneResponseAttemptRuntime(
|
||||||
|
buildInput({
|
||||||
|
carryoverMeta,
|
||||||
|
llmPreDecomposeMeta,
|
||||||
|
runAddressLaneResponseRuntime
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(runAddressLaneResponseRuntime).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
carryoverMeta,
|
||||||
|
llmPreDecomposeMeta
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue