ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
buildAssistantAddressAttemptRuntimeInput,
|
||||
buildAssistantDeepTurnAttemptRuntimeInput,
|
||||
buildAssistantUserTurnBootstrapRuntimeInput
|
||||
} from "../src/services/assistantTurnRuntimeInputBuilder";
|
||||
|
||||
function buildDeps(overrides: Record<string, unknown> = {}) {
|
||||
const noop = vi.fn(() => null);
|
||||
return {
|
||||
ensureSession: vi.fn(() => ({ session_id: "asst-1", items: [], investigation_state: null })),
|
||||
appendItem: vi.fn(),
|
||||
getSession: vi.fn(() => ({ session_id: "asst-1", items: [], investigation_state: null })),
|
||||
persistSession: vi.fn(),
|
||||
setInvestigationState: vi.fn(),
|
||||
normalize: vi.fn(async () => ({})),
|
||||
executeRouteRuntime: vi.fn(async () => ({})),
|
||||
tryAddressQueryHandle: vi.fn(async () => ({ response_type: "READY" })),
|
||||
chatClient: {},
|
||||
messageIdFactory: vi.fn(() => "msg-1"),
|
||||
nowIso: vi.fn(() => "2026-04-11T00:00:00.000Z"),
|
||||
defaultApiKey: "key",
|
||||
logEvent: vi.fn(),
|
||||
featureAssistantAddressQueryV1: true,
|
||||
featureAddressLlmPredecomposeV1: true,
|
||||
featureInvestigationStateV1: true,
|
||||
featureStateFollowupBindingV1: true,
|
||||
featureContractsV11: true,
|
||||
featureAnswerPolicyV11: true,
|
||||
featureProblemCentricAnswerV1: true,
|
||||
featureLifecycleAnswerV1: true,
|
||||
defaultModel: "gpt-5",
|
||||
defaultBaseUrl: "http://localhost",
|
||||
compactWhitespace: vi.fn((value: unknown) => String(value ?? "").trim()),
|
||||
repairAddressMojibake: vi.fn((value: unknown) => String(value ?? "")),
|
||||
resolveRuntimeAnalysisContext: vi.fn(() => ({ as_of_date: "2020-07-31" })),
|
||||
runAddressLlmPreDecompose: vi.fn(async () => ({})),
|
||||
buildAddressLlmPredecomposeContractV1: noop,
|
||||
sanitizeAddressMessageForFallback: noop,
|
||||
toNonEmptyString: vi.fn((value: unknown) =>
|
||||
typeof value === "string" && value.trim().length > 0 ? value.trim() : null
|
||||
),
|
||||
resolveAddressFollowupCarryoverContext: noop,
|
||||
resolveAssistantOrchestrationDecision: noop,
|
||||
buildAddressDialogContinuationContractV2: noop,
|
||||
mergeFollowupContextWithOrganizationScope: noop,
|
||||
isRetryableAddressLimitedResult: noop,
|
||||
mergeKnownOrganizations: noop,
|
||||
hasAssistantDataScopeMetaQuestionSignal: noop,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: noop,
|
||||
hasDestructiveDataActionSignal: noop,
|
||||
hasDangerOrCoercionSignal: noop,
|
||||
hasOperationalAdminActionRequestSignal: noop,
|
||||
hasOrganizationFactLookupSignal: noop,
|
||||
hasOrganizationFactFollowupSignal: noop,
|
||||
shouldEmitOrganizationSelectionReply: noop,
|
||||
hasAssistantCapabilityQuestionSignal: noop,
|
||||
resolveDataScopeProbe: vi.fn(() => null),
|
||||
applyScriptGuard: noop,
|
||||
applyGroundingGuard: noop,
|
||||
buildAssistantSafetyRefusalReply: noop,
|
||||
buildAssistantDataScopeContractReply: noop,
|
||||
buildAssistantOrganizationFactBoundaryReply: noop,
|
||||
buildAssistantDataScopeSelectionReply: noop,
|
||||
buildAssistantOperationalBoundaryReply: noop,
|
||||
buildAssistantCapabilityContractReply: noop,
|
||||
loadAssistantCanonExcerpt: noop,
|
||||
sanitizeOutgoingAssistantText: vi.fn((value: unknown, fallback = "") => {
|
||||
const text = typeof value === "string" ? value.trim() : "";
|
||||
return text || fallback;
|
||||
}),
|
||||
buildAddressDebugPayload: noop,
|
||||
buildAddressFollowupOffer: noop,
|
||||
buildFollowupStateBinding: noop,
|
||||
resolveBusinessScopeAlignment: noop,
|
||||
inferP0DomainFromMessage: noop,
|
||||
resolveBusinessScopeFromLiveContext: noop,
|
||||
extractRequirements: noop,
|
||||
toExecutionPlan: noop,
|
||||
enforceRbpLiveRoutePlan: noop,
|
||||
enforceFaLiveRoutePlan: noop,
|
||||
mapNoRouteReason: noop,
|
||||
buildSkippedResult: noop,
|
||||
evaluateCoverage: noop,
|
||||
checkGrounding: noop,
|
||||
collectRbpLiveRouteAudit: noop,
|
||||
collectFaLiveRouteAudit: noop,
|
||||
hasExplicitPeriodAnchorFromNormalized: noop,
|
||||
extractDroppedIntentSegments: noop,
|
||||
toDebugRoutes: noop,
|
||||
extractExecutionState: noop,
|
||||
...overrides
|
||||
} as any;
|
||||
}
|
||||
|
||||
describe("assistant turn runtime input builder", () => {
|
||||
it("builds bootstrap runtime input from shared deps", () => {
|
||||
const deps = buildDeps();
|
||||
const payload = {
|
||||
session_id: "asst-1",
|
||||
user_message: "hello"
|
||||
};
|
||||
|
||||
const runtimeInput = buildAssistantUserTurnBootstrapRuntimeInput(payload, deps);
|
||||
|
||||
expect(runtimeInput.payload).toBe(payload);
|
||||
expect(runtimeInput.ensureSession).toBe(deps.ensureSession);
|
||||
expect(runtimeInput.messageIdFactory?.()).toBe("msg-1");
|
||||
expect(runtimeInput.nowIso?.()).toBe("2026-04-11T00:00:00.000Z");
|
||||
});
|
||||
|
||||
it("builds address attempt input and preserves address context mapping", async () => {
|
||||
const runAddressLlmPreDecompose = vi.fn(async () => ({ mode: "supported" }));
|
||||
const deps = buildDeps({ runAddressLlmPreDecompose });
|
||||
const runtimeInput = {
|
||||
payload: { context: { period_hint: "2020-07-31" } },
|
||||
sessionId: "asst-1",
|
||||
userMessage: "где хвост",
|
||||
sessionItems: [],
|
||||
runtimeAnalysisContext: { as_of_date: "2020-07-31" },
|
||||
sessionOrganizationScope: {
|
||||
knownOrganizations: ["Org A"],
|
||||
selectedOrganization: "Org A",
|
||||
activeOrganization: "Org A"
|
||||
}
|
||||
} as any;
|
||||
|
||||
const built = buildAssistantAddressAttemptRuntimeInput(runtimeInput, deps);
|
||||
const predecompose = await built.runAddressLlmPreDecompose();
|
||||
await built.runAddressQueryTryHandle("message", { analysisDateHint: "2020-07-31" });
|
||||
|
||||
expect(predecompose).toEqual({ mode: "supported" });
|
||||
expect(runAddressLlmPreDecompose).toHaveBeenCalledWith(runtimeInput.payload, "где хвост");
|
||||
expect(built.runtimeAnalysisContextAsOfDate).toBe("2020-07-31");
|
||||
expect(built.sessionScope).toEqual(runtimeInput.sessionOrganizationScope);
|
||||
expect(deps.tryAddressQueryHandle).toHaveBeenCalledWith("message", { analysisDateHint: "2020-07-31" });
|
||||
});
|
||||
|
||||
it("builds deep attempt input with shared guards and state hooks", () => {
|
||||
const setInvestigationState = vi.fn();
|
||||
const sanitizeOutgoingAssistantText = vi.fn(() => "safe");
|
||||
const deps = buildDeps({ setInvestigationState, sanitizeOutgoingAssistantText });
|
||||
const runtimeInput = {
|
||||
payload: { useMock: true },
|
||||
sessionId: "asst-1",
|
||||
questionId: "msg-q1",
|
||||
userMessage: "почему долг не закрыт",
|
||||
runtimeAnalysisContext: { as_of_date: "2020-07-31" },
|
||||
sessionInvestigationState: { scope: "settlements_60_62" },
|
||||
addressRuntimeMetaForDeep: { attempted: true }
|
||||
} as any;
|
||||
|
||||
const built = buildAssistantDeepTurnAttemptRuntimeInput(runtimeInput, deps);
|
||||
const sanitized = built.sanitizeReply(" raw ", "fallback");
|
||||
built.persistInvestigationState("asst-1", { scope: "next" });
|
||||
|
||||
expect(sanitized).toBe("safe");
|
||||
expect(sanitizeOutgoingAssistantText).toHaveBeenCalledWith(" raw ", "fallback");
|
||||
expect(setInvestigationState).toHaveBeenCalledWith("asst-1", { scope: "next" });
|
||||
expect(built.sessionId).toBe("asst-1");
|
||||
expect(built.questionId).toBe("msg-q1");
|
||||
expect(built.addressRuntimeMetaForDeep).toEqual({ attempted: true });
|
||||
expect(built.featureContractsV11).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user