NODEDC_1C/llm_normalizer/backend/tests/assistantTurnMeaningPolicy....

192 lines
9.4 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 { createAssistantTurnMeaningPolicy } from "../src/services/assistantTurnMeaningPolicy";
import { resolveAddressIntent } from "../src/services/addressIntentResolver";
function buildPolicy(overrides: Record<string, unknown> = {}) {
return createAssistantTurnMeaningPolicy({
compactWhitespace: (value: string) => String(value ?? "").replace(/\s+/g, " ").trim(),
repairAddressMojibake: (value: string) => value,
resolveAddressIntent,
toNonEmptyString: (value: unknown) => {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
},
...overrides
});
}
describe("assistantTurnMeaningPolicy", () => {
it("recovers a supported receivables intent from light current-turn typo noise", () => {
const policy = buildPolicy();
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f"
});
expect(meaning.schema_version).toBe("assistant_turn_meaning_v1");
expect(meaning.explicit_intent_candidate).toBe("receivables_confirmed_as_of_date");
expect(meaning.asked_domain_family).toBe("receivables");
expect(meaning.carryover_budget).toBe("matching_family_only");
expect(meaning.stale_replay_forbidden).toBe(false);
});
it("promotes specific counterparty turnover to the supported revenue intent", () => {
const policy = buildPolicy();
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a"
});
expect(meaning.explicit_intent_candidate).toBe("customer_revenue_and_payments");
expect(meaning.asked_domain_family).toBe("counterparty");
expect(meaning.asked_action_family).toBe("counterparty_value_or_turnover");
expect(meaning.unsupported_but_understood_family).toBeNull();
expect(meaning.stale_replay_forbidden).toBe(false);
expect(meaning.carryover_budget).toBe("matching_family_only");
expect(meaning.explicit_entity_candidates).toEqual([
{
type: "counterparty",
value: "\u0441\u0432\u043a",
source: "current_turn_loose_entity_tail"
}
]);
});
it("ignores temporal tail words in all-time revenue ranking questions", () => {
const policy = buildPolicy({
resolveAddressIntent: (text: string) =>
text.includes("\u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0439")
? { intent: "customer_revenue_and_payments", confidence: "high" }
: resolveAddressIntent(text)
});
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0442\u043e \u0443 \u043d\u0430\u0441 \u0441\u0430\u043c\u044b\u0439 \u0434\u043e\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 \u0437\u0430 \u0432\u0441\u0435 \u0432\u0440\u0435\u043c\u044f"
});
expect(meaning.explicit_intent_candidate).toBe("customer_revenue_and_payments");
expect(meaning.explicit_entity_candidates).toEqual([]);
expect(meaning.stale_replay_forbidden).toBe(false);
});
it("treats VAT period questions as supported current-turn intent", () => {
const policy = buildPolicy({
resolveAddressIntent: (text: string) =>
text.includes("\u043d\u0434\u0441")
? { intent: "vat_liability_confirmed_for_tax_period", confidence: "high" }
: resolveAddressIntent(text)
});
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u0430 \u043a\u0430\u043a\u043e\u0439 \u043d\u0434\u0441 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0442\u044c \u0437\u0430 \u044d\u0442\u043e\u0442 \u043f\u0435\u0440\u0438\u043e\u0434"
});
expect(meaning.explicit_intent_candidate).toBe("vat_liability_confirmed_for_tax_period");
expect(meaning.asked_domain_family).toBe("vat");
expect(meaning.asked_action_family).toBe("confirmed_tax_period");
expect(meaning.stale_replay_forbidden).toBe(false);
});
it("marks broad business evaluation as unsupported-but-understood instead of stale lifecycle replay", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "counterparty_activity_lifecycle", confidence: "high" })
});
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage: "Как ты оценишь деятельность компании?"
});
expect(meaning.explicit_intent_candidate).toBeNull();
expect(meaning.asked_domain_family).toBe("business_summary");
expect(meaning.asked_action_family).toBe("broad_evaluation");
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(meaning.stale_replay_forbidden).toBe(true);
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
});
it("recognizes broad company analysis wording as the same bounded business overview ask", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "unknown", confidence: "low" })
});
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage: "Дай полный анализ компании и LLM-аудит бизнеса"
});
expect(meaning.explicit_intent_candidate).toBeNull();
expect(meaning.asked_domain_family).toBe("business_summary");
expect(meaning.asked_action_family).toBe("broad_evaluation");
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
});
it("lets explicit business overview wording beat turnover and net-flow cues", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
});
const meaning = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u0414\u0430\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u043d\u0435\u0442\u0442\u043e, \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c."
});
expect(meaning.explicit_intent_candidate).toBeNull();
expect(meaning.asked_domain_family).toBe("business_summary");
expect(meaning.asked_action_family).toBe("broad_evaluation");
expect(meaning.explicit_entity_candidates).toEqual([]);
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(meaning.stale_replay_forbidden).toBe(true);
expect(meaning.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
});
it("treats organization-level earnings and best-year wording as business overview", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
});
const earnings = policy.resolveAssistantTurnMeaning({
rawUserMessage: "сколько вообще денег мы заработали за все время?"
});
expect(earnings.explicit_intent_candidate).toBeNull();
expect(earnings.asked_domain_family).toBe("business_summary");
expect(earnings.asked_action_family).toBe("broad_evaluation");
expect(earnings.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(earnings.stale_replay_forbidden).toBe(true);
const bestYear = policy.resolveAssistantTurnMeaning({
rawUserMessage: "какой у нас самый доходный год"
});
expect(bestYear.explicit_intent_candidate).toBeNull();
expect(bestYear.asked_domain_family).toBe("business_summary");
expect(bestYear.asked_action_family).toBe("broad_evaluation");
expect(bestYear.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
const profitMargin = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0430\u043a\u0430\u044f \u0443 \u043d\u0430\u0441 \u0447\u0438\u0441\u0442\u0430\u044f \u043f\u0440\u0438\u0431\u044b\u043b\u044c \u0438 \u043c\u0430\u0440\u0436\u0430 \u0437\u0430 2020?"
});
expect(profitMargin.explicit_intent_candidate).toBeNull();
expect(profitMargin.asked_domain_family).toBe("business_summary");
expect(profitMargin.asked_action_family).toBe("broad_evaluation");
expect(profitMargin.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(profitMargin.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
const overdueDebt = policy.resolveAssistantTurnMeaning({
rawUserMessage:
"\u043a\u0430\u043a\u0430\u044f \u0443 \u043d\u0430\u0441 \u043f\u0440\u043e\u0441\u0440\u043e\u0447\u043a\u0430 \u043f\u043e \u0434\u043e\u043b\u0433\u0430\u043c \u0437\u0430 2020?"
});
expect(overdueDebt.explicit_intent_candidate).toBeNull();
expect(overdueDebt.asked_domain_family).toBe("business_summary");
expect(overdueDebt.asked_action_family).toBe("broad_evaluation");
expect(overdueDebt.unsupported_but_understood_family).toBe("broad_business_evaluation");
expect(overdueDebt.reason_codes).toContain("broad_business_evaluation_current_turn_signal");
});
});