ЮИ - редактура вопросов в АВТОПРОГОНАХ
This commit is contained in:
@@ -56,6 +56,23 @@ describe("assistantBoundaryPolicy", () => {
|
||||
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();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ function buildInput(overrides: Record<string, unknown> = {}) {
|
||||
hasOperationalAdminActionRequestSignal: () => false,
|
||||
hasOrganizationFactLookupSignal: () => false,
|
||||
hasOrganizationFactFollowupSignal: () => false,
|
||||
hasLivingChatSignal: () => true,
|
||||
shouldEmitOrganizationSelectionReply: () => false,
|
||||
hasAssistantCapabilityQuestionSignal: () => false,
|
||||
resolveDataScopeProbe: async () => null,
|
||||
|
||||
@@ -37,6 +37,7 @@ function buildRuntimeInput(overrides: Record<string, unknown> = {}) {
|
||||
hasOperationalAdminActionRequestSignal: () => false,
|
||||
hasOrganizationFactLookupSignal: () => false,
|
||||
hasOrganizationFactFollowupSignal: () => false,
|
||||
hasLivingChatSignal: () => true,
|
||||
shouldEmitOrganizationSelectionReply: () => false,
|
||||
hasAssistantCapabilityQuestionSignal: () => false,
|
||||
resolveDataScopeProbe,
|
||||
@@ -53,6 +54,7 @@ function buildRuntimeInput(overrides: Record<string, unknown> = {}) {
|
||||
}),
|
||||
buildAssistantSafetyRefusalReply: () => "safety",
|
||||
buildAssistantDataScopeContractReply: () => "scope",
|
||||
buildAssistantProactiveOrganizationOfferReply: () => "",
|
||||
buildAssistantOrganizationFactBoundaryReply: () => "org-boundary",
|
||||
buildAssistantDataScopeSelectionReply: () => "org-selection",
|
||||
buildAssistantOperationalBoundaryReply: () => "ops",
|
||||
@@ -134,6 +136,56 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(executeLlmChat).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("adds proactive organization offer on first smalltalk turn when multiple organizations are available", async () => {
|
||||
const resolveDataScopeProbe = vi.fn(async () => ({
|
||||
status: "resolved",
|
||||
channel: "default",
|
||||
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"],
|
||||
error: null
|
||||
}));
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "привет, как дела?",
|
||||
resolveDataScopeProbe,
|
||||
buildAssistantProactiveOrganizationOfferReply: (scopeProbe: Record<string, unknown> | null) => {
|
||||
const organizations = Array.isArray(scopeProbe?.organizations) ? scopeProbe.organizations.join(", ") : "";
|
||||
return `offer:${organizations}`;
|
||||
}
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("llm-text");
|
||||
expect(output.chatText).toContain("offer:ООО Альтернатива Плюс, ООО Лайсвуд, РАЙМ");
|
||||
expect(output.debug?.living_chat_response_source).toBe("llm_chat_with_proactive_scope_offer");
|
||||
expect(output.debug?.living_chat_proactive_scope_offer_applied).toBe(true);
|
||||
expect(output.debug?.living_chat_data_scope_probe_org_count).toBe(3);
|
||||
expect(output.debug?.assistant_known_organizations).toEqual(["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"]);
|
||||
});
|
||||
|
||||
it("does not add proactive organization offer after the session already has assistant context", async () => {
|
||||
const resolveDataScopeProbe = vi.fn(async () => ({
|
||||
status: "resolved",
|
||||
channel: "default",
|
||||
organizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд"],
|
||||
error: null
|
||||
}));
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "привет еще раз",
|
||||
sessionItems: [{ role: "assistant", text: "Ранее уже отвечал." }],
|
||||
resolveDataScopeProbe,
|
||||
buildAssistantProactiveOrganizationOfferReply: () => "offer"
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toBe("llm-text");
|
||||
expect(output.debug?.living_chat_response_source).toBe("llm_chat");
|
||||
expect(output.debug?.living_chat_proactive_scope_offer_applied).toBe(false);
|
||||
expect(resolveDataScopeProbe).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("builds deterministic memory recap for prior selected-object address context", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
@@ -144,6 +196,9 @@ describe("assistant living chat runtime adapter", () => {
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "inventory_purchase_provenance_for_item",
|
||||
extracted_filters: {
|
||||
item: "Зеркало для инвалидов поворотное травмобезопасное",
|
||||
@@ -160,7 +215,7 @@ describe("assistant living chat runtime adapter", () => {
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("Зеркало для инвалидов поворотное травмобезопасное");
|
||||
expect(output.chatText).toContain("кто поставил");
|
||||
expect(output.chatText).toContain("кто поставлял");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_memory_recap_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -212,6 +212,48 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(contract.decision).toBe("continue_previous");
|
||||
});
|
||||
|
||||
it("retargets same-date inventory follow-up away from receivables intent", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "Подтвержденная дебиторская задолженность на 31.03.2020 собрана.",
|
||||
debug: {
|
||||
detected_intent: "receivables_confirmed_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2020-03-31",
|
||||
period_from: "2020-03-01",
|
||||
period_to: "2020-03-31"
|
||||
},
|
||||
anchor_type: "organization",
|
||||
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => true,
|
||||
hasReferentialPointer: () => true,
|
||||
findRecentInventoryRootFrame: () => null,
|
||||
resolveAddressIntent: () => ({ intent: "unknown" })
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"остатки по складу на эту же дату",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupSelectionMode).toBe("carry_previous_intent");
|
||||
expect(carryover?.followupContext?.previous_intent).toBe("receivables_confirmed_as_of_date");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2020-03-31",
|
||||
period_from: "2020-03-01",
|
||||
period_to: "2020-03-31"
|
||||
});
|
||||
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
|
||||
});
|
||||
|
||||
it("drops stale carryover for a fresh standalone topic from another intent family", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
|
||||
Reference in New Issue
Block a user