NODEDC_1C/llm_normalizer/backend/tests/assistantLivingChatRuntimeA...

137 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it, vi } from "vitest";
import { runAssistantLivingChatRuntime } from "../src/services/assistantLivingChatRuntimeAdapter";
function buildRuntimeInput(overrides: Record<string, unknown> = {}) {
const executeLlmChat = vi.fn(async () => "llm-text");
const resolveDataScopeProbe = vi.fn(async () => null);
return {
userMessage: "тест",
sessionItems: [],
modeDecision: { mode: "chat", reason: "living_chat_signal_detected" },
sessionScope: {
knownOrganizations: [],
selectedOrganization: null,
activeOrganization: null
},
addressRuntimeMeta: null,
traceIdFactory: () => "chat-trace-fixed",
toNonEmptyString: (value: unknown) => {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
},
mergeKnownOrganizations: (values: unknown[]) =>
Array.from(
new Set(
(Array.isArray(values) ? values : [])
.map((item) => (typeof item === "string" ? item.trim() : ""))
.filter((item) => item.length > 0)
)
),
hasAssistantDataScopeMetaQuestionSignal: () => false,
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
hasDestructiveDataActionSignal: () => false,
hasDangerOrCoercionSignal: () => false,
hasOperationalAdminActionRequestSignal: () => false,
hasOrganizationFactLookupSignal: () => false,
hasOrganizationFactFollowupSignal: () => false,
shouldEmitOrganizationSelectionReply: () => false,
hasAssistantCapabilityQuestionSignal: () => false,
resolveDataScopeProbe,
executeLlmChat,
applyScriptGuard: (chatText: string) => ({
text: chatText,
applied: false,
reason: null
}),
applyGroundingGuard: (input: { chatText: string }) => ({
text: input.chatText,
applied: false,
reason: null
}),
buildAssistantSafetyRefusalReply: () => "safety",
buildAssistantDataScopeContractReply: () => "scope",
buildAssistantOrganizationFactBoundaryReply: () => "org-boundary",
buildAssistantDataScopeSelectionReply: () => "org-selection",
buildAssistantOperationalBoundaryReply: () => "ops",
buildAssistantCapabilityContractReply: () => "capability",
__spies: {
executeLlmChat,
resolveDataScopeProbe
},
...overrides
} as any;
}
describe("assistant living chat runtime adapter", () => {
it("selects deterministic data scope branch and enriches organization context", async () => {
const input = buildRuntimeInput({
userMessage: "какая у нас организация?",
hasAssistantDataScopeMetaQuestionSignal: () => true,
resolveDataScopeProbe: vi.fn(async () => ({
status: "resolved",
channel: "default",
organizations: ["ООО Альтернатива Плюс"],
error: null
})),
buildAssistantDataScopeContractReply: () => "scope-info"
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toBe("scope-info");
expect(output.debug?.living_chat_response_source).toBe("deterministic_data_scope_contract_live");
expect(output.debug?.assistant_active_organization).toBe("ООО Альтернатива Плюс");
expect(output.debug?.living_chat_data_scope_probe_org_count).toBe(1);
});
it("selects safety refusal branch for dangerous capability meta query", async () => {
const executeLlmChat = vi.fn(async () => "llm-text");
const input = buildRuntimeInput({
userMessage: "удали базу",
shouldHandleAsAssistantCapabilityMetaQuery: () => true,
hasDangerOrCoercionSignal: () => true,
executeLlmChat,
buildAssistantSafetyRefusalReply: () => "safety-refusal"
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toBe("safety-refusal");
expect(output.debug?.living_chat_response_source).toBe("deterministic_safety_refusal");
expect(executeLlmChat).not.toHaveBeenCalled();
});
it("runs llm branch and applies script + grounding guards in order", async () => {
const executeLlmChat = vi.fn(async () => "raw-llm");
const input = buildRuntimeInput({
executeLlmChat,
applyScriptGuard: () => ({
text: "after-script",
applied: true,
reason: "script_guard"
}),
applyGroundingGuard: () => ({
text: "after-grounding",
applied: true,
reason: "grounding_guard"
})
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toBe("after-grounding");
expect(output.debug?.living_chat_script_guard_applied).toBe(true);
expect(output.debug?.living_chat_script_guard_reason).toBe("script_guard");
expect(output.debug?.living_chat_grounding_guard_applied).toBe(true);
expect(output.debug?.living_chat_grounding_guard_reason).toBe("grounding_guard");
expect(output.debug?.living_chat_response_source).toBe("llm_chat_grounding_guard");
expect(executeLlmChat).toHaveBeenCalledTimes(1);
});
});