ARCH: замкнуть grounded entity retarget на value-flow follow-up
This commit is contained in:
parent
1fd8062dc7
commit
acacada0f6
|
|
@ -0,0 +1,95 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "address_truth_harness_phase28_entity_value_retarget_chain",
|
||||||
|
"domain": "address_phase28_entity_value_retarget_chain",
|
||||||
|
"title": "Phase 28 grounded entity value-flow retarget replay",
|
||||||
|
"description": "Targeted AGENT replay for Big Block C where a grounded 1C counterparty must survive side-switch and year-switch follow-ups across incoming, payout, and net value-flow questions without forcing the user to restate the resolved name.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_resolve_counterparty_alias",
|
||||||
|
"title": "Entity resolution grounds the checked 1C counterparty from a loose alias",
|
||||||
|
"question": "найди в 1С контрагента СВК",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)свк", "(?i)контрагент"],
|
||||||
|
"required_answer_patterns_any": [
|
||||||
|
"(?i)группа\\s+свк",
|
||||||
|
"(?i)каталог",
|
||||||
|
"(?i)найден",
|
||||||
|
"(?i)наиболее вероят"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)получили",
|
||||||
|
"(?i)заплатили",
|
||||||
|
"(?i)нетто",
|
||||||
|
"(?i)оборот",
|
||||||
|
"(?i)выручк",
|
||||||
|
"(?i)сумм(а|ы)"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "alias_grounding", "followup_anchor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_incoming_by_resolved_entity",
|
||||||
|
"title": "Incoming value-flow follow-up reuses the resolved counterparty anchor",
|
||||||
|
"question": "сколько получили по нему за 2020 год",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2020", "(?i)получил|входящ|поступ", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "incoming_value_flow", "followup_reuse"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_payout_switch_by_resolved_entity",
|
||||||
|
"title": "Outgoing payment follow-up keeps the same grounded counterparty and checked year",
|
||||||
|
"question": "а теперь сколько заплатили?",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2020", "(?i)заплатил|исходящ|списан|платеж", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту",
|
||||||
|
"(?i)за какой год"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "payout_switch", "followup_reuse", "date_carryover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_year_switch_on_payout",
|
||||||
|
"title": "Short year switch keeps the payout contour and grounded counterparty",
|
||||||
|
"question": "а за 2021?",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2021", "(?i)заплатил|исходящ|списан|платеж", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "payout_year_switch", "followup_reuse"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_05_net_switch_after_payout",
|
||||||
|
"title": "Net-flow follow-up keeps the same grounded counterparty and switched year",
|
||||||
|
"question": "а какое нетто?",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2021", "(?i)нетто|сальдо|разниц", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту",
|
||||||
|
"(?i)за какой год"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "net_switch", "followup_reuse", "date_carryover"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -533,7 +533,10 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||||
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
|
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
|
||||||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
|
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
|
||||||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1"));
|
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_value_flow_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_supplier_payout_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1"));
|
||||||
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
|
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
|
||||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||||
!rawLifecycleSignal &&
|
!rawLifecycleSignal &&
|
||||||
|
|
|
||||||
|
|
@ -2607,6 +2607,12 @@ function hasAddressFollowupContextSignal(userMessage) {
|
||||||
if (ultraShortFollowup && hasAny(/^(?:давай|показывай|показывыай|ещ[её]|also|again|go|ok|okay)(?=$|[\s,.;:!?])/iu)) {
|
if (ultraShortFollowup && hasAny(/^(?:давай|показывай|показывыай|ещ[её]|also|again|go|ok|okay)(?=$|[\s,.;:!?])/iu)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
const shortValueFlowRetargetCue = shortFollowup &&
|
||||||
|
(hasMarker() || hasPointer() || hasAny(/^(?:Р°|a|Рё|i|also|then|now)(?=$|[\s,.;:!?])/iu)) &&
|
||||||
|
hasAny(/(?:нетто|сальдо|разниц|получил|заплатил|поступ|входящ|исходящ|оборот|выручк|денеж)/iu);
|
||||||
|
if (shortValueFlowRetargetCue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (hasStandaloneAddressTopicSignal(rawText || repairedText)) {
|
if (hasStandaloneAddressTopicSignal(rawText || repairedText)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,23 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
];
|
];
|
||||||
return sameDatePhrases.some((phrase) => normalized.includes(phrase));
|
return sameDatePhrases.some((phrase) => normalized.includes(phrase));
|
||||||
}
|
}
|
||||||
|
function hasShortValueFlowRetargetCue(text) {
|
||||||
|
const normalized = normalizeFollowupText(text);
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tokenCount = deps.countTokens(normalized);
|
||||||
|
if (!Number.isFinite(tokenCount) || tokenCount > 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hasLeadCue = deps.hasFollowupMarker(text) ||
|
||||||
|
deps.hasReferentialPointer(text) ||
|
||||||
|
/^(?:\u0430|\u0438|also|then|now)(?=$|[\s,.;:!?])/iu.test(normalized);
|
||||||
|
if (!hasLeadCue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u043d\u0435\u0442\u0442\u043e|\u0441\u0430\u043b\u044c\u0434\u043e|\u0440\u0430\u0437\u043d\u0438\u0446|\u043f\u043e\u043b\u0443\u0447|\u0437\u0430\u043f\u043b\u0430\u0442|\u043f\u043e\u0441\u0442\u0443\u043f|\u0432\u0445\u043e\u0434\u044f\u0449|\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a|\u0434\u0435\u043d\u0435\u0436)/iu.test(normalized);
|
||||||
|
}
|
||||||
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
||||||
const normalized = deps.compactWhitespace(deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
const normalized = deps.compactWhitespace(deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||||||
if (!normalized || deps.countTokens(normalized) > 4) {
|
if (!normalized || deps.countTokens(normalized) > 4) {
|
||||||
|
|
@ -342,6 +359,11 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||||
: false));
|
: false));
|
||||||
const sourceIntentHint = (0, assistantContinuityPolicy_1.readAddressDebugIntent)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceIntentHint = (0, assistantContinuityPolicy_1.readAddressDebugIntent)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
|
const sourceDiscoveryPilotScopeHint = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
|
const hasValueFlowCarryoverSourceHint = sourceIntentHint === "customer_revenue_and_payments" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_value_flow_query_movements_v1" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_supplier_payout_query_movements_v1" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_bidirectional_value_flow_query_movements_v1";
|
||||||
const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue);
|
const navigationSessionState = (0, assistantContinuityPolicy_1.resolveNavigationSessionContextState)(addressNavigationState, deps.toNonEmptyString, deps.normalizeOrganizationScopeValue);
|
||||||
const navigationFocusObjectHint = navigationSessionState.focusObject;
|
const navigationFocusObjectHint = navigationSessionState.focusObject;
|
||||||
const hasNavigationInventoryItemFocusHint = Boolean(deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
const hasNavigationInventoryItemFocusHint = Boolean(deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
||||||
|
|
@ -363,13 +385,19 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
||||||
: null;
|
: null;
|
||||||
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
||||||
|
const shortValueFlowRetargetPrimary = hasValueFlowCarryoverSourceHint && hasShortValueFlowRetargetCue(userMessage);
|
||||||
|
const shortValueFlowRetargetAlternate = hasValueFlowCarryoverSourceHint && deps.toNonEmptyString(alternateMessage)
|
||||||
|
? hasShortValueFlowRetargetCue(String(alternateMessage ?? ""))
|
||||||
|
: false;
|
||||||
let hasPrimaryFollowupSignal = deps.hasAddressFollowupContextSignal(userMessage) ||
|
let hasPrimaryFollowupSignal = deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryPurchaseDateVatBridge;
|
inventoryPurchaseDateVatBridge;
|
||||||
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge
|
inventoryPurchaseDateVatBridge
|
||||||
: false;
|
: false;
|
||||||
|
|
@ -403,6 +431,8 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootRestatementAlternate ||
|
hasInventoryRootRestatementAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
@ -420,6 +450,8 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootRestatementAlternate ||
|
hasInventoryRootRestatementAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
@ -442,6 +474,8 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
!hasInventoryRootTemporalFollowupAlternate &&
|
!hasInventoryRootTemporalFollowupAlternate &&
|
||||||
!hasInventoryRootRestatementPrimary &&
|
!hasInventoryRootRestatementPrimary &&
|
||||||
!hasInventoryRootRestatementAlternate &&
|
!hasInventoryRootRestatementAlternate &&
|
||||||
|
!shortValueFlowRetargetPrimary &&
|
||||||
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal) {
|
!hasIndexReferenceSignal) {
|
||||||
|
|
@ -453,6 +487,8 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
!hasInventoryRootTemporalFollowupAlternate &&
|
!hasInventoryRootTemporalFollowupAlternate &&
|
||||||
!hasInventoryRootRestatementPrimary &&
|
!hasInventoryRootRestatementPrimary &&
|
||||||
!hasInventoryRootRestatementAlternate &&
|
!hasInventoryRootRestatementAlternate &&
|
||||||
|
!shortValueFlowRetargetPrimary &&
|
||||||
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal) {
|
!hasIndexReferenceSignal) {
|
||||||
|
|
@ -462,7 +498,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const sourceIntent = (0, assistantContinuityPolicy_1.readAddressDebugIntent)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceIntent = (0, assistantContinuityPolicy_1.readAddressDebugIntent)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryPilotScope = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryPilotScope)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryPilotScope = sourceDiscoveryPilotScopeHint;
|
||||||
const sourceDiscoveryMetadataRouteFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataRouteFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryMetadataRouteFamily = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataRouteFamily)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryMetadataSelectedEntitySet = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataSelectedEntitySet)(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryMetadataSelectedEntitySet = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataSelectedEntitySet)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryMetadataAmbiguityDetected = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityDetected)(carryoverSourceDebug);
|
const sourceDiscoveryMetadataAmbiguityDetected = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityDetected)(carryoverSourceDebug);
|
||||||
|
|
@ -556,12 +592,14 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasPrimaryFollowupSignal =
|
hasPrimaryFollowupSignal =
|
||||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
hasInventoryRootTemporalFollowupPrimary;
|
hasInventoryRootTemporalFollowupPrimary;
|
||||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
hasInventoryRootTemporalFollowupAlternate
|
hasInventoryRootTemporalFollowupAlternate
|
||||||
|
|
@ -577,6 +615,8 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootTemporalFollowupAlternate ||
|
hasInventoryRootTemporalFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
|
||||||
|
|
@ -727,7 +727,10 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||||
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
|
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
|
||||||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
|
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
|
||||||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1")
|
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_value_flow_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_supplier_payout_query_movements_v1" ||
|
||||||
|
followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1")
|
||||||
);
|
);
|
||||||
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(
|
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(
|
||||||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
|
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
|
||||||
|
|
|
||||||
|
|
@ -2563,6 +2563,12 @@ function hasAddressFollowupContextSignal(userMessage) {
|
||||||
if (ultraShortFollowup && hasAny(/^(?:давай|показывай|показывыай|ещ[её]|also|again|go|ok|okay)(?=$|[\s,.;:!?])/iu)) {
|
if (ultraShortFollowup && hasAny(/^(?:давай|показывай|показывыай|ещ[её]|also|again|go|ok|okay)(?=$|[\s,.;:!?])/iu)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
const shortValueFlowRetargetCue = shortFollowup &&
|
||||||
|
(hasMarker() || hasPointer() || hasAny(/^(?:Р°|a|Рё|i|also|then|now)(?=$|[\s,.;:!?])/iu)) &&
|
||||||
|
hasAny(/(?:нетто|сальдо|разниц|получил|заплатил|поступ|входящ|исходящ|оборот|выручк|денеж)/iu);
|
||||||
|
if (shortValueFlowRetargetCue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (hasStandaloneAddressTopicSignal(rawText || repairedText)) {
|
if (hasStandaloneAddressTopicSignal(rawText || repairedText)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,27 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
return sameDatePhrases.some((phrase) => normalized.includes(phrase));
|
return sameDatePhrases.some((phrase) => normalized.includes(phrase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasShortValueFlowRetargetCue(text) {
|
||||||
|
const normalized = normalizeFollowupText(text);
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tokenCount = deps.countTokens(normalized);
|
||||||
|
if (!Number.isFinite(tokenCount) || tokenCount > 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hasLeadCue =
|
||||||
|
deps.hasFollowupMarker(text) ||
|
||||||
|
deps.hasReferentialPointer(text) ||
|
||||||
|
/^(?:\u0430|\u0438|also|then|now)(?=$|[\s,.;:!?])/iu.test(normalized);
|
||||||
|
if (!hasLeadCue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u043d\u0435\u0442\u0442\u043e|\u0441\u0430\u043b\u044c\u0434\u043e|\u0440\u0430\u0437\u043d\u0438\u0446|\u043f\u043e\u043b\u0443\u0447|\u0437\u0430\u043f\u043b\u0430\u0442|\u043f\u043e\u0441\u0442\u0443\u043f|\u0432\u0445\u043e\u0434\u044f\u0449|\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u043e\u0431\u043e\u0440\u043e\u0442|\u0432\u044b\u0440\u0443\u0447\u043a|\u0434\u0435\u043d\u0435\u0436)/iu.test(
|
||||||
|
normalized
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
||||||
const normalized = deps.compactWhitespace(
|
const normalized = deps.compactWhitespace(
|
||||||
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
|
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
|
||||||
|
|
@ -450,6 +471,15 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||||
: false));
|
: false));
|
||||||
const sourceIntentHint = readAddressDebugIntent(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceIntentHint = readAddressDebugIntent(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
|
const sourceDiscoveryPilotScopeHint = readAssistantMcpDiscoveryPilotScope(
|
||||||
|
carryoverSourceDebug,
|
||||||
|
deps.toNonEmptyString
|
||||||
|
);
|
||||||
|
const hasValueFlowCarryoverSourceHint =
|
||||||
|
sourceIntentHint === "customer_revenue_and_payments" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_value_flow_query_movements_v1" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_supplier_payout_query_movements_v1" ||
|
||||||
|
sourceDiscoveryPilotScopeHint === "counterparty_bidirectional_value_flow_query_movements_v1";
|
||||||
const navigationSessionState = resolveNavigationSessionContextState(
|
const navigationSessionState = resolveNavigationSessionContextState(
|
||||||
addressNavigationState,
|
addressNavigationState,
|
||||||
deps.toNonEmptyString,
|
deps.toNonEmptyString,
|
||||||
|
|
@ -485,14 +515,22 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
||||||
: null;
|
: null;
|
||||||
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
||||||
|
const shortValueFlowRetargetPrimary =
|
||||||
|
hasValueFlowCarryoverSourceHint && hasShortValueFlowRetargetCue(userMessage);
|
||||||
|
const shortValueFlowRetargetAlternate =
|
||||||
|
hasValueFlowCarryoverSourceHint && deps.toNonEmptyString(alternateMessage)
|
||||||
|
? hasShortValueFlowRetargetCue(String(alternateMessage ?? ""))
|
||||||
|
: false;
|
||||||
let hasPrimaryFollowupSignal =
|
let hasPrimaryFollowupSignal =
|
||||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryPurchaseDateVatBridge;
|
inventoryPurchaseDateVatBridge;
|
||||||
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge
|
inventoryPurchaseDateVatBridge
|
||||||
: false;
|
: false;
|
||||||
|
|
@ -543,6 +581,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootRestatementAlternate ||
|
hasInventoryRootRestatementAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
@ -561,6 +601,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootRestatementAlternate ||
|
hasInventoryRootRestatementAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
@ -588,6 +630,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
!hasInventoryRootTemporalFollowupAlternate &&
|
!hasInventoryRootTemporalFollowupAlternate &&
|
||||||
!hasInventoryRootRestatementPrimary &&
|
!hasInventoryRootRestatementPrimary &&
|
||||||
!hasInventoryRootRestatementAlternate &&
|
!hasInventoryRootRestatementAlternate &&
|
||||||
|
!shortValueFlowRetargetPrimary &&
|
||||||
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal
|
!hasIndexReferenceSignal
|
||||||
|
|
@ -601,6 +645,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
!hasInventoryRootTemporalFollowupAlternate &&
|
!hasInventoryRootTemporalFollowupAlternate &&
|
||||||
!hasInventoryRootRestatementPrimary &&
|
!hasInventoryRootRestatementPrimary &&
|
||||||
!hasInventoryRootRestatementAlternate &&
|
!hasInventoryRootRestatementAlternate &&
|
||||||
|
!shortValueFlowRetargetPrimary &&
|
||||||
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal
|
!hasIndexReferenceSignal
|
||||||
|
|
@ -611,7 +657,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const sourceIntent = readAddressDebugIntent(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceIntent = readAddressDebugIntent(carryoverSourceDebug, deps.toNonEmptyString);
|
||||||
const sourceDiscoveryPilotScope = readAssistantMcpDiscoveryPilotScope(carryoverSourceDebug, deps.toNonEmptyString);
|
const sourceDiscoveryPilotScope = sourceDiscoveryPilotScopeHint;
|
||||||
const sourceDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily(
|
const sourceDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily(
|
||||||
carryoverSourceDebug,
|
carryoverSourceDebug,
|
||||||
deps.toNonEmptyString
|
deps.toNonEmptyString
|
||||||
|
|
@ -730,12 +776,14 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasPrimaryFollowupSignal =
|
hasPrimaryFollowupSignal =
|
||||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
hasInventoryRootTemporalFollowupPrimary;
|
hasInventoryRootTemporalFollowupPrimary;
|
||||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
hasInventoryRootTemporalFollowupAlternate
|
hasInventoryRootTemporalFollowupAlternate
|
||||||
|
|
@ -751,6 +799,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootTemporalFollowupAlternate ||
|
hasInventoryRootTemporalFollowupAlternate ||
|
||||||
inventoryPurchaseDateVatBridge ||
|
inventoryPurchaseDateVatBridge ||
|
||||||
Boolean(debtRoleSwapIntent) ||
|
Boolean(debtRoleSwapIntent) ||
|
||||||
|
shortValueFlowRetargetPrimary ||
|
||||||
|
shortValueFlowRetargetAlternate ||
|
||||||
deps.hasFollowupMarker(userMessage) ||
|
deps.hasFollowupMarker(userMessage) ||
|
||||||
deps.hasReferentialPointer(userMessage) ||
|
deps.hasReferentialPointer(userMessage) ||
|
||||||
(deps.toNonEmptyString(alternateMessage)
|
(deps.toNonEmptyString(alternateMessage)
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,80 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overrides a supported exact payout intent when a grounded value-flow follow-up switches from incoming to outgoing", () => {
|
||||||
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
userMessage: "а теперь сколько заплатили?",
|
||||||
|
assistantTurnMeaning: {
|
||||||
|
asked_domain_family: "counterparty",
|
||||||
|
asked_action_family: "turnover",
|
||||||
|
explicit_intent_candidate: "customer_revenue_and_payments"
|
||||||
|
},
|
||||||
|
followupContext: {
|
||||||
|
previous_discovery_pilot_scope: "counterparty_value_flow_query_movements_v1",
|
||||||
|
previous_filters: {
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
organization: "ООО Альтернатива Плюс",
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2020-12-31"
|
||||||
|
},
|
||||||
|
previous_anchor_type: "counterparty",
|
||||||
|
previous_anchor_value: "Группа СВК"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.adapter_status).toBe("ready");
|
||||||
|
expect(result.should_run_discovery).toBe(true);
|
||||||
|
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||||
|
expect(result.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "payout",
|
||||||
|
explicit_entity_candidates: ["Группа СВК"],
|
||||||
|
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||||||
|
stale_replay_forbidden: true
|
||||||
|
});
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_payout_signal_detected");
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("overrides a supported exact net intent when a grounded payout follow-up switches into net flow", () => {
|
||||||
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
userMessage: "а какое нетто?",
|
||||||
|
assistantTurnMeaning: {
|
||||||
|
asked_domain_family: "counterparty",
|
||||||
|
asked_action_family: "turnover",
|
||||||
|
explicit_intent_candidate: "customer_revenue_and_payments"
|
||||||
|
},
|
||||||
|
followupContext: {
|
||||||
|
previous_discovery_pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||||
|
previous_filters: {
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
period_from: "2021-01-01",
|
||||||
|
period_to: "2021-12-31"
|
||||||
|
},
|
||||||
|
previous_anchor_type: "counterparty",
|
||||||
|
previous_anchor_value: "Группа СВК"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.adapter_status).toBe("ready");
|
||||||
|
expect(result.should_run_discovery).toBe(true);
|
||||||
|
expect(result.semantic_data_need).toBe("counterparty value-flow evidence");
|
||||||
|
expect(result.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "net_value_flow",
|
||||||
|
explicit_entity_candidates: ["Группа СВК"],
|
||||||
|
explicit_date_scope: "2021",
|
||||||
|
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||||||
|
stale_replay_forbidden: true
|
||||||
|
});
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_grounded_value_flow_followup");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_bidirectional_value_flow_signal_detected");
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||||
|
});
|
||||||
|
|
||||||
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
it("seeds short monthly follow-up from prior bidirectional discovery context", () => {
|
||||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
userMessage: "а по месяцам?",
|
userMessage: "а по месяцам?",
|
||||||
|
|
|
||||||
|
|
@ -796,6 +796,7 @@ describe("assistantTransitionPolicy", () => {
|
||||||
|
|
||||||
it("retargets selected-object provenance follow-up from inventory root when semantic scope is already detected", () => {
|
it("retargets selected-object provenance follow-up from inventory root when semantic scope is already detected", () => {
|
||||||
const policy = buildPolicy({
|
const policy = buildPolicy({
|
||||||
|
hasAddressFollowupContextSignal: () => true,
|
||||||
findLastAddressAssistantItem: () => ({
|
findLastAddressAssistantItem: () => ({
|
||||||
text: "На 31.03.2016 на складе подтверждено 2 позиции.",
|
text: "На 31.03.2016 на складе подтверждено 2 позиции.",
|
||||||
debug: {
|
debug: {
|
||||||
|
|
@ -810,8 +811,7 @@ describe("assistantTransitionPolicy", () => {
|
||||||
anchor_type: "organization",
|
anchor_type: "organization",
|
||||||
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
anchor_value_resolved: 'ООО "Альтернатива Плюс"'
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
hasAddressFollowupContextSignal: () => true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||||
|
|
@ -1173,6 +1173,48 @@ describe("assistantTransitionPolicy", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps exact payout carryover for a short net follow-up without restating counterparty or year", () => {
|
||||||
|
const policy = buildPolicy({
|
||||||
|
findLastAddressAssistantItem: () => ({
|
||||||
|
role: "assistant",
|
||||||
|
text: "Платежи по Группа СВК за 2021",
|
||||||
|
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: {
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
period_from: "2021-01-01",
|
||||||
|
period_to: "2021-12-31"
|
||||||
|
},
|
||||||
|
anchor_type: "counterparty",
|
||||||
|
anchor_value_resolved: "Группа СВК"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
hasAddressFollowupContextSignal: () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||||
|
"а какое нетто?",
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(carryover?.followupSelectionMode).toBe("carry_previous_intent");
|
||||||
|
expect(carryover?.followupContext?.previous_intent).toBe("customer_revenue_and_payments");
|
||||||
|
expect(carryover?.followupContext?.target_intent).toBe("customer_revenue_and_payments");
|
||||||
|
expect(carryover?.followupContext?.previous_anchor_type).toBe("counterparty");
|
||||||
|
expect(carryover?.followupContext?.previous_anchor_value).toBe("Группа СВК");
|
||||||
|
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
period_from: "2021-01-01",
|
||||||
|
period_to: "2021-12-31"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("carries grounded metadata downstream route hints into followup context", () => {
|
it("carries grounded metadata downstream route hints into followup context", () => {
|
||||||
const policy = buildPolicy({
|
const policy = buildPolicy({
|
||||||
findLastAddressAssistantItem: () => null,
|
findLastAddressAssistantItem: () => null,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue