ARCH: добавить помесячный MCP discovery для нетто-потока
This commit is contained in:
@@ -191,6 +191,48 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
expect(draft.must_not_claim).toContain("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
||||
});
|
||||
|
||||
it("renders monthly bidirectional breakdown lines when the turn explicitly asked by month", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
asked_aggregation_axis: "month",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||||
}
|
||||
});
|
||||
const pilot = await executeAssistantMcpDiscoveryPilot(
|
||||
planner,
|
||||
buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||||
{ Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-10T00:00:00", Amount: 4000, Counterparty: "SVK" },
|
||||
{ Period: "2020-02-11T00:00:00", Amount: 1000, Counterparty: "SVK" }
|
||||
]
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
const confirmedText = draft.confirmed_lines.join("\n");
|
||||
|
||||
expect(draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(draft.headline).toContain("помесяч");
|
||||
expect(confirmedText).toContain("Помесячно: янв 2020");
|
||||
expect(confirmedText).toContain("получили 10 000 руб.");
|
||||
expect(confirmedText).toContain("заплатили 4 000 руб.");
|
||||
expect(confirmedText).toContain("Помесячно: фев 2020");
|
||||
expect(confirmedText).toContain("нетто в нашу сторону 1 500,50 руб.");
|
||||
expect(draft.reason_codes).toContain("answer_contains_monthly_breakdown");
|
||||
});
|
||||
|
||||
it("does not leak primitive names or query text into user-facing lines", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
||||
@@ -260,6 +260,65 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("derives monthly bidirectional value-flow breakdown when the turn explicitly asks by month", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
asked_aggregation_axis: "month",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||||
}
|
||||
});
|
||||
const deps = buildSequentialDeps([
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" },
|
||||
{ Period: "2020-02-20T00:00:00", Amount: "2 500,50", Counterparty: "SVK" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-01-10T00:00:00", Amount: 4000, Counterparty: "SVK" },
|
||||
{ Period: "2020-02-11T00:00:00", Amount: 1000, Counterparty: "SVK" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.derived_bidirectional_value_flow?.aggregation_axis).toBe("month");
|
||||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown).toMatchObject([
|
||||
{
|
||||
month_bucket: "2020-01",
|
||||
incoming_total_amount: 10000,
|
||||
incoming_rows_with_amount: 1,
|
||||
outgoing_total_amount: 4000,
|
||||
outgoing_rows_with_amount: 1,
|
||||
net_amount: 6000,
|
||||
net_direction: "net_incoming"
|
||||
},
|
||||
{
|
||||
month_bucket: "2020-02",
|
||||
incoming_total_amount: 2500.5,
|
||||
incoming_rows_with_amount: 1,
|
||||
outgoing_total_amount: 1000,
|
||||
outgoing_rows_with_amount: 1,
|
||||
net_amount: 1500.5,
|
||||
net_direction: "net_incoming"
|
||||
}
|
||||
]);
|
||||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.incoming_total_amount_human_ru).toContain("10 000");
|
||||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[0]?.net_amount_human_ru).toContain("6 000");
|
||||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.incoming_total_amount_human_ru).toContain("2 500,50");
|
||||
expect(result.derived_bidirectional_value_flow?.monthly_breakdown[1]?.net_amount_human_ru).toContain("1 500,50");
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_derived_bidirectional_monthly_breakdown_from_confirmed_rows");
|
||||
});
|
||||
|
||||
it("keeps non-lifecycle ready plans unsupported until a dedicated pilot exists", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
||||
@@ -40,6 +40,35 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.reason_codes).toContain("planner_needs_more_user_or_scope_context");
|
||||
});
|
||||
|
||||
it("keeps requested monthly aggregation as an explicit planning axis for value-flow discovery", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
asked_aggregation_axis: "month",
|
||||
explicit_entity_candidates: ["SVK"],
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.planner_status).toBe("ready_for_execution");
|
||||
expect(result.proposed_primitives).toEqual([
|
||||
"resolve_entity_reference",
|
||||
"query_movements",
|
||||
"aggregate_by_axis",
|
||||
"probe_coverage"
|
||||
]);
|
||||
expect(result.required_axes).toEqual([
|
||||
"counterparty",
|
||||
"period",
|
||||
"aggregate_axis",
|
||||
"amount",
|
||||
"coverage_target",
|
||||
"calendar_month"
|
||||
]);
|
||||
expect(result.reason_codes).toContain("planner_selected_monthly_value_flow_recipe");
|
||||
});
|
||||
|
||||
it("builds a document discovery plan without falling back to movement primitives", () => {
|
||||
const result = planAssistantMcpDiscovery({
|
||||
turnMeaning: {
|
||||
|
||||
@@ -151,6 +151,37 @@ describe("assistant MCP discovery response candidate", () => {
|
||||
expect(candidate.reply_text).not.toContain("query_movements");
|
||||
});
|
||||
|
||||
it("keeps monthly breakdown lines user-facing and localizes monthly inference basis", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||
entryPoint({
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "По данным 1С найдены строки входящих и исходящих денежных движений; нетто и помесячная раскладка могут называться только как расчет по найденным строкам и проверенному периоду.",
|
||||
confirmed_lines: [
|
||||
"1C bidirectional value-flow rows were checked for counterparty SVK: incoming=found, outgoing=found",
|
||||
"Помесячно: янв 2020 — получили 10 000 руб., заплатили 4 000 руб., нетто в нашу сторону 6 000 руб."
|
||||
],
|
||||
inference_lines: [
|
||||
"Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows"
|
||||
],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
expect(candidate.reply_text).toContain("Помесячно: янв 2020");
|
||||
expect(candidate.reply_text).toContain("Помесячная нетто-раскладка сгруппирована только по подтвержденным входящим и исходящим строкам 1С.");
|
||||
expect(candidate.reply_text).not.toContain("Counterparty monthly net value-flow breakdown");
|
||||
});
|
||||
|
||||
it("returns not applicable when discovery was skipped for an exact supported route", () => {
|
||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate({
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
|
||||
@@ -131,6 +131,26 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_payout_signal_detected");
|
||||
});
|
||||
|
||||
it("captures monthly aggregation as part of bidirectional value-flow meaning", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год по месяцам: сколько получили и сколько заплатили помесячно?",
|
||||
predecomposeContract: {
|
||||
entities: { counterparty: "Группа СВК" },
|
||||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
asked_aggregation_axis: "month",
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_date_scope: "2020"
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_monthly_aggregation_signal_detected");
|
||||
});
|
||||
|
||||
it("does not activate discovery for supported exact current-turn intent", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
assistantTurnMeaning: {
|
||||
|
||||
Reference in New Issue
Block a user