NODEDC_1C/llm_normalizer/backend/tests/assistantBoundaryPolicy.tes...

105 lines
4.0 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 } from "vitest";
import { createAssistantBoundaryPolicy } from "../src/services/assistantBoundaryPolicy";
function createPolicy() {
return createAssistantBoundaryPolicy({
activeMcpChannel: "default",
normalizeOrganizationScopeValue: (value: unknown) => {
if (value === null || value === undefined) {
return null;
}
const text = String(value ?? "")
.replace(/\\/g, "")
.replace(/([А-Яа-яA-Za-z])"([А-Яа-яA-Za-z])/gu, "$1в$2")
.trim();
return text.length > 0 ? text : null;
},
toNonEmptyString: (value: unknown) => {
if (value === null || value === undefined) {
return null;
}
const text = String(value ?? "").trim();
return text.length > 0 ? text : null;
},
hasOrganizationFactLookupSignal: (message: string) => /возраст|дата регистрации/i.test(message)
});
}
describe("assistantBoundaryPolicy", () => {
it("builds deterministic data-scope reply for single organization", () => {
const policy = createPolicy();
const reply = policy.buildAssistantDataScopeContractReply({
status: "resolved",
channel: "finance",
organizations: ["ООО Альтернатива Плюс"]
});
expect(reply).toContain("Сейчас доступна организация");
expect(reply).toContain("ООО Альтернатива Плюс");
expect(reply).not.toContain("MCP");
expect(reply.toLowerCase()).not.toContain("read-only");
});
it("normalizes noisy organization labels in data-scope reply", () => {
const policy = createPolicy();
const reply = policy.buildAssistantDataScopeContractReply({
status: "resolved",
channel: "default",
organizations: ['ООО \\Альтернати"а Плюс\\', 'ООО \\Лайс"уд\\']
});
expect(reply).toContain('ООО Альтернатива Плюс');
expect(reply).toContain('ООО Лайсвуд');
expect(reply).not.toContain('\\"');
expect(reply).not.toContain("\\");
});
it("builds proactive organization offer without technical labels", () => {
const policy = createPolicy();
const reply = policy.buildAssistantProactiveOrganizationOfferReply({
status: "resolved",
channel: "default",
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"]
});
expect(reply).toContain("Если дальше пойдём в данные 1С");
expect(reply).toContain("ООО Альтернатива Плюс");
expect(reply).toContain("ООО Лайсвуд");
expect(reply).toContain("РАЙМ");
expect(reply).not.toContain("MCP");
expect(reply.toLowerCase()).not.toContain("snapshot");
});
it("strips unexpected CJK fragments from live chat reply", () => {
const policy = createPolicy();
const guarded = policy.applyLivingChatScriptGuard(
"Прошу прощения, но я не могу продолжать этот разговор. 随时关注。",
"че как"
);
expect(guarded.applied).toBe(true);
expect(guarded.reason).toBe("unexpected_cjk_fragment_stripped");
expect(guarded.text).toContain("Прошу прощения");
expect(/[\u3400-\u9FFF\uF900-\uFAFF]/u.test(guarded.text)).toBe(false);
});
it("blocks ungrounded organization fact answer with deterministic boundary reply", () => {
const policy = createPolicy();
const guarded = policy.applyLivingChatGroundingGuard({
userMessage: "какой возраст у альтернативы?",
chatText: "Для ООО Альтернатива Плюс дата регистрации 01.07.2015, возраст 8 лет.",
organization: "ООО Альтернатива Плюс"
});
expect(guarded.applied).toBe(true);
expect(guarded.reason).toBe("organization_fact_without_live_source_blocked");
expect(guarded.text.toLowerCase()).toContain("не буду называть");
expect(guarded.text).not.toContain("01.07.2015");
});
});