АРЧ АП11 - Архитектура после регресса: + Архитектура: восстановить bridge от provenance выбранной позиции к VAT-периоду и закрыть phase10 replay

This commit is contained in:
2026-04-18 11:50:48 +03:00
parent 9872ef5446
commit 31cb4ccbbb
27 changed files with 1655 additions and 49 deletions
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vitest";
import { extractAddressFilters } from "../src/services/addressFilterExtractor";
describe("address filter extractor regressions", () => {
it("keeps explicit month window for confirmed VAT tax-period intent", () => {
const extracted = extractAddressFilters(
"сколько ндс надо заплатить в налоговую за декабрь 2019",
"vat_liability_confirmed_for_tax_period"
);
expect(extracted.extracted_filters.period_from).toBe("2019-12-01");
expect(extracted.extracted_filters.period_to).toBe("2019-12-31");
expect(extracted.warnings).toContain("period_derived_from_month_phrase");
expect(extracted.warnings).not.toContain("period_derived_from_tax_quarter_for_confirmed_vat_liability");
});
});
@@ -55,7 +55,7 @@ describe("address follow-up temporal regressions", () => {
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_payable_confirmed_as_of_date");
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2017-05-01");
expect(result?.filters.extracted_filters.period_to).toBe("2017-05-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2017-05-31");
@@ -76,7 +76,7 @@ describe("address follow-up temporal regressions", () => {
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_payable_confirmed_as_of_date");
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2017-05-01");
expect(result?.filters.extracted_filters.period_to).toBe("2017-05-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2017-05-31");
@@ -116,4 +116,26 @@ describe("address follow-up temporal regressions", () => {
expect(result?.filters.extracted_filters.warehouse).toBeUndefined();
expect(result?.baseReasons).toContain("as_of_date_from_followup_context");
});
it("retargets inventory purchase-date VAT bridge into confirmed VAT period with inherited purchase month", () => {
const result = runAddressDecomposeStage("ндс можешь прикинуть на дату покупки рабочей станции?", {
previous_intent: "inventory_purchase_provenance_for_item",
target_intent: "vat_liability_confirmed_for_tax_period",
previous_filters: {
item: "Рабочая станция универсального специалиста",
organization: 'ООО "Альтернатива Плюс"',
period_from: "2015-02-01",
period_to: "2015-02-28",
as_of_date: "2016-03-31"
},
previous_anchor_type: "item",
previous_anchor_value: "Рабочая станция универсального специалиста"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2015-02-01");
expect(result?.filters.extracted_filters.period_to).toBe("2015-02-28");
expect(result?.baseReasons).toContain("period_from_followup_context");
});
});
@@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";
import { resolveAddressIntent } from "../src/services/addressIntentResolver";
describe("addressIntentResolver regression bridges", () => {
it("detects colloquial VAT liability for a month period", () => {
const result = resolveAddressIntent("прикинь какой ндс нам надо заплатить на февраль 2017");
expect(result.intent).toBe("vat_liability_confirmed_for_tax_period");
});
it("detects payables snapshot wording in plain human form", () => {
const result = resolveAddressIntent("мы должны комуто денег на сегодня?");
expect(result.intent).toBe("payables_confirmed_as_of_date");
});
it("detects top customer all-time revenue wording", () => {
const result = resolveAddressIntent("кто у нас самый доходный клиент за все время");
expect(result.intent).toBe("customer_revenue_and_payments");
});
it("detects top-year company revenue wording", () => {
const result = resolveAddressIntent("какой у нас самый доходный год");
expect(result.intent).toBe("customer_revenue_and_payments");
});
it("does not collapse very old stock request into generic inventory snapshot", () => {
const result = resolveAddressIntent("Есть ли остатки товара, которые закупались очень давно");
expect(result.intent).toBe("inventory_aging_by_purchase_date");
});
it("detects bare historical inventory root with explicit month-year", () => {
const result = resolveAddressIntent("остатки на март 2016");
expect(result.intent).toBe("inventory_on_hand_as_of_date");
});
});
@@ -2747,14 +2747,15 @@ describe("address filter extraction for balance drilldown", () => {
expect(extracted.warnings).toContain("period_to_derived_from_as_of_date_for_vat_forecast");
});
it("derives full tax quarter window for confirmed VAT tax-period intent from month phrase", () => {
it("keeps explicit month window for confirmed VAT tax-period intent from month phrase", () => {
const extracted = extractAddressFilters(
"сколько ндс надо заплатить в налоговую за декабрь 2019",
"vat_liability_confirmed_for_tax_period"
);
expect(extracted.extracted_filters.period_from).toBe("2019-10-01");
expect(extracted.extracted_filters.period_from).toBe("2019-12-01");
expect(extracted.extracted_filters.period_to).toBe("2019-12-31");
expect(extracted.warnings).toContain("period_derived_from_tax_quarter_for_confirmed_vat_liability");
expect(extracted.warnings).toContain("period_derived_from_month_phrase");
expect(extracted.warnings).not.toContain("period_derived_from_tax_quarter_for_confirmed_vat_liability");
});
it("derives VAT forecast quarter-to-date window for explicit day+month+year phrase", () => {
@@ -0,0 +1,145 @@
import { describe, expect, it } from "vitest";
import { composeCounterpartyAnalyticsReply } from "../src/services/address_runtime/counterpartyAnalyticsReplyBuilders";
import { composeInventoryReply } from "../src/services/address_runtime/inventoryReplyBuilders";
describe("address reply builders regressions", () => {
it("starts top customer aggregate reply with a direct business answer", () => {
const result = composeCounterpartyAnalyticsReply(
"customer_revenue_and_payments",
[
{
amount: 250000,
period: "2020-03-31",
registrator: "Поступление 1"
} as any
],
{
userMessage: "кто у нас самый доходный клиент за все время?"
},
{
formatPercent: () => null,
formatDateRu: (value: string) => value,
formatMoneyRub: (value: number) => `${value}`,
extractYearFromIso: (value: string | null) => (value ? Number(value.slice(0, 4)) : null),
detectCounterpartyProfileFocus: () => "full_profile",
detectCounterpartyLifecycleFocus: () => "active_customers_all_time",
hasCounterpartyLifecycleLongevityQuestion: () => false,
hasCounterpartyActivityAgeQuestion: () => false,
detectRankingLimit: () => 5,
detectValueRankingFocus: () => "top_by_total",
detectContractValueFocus: () => "top_by_turnover",
detectMinOpsForAvgCheck: () => 1,
extractRequestedYearFromQuestion: () => null,
extractCounterpartyName: () => "Чапурнов",
extractContractName: () => null,
counterpartyLookupMatches: () => false,
toUtcDayTimestamp: () => null,
formatAgeYearsMonthsDays: () => "0 дней",
normalizeQuestionText: (value: string | null | undefined) => String(value ?? "")
}
);
expect(result?.text.split("\n")[0]).toContain("Самый доходный клиент");
expect(result?.text.split("\n")[0]).toContain("Чапурнов");
});
it("starts top year aggregate reply with a direct business answer", () => {
const result = composeCounterpartyAnalyticsReply(
"customer_revenue_and_payments",
[
{
amount: 320000,
period: "2021-05-12",
registrator: "Поступление 2"
} as any
],
{
userMessage: "какой у нас самый доходный год"
},
{
formatPercent: () => null,
formatDateRu: (value: string) => value,
formatMoneyRub: (value: number) => `${value}`,
extractYearFromIso: (value: string | null) => (value ? Number(value.slice(0, 4)) : null),
detectCounterpartyProfileFocus: () => "full_profile",
detectCounterpartyLifecycleFocus: () => "active_customers_all_time",
hasCounterpartyLifecycleLongevityQuestion: () => false,
hasCounterpartyActivityAgeQuestion: () => false,
detectRankingLimit: () => 5,
detectValueRankingFocus: () => "top_years_by_total",
detectContractValueFocus: () => "top_by_turnover",
detectMinOpsForAvgCheck: () => 1,
extractRequestedYearFromQuestion: () => null,
extractCounterpartyName: () => "Чапурнов",
extractContractName: () => null,
counterpartyLookupMatches: () => false,
toUtcDayTimestamp: () => null,
formatAgeYearsMonthsDays: () => "0 дней",
normalizeQuestionText: (value: string | null | undefined) => String(value ?? "")
}
);
expect(result?.text.split("\n")[0]).toContain("Самый доходный год");
expect(result?.text.split("\n")[0]).toContain("2021");
});
it("keeps very old stock answer free of explicit as-of date in the first line", () => {
const result = composeInventoryReply(
"inventory_aging_by_purchase_date",
[
{
amount: 1000,
period: "2015-02-05",
registrator: "Поступление 3"
} as any
],
{
userMessage: "Есть ли остатки товара, которые закупались очень давно",
asOfDate: "2026-04-18"
},
{
resolvePayablesAsOfDate: () => "2026-04-18",
buildInventoryOnHandAggregate: () => [],
uniqueStrings: (values: string[]) => values,
formatDateRu: (value: string) => value,
formatNumberWithDots: (value: number) => String(value),
formatMoneyRub: (value: number) => `${value}`,
isInventoryPurchaseMovement: () => true,
summarizeInventoryTraceRows: () => ({
item: "Рабочая станция",
warehouses: ["Основной склад"],
organizations: ["ООО Альтернатива Плюс"],
counterparties: ["Чапурнов"],
documents: ["Поступление 3"],
firstPeriod: "2015-02-05",
lastPeriod: "2015-02-05",
totalAmount: 1000
}),
formatInventoryTraceRows: () => [],
hasInventoryPurchaseDateActionFocus: () => false,
inventoryTraceDateLabel: (value: string | null) => value ?? "дата не указана",
extractInventoryCounterpartyCandidates: () => ["Чапурнов"],
buildInventoryAgingByItemAggregate: () => [
{
item: "Рабочая станция",
warehouse: "Основной склад",
organization: "ООО Альтернатива Плюс",
firstPurchasePeriod: "2015-02-05",
lastPurchasePeriod: "2015-02-05",
operations: 1,
documentCount: 1,
counterparties: ["Чапурнов"],
ageDays: 4089
}
],
formatInventoryAgingRows: () => ["1. Рабочая станция | первая закупка: 2015-02-05"],
isInventorySaleMovement: () => false
}
);
const firstLine = result?.text.split("\n")[0] ?? "";
expect(firstLine).toContain("К самым старым закупкам");
expect(firstLine).not.toContain("2026-04-18");
});
});
@@ -157,6 +157,53 @@ describe("assistantRoutePolicy", () => {
expect(decision.orchestrationContract?.semantic_route_arbitration?.supported_address_intent_detected).toBe(true);
});
it("does not let deep session continuation override an exact VAT period route", () => {
const policy = buildPolicy({
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "vat_liability_confirmed_for_tax_period", confidence: "high" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
}),
hasDeepSessionContinuationSignal: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "а какой НДС мы должны сгрузить на март 2020?",
effectiveAddressUserMessage: "а какой НДС мы должны сгрузить на март 2020?",
followupContext: null,
llmPreDecomposeMeta: {
applied: true,
llmCanonicalCandidateDetected: true,
predecomposeContract: {
mode: "address_query",
mode_confidence: "high",
intent: "vat_liability_confirmed_for_tax_period",
intent_confidence: "high"
},
semanticExtractionContract: {
valid: true,
apply_canonical_recommended: true,
extraction: {
query_shape: "UNKNOWN",
aggregation_profile: "unknown"
},
guard_hints: {
deep_investigation_signal_detected: false
}
}
} as any,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateDecision).toBe("run_address_lane");
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.deep_session_continuation_fallback_to_deep).toBe(false);
});
it("routes memory recap follow-up over grounded answer to chat", () => {
const policy = buildPolicy({
resolveRouteMemorySignals: () => ({
@@ -254,6 +254,133 @@ describe("assistantTransitionPolicy", () => {
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
});
it("bridges selected-item purchase provenance into a VAT period follow-up", () => {
const item = "Рабочая станция универсального специалиста";
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: [
`По позиции ${item} однозначный поставщик не подтвержден.`,
"Подтверждение:",
"- Первая найденная дата закупки: 05.02.2015.",
"- Последняя найденная дата закупки: 22.07.2015."
].join("\n"),
debug: {
detected_intent: "inventory_purchase_provenance_for_item",
extracted_filters: {
item,
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2016-03-31"
},
anchor_type: "item",
anchor_value_resolved: item
}
}),
hasAddressFollowupContextSignal: () => false,
findRecentInventoryRootFrame: () => null,
resolveAddressIntent: () => ({ intent: "unknown" })
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"ндс можешь прикинуть на дату покупки рабочей станции?",
[],
null,
null,
{
session_context: {
active_focus_object: {
object_type: "item",
label: item,
provenance_result_set_id: "rs-provenance"
},
active_result_set_id: "rs-provenance"
},
result_sets: [
{
result_set_id: "rs-provenance",
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("keeps selected-object continuity for purchase-date VAT bridge even when an inventory root frame exists", () => {
const item = "Рабочая станция универсального специалиста";
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({
text: [
`По позиции ${item} однозначный поставщик не подтвержден.`,
"Подтверждение:",
"- Первая найденная дата закупки: 05.02.2015."
].join("\n"),
debug: {
detected_intent: "inventory_purchase_provenance_for_item",
extracted_filters: {
item,
organization: 'ООО "Альтернатива Плюс"',
as_of_date: "2016-03-31"
},
anchor_type: "item",
anchor_value_resolved: item
}
}),
hasAddressFollowupContextSignal: () => false,
hasForeignAccountingPivotOverInventoryMessage: () => true,
resolveAddressIntent: () => ({ intent: "unknown" })
});
const carryover = policy.resolveAddressFollowupCarryoverContext(
"ндс можешь прикинуть на дату покупки рабочей станции?",
[],
null,
null,
{
session_context: {
active_focus_object: {
object_type: "item",
label: item,
provenance_result_set_id: "rs-provenance"
},
active_result_set_id: "rs-provenance"
},
result_sets: [
{
result_set_id: "rs-provenance",
intent: "inventory_purchase_provenance_for_item",
entity_refs: [
{
index: 1,
entity_type: "item",
value: "Поступление товаров и услуг 00000000023 от 05.02.2015 0:00:00"
}
]
}
]
}
);
expect(carryover?.followupSelectionMode).toBe("carry_previous_intent");
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
expect(carryover?.followupContext?.previous_intent).toBe("inventory_purchase_provenance_for_item");
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
});
it("drops stale carryover for a fresh standalone topic from another intent family", () => {
const policy = buildPolicy({
findLastAddressAssistantItem: () => ({