Закрепить бизнес-обзор контрагентских потоков
This commit is contained in:
parent
77c88a018a
commit
1507ca2bb9
|
|
@ -2,6 +2,7 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = void 0;
|
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = void 0;
|
||||||
exports.buildAssistantMcpDiscoveryDataNeedGraph = buildAssistantMcpDiscoveryDataNeedGraph;
|
exports.buildAssistantMcpDiscoveryDataNeedGraph = buildAssistantMcpDiscoveryDataNeedGraph;
|
||||||
|
const addressTextRepair_1 = require("./addressTextRepair");
|
||||||
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = "assistant_data_need_graph_v1";
|
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = "assistant_data_need_graph_v1";
|
||||||
function toNonEmptyString(value) {
|
function toNonEmptyString(value) {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
|
|
@ -203,6 +204,28 @@ function hasDetailedBusinessOverviewMoneyBreakdownHint(rawUtterance) {
|
||||||
}
|
}
|
||||||
return /(?:\u0440\u0430\u0441\u043a\u0440\u043e\p{L}*|\u043f\u043e\u0434\u0440\u043e\u0431\p{L}*|\u0440\u0430\u0437\u0431\u0435\p{L}*|\u0440\u0430\u0437\u0432\u0435\u0440\u043d\p{L}*|\u043a\u0442\u043e\s+\u0433\u043b\u0430\u0432\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043a\u043b\u0438\u0435\u043d\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043f\u043e\u0441\u0442\u0430\u0432\p{L}*|\u0447\u0438\u0441\u0442\p{L}*\s+\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043f\u043e\u0442\u043e\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0432\u0445\u043e\u0434\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\p{L}*|detail|breakdown|drill\s*down|main\s+customer|main\s+supplier)/iu.test(rawUtterance);
|
return /(?:\u0440\u0430\u0441\u043a\u0440\u043e\p{L}*|\u043f\u043e\u0434\u0440\u043e\u0431\p{L}*|\u0440\u0430\u0437\u0431\u0435\p{L}*|\u0440\u0430\u0437\u0432\u0435\u0440\u043d\p{L}*|\u043a\u0442\u043e\s+\u0433\u043b\u0430\u0432\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043a\u043b\u0438\u0435\u043d\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043f\u043e\u0441\u0442\u0430\u0432\p{L}*|\u0447\u0438\u0441\u0442\p{L}*\s+\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043f\u043e\u0442\u043e\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0432\u0445\u043e\u0434\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\p{L}*|detail|breakdown|drill\s*down|main\s+customer|main\s+supplier)/iu.test(rawUtterance);
|
||||||
}
|
}
|
||||||
|
function hasWideBusinessOverviewSurfaceHint(rawUtterance) {
|
||||||
|
if (!rawUtterance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const textVariants = Array.from(new Set([rawUtterance, (0, addressTextRepair_1.normalizeRussianComparableText)(rawUtterance)]));
|
||||||
|
if (textVariants.some((text) => /(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0434\u0435\u043d\p{L}*)/iu.test(text))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hasBroadCue = textVariants.some((text) => /(?:\u0431\u0438\u0437\u043d\u0435\u0441[-\s]*\u043e\u0431\u0437\u043e\u0440|\u043e\u0431\u0437\u043e\u0440\s+\u0431\u0438\u0437\u043d\u0435\u0441|\u0432\u0437\u0440\u043e\u0441\u043b\p{L}*\s+\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\p{L}*\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0446\u0435\u043d\p{L}*\s+\u0431\u0438\u0437\u043d\u0435\u0441|business\s+overview|business\s+review|company\s+overview)/iu.test(text));
|
||||||
|
if (!hasBroadCue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const surfaceMarkers = [
|
||||||
|
/(?:\u043d\u0434\u0441|vat|\u043d\u0430\u043b\u043e\u0433\p{L}*)/iu,
|
||||||
|
/(?:\u0434\u043e\u043b\u0433\p{L}*|\u0437\u0430\u0434\u043e\u043b\u0436\p{L}*|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440)/iu,
|
||||||
|
/(?:\u0441\u043a\u043b\u0430\u0434|\u043e\u0441\u0442\u0430\u0442\u043a|\u0437\u0430\u043f\u0430\u0441|\u0442\u043e\u0432\u0430\u0440\p{L}*)/iu,
|
||||||
|
/(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b)/iu,
|
||||||
|
/(?:\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b)/iu,
|
||||||
|
/(?:\u0447\u0442\u043e\s+(?:\u043f\u043e\u043a\u0430\s+)?\u043d\u0435\u043b\u044c\u0437\u044f\s+\u0443\u0442\u0432\u0435\u0440\u0436\u0434|\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d|\u0433\u0440\u0430\u043d\u0438\u0446\p{L}*)/iu
|
||||||
|
];
|
||||||
|
return textVariants.some((text) => surfaceMarkers.filter((marker) => marker.test(text)).length >= 3);
|
||||||
|
}
|
||||||
function supportsOrganizationScopedOpenTotal(action) {
|
function supportsOrganizationScopedOpenTotal(action) {
|
||||||
return action === "turnover" || action === "payout";
|
return action === "turnover" || action === "payout";
|
||||||
}
|
}
|
||||||
|
|
@ -421,7 +444,8 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
const rawAction = lower(turnMeaning?.asked_action_family);
|
const rawAction = lower(turnMeaning?.asked_action_family);
|
||||||
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
||||||
const rawUtterance = lower(input.rawUtterance);
|
const rawUtterance = lower(input.rawUtterance);
|
||||||
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
const rawQuestionSource = [input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" ");
|
||||||
|
const rawQuestionSignal = lower(rawQuestionSource);
|
||||||
const aggregationAxis = lower(turnMeaning?.asked_aggregation_axis);
|
const aggregationAxis = lower(turnMeaning?.asked_aggregation_axis);
|
||||||
const seededRankingNeed = toNonEmptyString(turnMeaning?.seeded_ranking_need);
|
const seededRankingNeed = toNonEmptyString(turnMeaning?.seeded_ranking_need);
|
||||||
const explicitDateScope = toNonEmptyString(turnMeaning?.explicit_date_scope);
|
const explicitDateScope = toNonEmptyString(turnMeaning?.explicit_date_scope);
|
||||||
|
|
@ -439,10 +463,13 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
});
|
});
|
||||||
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
||||||
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
||||||
|
const wideBusinessOverviewSurfaceHint = hasWideBusinessOverviewSurfaceHint(rawQuestionSource);
|
||||||
const oneSidedBusinessOverviewValueFlowOverride = initialBusinessFactFamily === "business_overview" &&
|
const oneSidedBusinessOverviewValueFlowOverride = initialBusinessFactFamily === "business_overview" &&
|
||||||
Boolean(oneSidedValueAction) &&
|
Boolean(oneSidedValueAction) &&
|
||||||
!detailedBusinessOverviewMoneyBreakdownHint;
|
!detailedBusinessOverviewMoneyBreakdownHint;
|
||||||
const bidirectionalBusinessOverviewValueFlowOverride = initialBusinessFactFamily === "business_overview" && hasIncomingVsOutgoingComparisonHint(rawQuestionSignal);
|
const bidirectionalBusinessOverviewValueFlowOverride = initialBusinessFactFamily === "business_overview" &&
|
||||||
|
hasIncomingVsOutgoingComparisonHint(rawQuestionSignal) &&
|
||||||
|
!wideBusinessOverviewSurfaceHint;
|
||||||
const oneSidedValueFlowActionOverride = initialBusinessFactFamily === "value_flow" &&
|
const oneSidedValueFlowActionOverride = initialBusinessFactFamily === "value_flow" &&
|
||||||
Boolean(oneSidedValueAction) &&
|
Boolean(oneSidedValueAction) &&
|
||||||
(rawAction === "counterparty_value_or_turnover" || rawAction === "counterparty_value");
|
(rawAction === "counterparty_value_or_turnover" || rawAction === "counterparty_value");
|
||||||
|
|
@ -574,6 +601,9 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
if (businessFactFamily === "business_overview" && !explicitDateScope) {
|
if (businessFactFamily === "business_overview" && !explicitDateScope) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_defaults_to_all_time_scope");
|
pushReason(reasonCodes, "data_need_graph_business_overview_defaults_to_all_time_scope");
|
||||||
}
|
}
|
||||||
|
if (initialBusinessFactFamily === "business_overview" && wideBusinessOverviewSurfaceHint) {
|
||||||
|
pushReason(reasonCodes, "data_need_graph_business_overview_wide_surface_preserved");
|
||||||
|
}
|
||||||
if (directBusinessOverviewMoneyAnswerHint) {
|
if (directBusinessOverviewMoneyAnswerHint) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,51 @@ function hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarificati
|
||||||
const boundedMovementAnswerAllowed = bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
const boundedMovementAnswerAllowed = bridge?.user_facing_response_allowed === true && bridge?.business_fact_answer_allowed === true;
|
||||||
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
||||||
}
|
}
|
||||||
|
function hasBusinessOverviewBoundaryCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate) {
|
||||||
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.candidate_status !== "clarification_candidate" && candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const discoveryFamily = toNonEmptyString(graph?.business_fact_family);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const separateCounterpartyCandidates = readStringArray(turnMeaning?.business_overview_separate_entity_candidates);
|
||||||
|
const hasPreviousCounterpartyBundle = Boolean(toRecordObject(turnMeaning?.previous_counterparty_value_flow_bundle) ||
|
||||||
|
toRecordObject(turnMeaning?.previous_counterparty_document_bundle));
|
||||||
|
const asksForBoundarySummary = Boolean((selectedChainId === "business_overview" ||
|
||||||
|
discoveryFamily === "business_overview" ||
|
||||||
|
askedDomain === "business_overview") &&
|
||||||
|
(separateCounterpartyCandidates.length > 0 || hasPreviousCounterpartyBundle));
|
||||||
|
const truthAnswerShape = readTruthAnswerShape(input);
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const capabilityId = toNonEmptyString(input.addressRuntimeMeta?.capability_id) ??
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_contract_id) ??
|
||||||
|
toNonEmptyString(truthAnswerShape?.capability_contract_id);
|
||||||
|
const missingFilters = [
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_required_filters),
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_anchors),
|
||||||
|
...readStringArray(toRecordObject(input.addressRuntimeMeta?.capability_binding_contract)?.missing_anchors)
|
||||||
|
];
|
||||||
|
const staleInventoryItemClarification = Boolean(missingFilters.includes("item") &&
|
||||||
|
(detectedIntent?.startsWith("inventory_") ||
|
||||||
|
selectedRecipe?.includes("inventory_") ||
|
||||||
|
capabilityId?.includes("inventory_")));
|
||||||
|
return asksForBoundarySummary && staleInventoryItemClarification;
|
||||||
|
}
|
||||||
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate) {
|
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate) {
|
||||||
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -934,11 +979,13 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
const discoveryClarificationCandidatePriority = hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
const discoveryClarificationCandidatePriority = hasDiscoveryClarificationCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
||||||
const groundedValueFlowCandidatePriority = hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
const groundedValueFlowCandidatePriority = hasGroundedValueFlowCandidatePriorityOverCurrentClarification(input, entryPoint, candidate);
|
||||||
const groundedMovementCandidatePriority = hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
const groundedMovementCandidatePriority = hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
|
const businessOverviewBoundaryCandidatePriority = hasBusinessOverviewBoundaryCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
const groundedValueFlowRankingCandidatePriority = hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
const groundedValueFlowRankingCandidatePriority = hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
||||||
const currentClarificationProtectsCurrentReply = currentClarificationRequiredAddressReply &&
|
const currentClarificationProtectsCurrentReply = currentClarificationRequiredAddressReply &&
|
||||||
!discoveryClarificationCandidatePriority &&
|
!discoveryClarificationCandidatePriority &&
|
||||||
!groundedValueFlowCandidatePriority &&
|
!groundedValueFlowCandidatePriority &&
|
||||||
!groundedMovementCandidatePriority &&
|
!groundedMovementCandidatePriority &&
|
||||||
|
!businessOverviewBoundaryCandidatePriority &&
|
||||||
!groundedValueFlowRankingCandidatePriority;
|
!groundedValueFlowRankingCandidatePriority;
|
||||||
const alignedFactualAddressReplyProtectsCurrent = alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
const alignedFactualAddressReplyProtectsCurrent = alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
const matchedFactualAddressContinuationTargetProtectsCurrent = matchedFactualAddressContinuationTarget && !groundedValueFlowRankingCandidatePriority;
|
const matchedFactualAddressContinuationTargetProtectsCurrent = matchedFactualAddressContinuationTarget && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
|
@ -989,6 +1036,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
if (groundedMovementCandidatePriority) {
|
if (groundedMovementCandidatePriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification");
|
||||||
}
|
}
|
||||||
|
if (businessOverviewBoundaryCandidatePriority) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification");
|
||||||
|
}
|
||||||
if (groundedValueFlowRankingCandidatePriority) {
|
if (groundedValueFlowRankingCandidatePriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_value_flow_ranking_candidate_priority_over_current_exact_reply");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_grounded_value_flow_ranking_candidate_priority_over_current_exact_reply");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { AssistantMcpDiscoveryTurnMeaningRef } from "./assistantMcpDiscoveryPolicy";
|
import type { AssistantMcpDiscoveryTurnMeaningRef } from "./assistantMcpDiscoveryPolicy";
|
||||||
|
import { normalizeRussianComparableText } from "./addressTextRepair";
|
||||||
|
|
||||||
export const ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION =
|
export const ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION =
|
||||||
"assistant_data_need_graph_v1" as const;
|
"assistant_data_need_graph_v1" as const;
|
||||||
|
|
@ -307,6 +308,40 @@ function hasDetailedBusinessOverviewMoneyBreakdownHint(rawUtterance: string): bo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasWideBusinessOverviewSurfaceHint(rawUtterance: string): boolean {
|
||||||
|
if (!rawUtterance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const textVariants = Array.from(new Set([rawUtterance, normalizeRussianComparableText(rawUtterance)]));
|
||||||
|
if (
|
||||||
|
textVariants.some((text) =>
|
||||||
|
/(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0434\u0435\u043d\p{L}*)/iu.test(
|
||||||
|
text
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hasBroadCue =
|
||||||
|
textVariants.some((text) =>
|
||||||
|
/(?:\u0431\u0438\u0437\u043d\u0435\u0441[-\s]*\u043e\u0431\u0437\u043e\u0440|\u043e\u0431\u0437\u043e\u0440\s+\u0431\u0438\u0437\u043d\u0435\u0441|\u0432\u0437\u0440\u043e\u0441\u043b\p{L}*\s+\u0431\u0438\u0437\u043d\u0435\u0441|\u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\p{L}*\s+\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0446\u0435\u043d\p{L}*\s+\u0431\u0438\u0437\u043d\u0435\u0441|business\s+overview|business\s+review|company\s+overview)/iu.test(
|
||||||
|
text
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!hasBroadCue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const surfaceMarkers = [
|
||||||
|
/(?:\u043d\u0434\u0441|vat|\u043d\u0430\u043b\u043e\u0433\p{L}*)/iu,
|
||||||
|
/(?:\u0434\u043e\u043b\u0433\p{L}*|\u0437\u0430\u0434\u043e\u043b\u0436\p{L}*|\u0434\u0435\u0431\u0438\u0442\u043e\u0440|\u043a\u0440\u0435\u0434\u0438\u0442\u043e\u0440)/iu,
|
||||||
|
/(?:\u0441\u043a\u043b\u0430\u0434|\u043e\u0441\u0442\u0430\u0442\u043a|\u0437\u0430\u043f\u0430\u0441|\u0442\u043e\u0432\u0430\u0440\p{L}*)/iu,
|
||||||
|
/(?:\u043a\u043b\u0438\u0435\u043d\u0442|\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b)/iu,
|
||||||
|
/(?:\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b)/iu,
|
||||||
|
/(?:\u0447\u0442\u043e\s+(?:\u043f\u043e\u043a\u0430\s+)?\u043d\u0435\u043b\u044c\u0437\u044f\s+\u0443\u0442\u0432\u0435\u0440\u0436\u0434|\u043d\u0435\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434|\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d|\u0433\u0440\u0430\u043d\u0438\u0446\p{L}*)/iu
|
||||||
|
];
|
||||||
|
return textVariants.some((text) => surfaceMarkers.filter((marker) => marker.test(text)).length >= 3);
|
||||||
|
}
|
||||||
|
|
||||||
function supportsOrganizationScopedOpenTotal(action: string): boolean {
|
function supportsOrganizationScopedOpenTotal(action: string): boolean {
|
||||||
return action === "turnover" || action === "payout";
|
return action === "turnover" || action === "payout";
|
||||||
}
|
}
|
||||||
|
|
@ -565,7 +600,8 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
const rawAction = lower(turnMeaning?.asked_action_family);
|
const rawAction = lower(turnMeaning?.asked_action_family);
|
||||||
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
||||||
const rawUtterance = lower(input.rawUtterance);
|
const rawUtterance = lower(input.rawUtterance);
|
||||||
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
const rawQuestionSource = [input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" ");
|
||||||
|
const rawQuestionSignal = lower(rawQuestionSource);
|
||||||
const aggregationAxis = lower(turnMeaning?.asked_aggregation_axis);
|
const aggregationAxis = lower(turnMeaning?.asked_aggregation_axis);
|
||||||
const seededRankingNeed = toNonEmptyString(turnMeaning?.seeded_ranking_need);
|
const seededRankingNeed = toNonEmptyString(turnMeaning?.seeded_ranking_need);
|
||||||
const explicitDateScope = toNonEmptyString(turnMeaning?.explicit_date_scope);
|
const explicitDateScope = toNonEmptyString(turnMeaning?.explicit_date_scope);
|
||||||
|
|
@ -583,12 +619,15 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
});
|
});
|
||||||
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
||||||
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
||||||
|
const wideBusinessOverviewSurfaceHint = hasWideBusinessOverviewSurfaceHint(rawQuestionSource);
|
||||||
const oneSidedBusinessOverviewValueFlowOverride =
|
const oneSidedBusinessOverviewValueFlowOverride =
|
||||||
initialBusinessFactFamily === "business_overview" &&
|
initialBusinessFactFamily === "business_overview" &&
|
||||||
Boolean(oneSidedValueAction) &&
|
Boolean(oneSidedValueAction) &&
|
||||||
!detailedBusinessOverviewMoneyBreakdownHint;
|
!detailedBusinessOverviewMoneyBreakdownHint;
|
||||||
const bidirectionalBusinessOverviewValueFlowOverride =
|
const bidirectionalBusinessOverviewValueFlowOverride =
|
||||||
initialBusinessFactFamily === "business_overview" && hasIncomingVsOutgoingComparisonHint(rawQuestionSignal);
|
initialBusinessFactFamily === "business_overview" &&
|
||||||
|
hasIncomingVsOutgoingComparisonHint(rawQuestionSignal) &&
|
||||||
|
!wideBusinessOverviewSurfaceHint;
|
||||||
const oneSidedValueFlowActionOverride =
|
const oneSidedValueFlowActionOverride =
|
||||||
initialBusinessFactFamily === "value_flow" &&
|
initialBusinessFactFamily === "value_flow" &&
|
||||||
Boolean(oneSidedValueAction) &&
|
Boolean(oneSidedValueAction) &&
|
||||||
|
|
@ -735,6 +774,9 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
if (businessFactFamily === "business_overview" && !explicitDateScope) {
|
if (businessFactFamily === "business_overview" && !explicitDateScope) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_defaults_to_all_time_scope");
|
pushReason(reasonCodes, "data_need_graph_business_overview_defaults_to_all_time_scope");
|
||||||
}
|
}
|
||||||
|
if (initialBusinessFactFamily === "business_overview" && wideBusinessOverviewSurfaceHint) {
|
||||||
|
pushReason(reasonCodes, "data_need_graph_business_overview_wide_surface_preserved");
|
||||||
|
}
|
||||||
if (directBusinessOverviewMoneyAnswerHint) {
|
if (directBusinessOverviewMoneyAnswerHint) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,66 @@ function hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarificati
|
||||||
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
return asksForMovements && staleInventoryItemClarification && mcpExecutionPerformed && boundedMovementAnswerAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasBusinessOverviewBoundaryCandidatePriorityOverCurrentInventoryItemClarification(
|
||||||
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
||||||
|
candidate: AssistantMcpDiscoveryResponseCandidateContract
|
||||||
|
): boolean {
|
||||||
|
if (!hasCurrentClarificationRequiredAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.candidate_status !== "clarification_candidate" && candidate.candidate_status !== "ready_for_guarded_use") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!candidate.eligible_for_future_hot_runtime || !toNonEmptyString(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (candidate.reply_text && hasInternalMechanics(candidate.reply_text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph = readDiscoveryDataNeedGraph(entryPoint);
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const selectedChainId = readDiscoverySelectedChainId(entryPoint);
|
||||||
|
const discoveryFamily = toNonEmptyString(graph?.business_fact_family);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const separateCounterpartyCandidates = readStringArray(turnMeaning?.business_overview_separate_entity_candidates);
|
||||||
|
const hasPreviousCounterpartyBundle = Boolean(
|
||||||
|
toRecordObject(turnMeaning?.previous_counterparty_value_flow_bundle) ||
|
||||||
|
toRecordObject(turnMeaning?.previous_counterparty_document_bundle)
|
||||||
|
);
|
||||||
|
const asksForBoundarySummary = Boolean(
|
||||||
|
(selectedChainId === "business_overview" ||
|
||||||
|
discoveryFamily === "business_overview" ||
|
||||||
|
askedDomain === "business_overview") &&
|
||||||
|
(separateCounterpartyCandidates.length > 0 || hasPreviousCounterpartyBundle)
|
||||||
|
);
|
||||||
|
|
||||||
|
const truthAnswerShape = readTruthAnswerShape(input);
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
const capabilityId =
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_id) ??
|
||||||
|
toNonEmptyString(input.addressRuntimeMeta?.capability_contract_id) ??
|
||||||
|
toNonEmptyString(truthAnswerShape?.capability_contract_id);
|
||||||
|
const missingFilters = [
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_required_filters),
|
||||||
|
...readStringArray(input.addressRuntimeMeta?.missing_anchors),
|
||||||
|
...readStringArray(toRecordObject(input.addressRuntimeMeta?.capability_binding_contract)?.missing_anchors)
|
||||||
|
];
|
||||||
|
const staleInventoryItemClarification = Boolean(
|
||||||
|
missingFilters.includes("item") &&
|
||||||
|
(detectedIntent?.startsWith("inventory_") ||
|
||||||
|
selectedRecipe?.includes("inventory_") ||
|
||||||
|
capabilityId?.includes("inventory_"))
|
||||||
|
);
|
||||||
|
|
||||||
|
return asksForBoundarySummary && staleInventoryItemClarification;
|
||||||
|
}
|
||||||
|
|
||||||
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
function hasGroundedValueFlowCandidatePriorityOverCurrentClarification(
|
||||||
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null,
|
||||||
|
|
@ -1268,6 +1328,8 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
);
|
);
|
||||||
const groundedMovementCandidatePriority =
|
const groundedMovementCandidatePriority =
|
||||||
hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
hasGroundedMovementCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
|
const businessOverviewBoundaryCandidatePriority =
|
||||||
|
hasBusinessOverviewBoundaryCandidatePriorityOverCurrentInventoryItemClarification(input, entryPoint, candidate);
|
||||||
const groundedValueFlowRankingCandidatePriority =
|
const groundedValueFlowRankingCandidatePriority =
|
||||||
hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
hasGroundedValueFlowRankingCandidatePriorityOverCurrentExactReply(input, entryPoint, candidate);
|
||||||
const currentClarificationProtectsCurrentReply =
|
const currentClarificationProtectsCurrentReply =
|
||||||
|
|
@ -1275,6 +1337,7 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
!discoveryClarificationCandidatePriority &&
|
!discoveryClarificationCandidatePriority &&
|
||||||
!groundedValueFlowCandidatePriority &&
|
!groundedValueFlowCandidatePriority &&
|
||||||
!groundedMovementCandidatePriority &&
|
!groundedMovementCandidatePriority &&
|
||||||
|
!businessOverviewBoundaryCandidatePriority &&
|
||||||
!groundedValueFlowRankingCandidatePriority;
|
!groundedValueFlowRankingCandidatePriority;
|
||||||
const alignedFactualAddressReplyProtectsCurrent =
|
const alignedFactualAddressReplyProtectsCurrent =
|
||||||
alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
alignedFactualAddressReply && !groundedValueFlowRankingCandidatePriority;
|
||||||
|
|
@ -1341,6 +1404,12 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
"mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification"
|
"mcp_discovery_response_policy_grounded_movement_candidate_priority_over_inventory_item_clarification"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (businessOverviewBoundaryCandidatePriority) {
|
||||||
|
pushReason(
|
||||||
|
reasonCodes,
|
||||||
|
"mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification"
|
||||||
|
);
|
||||||
|
}
|
||||||
if (groundedValueFlowRankingCandidatePriority) {
|
if (groundedValueFlowRankingCandidatePriority) {
|
||||||
pushReason(
|
pushReason(
|
||||||
reasonCodes,
|
reasonCodes,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
|
import iconv from "iconv-lite";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { buildAssistantMcpDiscoveryDataNeedGraph } from "../src/services/assistantMcpDiscoveryDataNeedGraph";
|
import { buildAssistantMcpDiscoveryDataNeedGraph } from "../src/services/assistantMcpDiscoveryDataNeedGraph";
|
||||||
|
|
||||||
|
function toWin1251Mojibake(value: string): string {
|
||||||
|
return iconv.decode(Buffer.from(value, "utf8"), "win1251");
|
||||||
|
}
|
||||||
|
|
||||||
describe("assistant MCP discovery data need graph", () => {
|
describe("assistant MCP discovery data need graph", () => {
|
||||||
it("builds a monthly bidirectional value-flow graph from grounded turn meaning", () => {
|
it("builds a monthly bidirectional value-flow graph from grounded turn meaning", () => {
|
||||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
|
|
@ -403,6 +408,59 @@ describe("assistant MCP discovery data need graph", () => {
|
||||||
expect(result.reason_codes).toContain("data_need_graph_comparison_incoming_vs_outgoing");
|
expect(result.reason_codes).toContain("data_need_graph_comparison_incoming_vs_outgoing");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps a wide formal business overview on the business-overview graph even when money net is requested", () => {
|
||||||
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
|
semanticDataNeed: "business overview evidence with bounded analyst interpretation",
|
||||||
|
rawUtterance:
|
||||||
|
"\u0414\u0430\u0439 \u0432\u0437\u0440\u043e\u0441\u043b\u044b\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u0437\u0430 2020 \u0433\u043e\u0434 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0434\u0435\u043d\u044c\u0433\u0438, \u043d\u0435\u0442\u0442\u043e, \u041d\u0414\u0421, \u0434\u043e\u043b\u0433\u0438, \u0441\u043a\u043b\u0430\u0434, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c.",
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
explicit_organization_scope:
|
||||||
|
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.business_fact_family).toBe("business_overview");
|
||||||
|
expect(result.action_family).toBe("broad_evaluation");
|
||||||
|
expect(result.comparison_need).toBeNull();
|
||||||
|
expect(result.subject_candidates).toEqual([]);
|
||||||
|
expect(result.reason_codes).toContain("data_need_graph_business_overview_wide_surface_preserved");
|
||||||
|
expect(result.reason_codes).not.toContain(
|
||||||
|
"data_need_graph_business_overview_bidirectional_money_comparison_routed_to_value_flow"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps a wide formal business overview when the runtime signal is win1251 mojibake", () => {
|
||||||
|
const wideQuestion =
|
||||||
|
"\u0414\u0430\u0439 \u0432\u0437\u0440\u043e\u0441\u043b\u044b\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u0431\u0437\u043e\u0440 \u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441 \u0437\u0430 2020 \u0433\u043e\u0434 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c 1\u0421: \u043e\u0431\u043e\u0440\u043e\u0442\u044b, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0434\u0435\u043d\u044c\u0433\u0438, \u043d\u0435\u0442\u0442\u043e, \u041d\u0414\u0421, \u0434\u043e\u043b\u0433\u0438, \u0441\u043a\u043b\u0430\u0434, \u043a\u043b\u0438\u0435\u043d\u0442\u044b, \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043d\u0435\u043b\u044c\u0437\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c.";
|
||||||
|
const legacyQuestion = toWin1251Mojibake(wideQuestion);
|
||||||
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
|
semanticDataNeed: "business overview evidence with bounded analyst interpretation",
|
||||||
|
rawUtterance: legacyQuestion,
|
||||||
|
turnMeaning: {
|
||||||
|
raw_message: legacyQuestion,
|
||||||
|
effective_message: legacyQuestion,
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
explicit_organization_scope:
|
||||||
|
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.business_fact_family).toBe("business_overview");
|
||||||
|
expect(result.action_family).toBe("broad_evaluation");
|
||||||
|
expect(result.comparison_need).toBeNull();
|
||||||
|
expect(result.reason_codes).toContain("data_need_graph_business_overview_wide_surface_preserved");
|
||||||
|
expect(result.reason_codes).not.toContain(
|
||||||
|
"data_need_graph_business_overview_bidirectional_money_comparison_routed_to_value_flow"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("treats a generic incoming total as an understood open-scope ask that still needs organization", () => {
|
it("treats a generic incoming total as an understood open-scope ask that still needs organization", () => {
|
||||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
semanticDataNeed: "counterparty value-flow evidence",
|
semanticDataNeed: "counterparty value-flow evidence",
|
||||||
|
|
|
||||||
|
|
@ -525,6 +525,177 @@ describe("assistant MCP discovery response policy", () => {
|
||||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses a business-overview boundary clarification over a stale inventory-item clarification", () => {
|
||||||
|
const valueFlowBundle = {
|
||||||
|
counterparty: "Group SVK",
|
||||||
|
incoming_customer_revenue: { total_amount_human_ru: "20 653 490 rub." },
|
||||||
|
outgoing_supplier_payout: { total_amount_human_ru: "2 129 651 rub." },
|
||||||
|
net_amount_human_ru: "18 523 839 rub.",
|
||||||
|
net_direction: "net_incoming"
|
||||||
|
};
|
||||||
|
const documentBundle = { counterparty: "Group SVK", document_count: 19 };
|
||||||
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
|
currentReply: "Need item before I can continue.",
|
||||||
|
currentReplySource: "address_query_runtime_v1",
|
||||||
|
currentReplyType: "partial_coverage",
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
detected_intent: "inventory_purchase_documents_for_item",
|
||||||
|
selected_recipe: "address_inventory_purchase_documents_for_item_v1",
|
||||||
|
capability_id: "inventory_inventory_purchase_documents_for_item",
|
||||||
|
truth_mode: "clarification_required",
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
missing_required_filters: ["item"],
|
||||||
|
answer_shape_contract: {
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
reply_type: "partial_coverage",
|
||||||
|
capability_contract_id: "inventory_inventory_purchase_documents_for_item"
|
||||||
|
},
|
||||||
|
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
should_run_discovery: true,
|
||||||
|
turn_meaning_ref: {
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
business_overview_separate_entity_candidates: ["Group SVK"],
|
||||||
|
previous_counterparty_value_flow_bundle: valueFlowBundle,
|
||||||
|
previous_counterparty_document_bundle: documentBundle,
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation"
|
||||||
|
},
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "business_overview",
|
||||||
|
action_family: "broad_evaluation",
|
||||||
|
clarification_gaps: ["organization"],
|
||||||
|
reason_codes: ["data_need_graph_family_business_overview"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "needs_clarification",
|
||||||
|
user_facing_response_allowed: true,
|
||||||
|
business_fact_answer_allowed: false,
|
||||||
|
requires_user_clarification: true,
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "needs_clarification",
|
||||||
|
headline: "Specify organization.",
|
||||||
|
confirmed_lines: [],
|
||||||
|
inference_lines: [],
|
||||||
|
unknown_lines: [],
|
||||||
|
limitation_lines: [],
|
||||||
|
next_step_line: "Specify organization."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.applied).toBe(true);
|
||||||
|
expect(result.decision).toBe("apply_candidate");
|
||||||
|
expect(result.reply_text).toContain("Group SVK");
|
||||||
|
expect(result.reply_text).toContain("20 653 490 rub.");
|
||||||
|
expect(result.reply_text).toContain("19");
|
||||||
|
expect(result.reply_text).not.toContain("Need item");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification"
|
||||||
|
);
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses a ready business-overview boundary answer over a stale inventory-item clarification", () => {
|
||||||
|
const valueFlowBundle = {
|
||||||
|
counterparty: "Group SVK",
|
||||||
|
incoming_customer_revenue: { total_amount_human_ru: "20 653 490 rub." },
|
||||||
|
outgoing_supplier_payout: { total_amount_human_ru: "2 129 651 rub." },
|
||||||
|
net_amount_human_ru: "18 523 839 rub.",
|
||||||
|
net_direction: "net_incoming"
|
||||||
|
};
|
||||||
|
const documentBundle = { counterparty: "Group SVK", document_count: 19 };
|
||||||
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
|
currentReply: "Need item before I can continue.",
|
||||||
|
currentReplySource: "address_query_runtime_v1",
|
||||||
|
currentReplyType: "partial_coverage",
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
detected_intent: "inventory_purchase_documents_for_item",
|
||||||
|
selected_recipe: "address_inventory_purchase_documents_for_item_v1",
|
||||||
|
capability_id: "inventory_inventory_purchase_documents_for_item",
|
||||||
|
truth_mode: "clarification_required",
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
missing_required_filters: ["item"],
|
||||||
|
answer_shape_contract: {
|
||||||
|
answer_shape: "clarification_required",
|
||||||
|
reply_type: "partial_coverage",
|
||||||
|
capability_contract_id: "inventory_inventory_purchase_documents_for_item"
|
||||||
|
},
|
||||||
|
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||||
|
hot_runtime_wired: true,
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
should_run_discovery: true,
|
||||||
|
turn_meaning_ref: {
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
explicit_organization_scope: "Alternative Plus",
|
||||||
|
business_overview_separate_entity_candidates: ["Group SVK"],
|
||||||
|
previous_counterparty_value_flow_bundle: valueFlowBundle,
|
||||||
|
previous_counterparty_document_bundle: documentBundle,
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation"
|
||||||
|
},
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "business_overview",
|
||||||
|
action_family: "broad_evaluation",
|
||||||
|
clarification_gaps: [],
|
||||||
|
reason_codes: ["data_need_graph_family_business_overview"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "answer_draft_ready",
|
||||||
|
hot_runtime_wired: true,
|
||||||
|
user_facing_response_allowed: true,
|
||||||
|
business_fact_answer_allowed: true,
|
||||||
|
requires_user_clarification: false,
|
||||||
|
execution_handoff: {
|
||||||
|
mcp_execution_performed: true,
|
||||||
|
can_use_guarded_response: true,
|
||||||
|
must_keep_internal_mechanics_hidden: true
|
||||||
|
},
|
||||||
|
pilot: {
|
||||||
|
pilot_scope: "business_overview_route_template_v1",
|
||||||
|
mcp_execution_performed: true,
|
||||||
|
derived_business_overview: {
|
||||||
|
period_scope: "all_time",
|
||||||
|
incoming_customer_revenue: { total_amount_human_ru: "285 819 547 rub." },
|
||||||
|
outgoing_supplier_payout: { total_amount_human_ru: "137 963 720 rub." },
|
||||||
|
net_amount_human_ru: "147 855 827 rub.",
|
||||||
|
net_direction: "net_incoming",
|
||||||
|
top_customers: [],
|
||||||
|
top_suppliers: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "confirmed_with_bounded_inference",
|
||||||
|
headline: "Company summary.",
|
||||||
|
confirmed_lines: [],
|
||||||
|
inference_lines: [],
|
||||||
|
unknown_lines: [],
|
||||||
|
limitation_lines: [],
|
||||||
|
next_step_line: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.applied).toBe(true);
|
||||||
|
expect(result.decision).toBe("apply_candidate");
|
||||||
|
expect(result.reply_text).toContain("Alternative Plus");
|
||||||
|
expect(result.reply_text).toContain("Group SVK");
|
||||||
|
expect(result.reply_text).toContain("20 653 490 rub.");
|
||||||
|
expect(result.reply_text).not.toContain("Need item");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"mcp_discovery_response_policy_business_overview_boundary_candidate_priority_over_inventory_item_clarification"
|
||||||
|
);
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_current_clarification_required_reply");
|
||||||
|
});
|
||||||
|
|
||||||
it("overrides exact document-list replies for explicit counterparty received-paid-net questions", () => {
|
it("overrides exact document-list replies for explicit counterparty received-paid-net questions", () => {
|
||||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
currentReply: "Контрагент: Группа СВК. Найдено документов: 19.",
|
currentReply: "Контрагент: Группа СВК. Найдено документов: 19.",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue