Согласовать MCP planner с value-flow цепочками
This commit is contained in:
parent
924f6fb0ea
commit
4c00d8c854
|
|
@ -622,9 +622,7 @@ function searchAssistantMcpCatalogPrimitivesByDecompositionCandidates(input) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (const contract of PRIMITIVE_CONTRACTS) {
|
for (const contract of PRIMITIVE_CONTRACTS) {
|
||||||
if (contract.primitive_id === "aggregate_by_axis" &&
|
if (contract.primitive_id === "aggregate_by_axis" && !allowAggregateByAxis) {
|
||||||
normalizedCandidate === "aggregate_by_month" &&
|
|
||||||
!allowAggregateByAxis) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!contract.decomposition_hints.includes(normalizedCandidate)) {
|
if (!contract.decomposition_hints.includes(normalizedCandidate)) {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,13 @@ function dateScopeToFilters(dateScope) {
|
||||||
period_to: `${yearMatch[1]}-12-31`
|
period_to: `${yearMatch[1]}-12-31`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const rangeMatch = dateScope.match(/^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/);
|
||||||
|
if (rangeMatch) {
|
||||||
|
return {
|
||||||
|
period_from: rangeMatch[1],
|
||||||
|
period_to: rangeMatch[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||||
if (dateMatch) {
|
if (dateMatch) {
|
||||||
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,26 @@ function preferredPrimitiveFromExplicitDataNeedGraph(graph) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
function catalogTemplateIdFromChainId(chainId) {
|
||||||
|
if (chainId === "metadata_lane_clarification") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return chainId;
|
||||||
|
}
|
||||||
|
function promoteConfirmedMetadataSurfaceChainTemplate(input) {
|
||||||
|
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(input.metadataSurface);
|
||||||
|
const selectedChainId = input.selectedChainId ?? null;
|
||||||
|
if (!surfaceRouteFamily || !selectedChainId || selectedChainId !== surfaceRouteFamily) {
|
||||||
|
return input.matches;
|
||||||
|
}
|
||||||
|
if (input.matches[0] === selectedChainId || !input.matches.includes(selectedChainId)) {
|
||||||
|
return input.matches;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
selectedChainId,
|
||||||
|
...input.matches.filter((chainId) => chainId !== selectedChainId)
|
||||||
|
];
|
||||||
|
}
|
||||||
function hasCarriedMetadataSurfaceScoringEvidence(surface) {
|
function hasCarriedMetadataSurfaceScoringEvidence(surface) {
|
||||||
return Boolean(surface &&
|
return Boolean(surface &&
|
||||||
(toNonEmptyString(surface.selected_entity_set) ||
|
(toNonEmptyString(surface.selected_entity_set) ||
|
||||||
|
|
@ -298,7 +318,7 @@ function selectPrimitivesFromGraphAndCatalog(input) {
|
||||||
if (factAxisPrimitives.length > 0) {
|
if (factAxisPrimitives.length > 0) {
|
||||||
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
||||||
}
|
}
|
||||||
const chainTemplateMatches = input.dataNeedGraph
|
const rawChainTemplateMatches = input.dataNeedGraph
|
||||||
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
|
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
|
||||||
business_fact_family: input.dataNeedGraph.business_fact_family,
|
business_fact_family: input.dataNeedGraph.business_fact_family,
|
||||||
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
||||||
|
|
@ -308,8 +328,16 @@ function selectPrimitivesFromGraphAndCatalog(input) {
|
||||||
aggregation_need: input.dataNeedGraph.aggregation_need
|
aggregation_need: input.dataNeedGraph.aggregation_need
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
const chainTemplateMatches = promoteConfirmedMetadataSurfaceChainTemplate({
|
||||||
|
matches: rawChainTemplateMatches,
|
||||||
|
metadataSurface: input.metadataSurface,
|
||||||
|
selectedChainId: input.selectedChainId
|
||||||
|
});
|
||||||
if (chainTemplateMatches.length > 0) {
|
if (chainTemplateMatches.length > 0) {
|
||||||
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
||||||
|
if (rawChainTemplateMatches[0] !== chainTemplateMatches[0]) {
|
||||||
|
reasonCodes.push("planner_catalog_chain_template_promoted_by_confirmed_metadata_surface");
|
||||||
|
}
|
||||||
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
||||||
}
|
}
|
||||||
const combinedCatalogPrimitives = [];
|
const combinedCatalogPrimitives = [];
|
||||||
|
|
@ -353,8 +381,10 @@ function selectPrimitivesFromGraphAndCatalog(input) {
|
||||||
function budgetOverrideFor(input, recipe) {
|
function budgetOverrideFor(input, recipe) {
|
||||||
const meaning = input.turnMeaning ?? null;
|
const meaning = input.turnMeaning ?? null;
|
||||||
const requestedAggregationAxis = aggregationAxis(meaning);
|
const requestedAggregationAxis = aggregationAxis(meaning);
|
||||||
const isValueFlowRecipe = recipe.semanticDataNeed === "counterparty value-flow evidence" &&
|
const isValueFlowRecipe = recipe.primitives.includes("query_movements") &&
|
||||||
recipe.primitives.includes("query_movements");
|
(recipe.semanticDataNeed === "counterparty value-flow evidence" ||
|
||||||
|
recipe.semanticDataNeed === "bidirectional value-flow comparison evidence" ||
|
||||||
|
recipe.semanticDataNeed === "ranked value-flow evidence");
|
||||||
if (!isValueFlowRecipe) {
|
if (!isValueFlowRecipe) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -370,7 +400,7 @@ function catalogChainTemplateMatchesForContract(input, recipe) {
|
||||||
if (!dataNeedGraph) {
|
if (!dataNeedGraph) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
|
const matches = (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
|
||||||
business_fact_family: dataNeedGraph.business_fact_family,
|
business_fact_family: dataNeedGraph.business_fact_family,
|
||||||
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
|
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
|
||||||
required_axes: recipe.axes,
|
required_axes: recipe.axes,
|
||||||
|
|
@ -378,6 +408,11 @@ function catalogChainTemplateMatchesForContract(input, recipe) {
|
||||||
ranking_need: dataNeedGraph.ranking_need,
|
ranking_need: dataNeedGraph.ranking_need,
|
||||||
aggregation_need: dataNeedGraph.aggregation_need
|
aggregation_need: dataNeedGraph.aggregation_need
|
||||||
});
|
});
|
||||||
|
return promoteConfirmedMetadataSurfaceChainTemplate({
|
||||||
|
matches,
|
||||||
|
metadataSurface: input.metadataSurface,
|
||||||
|
selectedChainId: catalogTemplateIdFromChainId(recipe.chainId)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function catalogChainTemplateAlignmentForContract(recipe, matches) {
|
function catalogChainTemplateAlignmentForContract(recipe, matches) {
|
||||||
const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification";
|
const selectedChainIsCatalogTemplate = recipe.chainId !== "metadata_lane_clarification";
|
||||||
|
|
@ -472,7 +507,8 @@ function recipeFor(input) {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "document_evidence"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "document_evidence",
|
chainId: "document_evidence",
|
||||||
|
|
@ -496,7 +532,8 @@ function recipeFor(input) {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "movement_evidence"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "movement_evidence",
|
chainId: "movement_evidence",
|
||||||
|
|
@ -520,7 +557,8 @@ function recipeFor(input) {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "catalog_drilldown"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "catalog_drilldown",
|
chainId: "catalog_drilldown",
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,28 @@ function readStringArray(value) {
|
||||||
? value.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item))
|
? value.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item))
|
||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
function hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint) {
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
|
||||||
|
if (askedDomain !== "counterparty_value") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
if (askedAction === "payout") {
|
||||||
|
return detectedIntent !== "supplier_payouts_profile";
|
||||||
|
}
|
||||||
|
if (askedAction === "net_value_flow") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
function hasExactMatchedFactualAddressReply(input, entryPoint) {
|
function hasExactMatchedFactualAddressReply(input, entryPoint) {
|
||||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -212,6 +234,9 @@ function hasExactMatchedFactualAddressReply(input, entryPoint) {
|
||||||
if (hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint)) {
|
if (hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
||||||
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
|
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
|
||||||
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
|
@ -378,6 +403,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
|
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
|
||||||
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
|
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
|
||||||
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
||||||
|
const valueFlowActionConflictWithDiscoveryTurnMeaning = hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint);
|
||||||
if (!entryPoint) {
|
if (!entryPoint) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +425,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||||
if (semanticConflictWithDiscoveryTurnMeaning) {
|
if (semanticConflictWithDiscoveryTurnMeaning) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
||||||
}
|
}
|
||||||
|
if (valueFlowActionConflictWithDiscoveryTurnMeaning) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_value_flow_action_conflict_allows_candidate_override");
|
||||||
|
}
|
||||||
if (openScopeValueFlowDiscoveryPriority) {
|
if (openScopeValueFlowDiscoveryPriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -766,11 +766,7 @@ export function searchAssistantMcpCatalogPrimitivesByDecompositionCandidates(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const contract of PRIMITIVE_CONTRACTS) {
|
for (const contract of PRIMITIVE_CONTRACTS) {
|
||||||
if (
|
if (contract.primitive_id === "aggregate_by_axis" && !allowAggregateByAxis) {
|
||||||
contract.primitive_id === "aggregate_by_axis" &&
|
|
||||||
normalizedCandidate === "aggregate_by_month" &&
|
|
||||||
!allowAggregateByAxis
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!contract.decomposition_hints.includes(normalizedCandidate)) {
|
if (!contract.decomposition_hints.includes(normalizedCandidate)) {
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,13 @@ function dateScopeToFilters(dateScope: string | null): Pick<AddressFilterSet, "p
|
||||||
period_to: `${yearMatch[1]}-12-31`
|
period_to: `${yearMatch[1]}-12-31`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const rangeMatch = dateScope.match(/^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/);
|
||||||
|
if (rangeMatch) {
|
||||||
|
return {
|
||||||
|
period_from: rangeMatch[1],
|
||||||
|
period_to: rangeMatch[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
const dateMatch = dateScope.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||||
if (dateMatch) {
|
if (dateMatch) {
|
||||||
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
const date = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,34 @@ function preferredPrimitiveFromExplicitDataNeedGraph(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function catalogTemplateIdFromChainId(
|
||||||
|
chainId: AssistantMcpDiscoveryChainId
|
||||||
|
): AssistantMcpCatalogChainTemplateId | null {
|
||||||
|
if (chainId === "metadata_lane_clarification") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return chainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function promoteConfirmedMetadataSurfaceChainTemplate(input: {
|
||||||
|
matches: AssistantMcpCatalogChainTemplateId[];
|
||||||
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
||||||
|
selectedChainId?: AssistantMcpCatalogChainTemplateId | null;
|
||||||
|
}): AssistantMcpCatalogChainTemplateId[] {
|
||||||
|
const surfaceRouteFamily = routeFamilyFromMetadataSurfaceRef(input.metadataSurface);
|
||||||
|
const selectedChainId = input.selectedChainId ?? null;
|
||||||
|
if (!surfaceRouteFamily || !selectedChainId || selectedChainId !== surfaceRouteFamily) {
|
||||||
|
return input.matches;
|
||||||
|
}
|
||||||
|
if (input.matches[0] === selectedChainId || !input.matches.includes(selectedChainId)) {
|
||||||
|
return input.matches;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
selectedChainId,
|
||||||
|
...input.matches.filter((chainId) => chainId !== selectedChainId)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function hasCarriedMetadataSurfaceScoringEvidence(
|
function hasCarriedMetadataSurfaceScoringEvidence(
|
||||||
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
surface: AssistantMcpDiscoveryMetadataSurfaceRef | null | undefined
|
||||||
): boolean {
|
): boolean {
|
||||||
|
|
@ -456,6 +484,7 @@ function selectPrimitivesFromGraphAndCatalog(input: {
|
||||||
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
|
||||||
actionFamily?: string | null;
|
actionFamily?: string | null;
|
||||||
allowAggregateByAxis?: boolean;
|
allowAggregateByAxis?: boolean;
|
||||||
|
selectedChainId?: AssistantMcpCatalogChainTemplateId | null;
|
||||||
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
|
}): { primitives: AssistantMcpDiscoveryPrimitive[]; reasonCodes: string[] } {
|
||||||
const reasonCodes: string[] = [];
|
const reasonCodes: string[] = [];
|
||||||
const decompositionCandidates = input.dataNeedGraph?.decomposition_candidates ?? [];
|
const decompositionCandidates = input.dataNeedGraph?.decomposition_candidates ?? [];
|
||||||
|
|
@ -500,7 +529,7 @@ function selectPrimitivesFromGraphAndCatalog(input: {
|
||||||
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
||||||
}
|
}
|
||||||
|
|
||||||
const chainTemplateMatches = input.dataNeedGraph
|
const rawChainTemplateMatches = input.dataNeedGraph
|
||||||
? searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
? searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||||
business_fact_family: input.dataNeedGraph.business_fact_family,
|
business_fact_family: input.dataNeedGraph.business_fact_family,
|
||||||
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
||||||
|
|
@ -510,8 +539,16 @@ function selectPrimitivesFromGraphAndCatalog(input: {
|
||||||
aggregation_need: input.dataNeedGraph.aggregation_need
|
aggregation_need: input.dataNeedGraph.aggregation_need
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
const chainTemplateMatches = promoteConfirmedMetadataSurfaceChainTemplate({
|
||||||
|
matches: rawChainTemplateMatches,
|
||||||
|
metadataSurface: input.metadataSurface,
|
||||||
|
selectedChainId: input.selectedChainId
|
||||||
|
});
|
||||||
if (chainTemplateMatches.length > 0) {
|
if (chainTemplateMatches.length > 0) {
|
||||||
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
||||||
|
if (rawChainTemplateMatches[0] !== chainTemplateMatches[0]) {
|
||||||
|
reasonCodes.push("planner_catalog_chain_template_promoted_by_confirmed_metadata_surface");
|
||||||
|
}
|
||||||
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -565,8 +602,10 @@ function budgetOverrideFor(input: AssistantMcpDiscoveryPlannerInput, recipe: Pla
|
||||||
const meaning = input.turnMeaning ?? null;
|
const meaning = input.turnMeaning ?? null;
|
||||||
const requestedAggregationAxis = aggregationAxis(meaning);
|
const requestedAggregationAxis = aggregationAxis(meaning);
|
||||||
const isValueFlowRecipe =
|
const isValueFlowRecipe =
|
||||||
recipe.semanticDataNeed === "counterparty value-flow evidence" &&
|
recipe.primitives.includes("query_movements") &&
|
||||||
recipe.primitives.includes("query_movements");
|
(recipe.semanticDataNeed === "counterparty value-flow evidence" ||
|
||||||
|
recipe.semanticDataNeed === "bidirectional value-flow comparison evidence" ||
|
||||||
|
recipe.semanticDataNeed === "ranked value-flow evidence");
|
||||||
if (!isValueFlowRecipe) {
|
if (!isValueFlowRecipe) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -586,7 +625,7 @@ function catalogChainTemplateMatchesForContract(
|
||||||
if (!dataNeedGraph) {
|
if (!dataNeedGraph) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
const matches = searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||||
business_fact_family: dataNeedGraph.business_fact_family,
|
business_fact_family: dataNeedGraph.business_fact_family,
|
||||||
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
|
action_family: toNonEmptyString(input.turnMeaning?.asked_action_family) ?? dataNeedGraph.action_family,
|
||||||
required_axes: recipe.axes,
|
required_axes: recipe.axes,
|
||||||
|
|
@ -594,6 +633,11 @@ function catalogChainTemplateMatchesForContract(
|
||||||
ranking_need: dataNeedGraph.ranking_need,
|
ranking_need: dataNeedGraph.ranking_need,
|
||||||
aggregation_need: dataNeedGraph.aggregation_need
|
aggregation_need: dataNeedGraph.aggregation_need
|
||||||
});
|
});
|
||||||
|
return promoteConfirmedMetadataSurfaceChainTemplate({
|
||||||
|
matches,
|
||||||
|
metadataSurface: input.metadataSurface,
|
||||||
|
selectedChainId: catalogTemplateIdFromChainId(recipe.chainId)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function catalogChainTemplateAlignmentForContract(
|
function catalogChainTemplateAlignmentForContract(
|
||||||
|
|
@ -700,7 +744,8 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "document_evidence"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "document_evidence",
|
chainId: "document_evidence",
|
||||||
|
|
@ -725,7 +770,8 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "movement_evidence"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "movement_evidence",
|
chainId: "movement_evidence",
|
||||||
|
|
@ -750,7 +796,8 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
fallbackPrimitives: template.fallback_primitives,
|
fallbackPrimitives: template.fallback_primitives,
|
||||||
requiredAxes: axes,
|
requiredAxes: axes,
|
||||||
metadataSurface: input.metadataSurface,
|
metadataSurface: input.metadataSurface,
|
||||||
actionFamily: action
|
actionFamily: action,
|
||||||
|
selectedChainId: "catalog_drilldown"
|
||||||
});
|
});
|
||||||
return recipeFromCatalogChainTemplate({
|
return recipeFromCatalogChainTemplate({
|
||||||
chainId: "catalog_drilldown",
|
chainId: "catalog_drilldown",
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,32 @@ function readStringArray(value: unknown): string[] {
|
||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasValueFlowActionConflictWithDiscoveryTurnMeaning(
|
||||||
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
): boolean {
|
||||||
|
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const turnMeaning = readDiscoveryTurnMeaning(entryPoint);
|
||||||
|
const askedDomain = toNonEmptyString(turnMeaning?.asked_domain_family);
|
||||||
|
const askedAction = toNonEmptyString(turnMeaning?.asked_action_family);
|
||||||
|
if (askedDomain !== "counterparty_value") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||||
|
if (askedAction === "payout") {
|
||||||
|
return detectedIntent !== "supplier_payouts_profile";
|
||||||
|
}
|
||||||
|
if (askedAction === "net_value_flow") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function hasExactMatchedFactualAddressReply(
|
function hasExactMatchedFactualAddressReply(
|
||||||
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
input: ApplyAssistantMcpDiscoveryResponsePolicyInput,
|
||||||
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null
|
||||||
|
|
@ -311,6 +337,9 @@ function hasExactMatchedFactualAddressReply(
|
||||||
if (hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint)) {
|
if (hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (hasValueFlowActionConflictWithDiscoveryTurnMeaning(input, entryPoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
const mcpCallStatus = toNonEmptyString(input.addressRuntimeMeta?.mcp_call_status);
|
||||||
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
|
const truthMode = toNonEmptyString(input.addressRuntimeMeta?.truth_mode);
|
||||||
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
const selectedRecipe = toNonEmptyString(input.addressRuntimeMeta?.selected_recipe);
|
||||||
|
|
@ -522,6 +551,10 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
|
const exactMatchedFactualAddressReply = hasExactMatchedFactualAddressReply(input, entryPoint);
|
||||||
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
|
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
|
||||||
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
const openScopeValueFlowDiscoveryPriority = hasOpenScopeValueFlowDiscoveryPriority(input, entryPoint);
|
||||||
|
const valueFlowActionConflictWithDiscoveryTurnMeaning = hasValueFlowActionConflictWithDiscoveryTurnMeaning(
|
||||||
|
input,
|
||||||
|
entryPoint
|
||||||
|
);
|
||||||
|
|
||||||
if (!entryPoint) {
|
if (!entryPoint) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
|
||||||
|
|
@ -544,6 +577,9 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
|
||||||
if (semanticConflictWithDiscoveryTurnMeaning) {
|
if (semanticConflictWithDiscoveryTurnMeaning) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
||||||
}
|
}
|
||||||
|
if (valueFlowActionConflictWithDiscoveryTurnMeaning) {
|
||||||
|
pushReason(reasonCodes, "mcp_discovery_response_policy_value_flow_action_conflict_allows_candidate_override");
|
||||||
|
}
|
||||||
if (openScopeValueFlowDiscoveryPriority) {
|
if (openScopeValueFlowDiscoveryPriority) {
|
||||||
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
pushReason(reasonCodes, "mcp_discovery_response_policy_open_scope_value_flow_candidate_priority");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -829,6 +829,37 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves explicit date ranges when building bidirectional value-flow probes", async () => {
|
||||||
|
const planner = planAssistantMcpDiscovery({
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "net_value_flow",
|
||||||
|
explicit_entity_candidates: ["SVK"],
|
||||||
|
explicit_date_scope: "2020-01-01..2020-12-31",
|
||||||
|
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const deps = buildSequentialDeps([
|
||||||
|
{
|
||||||
|
rows: [{ Period: "2020-01-15T00:00:00", Amount: 10000, Counterparty: "SVK" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rows: [{ Period: "2020-03-10T00:00:00", Amount: 4000, Counterparty: "SVK" }]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||||
|
|
||||||
|
expect(result.pilot_status).toBe("executed");
|
||||||
|
expect(result.derived_bidirectional_value_flow?.period_scope).toBe("2020-01-01..2020-12-31");
|
||||||
|
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(2);
|
||||||
|
for (const call of deps.executeAddressMcpQuery.mock.calls) {
|
||||||
|
const query = String(call[0]?.query ?? "");
|
||||||
|
expect(query).toContain("2020, 1, 1");
|
||||||
|
expect(query).toContain("2020, 12, 31");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("derives monthly bidirectional value-flow breakdown when the turn explicitly asks by month", async () => {
|
it("derives monthly bidirectional value-flow breakdown when the turn explicitly asks by month", async () => {
|
||||||
const planner = planAssistantMcpDiscovery({
|
const planner = planAssistantMcpDiscovery({
|
||||||
turnMeaning: {
|
turnMeaning: {
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,48 @@ describe("assistant MCP discovery planner", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps bidirectional value-flow comparison executable when checked totals are derived without aggregate_by_axis", () => {
|
||||||
|
const result = planAssistantMcpDiscovery({
|
||||||
|
dataNeedGraph: {
|
||||||
|
schema_version: "assistant_data_need_graph_v1",
|
||||||
|
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||||
|
subject_candidates: ["SVK"],
|
||||||
|
business_fact_family: "value_flow",
|
||||||
|
action_family: "net_value_flow",
|
||||||
|
aggregation_need: null,
|
||||||
|
time_scope_need: "explicit_period",
|
||||||
|
comparison_need: "incoming_vs_outgoing",
|
||||||
|
ranking_need: null,
|
||||||
|
proof_expectation: "coverage_checked_fact",
|
||||||
|
clarification_gaps: [],
|
||||||
|
decomposition_candidates: [
|
||||||
|
"resolve_entity_reference",
|
||||||
|
"collect_incoming_movements",
|
||||||
|
"collect_outgoing_movements",
|
||||||
|
"aggregate_checked_amounts",
|
||||||
|
"probe_coverage"
|
||||||
|
],
|
||||||
|
forbidden_overclaim_flags: ["no_raw_model_claims", "no_unchecked_fact_totals"],
|
||||||
|
reason_codes: ["data_need_graph_built", "data_need_graph_comparison_incoming_vs_outgoing"]
|
||||||
|
},
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "net_value_flow",
|
||||||
|
explicit_entity_candidates: ["SVK"],
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.planner_status).toBe("ready_for_execution");
|
||||||
|
expect(result.selected_chain_id).toBe("value_flow_comparison");
|
||||||
|
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_movements", "probe_coverage"]);
|
||||||
|
expect(result.proposed_primitives).not.toContain("aggregate_by_axis");
|
||||||
|
expect(result.required_axes).toEqual(["counterparty", "period", "amount", "coverage_target"]);
|
||||||
|
expect(result.discovery_plan.execution_budget.max_probe_count).toBe(30);
|
||||||
|
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
||||||
|
expect(result.reason_codes).toContain("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps a value-flow plan in clarification state when period axis is missing", () => {
|
it("keeps a value-flow plan in clarification state when period axis is missing", () => {
|
||||||
const result = planAssistantMcpDiscovery({
|
const result = planAssistantMcpDiscovery({
|
||||||
turnMeaning: {
|
turnMeaning: {
|
||||||
|
|
@ -537,6 +579,23 @@ describe("assistant MCP discovery planner", () => {
|
||||||
|
|
||||||
it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => {
|
it("can select catalog drilldown directly from a confirmed catalog metadata surface when the follow-up itself is thin", () => {
|
||||||
const result = planAssistantMcpDiscovery({
|
const result = planAssistantMcpDiscovery({
|
||||||
|
dataNeedGraph: {
|
||||||
|
schema_version: "assistant_data_need_graph_v1",
|
||||||
|
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||||
|
subject_candidates: ["counterparty"],
|
||||||
|
metadata_scope_hint: "counterparty",
|
||||||
|
business_fact_family: "schema_surface",
|
||||||
|
action_family: "inspect_catalog",
|
||||||
|
aggregation_need: null,
|
||||||
|
time_scope_need: null,
|
||||||
|
comparison_need: null,
|
||||||
|
ranking_need: null,
|
||||||
|
proof_expectation: "schema_surface",
|
||||||
|
clarification_gaps: [],
|
||||||
|
decomposition_candidates: ["inspect_metadata_surface"],
|
||||||
|
forbidden_overclaim_flags: ["no_raw_model_claims", "no_fake_schema_surface"],
|
||||||
|
reason_codes: ["data_need_graph_built", "data_need_graph_family_schema_surface"]
|
||||||
|
},
|
||||||
metadataSurface: {
|
metadataSurface: {
|
||||||
selected_entity_set: "Catalog",
|
selected_entity_set: "Catalog",
|
||||||
selected_surface_objects: ["Catalog.Counterparties"],
|
selected_surface_objects: ["Catalog.Counterparties"],
|
||||||
|
|
@ -557,8 +616,17 @@ describe("assistant MCP discovery planner", () => {
|
||||||
expect(result.selected_chain_id).toBe("catalog_drilldown");
|
expect(result.selected_chain_id).toBe("catalog_drilldown");
|
||||||
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
expect(result.proposed_primitives).toEqual(["inspect_1c_metadata"]);
|
||||||
expect(result.required_axes).toEqual(["metadata_scope"]);
|
expect(result.required_axes).toEqual(["metadata_scope"]);
|
||||||
|
expect(result.catalog_chain_template_matches[0]).toBe("catalog_drilldown");
|
||||||
|
expect(result.catalog_chain_template_alignment).toMatchObject({
|
||||||
|
alignment_status: "selected_matches_top",
|
||||||
|
top_chain_template_match: "catalog_drilldown",
|
||||||
|
selected_chain_template_rank: 1,
|
||||||
|
selected_chain_matches_top: true
|
||||||
|
});
|
||||||
expect(result.reason_codes).toContain("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref");
|
expect(result.reason_codes).toContain("planner_selected_catalog_drilldown_from_confirmed_metadata_surface_ref");
|
||||||
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
|
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_metadata_surface_search");
|
||||||
|
expect(result.reason_codes).toContain("planner_catalog_chain_template_promoted_by_confirmed_metadata_surface");
|
||||||
|
expect(result.reason_codes).toContain("planner_catalog_chain_template_search_top_catalog_drilldown");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("scores an explicit document data-need over an ambiguous metadata surface without carrying movement primitives", () => {
|
it("scores an explicit document data-need over an ambiguous metadata surface without carrying movement primitives", () => {
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,43 @@ describe("assistant MCP discovery response policy", () => {
|
||||||
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_not_discovery_ready_address_candidate");
|
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_not_discovery_ready_address_candidate");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overrides exact inbound value-flow replies when the discovery turn meaning asks for payouts", () => {
|
||||||
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
|
currentReply: "Incoming turnover by SVK: 12 224 925.00 rub.",
|
||||||
|
currentReplySource: "address_query_runtime_v1",
|
||||||
|
currentReplyType: "factual",
|
||||||
|
addressRuntimeMeta: {
|
||||||
|
detected_intent: "customer_revenue_and_payments",
|
||||||
|
selected_recipe: "address_customer_revenue_and_payments_v1",
|
||||||
|
mcp_call_status: "matched_non_empty",
|
||||||
|
truth_mode: "confirmed",
|
||||||
|
capability_binding_status: "bound",
|
||||||
|
capability_binding_violations: [],
|
||||||
|
assistant_mcp_discovery_entry_point_v1: entryPoint({
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
should_run_discovery: true,
|
||||||
|
turn_meaning_ref: {
|
||||||
|
asked_domain_family: "counterparty_value",
|
||||||
|
asked_action_family: "payout",
|
||||||
|
explicit_entity_candidates: ["SVK"],
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.applied).toBe(true);
|
||||||
|
expect(result.decision).toBe("apply_candidate");
|
||||||
|
expect(result.reply_source).toBe("mcp_discovery_response_candidate_guarded");
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_response_policy_semantic_conflict_allows_candidate_override");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"mcp_discovery_response_policy_value_flow_action_conflict_allows_candidate_override"
|
||||||
|
);
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_response_policy_keep_exact_matched_factual_address_reply");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps exact matched inventory address replies over stale metadata discovery candidates", () => {
|
it("keeps exact matched inventory address replies over stale metadata discovery candidates", () => {
|
||||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||||
currentReply: "По товару Шкаф картотечный 1000*400*2100 цепочка поставки и продажи подтверждена.",
|
currentReply: "По товару Шкаф картотечный 1000*400*2100 цепочка поставки и продажи подтверждена.",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue