NODEDC_1C/llm_normalizer/backend/tests/assistantAddressAttemptRunt...

197 lines
6.8 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { runAssistantAddressAttemptRuntime } from "../src/services/assistantAddressAttemptRuntimeAdapter";
function buildInput(overrides: Record<string, unknown> = {}) {
return {
featureAssistantAddressQueryV1: true,
sessionId: "asst-1",
userMessage: "where are overdue docs",
sessionItems: [],
payload: {
llmProvider: "openai",
useMock: 1,
context: {
period_hint: "2020-07-31"
},
apiKey: "key",
model: "gpt-5",
baseUrl: "http://localhost"
},
sessionScope: {
knownOrganizations: ["Org A"],
selectedOrganization: "Org A",
activeOrganization: "Org A"
},
featureAddressLlmPredecomposeV1: true,
runAddressLlmPreDecompose: async () => ({}),
buildAddressLlmPredecomposeContractV1: () => ({}),
sanitizeAddressMessageForFallback: (value: string) => value,
toNonEmptyString: (value: unknown) =>
typeof value === "string" && value.trim().length > 0 ? value.trim() : null,
resolveAddressFollowupCarryoverContext: () => null,
resolveAssistantOrchestrationDecision: () => ({ mode: "address_query", runAddressLane: true }),
buildAddressDialogContinuationContractV2: () => ({}),
runtimeAnalysisContextAsOfDate: "2020-07-31",
compactWhitespace: (value: string) => String(value ?? "").replace(/\s+/g, " ").trim(),
mergeFollowupContextWithOrganizationScope: (followupContext: Record<string, unknown> | null) =>
followupContext,
runAddressQueryTryHandle: async () => ({ response_type: "READY" }),
isRetryableAddressLimitedResult: () => false,
mergeKnownOrganizations: (knownOrganizations: string[], selectedOrganization: string | null) => ({
knownOrganizations,
selectedOrganization
}),
hasAssistantDataScopeMetaQuestionSignal: () => false,
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
hasDestructiveDataActionSignal: () => false,
hasDangerOrCoercionSignal: () => false,
hasOperationalAdminActionRequestSignal: () => false,
hasOrganizationFactLookupSignal: () => false,
hasOrganizationFactFollowupSignal: () => false,
shouldEmitOrganizationSelectionReply: () => false,
hasAssistantCapabilityQuestionSignal: () => false,
resolveDataScopeProbe: () => null,
applyScriptGuard: (chatText: string) => chatText,
applyGroundingGuard: (guardInput: Record<string, unknown>) => guardInput,
buildAssistantSafetyRefusalReply: () => "safety",
buildAssistantDataScopeContractReply: () => "scope",
buildAssistantOrganizationFactBoundaryReply: () => "boundary",
buildAssistantDataScopeSelectionReply: () => "selection",
buildAssistantOperationalBoundaryReply: () => "operational",
buildAssistantCapabilityContractReply: () => "capability",
chatClient: {} as any,
loadAssistantCanonExcerpt: () => "",
sanitizeOutgoingAssistantText: (value: unknown, fallback = "") => {
const text = typeof value === "string" ? value.trim() : "";
return text || fallback;
},
defaultModel: "gpt-5",
defaultBaseUrl: "http://localhost",
defaultApiKey: "key",
buildAddressDebugPayload: () => ({}),
buildAddressFollowupOffer: () => null,
appendItem: () => {},
getSession: () => ({
session_id: "asst-1",
updated_at: "",
items: [],
investigation_state: null
}),
persistSession: () => {},
cloneConversation: (items: unknown[]) => items,
logEvent: () => {},
messageIdFactory: () => "msg-111",
nowIso: () => "2026-01-01T00:00:00.000Z",
...overrides
} as any;
}
describe("assistant address attempt runtime adapter", () => {
it("wires lane, response and living-chat attempt runtimes through one boundary", async () => {
const runAddressLaneAttemptRuntime = vi.fn(async () => ({
response_type: "READY"
}));
const runAddressLaneResponseAttemptRuntime = vi.fn(() => ({
kind: "address"
}));
const runLivingChatAttemptRuntime = vi.fn(async () => ({
kind: "chat"
}));
const runAddressRuntime = vi.fn(async (input: any) => {
const laneResult = await input.runAddressLaneAttempt(
"lane-message",
{ followupContext: { previous_intent: "docs_by_counterparty" } },
"2020-08-31"
);
expect(laneResult).toEqual({ response_type: "READY" });
const livingChatResult = await input.tryHandleLivingChat(
{ mode: "chat", reason: "living_chat_signal_detected" },
{ source: "address_runtime" }
);
expect(livingChatResult).toEqual({ kind: "chat" });
const response = input.finalizeAddressLaneResponse(
{ reply_text: "address reply", reply_type: "factual_with_explanation" },
"lane-message",
{ previousReplyType: "partial_coverage" },
{ mode: "supported", confidence: "high" }
);
expect(response).toEqual({ kind: "address" });
return {
handled: true,
response: { ok: true, lane: "address" },
addressRuntimeMetaForDeep: { source: "address_runtime" }
};
});
const runtime = await runAssistantAddressAttemptRuntime(
buildInput({
runAddressRuntime,
runAddressLaneAttemptRuntime,
runAddressLaneResponseAttemptRuntime,
runLivingChatAttemptRuntime
})
);
expect(runtime).toEqual({
handled: true,
response: { ok: true, lane: "address" },
addressRuntimeMetaForDeep: { source: "address_runtime" }
});
expect(runAddressRuntime).toHaveBeenCalledWith(
expect.objectContaining({
llmProvider: "openai",
useMock: true,
payloadContextPeriodHint: "2020-07-31"
})
);
expect(runAddressLaneAttemptRuntime).toHaveBeenCalledWith(
expect.objectContaining({
messageUsed: "lane-message",
analysisDateHint: "2020-08-31",
activeOrganization: "Org A"
})
);
expect(runLivingChatAttemptRuntime).toHaveBeenCalledWith(
expect.objectContaining({
sessionId: "asst-1",
sessionScope: expect.objectContaining({
selectedOrganization: "Org A"
})
})
);
expect(runAddressLaneResponseAttemptRuntime).toHaveBeenCalledWith(
expect.objectContaining({
sessionId: "asst-1",
effectiveAddressUserMessage: "lane-message",
knownOrganizations: ["Org A"]
})
);
});
it("passes empty payload fields to address runtime without breaking defaults", async () => {
const runAddressRuntime = vi.fn(async () => ({
handled: false,
response: null,
addressRuntimeMetaForDeep: null
}));
await runAssistantAddressAttemptRuntime(
buildInput({
payload: {},
runAddressRuntime
})
);
expect(runAddressRuntime).toHaveBeenCalledWith(
expect.objectContaining({
llmProvider: undefined,
useMock: false,
payloadContextPeriodHint: undefined
})
);
});
});