ЮИ - Автопрогоны: доработать иконки и hover-состояния saved sessions
This commit is contained in:
@@ -130,6 +130,134 @@ describe("counterparty shipment item flow and open-items routing", () => {
|
||||
expect(query).not.toContain("контрагентом");
|
||||
});
|
||||
|
||||
it("keeps resolved counterparty group name in user-facing document reply for svk wording", async () => {
|
||||
executeAddressMcpQueryMock
|
||||
.mockResolvedValueOnce({
|
||||
fetched_rows: 1,
|
||||
matched_rows: 1,
|
||||
raw_rows: [
|
||||
{
|
||||
Counterparty: "Группа СВК",
|
||||
Registrator: "Группа СВК"
|
||||
}
|
||||
],
|
||||
rows: [],
|
||||
error: null
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
fetched_rows: 1,
|
||||
matched_rows: 1,
|
||||
raw_rows: [
|
||||
{
|
||||
Period: "2021-11-10T12:00:07Z",
|
||||
Registrator: "Поступление на расчетный счет 00000000013 от 10.11.2021 12:00:07",
|
||||
AccountDt: "0",
|
||||
AccountKt: "0",
|
||||
Amount: 20000,
|
||||
Counterparty: "Группа СВК",
|
||||
Contract: "Договор № 1-ПМ/2020 от 05.06.2020",
|
||||
Organization: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
],
|
||||
rows: [],
|
||||
error: null
|
||||
});
|
||||
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle("покажи документы по свк");
|
||||
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.debug.detected_intent).toBe("list_documents_by_counterparty");
|
||||
expect(String(result?.reply_text ?? "")).toContain("Контрагент: Группа СВК");
|
||||
expect(String(result?.reply_text ?? "")).not.toContain("Контрагент: Группа Найдено");
|
||||
});
|
||||
|
||||
it("prefers requested full counterparty label when document rows only expose a generic group label", () => {
|
||||
const reply = composeFactualReply(
|
||||
"list_documents_by_counterparty",
|
||||
[
|
||||
{
|
||||
period: "2021-11-10T12:00:07Z",
|
||||
registrator: "Поступление на расчетный счет 00000000013 от 10.11.2021 12:00:07",
|
||||
account_dt: "0",
|
||||
account_kt: "0",
|
||||
amount: 20000,
|
||||
analytics: ["Группа", "Договор № 1-ПМ/2020 от 05.06.2020"],
|
||||
organization: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
],
|
||||
{
|
||||
userMessage: "а по свк",
|
||||
counterpartyHint: "Группа СВК"
|
||||
}
|
||||
);
|
||||
|
||||
expect(reply.text).toContain("Контрагент: Группа СВК. Найдено документов: 1.");
|
||||
expect(reply.text).not.toContain("Контрагент: Группа. Найдено документов");
|
||||
});
|
||||
|
||||
it("keeps current resolved counterparty label over stale follow-up anchor during short retarget", async () => {
|
||||
executeAddressMcpQueryMock
|
||||
.mockResolvedValueOnce({
|
||||
fetched_rows: 1,
|
||||
matched_rows: 1,
|
||||
raw_rows: [
|
||||
{
|
||||
Counterparty: "Группа СВК",
|
||||
Registrator: "Группа СВК"
|
||||
}
|
||||
],
|
||||
rows: [],
|
||||
error: null
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
fetched_rows: 2,
|
||||
matched_rows: 2,
|
||||
raw_rows: [
|
||||
{
|
||||
Period: "2021-11-10T12:00:07Z",
|
||||
Registrator: "Поступление на расчетный счет 00000000013 от 10.11.2021 12:00:07",
|
||||
AccountDt: "0",
|
||||
AccountKt: "0",
|
||||
Amount: 20000,
|
||||
Counterparty: "Группа",
|
||||
Contract: "Договор № 1-ПМ/2020 от 05.06.2020",
|
||||
Organization: 'ООО "Альтернатива Плюс"'
|
||||
},
|
||||
{
|
||||
Period: "2021-09-29T12:00:03Z",
|
||||
Registrator: "Поступление на расчетный счет 00000000012 от 29.09.2021 12:00:03",
|
||||
AccountDt: "0",
|
||||
AccountKt: "0",
|
||||
Amount: 50000,
|
||||
Counterparty: "Группа",
|
||||
Contract: "Договор № 1-ПМ/2020 от 05.06.2020",
|
||||
Organization: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
],
|
||||
rows: [],
|
||||
error: null
|
||||
});
|
||||
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle("а по свк", {
|
||||
followupContext: {
|
||||
previous_intent: "list_documents_by_counterparty",
|
||||
previous_filters: {
|
||||
counterparty: "Чепурнов П.Д."
|
||||
},
|
||||
previous_anchor_type: "counterparty",
|
||||
previous_anchor_value: "Чепурнов П.Д."
|
||||
}
|
||||
});
|
||||
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.debug.detected_intent).toBe("list_documents_by_counterparty");
|
||||
expect(String(result?.reply_text ?? "")).toContain("Контрагент: Группа СВК");
|
||||
expect(String(result?.reply_text ?? "")).not.toContain("Контрагент: Группа Найдено");
|
||||
expect(String(result?.reply_text ?? "")).not.toContain("Контрагент: Чепурнов");
|
||||
});
|
||||
|
||||
it("explains supplier payments and return when no supply rows are found", async () => {
|
||||
executeAddressMcpQueryMock.mockImplementation(async (request?: { query?: string }) => {
|
||||
const query = String(request?.query ?? "");
|
||||
|
||||
@@ -9,18 +9,27 @@ import { AddressQueryService } from "../src/services/addressQueryService";
|
||||
describe("vat payable confirmed as-of route", () => {
|
||||
it("routes VAT payable question into exact confirmed intent", () => {
|
||||
const result = resolveAddressIntent("сколько НДС к уплате на март 2020");
|
||||
expect(result.intent).toBe("vat_payable_confirmed_as_of_date");
|
||||
expect(result.reasons).toContain("vat_payable_confirmed_signal_detected");
|
||||
expect(result.intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(
|
||||
result.reasons.includes("vat_liability_confirmed_tax_period_signal_detected") ||
|
||||
result.reasons.includes("vat_liability_colloquial_bridge_signal_detected")
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("treats colloquial 'сгрузить' wording as confirmed VAT payable intent", () => {
|
||||
const result = resolveAddressIntent("какой НДС мы должны сгрузить на март 2020");
|
||||
expect(result.intent).toBe("vat_payable_confirmed_as_of_date");
|
||||
expect(result.reasons).toContain("vat_payable_confirmed_signal_detected");
|
||||
expect(result.intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(result.reasons).toContain("vat_liability_colloquial_bridge_signal_detected");
|
||||
});
|
||||
|
||||
it("treats colloquial VAT wording with month in prepositional case as confirmed intent", () => {
|
||||
const result = resolveAddressIntent("Какой НДС необходимо сгрузить в марте 2020 года?");
|
||||
expect(result.intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(result.reasons).toContain("vat_liability_colloquial_bridge_signal_detected");
|
||||
});
|
||||
|
||||
it("keeps VAT forecast intent when explicit forecast wording is used", () => {
|
||||
const result = resolveAddressIntent("какой прогноз оплаты ндс на март 2020");
|
||||
const result = resolveAddressIntent("мож прикинусь плиз скока ндс надо заплатить на 15 марта 2020 года");
|
||||
expect(result.intent).toBe("vat_payable_forecast");
|
||||
expect(result.reasons).toContain("forecast_tax_signal_detected");
|
||||
});
|
||||
@@ -63,8 +72,8 @@ describe("vat payable confirmed as-of route", () => {
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle("сколько НДС к уплате на март 2020");
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.debug.detected_intent).toBe("vat_payable_confirmed_as_of_date");
|
||||
expect(result?.debug.selected_recipe).toBe("address_vat_payable_confirmed_as_of_date_v1");
|
||||
expect(result?.debug.detected_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(result?.debug.selected_recipe).toBe("address_vat_liability_confirmed_tax_period_v1");
|
||||
expect(result?.debug.requested_result_mode).toBe("confirmed_balance");
|
||||
expect(result?.debug.route_expectation_status).toBe("matched");
|
||||
expect(result?.debug.limited_reason_category).not.toBe("unsupported");
|
||||
|
||||
@@ -44,5 +44,17 @@ describe("answer composer user-facing formatting", () => {
|
||||
expect(sanitized).not.toContain("technical_debug_payload_json");
|
||||
expect(sanitized).not.toContain("trace_id");
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps short uppercase Cyrillic counterparty acronyms in user-facing text", () => {
|
||||
const source = [
|
||||
"Контрагент: Группа СВК. Найдено документов: 2.",
|
||||
"1. 2021-11-10 | Поступление на расчетный счет.",
|
||||
"2. 2021-09-29 | Поступление на расчетный счет."
|
||||
].join("\n");
|
||||
|
||||
const sanitized = sanitizeAssistantReplyForUserFacing(source);
|
||||
|
||||
expect(sanitized).toContain("Группа СВК");
|
||||
expect(sanitized).toContain("Найдено документов: 2.");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -410,7 +410,7 @@ describe("assistant living chat mode", () => {
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual_with_explanation");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("могу помочь");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("\u0440\u0435\u0436\u0438\u043c\u0435 \u0447\u0442\u0435\u043d\u0438\u044f");
|
||||
expect(String(response.assistant_reply).toLowerCase()).not.toContain("режиме чтения");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("\u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u044e 1\u0441");
|
||||
expect(String(response.assistant_reply)).not.toContain("vat_period_snapshot");
|
||||
expect(String(response.assistant_reply)).not.toContain("inventory_on_hand_as_of_date");
|
||||
@@ -608,7 +608,7 @@ describe("assistant living chat mode", () => {
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual_with_explanation");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("\u0440\u0435\u0436\u0438\u043c\u0435 \u0447\u0442\u0435\u043d\u0438\u044f");
|
||||
expect(String(response.assistant_reply).toLowerCase()).not.toContain("режиме чтения");
|
||||
expect(response.debug?.tool_gate_reason).toBe("assistant_capability_query_detected");
|
||||
expect(chatClient.chat).toHaveBeenCalledTimes(0);
|
||||
expect(addressQueryService.tryHandle).toHaveBeenCalledTimes(0);
|
||||
@@ -655,13 +655,165 @@ describe("assistant living chat mode", () => {
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual_with_explanation");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("режиме чтения");
|
||||
expect(String(response.assistant_reply).toLowerCase()).not.toContain("режиме чтения");
|
||||
expect(response.debug?.tool_gate_reason).toBe("assistant_capability_query_detected");
|
||||
expect(response.debug?.living_chat_response_source).toBe("deterministic_capability_contract");
|
||||
expect(chatClient.chat).toHaveBeenCalledTimes(0);
|
||||
expect(addressQueryService.tryHandle).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("treats delta-by-contracts wording as capability meta instead of stale revenue follow-up", async () => {
|
||||
const normalizer = {
|
||||
normalize: vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
trace_id: "norm-contract-delta-capability",
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
normalized: null,
|
||||
validation: { passed: false, errors: ["mock"] },
|
||||
route_hint_summary: null,
|
||||
raw_model_output: {},
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },
|
||||
latency_ms: 1,
|
||||
request_count_for_case: 1
|
||||
})
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const sessionId = "asst-living-chat-contract-delta-capability";
|
||||
sessions.ensureSession(sessionId);
|
||||
sessions.appendItem(sessionId, {
|
||||
message_id: "msg-seed-revenue-answer",
|
||||
session_id: sessionId,
|
||||
role: "assistant",
|
||||
text: "Самый доходный клиент за все время — Гамма-мебель, ООО.",
|
||||
reply_type: "factual",
|
||||
created_at: new Date().toISOString(),
|
||||
trace_id: "address-seed-revenue-answer",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "customer_revenue_and_payments",
|
||||
selected_recipe: "address_customer_revenue_and_payments_v1",
|
||||
extracted_filters: {
|
||||
period_mode: "all_time"
|
||||
}
|
||||
}
|
||||
} as any);
|
||||
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn().mockResolvedValue({ handled: false })
|
||||
} as any;
|
||||
const chatClient = {
|
||||
chat: vi.fn().mockResolvedValue({
|
||||
raw: { id: "chat-contract-delta-capability-should-not-run" },
|
||||
outputText: "unused",
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 }
|
||||
})
|
||||
} as any;
|
||||
|
||||
const service = new AssistantService(normalizer as any, sessions, undefined as any, undefined as any, addressQueryService, chatClient);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: sessionId,
|
||||
user_message: "ты умеешь считать дельту по договорам?",
|
||||
llmProvider: "local",
|
||||
model: "qwen2.5",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual_with_explanation");
|
||||
expect(response.debug?.tool_gate_reason).toBe("assistant_capability_query_detected");
|
||||
expect(response.debug?.living_chat_response_source).toBe("deterministic_capability_contract");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("дельт");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("договор");
|
||||
expect(String(response.assistant_reply).toLowerCase()).not.toContain("самый доходный клиент");
|
||||
expect(chatClient.chat).toHaveBeenCalledTimes(0);
|
||||
expect(addressQueryService.tryHandle).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("keeps delta-by-contracts wording in capability meta mode after canonical rewrite", async () => {
|
||||
const normalizer = {
|
||||
normalize: vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
trace_id: "norm-contract-delta-capability-rewrite",
|
||||
prompt_version: "normalizer_v2_0_2",
|
||||
schema_version: "v2_0_2",
|
||||
normalized: {
|
||||
query: "проверить возможность расчета дельты по договорам"
|
||||
},
|
||||
validation: { passed: true, errors: [] },
|
||||
route_hint_summary: null,
|
||||
raw_model_output: {},
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },
|
||||
latency_ms: 1,
|
||||
request_count_for_case: 1
|
||||
})
|
||||
} as any;
|
||||
|
||||
const sessions = new AssistantSessionStore();
|
||||
const sessionId = "asst-living-chat-contract-delta-capability-rewrite";
|
||||
sessions.ensureSession(sessionId);
|
||||
sessions.appendItem(sessionId, {
|
||||
message_id: "msg-seed-vat-answer",
|
||||
session_id: sessionId,
|
||||
role: "assistant",
|
||||
text: "Собран подтвержденный расчет НДС к уплате за февраль 2017.",
|
||||
reply_type: "factual",
|
||||
created_at: new Date().toISOString(),
|
||||
trace_id: "address-seed-vat-answer",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "vat_liability_confirmed_for_tax_period",
|
||||
selected_recipe: "address_vat_liability_confirmed_tax_period_v1",
|
||||
extracted_filters: {
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
as_of_date: "2017-02-28",
|
||||
period_from: "2017-02-01",
|
||||
period_to: "2017-02-28"
|
||||
}
|
||||
}
|
||||
} as any);
|
||||
|
||||
const addressQueryService = {
|
||||
tryHandle: vi.fn().mockResolvedValue({ handled: false })
|
||||
} as any;
|
||||
const chatClient = {
|
||||
chat: vi.fn().mockResolvedValue({
|
||||
raw: { id: "chat-contract-delta-capability-rewrite-should-not-run" },
|
||||
outputText: "unused",
|
||||
usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 }
|
||||
})
|
||||
} as any;
|
||||
|
||||
const service = new AssistantService(normalizer as any, sessions, undefined as any, undefined as any, addressQueryService, chatClient);
|
||||
|
||||
const response = await service.handleMessage({
|
||||
session_id: sessionId,
|
||||
user_message: "ты умеешь считать дельту по договорам?",
|
||||
llmProvider: "local",
|
||||
model: "qwen2.5",
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.reply_type).toBe("factual_with_explanation");
|
||||
expect(response.debug?.tool_gate_reason).toBe("assistant_capability_query_detected");
|
||||
expect(response.debug?.living_chat_response_source).toBe("deterministic_capability_contract");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("дельт");
|
||||
expect(String(response.assistant_reply).toLowerCase()).toContain("договор");
|
||||
expect(String(response.assistant_reply)).not.toContain("февраль 2017");
|
||||
expect(String(response.assistant_reply)).not.toContain("0,00");
|
||||
expect(chatClient.chat).toHaveBeenCalledTimes(0);
|
||||
expect(addressQueryService.tryHandle).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("answers historical capability follow-up in current inventory context instead of generic capability contract", async () => {
|
||||
const normalizer = {
|
||||
normalize: vi.fn().mockResolvedValue({
|
||||
|
||||
@@ -15,6 +15,8 @@ function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
compactWhitespace: (value: string) => String(value ?? "").replace(/\s+/g, " ").trim(),
|
||||
repairAddressMojibake: (value: string) => value,
|
||||
countTokens: (value: string) => String(value ?? "").split(/\s+/).filter(Boolean).length,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: () => false,
|
||||
hasDataRetrievalRequestSignal: () => false,
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "1. Рабочая станция",
|
||||
debug: {
|
||||
@@ -381,6 +383,88 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
});
|
||||
|
||||
it("keeps purchase-date VAT bridge after unsupported verification interrupt", () => {
|
||||
const item = "Рабочая станция универрсального специалиста";
|
||||
const provenanceItem = {
|
||||
role: "assistant",
|
||||
text: [
|
||||
`По позиции ${item} однозначный поставщик не подтвержден.`,
|
||||
"Подтверждение:",
|
||||
"- Первая найденная дата закупки: 05.02.2015."
|
||||
].join("\n"),
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: {
|
||||
status: "grounded"
|
||||
},
|
||||
detected_intent: "inventory_purchase_provenance_for_item",
|
||||
extracted_filters: {
|
||||
item,
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2016-03-31"
|
||||
},
|
||||
anchor_type: "item",
|
||||
anchor_value_resolved: item
|
||||
}
|
||||
} as any;
|
||||
const unsupportedInterrupt = {
|
||||
role: "assistant",
|
||||
text: "Пока не могу точно подтвердить, что именно это ты имеешь в виду.",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
detected_intent: "unknown",
|
||||
answer_grounding_check: {
|
||||
status: "unsupported"
|
||||
}
|
||||
}
|
||||
} as any;
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => unsupportedInterrupt,
|
||||
hasAddressFollowupContextSignal: () => false,
|
||||
findRecentInventoryRootFrame: () => null,
|
||||
resolveAddressIntent: () => ({ intent: "unknown" })
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
[provenanceItem, unsupportedInterrupt],
|
||||
null,
|
||||
null,
|
||||
{
|
||||
session_context: {
|
||||
active_focus_object: {
|
||||
object_type: "item",
|
||||
label: item,
|
||||
provenance_result_set_id: "rs-provenance-interrupt"
|
||||
},
|
||||
active_result_set_id: "rs-provenance-interrupt"
|
||||
},
|
||||
result_sets: [
|
||||
{
|
||||
result_set_id: "rs-provenance-interrupt",
|
||||
intent: "inventory_purchase_provenance_for_item",
|
||||
entity_refs: [
|
||||
{
|
||||
index: 1,
|
||||
entity_type: "item",
|
||||
value: "Поступление товаров и услуг 00000000023 от 05.02.2015 0:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_intent).toBe("inventory_purchase_provenance_for_item");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
item,
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
period_from: "2015-02-01",
|
||||
period_to: "2015-02-28"
|
||||
});
|
||||
});
|
||||
|
||||
it("drops stale carryover for a fresh standalone topic from another intent family", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
@@ -520,4 +604,71 @@ describe("assistantTransitionPolicy", () => {
|
||||
period_to: "2020-03-31"
|
||||
});
|
||||
});
|
||||
|
||||
it("does not attach address follow-up carryover to explicit capability-meta questions", () => {
|
||||
const policy = buildPolicy({
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (message: unknown) =>
|
||||
/дельт[ауы]?\s+по\s+договорам/iu.test(String(message ?? "")),
|
||||
hasAddressFollowupContextSignal: () => true
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"ты умеешь считать дельту по договорам?",
|
||||
[],
|
||||
"проверить возможность расчета дельты по договорам",
|
||||
{
|
||||
predecomposeContract: {
|
||||
mode: "address_query",
|
||||
intent: "unknown"
|
||||
}
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover).toBeNull();
|
||||
});
|
||||
|
||||
it("retargets selected-object provenance follow-up from inventory root when semantic scope is already detected", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "На 31.03.2016 на складе подтверждено 2 позиции.",
|
||||
debug: {
|
||||
detected_intent: "inventory_on_hand_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
warehouse: "Основной склад",
|
||||
as_of_date: "2016-03-31",
|
||||
period_from: "2016-03-01",
|
||||
period_to: "2016-03-31"
|
||||
},
|
||||
anchor_type: "organization",
|
||||
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => true
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
'По выбранному объекту "Рабочая станция универсального специалиста (индивидуальное изготовление)": где взяли это?',
|
||||
[],
|
||||
null,
|
||||
{
|
||||
predecomposeContract: {
|
||||
mode: "address_query",
|
||||
intent: "unknown",
|
||||
semantics: {
|
||||
selected_object_scope_detected: true
|
||||
}
|
||||
}
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.target_intent).toBe("inventory_purchase_provenance_for_item");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
item: "Рабочая станция универсального специалиста (индивидуальное изготовление)"
|
||||
});
|
||||
expect(carryover?.followupContext?.previous_anchor_type).toBe("item");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user