Укрепить exact value-flow аналитику адресного контура
This commit is contained in:
@@ -2822,9 +2822,9 @@ describe("address filter extraction for balance drilldown", () => {
|
||||
expect(counterpartyProfile.extracted_filters.limit).toBeUndefined();
|
||||
expect(counterpartyLifecycle.extracted_filters.limit).toBeUndefined();
|
||||
expect(contractOverview.extracted_filters.limit).toBeUndefined();
|
||||
expect(customerValue.extracted_filters.limit).toBe(20);
|
||||
expect(supplierValue.extracted_filters.limit).toBe(20);
|
||||
expect(contractValue.extracted_filters.limit).toBe(20);
|
||||
expect(customerValue.extracted_filters.limit).toBe(1000);
|
||||
expect(supplierValue.extracted_filters.limit).toBe(1000);
|
||||
expect(contractValue.extracted_filters.limit).toBe(1000);
|
||||
expect(vatForecast.extracted_filters.limit).toBeUndefined();
|
||||
expect(periodProfile.extracted_filters.period_to).toBeDefined();
|
||||
expect(docSectionProfile.extracted_filters.period_to).toBeDefined();
|
||||
@@ -2844,6 +2844,9 @@ describe("address filter extraction for balance drilldown", () => {
|
||||
expect(customerValue.warnings).toContain("period_to_defaulted_today_for_management_profile");
|
||||
expect(supplierValue.warnings).toContain("period_to_defaulted_today_for_management_profile");
|
||||
expect(contractValue.warnings).toContain("period_to_defaulted_today_for_management_profile");
|
||||
expect(customerValue.warnings).toContain("value_analytics_sample_limit_expanded");
|
||||
expect(supplierValue.warnings).toContain("value_analytics_sample_limit_expanded");
|
||||
expect(contractValue.warnings).toContain("value_analytics_sample_limit_expanded");
|
||||
expect(vatForecast.warnings).toContain("period_derived_from_month_phrase");
|
||||
expect(vatForecast.warnings).toContain("period_from_derived_from_quarter_for_vat_forecast");
|
||||
expect(vatForecast.warnings).toContain("period_to_derived_from_as_of_date_for_vat_forecast");
|
||||
@@ -5066,6 +5069,17 @@ describe("address recipe catalog counterparty filtering", () => {
|
||||
expect(plan.query).toContain("БанкПоступление.ДоговорКонтрагента");
|
||||
});
|
||||
|
||||
it("expands customer value analytics sample independently from visible ranking size", () => {
|
||||
const filters = extractAddressFilters("какой у нас самый доходный год", "customer_revenue_and_payments");
|
||||
const selected = selectAddressRecipe("customer_revenue_and_payments", filters.extracted_filters);
|
||||
expect(selected.selected_recipe).toBeTruthy();
|
||||
const plan = buildAddressRecipePlan(selected.selected_recipe!, filters.extracted_filters);
|
||||
|
||||
expect(filters.extracted_filters.limit).toBe(1000);
|
||||
expect(filters.warnings).toContain("value_analytics_sample_limit_expanded");
|
||||
expect(plan.limit).toBe(1000);
|
||||
});
|
||||
|
||||
it("selects supplier payouts recipe and keeps top-20 default", () => {
|
||||
const selected = selectAddressRecipe("supplier_payouts_profile", {});
|
||||
expect(selected.selected_recipe).toBeTruthy();
|
||||
|
||||
@@ -9,9 +9,16 @@ describe("address reply builders regressions", () => {
|
||||
"customer_revenue_and_payments",
|
||||
[
|
||||
{
|
||||
counterparty: "Чапурнов",
|
||||
amount: 250000,
|
||||
period: "2020-03-31",
|
||||
registrator: "Поступление 1"
|
||||
} as any,
|
||||
{
|
||||
counterparty: "Малый клиент",
|
||||
amount: 100000,
|
||||
period: "2020-04-30",
|
||||
registrator: "Поступление 2"
|
||||
} as any
|
||||
],
|
||||
{
|
||||
@@ -31,7 +38,7 @@ describe("address reply builders regressions", () => {
|
||||
detectContractValueFocus: () => "top_by_turnover",
|
||||
detectMinOpsForAvgCheck: () => 1,
|
||||
extractRequestedYearFromQuestion: () => null,
|
||||
extractCounterpartyName: () => "Чапурнов",
|
||||
extractCounterpartyName: (row: any) => row.counterparty ?? "Чапурнов",
|
||||
extractContractName: () => null,
|
||||
counterpartyLookupMatches: () => false,
|
||||
toUtcDayTimestamp: () => null,
|
||||
@@ -44,6 +51,47 @@ describe("address reply builders regressions", () => {
|
||||
expect(result?.text.split("\n")[0]).toContain("Чапурнов");
|
||||
});
|
||||
|
||||
it("does not overclaim a comparative top customer ranking when only one candidate is present", () => {
|
||||
const result = composeCounterpartyAnalyticsReply(
|
||||
"customer_revenue_and_payments",
|
||||
[
|
||||
{
|
||||
amount: 250000,
|
||||
period: "2021-03-31",
|
||||
registrator: "Поступление 1"
|
||||
} as any
|
||||
],
|
||||
{
|
||||
userMessage: "кто больше всего принес денег в 2021"
|
||||
},
|
||||
{
|
||||
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("не полноценный сравнительный рейтинг");
|
||||
expect(result?.text).not.toContain("Самый доходный клиент");
|
||||
});
|
||||
|
||||
it("starts top year aggregate reply with a direct business answer", () => {
|
||||
const result = composeCounterpartyAnalyticsReply(
|
||||
"customer_revenue_and_payments",
|
||||
|
||||
@@ -761,6 +761,46 @@ describe("assistant orchestration contract", () => {
|
||||
expect(decision.orchestrationContract?.aggregate_analytics_signal_fallback_to_deep).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps all-time top customer ranking in address lane instead of stale deep problem answer", () => {
|
||||
const decision = resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "кто у нас самый доходный клиент за все время",
|
||||
effectiveAddressUserMessage: "определить самого доходного клиента за весь период",
|
||||
followupContext: {
|
||||
previous_intent: "counterparty_activity_lifecycle",
|
||||
previous_filters: {
|
||||
organization: "ООО Альтернатива Плюс"
|
||||
}
|
||||
},
|
||||
llmPreDecomposeMeta: {
|
||||
applied: true,
|
||||
llmCanonicalCandidateDetected: true,
|
||||
predecomposeContract: {
|
||||
mode: "address_query",
|
||||
mode_confidence: "medium",
|
||||
intent: "customer_revenue_and_payments",
|
||||
intent_confidence: "medium"
|
||||
},
|
||||
semanticExtractionContract: {
|
||||
valid: false,
|
||||
apply_canonical_recommended: false,
|
||||
extraction: {
|
||||
query_shape: "AGGREGATE_LOOKUP",
|
||||
aggregation_profile: "management_profile"
|
||||
},
|
||||
guard_hints: {},
|
||||
reason_codes: ["ranking_semantic_guard_rejected"]
|
||||
}
|
||||
} as any,
|
||||
useMock: false
|
||||
} as any);
|
||||
|
||||
expect(decision.runAddressLane).toBe(true);
|
||||
expect(decision.toolGateDecision).toBe("run_address_lane");
|
||||
expect(decision.livingMode).toBe("address_data");
|
||||
expect(decision.livingReason).toBe("address_lane_triggered");
|
||||
expect(decision.orchestrationContract?.aggregate_analytics_signal_fallback_to_deep).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps unsupported retrieval query in address lane when LLM runtime is unavailable", () => {
|
||||
const decision = resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage:
|
||||
|
||||
Reference in New Issue
Block a user