ARCH: ввести data-need graph и довести open-scope comparison до live replay
This commit is contained in:
+78
-2
@@ -141,6 +141,16 @@ function isMovementLaneClarification(pilot) {
|
||||
askedActionFamily(pilot) === "list_movements" ||
|
||||
unsupportedFamily(pilot) === "movement_evidence");
|
||||
}
|
||||
function isRankedValueFlowClarification(pilot) {
|
||||
return (pilot.reason_codes.includes("planner_selected_top_ranked_value_flow_from_data_need_graph") ||
|
||||
pilot.reason_codes.includes("planner_selected_bottom_ranked_value_flow_from_data_need_graph") ||
|
||||
pilot.dry_run.reason_codes.includes("planner_selected_top_ranked_value_flow_from_data_need_graph") ||
|
||||
pilot.dry_run.reason_codes.includes("planner_selected_bottom_ranked_value_flow_from_data_need_graph"));
|
||||
}
|
||||
function isBidirectionalValueFlowComparisonClarification(pilot) {
|
||||
return (pilot.reason_codes.includes("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph") ||
|
||||
pilot.dry_run.reason_codes.includes("planner_selected_bidirectional_value_flow_comparison_from_data_need_graph"));
|
||||
}
|
||||
function isDocumentLaneClarification(pilot) {
|
||||
return (isDocumentPilot(pilot) ||
|
||||
pilot.reason_codes.includes("planner_selected_document_recipe") ||
|
||||
@@ -152,12 +162,20 @@ function laneScopeSuffix(pilot) {
|
||||
const entity = firstEntityCandidate(pilot);
|
||||
return entity ? ` по "${entity}"` : "";
|
||||
}
|
||||
function dryRunHasAxis(pilot, axis) {
|
||||
return pilot.dry_run.execution_steps.some((step) => step.provided_axes.includes(axis));
|
||||
}
|
||||
function dryRunMissingAxis(pilot, axis) {
|
||||
if (dryRunHasAxis(pilot, axis)) {
|
||||
return false;
|
||||
}
|
||||
return pilot.dry_run.execution_steps.some((step) => step.missing_axis_options.some((option) => option.includes(axis)));
|
||||
}
|
||||
function clarificationNeedRu(pilot) {
|
||||
const hasCounterparty = dryRunHasAxis(pilot, "counterparty");
|
||||
const hasAccount = dryRunHasAxis(pilot, "account");
|
||||
const needsPeriod = dryRunMissingAxis(pilot, "period");
|
||||
const needsOrganization = dryRunMissingAxis(pilot, "organization");
|
||||
const needsOrganization = !hasCounterparty && !hasAccount && dryRunMissingAxis(pilot, "organization");
|
||||
if (needsPeriod && needsOrganization) {
|
||||
return { subject: "проверяемый период и организацию", verb: "нужно" };
|
||||
}
|
||||
@@ -210,6 +228,9 @@ function headlineFor(mode, pilot) {
|
||||
pilot.derived_entity_resolution?.resolution_status === "not_found") {
|
||||
return "По текущему каталожному поиску 1С точный контрагент пока не подтвержден.";
|
||||
}
|
||||
if (pilot.derived_ranked_value_flow && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С можно построить ограниченный ranking по контрагентам на подтвержденных строках денежных движений.";
|
||||
}
|
||||
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
|
||||
}
|
||||
@@ -269,6 +290,14 @@ function headlineFor(mode, pilot) {
|
||||
const need = clarificationNeedRu(pilot);
|
||||
return `Могу идти дальше по документам${laneScopeSuffix(pilot)}, но для запуска поиска в 1С ${need.verb} ${need.subject}.`;
|
||||
}
|
||||
if (mode === "needs_clarification" && isBidirectionalValueFlowComparisonClarification(pilot)) {
|
||||
const need = clarificationNeedRu(pilot);
|
||||
return `Могу сравнить входящий и исходящий денежный поток, но для bounded поиска в 1С ${need.verb} ${need.subject}.`;
|
||||
}
|
||||
if (mode === "needs_clarification" && isRankedValueFlowClarification(pilot)) {
|
||||
const need = clarificationNeedRu(pilot);
|
||||
return `Могу посчитать ranking по денежному потоку между контрагентами, но для bounded поиска в 1С ${need.verb} ${need.subject}.`;
|
||||
}
|
||||
if (mode === "needs_clarification") {
|
||||
return "Нужно уточнить контекст перед поиском в 1С.";
|
||||
}
|
||||
@@ -302,6 +331,12 @@ function nextStepFor(mode, pilot) {
|
||||
if (mode === "needs_clarification" && isDocumentLaneClarification(pilot)) {
|
||||
return clarificationNextStepLine(pilot, "документам");
|
||||
}
|
||||
if (mode === "needs_clarification" && isBidirectionalValueFlowComparisonClarification(pilot)) {
|
||||
return clarificationNextStepLine(pilot, "сравнению входящих и исходящих денежных потоков");
|
||||
}
|
||||
if (mode === "needs_clarification" && isRankedValueFlowClarification(pilot)) {
|
||||
return clarificationNextStepLine(pilot, "ranking-поиску между контрагентами");
|
||||
}
|
||||
if (mode === "needs_clarification") {
|
||||
return "Уточните контрагента, период или организацию, и я смогу выполнить проверку по 1С.";
|
||||
}
|
||||
@@ -336,6 +371,10 @@ function buildMustNotClaim(pilot) {
|
||||
claims.push("Do not claim full all-time turnover unless the checked period and coverage prove it.");
|
||||
claims.push("Do not present a derived sum as a legal/accounting final total outside the checked 1C rows.");
|
||||
}
|
||||
if (pilot.derived_ranked_value_flow) {
|
||||
claims.push("Do not present a bounded ranking as a complete all-time ranking outside the checked period and organization.");
|
||||
claims.push("Do not imply the top-ranked counterparty is globally final when probe-limit or scope boundaries still exist.");
|
||||
}
|
||||
if (isDocumentPilot(pilot)) {
|
||||
claims.push("Do not claim full document history outside the checked period.");
|
||||
claims.push("Do not present the confirmed document rows as a complete document universe.");
|
||||
@@ -463,6 +502,40 @@ function derivedEntityResolutionInferenceLine(pilot) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function derivedRankedValueFlowInferenceLine(pilot) {
|
||||
const ranking = pilot.derived_ranked_value_flow;
|
||||
if (!ranking) {
|
||||
return null;
|
||||
}
|
||||
const organization = ranking.organization_scope ? ` по организации ${ranking.organization_scope}` : "";
|
||||
const period = ranking.period_scope ? ` за период ${ranking.period_scope}` : " в проверенном окне";
|
||||
return `Ranking по контрагентам${organization}${period} рассчитан только по подтвержденным строкам 1С и не доказывает полный исторический срез вне проверенного окна.`;
|
||||
}
|
||||
function derivedRankedValueFlowConfirmedLine(pilot) {
|
||||
const ranking = pilot.derived_ranked_value_flow;
|
||||
if (!ranking || ranking.ranked_values.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
const leader = ranking.ranked_values[0];
|
||||
const organization = ranking.organization_scope ? ` по организации ${ranking.organization_scope}` : "";
|
||||
const period = ranking.period_scope ? ` за период ${ranking.period_scope}` : " в проверенном окне";
|
||||
const directionLead = ranking.ranking_need === "bottom_asc"
|
||||
? ranking.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "Меньше всего заплатили контрагенту"
|
||||
: "Меньше всего денег принёс контрагент"
|
||||
: ranking.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "Больше всего заплатили контрагенту"
|
||||
: "Больше всего денег принёс контрагент";
|
||||
const tail = ranking.ranked_values
|
||||
.slice(1, 3)
|
||||
.map((bucket) => `${bucket.axis_value} — ${bucket.total_amount_human_ru}`)
|
||||
.join("; ");
|
||||
const trail = tail ? ` Следом: ${tail}.` : "";
|
||||
const limitCaveat = ranking.coverage_limited_by_probe_limit
|
||||
? " Лимит строк проверки достигнут; ranking может быть неполным."
|
||||
: "";
|
||||
return `${directionLead} ${leader.axis_value}${organization}${period}: ${leader.total_amount_human_ru} по ${leader.rows_with_amount} строкам с суммой.${trail}${limitCaveat}`;
|
||||
}
|
||||
function derivedValueFlowConfirmedLine(pilot) {
|
||||
const flow = pilot.derived_value_flow;
|
||||
if (!flow) {
|
||||
@@ -553,13 +626,16 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
|
||||
}
|
||||
const derivedInferenceLine = derivedActivityInferenceLine(pilot) ??
|
||||
derivedMetadataInferenceLine(pilot) ??
|
||||
derivedRankedValueFlowInferenceLine(pilot) ??
|
||||
derivedEntityResolutionInferenceLine(pilot);
|
||||
const inferenceLines = derivedInferenceLine
|
||||
? [derivedInferenceLine]
|
||||
: pilot.evidence.inferred_facts;
|
||||
const derivedMetadataLine = derivedMetadataConfirmedLine(pilot);
|
||||
const derivedEntityResolutionLine = derivedEntityResolutionConfirmedLine(pilot);
|
||||
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ?? derivedValueFlowConfirmedLine(pilot);
|
||||
const derivedValueLine = derivedBidirectionalValueFlowConfirmedLine(pilot) ??
|
||||
derivedRankedValueFlowConfirmedLine(pilot) ??
|
||||
derivedValueFlowConfirmedLine(pilot);
|
||||
const monthlyConfirmedLines = derivedBidirectionalValueFlowMonthlyLines(pilot).length > 0
|
||||
? derivedBidirectionalValueFlowMonthlyLines(pilot)
|
||||
: derivedValueFlowMonthlyLines(pilot);
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = void 0;
|
||||
exports.buildAssistantMcpDiscoveryDataNeedGraph = buildAssistantMcpDiscoveryDataNeedGraph;
|
||||
exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION = "assistant_data_need_graph_v1";
|
||||
function toNonEmptyString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
const text = String(value).trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
function lower(value) {
|
||||
return String(value ?? "").trim().toLowerCase();
|
||||
}
|
||||
function normalizeReasonCode(value) {
|
||||
const normalized = value
|
||||
.trim()
|
||||
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
||||
.replace(/^_+|_+$/g, "")
|
||||
.toLowerCase();
|
||||
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
||||
}
|
||||
function pushReason(target, value) {
|
||||
const normalized = normalizeReasonCode(value);
|
||||
if (normalized && !target.includes(normalized)) {
|
||||
target.push(normalized);
|
||||
}
|
||||
}
|
||||
function pushUnique(target, value) {
|
||||
const text = toNonEmptyString(value);
|
||||
if (text && !target.includes(text)) {
|
||||
target.push(text);
|
||||
}
|
||||
}
|
||||
function businessFactFamilyFor(input) {
|
||||
const combined = `${input.semanticDataNeed} ${input.domain} ${input.action} ${input.unsupported}`.trim();
|
||||
if (combined.includes("metadata lane clarification")) {
|
||||
return "schema_surface";
|
||||
}
|
||||
if (combined.includes("metadata")) {
|
||||
return "schema_surface";
|
||||
}
|
||||
if (combined.includes("entity discovery") || combined.includes("entity_resolution")) {
|
||||
return "entity_grounding";
|
||||
}
|
||||
if (combined.includes("lifecycle") || combined.includes("activity")) {
|
||||
return "activity_lifecycle";
|
||||
}
|
||||
if (combined.includes("movement")) {
|
||||
return "movement_evidence";
|
||||
}
|
||||
if (combined.includes("document")) {
|
||||
return "document_evidence";
|
||||
}
|
||||
if (combined.includes("value-flow") || combined.includes("turnover") || combined.includes("payout") || combined.includes("net")) {
|
||||
return "value_flow";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function aggregationNeedFor(axis) {
|
||||
if (!axis) {
|
||||
return null;
|
||||
}
|
||||
if (axis === "month") {
|
||||
return "by_month";
|
||||
}
|
||||
return `by_${axis}`;
|
||||
}
|
||||
function timeScopeNeedFor(input) {
|
||||
if (input.explicitDateScope) {
|
||||
return "explicit_period";
|
||||
}
|
||||
if (input.family === "value_flow" || input.family === "movement_evidence" || input.family === "document_evidence") {
|
||||
return "period_required";
|
||||
}
|
||||
if (input.family === "activity_lifecycle") {
|
||||
return "open_activity_window";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function comparisonNeedFor(action) {
|
||||
if (action === "net_value_flow") {
|
||||
return "incoming_vs_outgoing";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function allowsOpenScopeWithoutSubject(input) {
|
||||
if (input.family !== "value_flow") {
|
||||
return false;
|
||||
}
|
||||
return Boolean(input.rankingNeed || input.comparisonNeed === "incoming_vs_outgoing");
|
||||
}
|
||||
function rankingNeedFromRawUtterance(value) {
|
||||
const text = lower(value);
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
if (/(?:\btop[-\s]?\d+\b|\btop\b|топ[-\s]?\d+|топ\b|сам(?:ый|ая|ое|ые)\b|больше\s+всего|наибол[её]е|highest|largest|most)/iu.test(text)) {
|
||||
return "top_desc";
|
||||
}
|
||||
if (/(?:меньше\s+всего|наимен[ьш]е|lowest|smallest|least)/iu.test(text)) {
|
||||
return "bottom_asc";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function proofExpectationFor(input) {
|
||||
if (input.clarificationGaps.length > 0) {
|
||||
return "clarification_required";
|
||||
}
|
||||
if (input.family === "schema_surface") {
|
||||
return "schema_surface";
|
||||
}
|
||||
if (input.family === "entity_grounding") {
|
||||
return "entity_grounding";
|
||||
}
|
||||
if (input.family === "activity_lifecycle") {
|
||||
return "bounded_inference";
|
||||
}
|
||||
return "coverage_checked_fact";
|
||||
}
|
||||
function decompositionCandidatesFor(input) {
|
||||
const result = [];
|
||||
if (input.family === "schema_surface") {
|
||||
pushUnique(result, "inspect_metadata_surface");
|
||||
return result;
|
||||
}
|
||||
if (input.family === "entity_grounding") {
|
||||
pushUnique(result, "search_business_entity");
|
||||
pushUnique(result, "resolve_entity_reference");
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
if (input.family === "value_flow") {
|
||||
if (input.rankingNeed && input.openScopeWithoutSubject) {
|
||||
pushUnique(result, "collect_scoped_movements");
|
||||
pushUnique(result, "aggregate_ranked_axis_values");
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
if (input.comparisonNeed === "incoming_vs_outgoing" && input.openScopeWithoutSubject) {
|
||||
pushUnique(result, "collect_incoming_movements");
|
||||
pushUnique(result, "collect_outgoing_movements");
|
||||
if (input.aggregationNeed === "by_month") {
|
||||
pushUnique(result, "aggregate_by_month");
|
||||
}
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
pushUnique(result, "resolve_entity_reference");
|
||||
if (input.action === "net_value_flow") {
|
||||
pushUnique(result, "collect_incoming_movements");
|
||||
pushUnique(result, "collect_outgoing_movements");
|
||||
}
|
||||
else {
|
||||
pushUnique(result, "collect_scoped_movements");
|
||||
}
|
||||
pushUnique(result, input.aggregationNeed === "by_month" ? "aggregate_by_month" : "aggregate_checked_amounts");
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
if (input.family === "movement_evidence") {
|
||||
pushUnique(result, "resolve_entity_reference");
|
||||
pushUnique(result, "fetch_scoped_movements");
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
if (input.family === "document_evidence") {
|
||||
pushUnique(result, "resolve_entity_reference");
|
||||
pushUnique(result, "fetch_scoped_documents");
|
||||
pushUnique(result, "probe_coverage");
|
||||
return result;
|
||||
}
|
||||
if (input.family === "activity_lifecycle") {
|
||||
pushUnique(result, "resolve_entity_reference");
|
||||
pushUnique(result, "fetch_supporting_documents");
|
||||
pushUnique(result, "probe_coverage");
|
||||
pushUnique(result, "explain_evidence_basis");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function forbiddenOverclaimFlagsFor(family) {
|
||||
const result = ["no_raw_model_claims"];
|
||||
if (family === "schema_surface") {
|
||||
pushUnique(result, "no_fake_schema_surface");
|
||||
}
|
||||
if (family === "entity_grounding") {
|
||||
pushUnique(result, "no_unresolved_entity_claim");
|
||||
}
|
||||
if (family === "activity_lifecycle") {
|
||||
pushUnique(result, "no_legal_age_claim_without_evidence");
|
||||
}
|
||||
if (family === "value_flow" || family === "movement_evidence" || family === "document_evidence") {
|
||||
pushUnique(result, "no_unchecked_fact_totals");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||
const semanticDataNeed = lower(input.semanticDataNeed);
|
||||
const turnMeaning = input.turnMeaning ?? null;
|
||||
const domain = lower(turnMeaning?.asked_domain_family);
|
||||
const action = lower(turnMeaning?.asked_action_family);
|
||||
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
||||
const rawUtterance = lower(input.rawUtterance);
|
||||
const aggregationAxis = lower(turnMeaning?.asked_aggregation_axis);
|
||||
const explicitDateScope = toNonEmptyString(turnMeaning?.explicit_date_scope);
|
||||
const subjectCandidates = (turnMeaning?.explicit_entity_candidates ?? [])
|
||||
.map((item) => toNonEmptyString(item))
|
||||
.filter((item) => Boolean(item));
|
||||
const businessFactFamily = businessFactFamilyFor({
|
||||
semanticDataNeed,
|
||||
domain,
|
||||
action,
|
||||
unsupported
|
||||
});
|
||||
const aggregationNeed = aggregationNeedFor(aggregationAxis);
|
||||
const comparisonNeed = comparisonNeedFor(action);
|
||||
const rankingNeed = rankingNeedFromRawUtterance(rawUtterance);
|
||||
const openScopeWithoutSubject = subjectCandidates.length === 0 &&
|
||||
allowsOpenScopeWithoutSubject({
|
||||
family: businessFactFamily,
|
||||
comparisonNeed,
|
||||
rankingNeed
|
||||
});
|
||||
const clarificationGaps = [];
|
||||
if (unsupported === "metadata_lane_choice_clarification" || action === "resolve_next_lane") {
|
||||
pushUnique(clarificationGaps, "lane_family_choice");
|
||||
}
|
||||
if (subjectCandidates.length === 0 && businessFactFamily !== "schema_surface" && !openScopeWithoutSubject) {
|
||||
pushUnique(clarificationGaps, "subject");
|
||||
}
|
||||
const timeScopeNeed = timeScopeNeedFor({
|
||||
family: businessFactFamily,
|
||||
explicitDateScope
|
||||
});
|
||||
if (timeScopeNeed === "period_required" && !explicitDateScope) {
|
||||
pushUnique(clarificationGaps, "period");
|
||||
}
|
||||
const decompositionCandidates = decompositionCandidatesFor({
|
||||
family: businessFactFamily,
|
||||
action,
|
||||
aggregationNeed,
|
||||
comparisonNeed,
|
||||
rankingNeed,
|
||||
openScopeWithoutSubject
|
||||
});
|
||||
const reasonCodes = [];
|
||||
pushReason(reasonCodes, "data_need_graph_built");
|
||||
if (businessFactFamily) {
|
||||
pushReason(reasonCodes, `data_need_graph_family_${businessFactFamily}`);
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "data_need_graph_family_unknown");
|
||||
}
|
||||
if (aggregationNeed) {
|
||||
pushReason(reasonCodes, `data_need_graph_aggregation_${aggregationNeed}`);
|
||||
}
|
||||
if (rankingNeed) {
|
||||
pushReason(reasonCodes, `data_need_graph_ranking_${rankingNeed}`);
|
||||
}
|
||||
if (comparisonNeed) {
|
||||
pushReason(reasonCodes, `data_need_graph_comparison_${comparisonNeed}`);
|
||||
}
|
||||
if (clarificationGaps.length > 0) {
|
||||
pushReason(reasonCodes, "data_need_graph_has_clarification_gaps");
|
||||
}
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_DATA_NEED_GRAPH_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: subjectCandidates,
|
||||
business_fact_family: businessFactFamily,
|
||||
action_family: toNonEmptyString(turnMeaning?.asked_action_family),
|
||||
aggregation_need: aggregationNeed,
|
||||
time_scope_need: timeScopeNeed,
|
||||
comparison_need: comparisonNeed,
|
||||
ranking_need: rankingNeed,
|
||||
proof_expectation: proofExpectationFor({
|
||||
family: businessFactFamily,
|
||||
clarificationGaps
|
||||
}),
|
||||
clarification_gaps: clarificationGaps,
|
||||
decomposition_candidates: decompositionCandidates,
|
||||
forbidden_overclaim_flags: forbiddenOverclaimFlagsFor(businessFactFamily),
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
+156
-1
@@ -133,6 +133,16 @@ function buildValueFlowFilters(planner) {
|
||||
sort: "period_asc"
|
||||
};
|
||||
}
|
||||
function organizationScopeForPlanner(planner) {
|
||||
return toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_organization_scope);
|
||||
}
|
||||
function rankingNeedForPlanner(planner) {
|
||||
const rankingNeed = toNonEmptyString(planner.data_need_graph?.ranking_need)?.toLowerCase();
|
||||
if (rankingNeed === "top_desc" || rankingNeed === "bottom_asc") {
|
||||
return rankingNeed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function normalizeEntityResolutionText(value) {
|
||||
return String(value ?? "")
|
||||
.toLowerCase()
|
||||
@@ -313,7 +323,9 @@ function isMovementEvidencePilotEligible(planner) {
|
||||
combined.includes("list_movements")));
|
||||
}
|
||||
function isValueFlowPilotEligible(planner) {
|
||||
if (planner.selected_chain_id === "value_flow") {
|
||||
if (planner.selected_chain_id === "value_flow" ||
|
||||
planner.selected_chain_id === "value_flow_ranking" ||
|
||||
planner.selected_chain_id === "value_flow_comparison") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
@@ -1040,6 +1052,16 @@ function rowAmountValue(row) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function rowCounterpartyValue(row) {
|
||||
const candidates = [row["Контрагент"], row["Counterparty"], row["counterparty"], row["Наименование"], row["name"]];
|
||||
for (const candidate of candidates) {
|
||||
const text = toNonEmptyString(candidate);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function monthBucketFromIsoDate(isoDate) {
|
||||
const match = isoDate?.match(/^(\d{4})-(\d{2})-\d{2}$/);
|
||||
return match ? `${match[1]}-${match[2]}` : null;
|
||||
@@ -1213,6 +1235,62 @@ function deriveValueFlow(result, counterparty, periodScope, direction, aggregati
|
||||
inference_basis: "sum_of_confirmed_1c_value_flow_rows"
|
||||
};
|
||||
}
|
||||
function deriveRankedValueFlow(result, input) {
|
||||
if (!result || result.error || result.matched_rows <= 0) {
|
||||
return null;
|
||||
}
|
||||
const buckets = new Map();
|
||||
let rowsWithAmount = 0;
|
||||
for (const row of result.rows) {
|
||||
const axisValue = rowCounterpartyValue(row);
|
||||
const amount = rowAmountValue(row);
|
||||
if (!axisValue || amount === null) {
|
||||
continue;
|
||||
}
|
||||
rowsWithAmount += 1;
|
||||
const current = buckets.get(axisValue) ?? { rows_with_amount: 0, total_amount: 0 };
|
||||
current.rows_with_amount += 1;
|
||||
current.total_amount += amount;
|
||||
buckets.set(axisValue, current);
|
||||
}
|
||||
if (rowsWithAmount <= 0 || buckets.size <= 0) {
|
||||
return null;
|
||||
}
|
||||
const rankedValues = Array.from(buckets.entries())
|
||||
.map(([axisValue, bucket]) => ({
|
||||
axis_value: axisValue,
|
||||
rows_with_amount: bucket.rows_with_amount,
|
||||
total_amount: bucket.total_amount,
|
||||
total_amount_human_ru: formatAmountHumanRu(bucket.total_amount)
|
||||
}))
|
||||
.sort((left, right) => {
|
||||
const amountDelta = right.total_amount - left.total_amount;
|
||||
if (input.rankingNeed === "bottom_asc") {
|
||||
if (amountDelta !== 0) {
|
||||
return -amountDelta;
|
||||
}
|
||||
}
|
||||
else if (amountDelta !== 0) {
|
||||
return amountDelta;
|
||||
}
|
||||
return left.axis_value.localeCompare(right.axis_value, "ru");
|
||||
})
|
||||
.slice(0, 5);
|
||||
return {
|
||||
value_flow_direction: input.direction,
|
||||
ranking_need: input.rankingNeed,
|
||||
ranking_axis: "counterparty",
|
||||
organization_scope: input.organizationScope,
|
||||
period_scope: input.periodScope,
|
||||
rows_matched: result.matched_rows,
|
||||
rows_with_amount: rowsWithAmount,
|
||||
ranked_values: rankedValues,
|
||||
coverage_limited_by_probe_limit: result.coverage_limited_by_probe_limit,
|
||||
coverage_recovered_by_period_chunking: result.coverage_recovered_by_period_chunking,
|
||||
period_chunking_granularity: result.period_chunking_granularity,
|
||||
inference_basis: "ranked_counterparty_totals_from_confirmed_1c_value_flow_rows"
|
||||
};
|
||||
}
|
||||
function deriveValueFlowSideSummary(result) {
|
||||
if (!result || result.error || result.matched_rows <= 0) {
|
||||
return {
|
||||
@@ -1345,6 +1423,16 @@ function buildValueFlowConfirmedFacts(result, counterparty, direction) {
|
||||
: "1C value-flow rows were found for the requested counterparty scope"
|
||||
];
|
||||
}
|
||||
function buildRankedValueFlowConfirmedFacts(derived) {
|
||||
if (!derived || derived.ranked_values.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
const leader = derived.ranked_values[0];
|
||||
const directionLabel = derived.value_flow_direction === "outgoing_supplier_payout" ? "supplier-payout" : "incoming value-flow";
|
||||
return [
|
||||
`1C ${directionLabel} rows were ranked by counterparty for the checked scope; leader=${leader.axis_value}, rows_with_amount=${leader.rows_with_amount}`
|
||||
];
|
||||
}
|
||||
function buildBidirectionalValueFlowConfirmedFacts(derived) {
|
||||
if (!derived) {
|
||||
return [];
|
||||
@@ -1411,6 +1499,16 @@ function buildValueFlowInferredFacts(derived) {
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
function buildRankedValueFlowInferredFacts(derived) {
|
||||
if (!derived) {
|
||||
return [];
|
||||
}
|
||||
const facts = ["Counterparty ranking was calculated from confirmed 1C movement rows grouped by counterparty"];
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push("Requested period coverage for counterparty ranking was recovered through monthly 1C probes after a broad probe hit the row limit");
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
function buildBidirectionalValueFlowInferredFacts(derived) {
|
||||
if (!derived) {
|
||||
return [];
|
||||
@@ -1453,6 +1551,16 @@ function buildValueFlowUnknownFacts(periodScope, direction, derived) {
|
||||
: "Full all-time turnover is not proven without an explicit checked period");
|
||||
return unknownFacts;
|
||||
}
|
||||
function buildRankedValueFlowUnknownFacts(periodScope, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period ranking coverage is not proven because the MCP discovery probe row limit was reached");
|
||||
}
|
||||
unknownFacts.push(periodScope
|
||||
? "Full ranking outside the checked period is not proven by this MCP discovery pilot"
|
||||
: "Full all-time counterparty ranking is not proven without an explicit checked period");
|
||||
return unknownFacts;
|
||||
}
|
||||
function buildBidirectionalValueFlowUnknownFacts(periodScope, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
@@ -1479,6 +1587,8 @@ function pilotScopeForPlanner(planner) {
|
||||
return "metadata_inspection_v1";
|
||||
case "movement_evidence":
|
||||
return "counterparty_movement_evidence_query_movements_v1";
|
||||
case "value_flow_comparison":
|
||||
case "value_flow_ranking":
|
||||
case "value_flow":
|
||||
return valueFlowPilotProfile(planner).scope;
|
||||
case "document_evidence":
|
||||
@@ -1595,7 +1705,9 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||
}
|
||||
const counterparty = firstEntityCandidate(planner);
|
||||
const dateScope = toNonEmptyString(planner.discovery_plan.turn_meaning_ref?.explicit_date_scope);
|
||||
const organizationScope = organizationScopeForPlanner(planner);
|
||||
const aggregationAxis = aggregationAxisForPlanner(planner);
|
||||
const rankingNeed = rankingNeedForPlanner(planner);
|
||||
if (metadataPilotEligible) {
|
||||
let metadataResult = null;
|
||||
const metadataScope = metadataScopeForPlanner(planner);
|
||||
@@ -2151,6 +2263,48 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||
}
|
||||
}
|
||||
const sourceRowsSummary = queryResult ? summarizeValueFlowRows(queryResult) : null;
|
||||
if (planner.selected_chain_id === "value_flow_ranking" && rankingNeed) {
|
||||
const derivedRankedValueFlow = deriveRankedValueFlow(queryResult, {
|
||||
organizationScope,
|
||||
periodScope: dateScope,
|
||||
direction: valueFlowProfile.direction,
|
||||
rankingNeed
|
||||
});
|
||||
if (derivedRankedValueFlow) {
|
||||
pushReason(reasonCodes, "pilot_derived_ranked_value_flow_from_confirmed_rows");
|
||||
}
|
||||
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: buildRankedValueFlowConfirmedFacts(derivedRankedValueFlow),
|
||||
inferredFacts: buildRankedValueFlowInferredFacts(derivedRankedValueFlow),
|
||||
unknownFacts: buildRankedValueFlowUnknownFacts(dateScope, derivedRankedValueFlow),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
});
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "executed",
|
||||
pilot_scope: valueFlowProfile.scope,
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: executedPrimitives.length > 0,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_ranked_value_flow: derivedRankedValueFlow,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
const derivedValueFlow = deriveValueFlow(queryResult, counterparty, dateScope, valueFlowProfile.direction, aggregationAxis);
|
||||
if (derivedValueFlow) {
|
||||
pushReason(reasonCodes, "pilot_derived_value_flow_from_confirmed_rows");
|
||||
@@ -2183,6 +2337,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_ranked_value_flow: null,
|
||||
derived_value_flow: derivedValueFlow,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
|
||||
@@ -38,6 +38,9 @@ function pushUnique(target, value) {
|
||||
function hasEntity(meaning) {
|
||||
return (meaning?.explicit_entity_candidates?.length ?? 0) > 0;
|
||||
}
|
||||
function hasSubjectCandidates(graph) {
|
||||
return (graph?.subject_candidates.length ?? 0) > 0;
|
||||
}
|
||||
function aggregationAxis(meaning) {
|
||||
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
||||
}
|
||||
@@ -75,13 +78,137 @@ function budgetOverrideFor(input, recipe) {
|
||||
}
|
||||
function recipeFor(input) {
|
||||
const meaning = input.turnMeaning ?? null;
|
||||
const dataNeedGraph = input.dataNeedGraph ?? null;
|
||||
const domain = lower(meaning?.asked_domain_family);
|
||||
const action = lower(meaning?.asked_action_family);
|
||||
const unsupported = lower(meaning?.unsupported_but_understood_family);
|
||||
const graphFactFamily = lower(dataNeedGraph?.business_fact_family);
|
||||
const graphAction = lower(dataNeedGraph?.action_family);
|
||||
const graphAggregation = lower(dataNeedGraph?.aggregation_need);
|
||||
const graphClarificationGaps = (dataNeedGraph?.clarification_gaps ?? []).map((item) => lower(item));
|
||||
const combined = `${domain} ${action} ${unsupported}`.trim();
|
||||
const axes = [];
|
||||
const requestedAggregationAxis = aggregationAxis(meaning);
|
||||
addScopeAxes(axes, meaning);
|
||||
if (graphClarificationGaps.includes("lane_family_choice")) {
|
||||
pushUnique(axes, "lane_family_choice");
|
||||
return {
|
||||
semanticDataNeed: "metadata lane clarification",
|
||||
chainId: "metadata_lane_clarification",
|
||||
chainSummary: "Preserve the ambiguous metadata surface and ask the user to choose the next data lane before running MCP probes.",
|
||||
primitives: [],
|
||||
axes,
|
||||
reason: "planner_selected_metadata_lane_clarification_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "value_flow") {
|
||||
if (dataNeedGraph?.comparison_need === "incoming_vs_outgoing" && !hasSubjectCandidates(dataNeedGraph)) {
|
||||
pushUnique(axes, "amount");
|
||||
pushUnique(axes, "coverage_target");
|
||||
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
|
||||
pushUnique(axes, "calendar_month");
|
||||
}
|
||||
return {
|
||||
semanticDataNeed: "bidirectional value-flow comparison evidence",
|
||||
chainId: "value_flow_comparison",
|
||||
chainSummary: "Query incoming and outgoing movements for the checked period and organization, compare the checked sides, and probe coverage before answering a bounded comparison.",
|
||||
primitives: ["query_movements", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_bidirectional_value_flow_comparison_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (dataNeedGraph?.ranking_need && !hasSubjectCandidates(dataNeedGraph)) {
|
||||
pushUnique(axes, "aggregate_axis");
|
||||
pushUnique(axes, "amount");
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "ranked value-flow evidence",
|
||||
chainId: "value_flow_ranking",
|
||||
chainSummary: "Query scoped movements for the checked period and organization, aggregate checked amounts by counterparty, then probe coverage before answering a bounded ranking.",
|
||||
primitives: ["query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
axes,
|
||||
reason: dataNeedGraph.ranking_need === "bottom_asc"
|
||||
? "planner_selected_bottom_ranked_value_flow_from_data_need_graph"
|
||||
: "planner_selected_top_ranked_value_flow_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
pushUnique(axes, "aggregate_axis");
|
||||
pushUnique(axes, "amount");
|
||||
pushUnique(axes, "coverage_target");
|
||||
if (requestedAggregationAxis === "month" || graphAggregation === "by_month") {
|
||||
pushUnique(axes, "calendar_month");
|
||||
}
|
||||
return {
|
||||
semanticDataNeed: "counterparty value-flow evidence",
|
||||
chainId: "value_flow",
|
||||
chainSummary: "Resolve the business entity, query scoped movements, aggregate checked amounts, then probe coverage before answering.",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "aggregate_by_axis", "probe_coverage"],
|
||||
axes,
|
||||
reason: requestedAggregationAxis === "month" || graphAggregation === "by_month"
|
||||
? "planner_selected_monthly_value_flow_from_data_need_graph"
|
||||
: "planner_selected_value_flow_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "activity_lifecycle") {
|
||||
pushUnique(axes, "document_date");
|
||||
pushUnique(axes, "coverage_target");
|
||||
pushUnique(axes, "evidence_basis");
|
||||
return {
|
||||
semanticDataNeed: "counterparty lifecycle evidence",
|
||||
chainId: "lifecycle",
|
||||
chainSummary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
axes,
|
||||
reason: "planner_selected_lifecycle_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "schema_surface") {
|
||||
pushUnique(axes, "metadata_scope");
|
||||
return {
|
||||
semanticDataNeed: "1C metadata evidence",
|
||||
chainId: "metadata_inspection",
|
||||
chainSummary: "Inspect the 1C metadata surface first, then ground the next safe lane from confirmed schema evidence.",
|
||||
primitives: ["inspect_1c_metadata"],
|
||||
axes,
|
||||
reason: "planner_selected_metadata_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "movement_evidence") {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "movement evidence",
|
||||
chainId: "movement_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped movement rows, and probe coverage without pretending to have a full movement universe.",
|
||||
primitives: ["resolve_entity_reference", "query_movements", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_movement_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "document_evidence") {
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "document evidence",
|
||||
chainId: "document_evidence",
|
||||
chainSummary: "Resolve the business entity, fetch scoped document rows, and probe coverage before stating the checked document evidence.",
|
||||
primitives: ["resolve_entity_reference", "query_documents", "probe_coverage"],
|
||||
axes,
|
||||
reason: "planner_selected_document_from_data_need_graph"
|
||||
};
|
||||
}
|
||||
if (graphFactFamily === "entity_grounding" || (!graphFactFamily && (dataNeedGraph?.subject_candidates.length ?? 0) > 0)) {
|
||||
pushUnique(axes, "business_entity");
|
||||
pushUnique(axes, "coverage_target");
|
||||
return {
|
||||
semanticDataNeed: "entity discovery evidence",
|
||||
chainId: "entity_resolution",
|
||||
chainSummary: "Search candidate business entities, resolve the most relevant 1C reference, and prove whether the entity grounding is stable enough for the next probe.",
|
||||
primitives: ["search_business_entity", "resolve_entity_reference", "probe_coverage"],
|
||||
axes,
|
||||
reason: graphAction === "search_business_entity"
|
||||
? "planner_selected_entity_resolution_from_data_need_graph"
|
||||
: "planner_selected_entity_resolution_recipe"
|
||||
};
|
||||
}
|
||||
if (includesAny(combined, ["metadata_lane_choice_clarification", "resolve_next_lane"])) {
|
||||
pushUnique(axes, "lane_family_choice");
|
||||
return {
|
||||
@@ -191,8 +318,12 @@ function planAssistantMcpDiscovery(input) {
|
||||
const recipe = recipeFor(input);
|
||||
const budgetOverride = budgetOverrideFor(input, recipe);
|
||||
const semanticDataNeed = toNonEmptyString(input.semanticDataNeed) ?? recipe.semanticDataNeed;
|
||||
const dataNeedGraph = input.dataNeedGraph ?? null;
|
||||
const reasonCodes = [];
|
||||
pushReason(reasonCodes, recipe.reason);
|
||||
if (dataNeedGraph) {
|
||||
pushReason(reasonCodes, "planner_consumed_data_need_graph_v1");
|
||||
}
|
||||
if (budgetOverride.maxProbeCount) {
|
||||
pushReason(reasonCodes, "planner_enabled_chunked_coverage_probe_budget");
|
||||
}
|
||||
@@ -219,6 +350,7 @@ function planAssistantMcpDiscovery(input) {
|
||||
policy_owner: "assistantMcpDiscoveryPlanner",
|
||||
planner_status: plannerStatus,
|
||||
semantic_data_need: semanticDataNeed,
|
||||
data_need_graph: dataNeedGraph,
|
||||
selected_chain_id: recipe.chainId,
|
||||
selected_chain_summary: recipe.chainSummary,
|
||||
proposed_primitives: recipe.primitives,
|
||||
|
||||
+22
@@ -61,6 +61,28 @@ function userFacingLines(values) {
|
||||
return uniqueStrings(values).filter((line) => !hasInternalMechanics(line));
|
||||
}
|
||||
function localizeLine(value) {
|
||||
if (/^1C activity rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки активности в запрошенном срезе.";
|
||||
}
|
||||
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки входящих денежных поступлений в запрошенном срезе.";
|
||||
}
|
||||
if (/^1C supplier-payout rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки исходящих платежей и списаний в запрошенном срезе.";
|
||||
}
|
||||
const openScopeBidirectionalMatch = value.match(/^1C bidirectional value-flow rows were checked for the requested counterparty scope: incoming=(found|not_found), outgoing=(found|not_found)$/i);
|
||||
if (openScopeBidirectionalMatch) {
|
||||
const incoming = openScopeBidirectionalMatch[1] === "found"
|
||||
? "входящие строки найдены"
|
||||
: "входящие строки не найдены";
|
||||
const outgoing = openScopeBidirectionalMatch[2] === "found"
|
||||
? "исходящие строки найдены"
|
||||
: "исходящие строки не найдены";
|
||||
return `В 1С проверены входящие и исходящие денежные строки в запрошенном срезе: ${incoming}, ${outgoing}.`;
|
||||
}
|
||||
if (/^Requested period hit the MCP row limit, but the approved monthly recovery probe budget is smaller than the required subperiod count$/i.test(value)) {
|
||||
return "Запрошенный период уперся в лимит строк MCP; доступного бюджета помесячных дозапросов не хватило, чтобы покрыть все подпериоды.";
|
||||
}
|
||||
const counterpartyMatch = value.match(/^1C activity rows were found for counterparty\s+(.+)$/i);
|
||||
if (counterpartyMatch) {
|
||||
return `В 1С найдены строки активности по контрагенту ${counterpartyMatch[1]}.`;
|
||||
|
||||
@@ -51,6 +51,7 @@ function businessFactAnswerAllowed(draft) {
|
||||
async function runAssistantMcpDiscoveryRuntimeBridge(input) {
|
||||
const planner = (0, assistantMcpDiscoveryPlanner_1.planAssistantMcpDiscovery)({
|
||||
semanticDataNeed: input.semanticDataNeed,
|
||||
dataNeedGraph: input.dataNeedGraph,
|
||||
turnMeaning: input.turnMeaning
|
||||
});
|
||||
const pilot = await (0, assistantMcpDiscoveryPilotExecutor_1.executeAssistantMcpDiscoveryPilot)(planner, input.deps);
|
||||
|
||||
+1
@@ -62,6 +62,7 @@ async function runAssistantMcpDiscoveryRuntimeEntryPoint(input) {
|
||||
}
|
||||
const bridge = await (0, assistantMcpDiscoveryRuntimeBridge_1.runAssistantMcpDiscoveryRuntimeBridge)({
|
||||
semanticDataNeed: turnInput.semantic_data_need,
|
||||
dataNeedGraph: turnInput.data_need_graph,
|
||||
turnMeaning: turnInput.turn_meaning_ref,
|
||||
deps: input.deps
|
||||
});
|
||||
|
||||
+45
-8
@@ -2,6 +2,7 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION = void 0;
|
||||
exports.buildAssistantMcpDiscoveryTurnInput = buildAssistantMcpDiscoveryTurnInput;
|
||||
const assistantMcpDiscoveryDataNeedGraph_1 = require("./assistantMcpDiscoveryDataNeedGraph");
|
||||
exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION = "assistant_mcp_discovery_turn_input_v1";
|
||||
function toRecordObject(value) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
@@ -71,6 +72,9 @@ function compactLower(value) {
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
function sameScopedName(left, right) {
|
||||
return Boolean(left && right && compactLower(left) === compactLower(right));
|
||||
}
|
||||
function candidateValue(value) {
|
||||
const direct = toNonEmptyString(value);
|
||||
if (direct && direct !== "[object Object]") {
|
||||
@@ -298,6 +302,9 @@ function hasPayoutSignal(text) {
|
||||
function hasBidirectionalValueFlowSignal(text) {
|
||||
return /(?:нетто|сальдо|баланс\s+(?:плат|денег|денеж)|взаиморасч[её]т|получил[иа]?.*(?:за)?платил|(?:за)?платил[иа]?.*получил|входящ.*исходящ|исходящ.*входящ|дебет.*кредит|кредит.*дебет|net\s+(?:flow|cash|payment)|cash\s+net|incoming\s+and\s+outgoing|received\s+and\s+paid|paid\s+and\s+received)/iu.test(text);
|
||||
}
|
||||
function hasValueRankingSignal(text) {
|
||||
return /(?:кто\s+больше\s+всего.*ден[её]г|больше\s+всего.*ден[её]г|прин[её]с.*ден[её]г|сам(?:ый|ая|ое|ые).*(?:доходн|прибыльн)|most.*money|highest\s+(?:revenue|payment))/iu.test(text);
|
||||
}
|
||||
function hasMonthlyAggregationSignal(text) {
|
||||
return /(?:\u043f\u043e\s+\u043c\u0435\u0441\u044f\u0446\u0430\u043c|\u043f\u043e\u043c\u0435\u0441\u044f\u0447\u043d\u043e|\u0435\u0436\u0435\u043c\u0435\u0441\u044f\u0447\u043d\u043e|month\s+by\s+month|by\s+month|monthly)/iu.test(text);
|
||||
}
|
||||
@@ -551,7 +558,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const rawText = compactLower(rawSignalSourceText);
|
||||
const rawLifecycleSignal = hasLifecycleSignal(rawText);
|
||||
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
||||
const rawValueFlowSignal = !rawLifecycleSignal && (hasValueFlowSignal(rawText) || rawBidirectionalValueFlowSignal);
|
||||
const rawValueFlowSignal = !rawLifecycleSignal &&
|
||||
(hasValueFlowSignal(rawText) || hasValueRankingSignal(rawText) || rawBidirectionalValueFlowSignal);
|
||||
const rawMetadataSignal = !rawLifecycleSignal && !rawValueFlowSignal && hasMetadataSignal(rawText);
|
||||
const rawEntityResolutionSignal = !rawLifecycleSignal && !rawValueFlowSignal && !rawMetadataSignal && hasEntityResolutionSignal(rawText);
|
||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||
@@ -574,6 +582,13 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const explicitIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
|
||||
const assistantTurnMeaningDateScope = toNonEmptyString(assistantTurnMeaning?.explicit_date_scope);
|
||||
const assistantTurnMeaningOrganizationScope = toNonEmptyString(assistantTurnMeaning?.explicit_organization_scope);
|
||||
const predecomposeOrganizationMirrorsCounterparty = sameScopedName(predecomposeEntities.counterparty, predecomposeEntities.organization);
|
||||
const organizationMirrorsPredecomposeCounterparty = Boolean((rawBidirectionalValueFlowSignal || hasValueRankingSignal(rawText)) &&
|
||||
(sameScopedName(predecomposeEntities.counterparty, assistantTurnMeaningOrganizationScope) ||
|
||||
predecomposeOrganizationMirrorsCounterparty));
|
||||
const normalizedPredecomposeCounterparty = organizationMirrorsPredecomposeCounterparty
|
||||
? null
|
||||
: predecomposeEntities.counterparty;
|
||||
const predecomposeDateScope = collectDateScope(predecomposeContract);
|
||||
const followupDiscoverySeedApplicable = Boolean(followupSeed.domain &&
|
||||
!rawLifecycleSignal &&
|
||||
@@ -791,7 +806,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, candidate);
|
||||
}
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, predecomposeEntities.counterparty);
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, normalizedPredecomposeCounterparty);
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, followupSeed.counterparty);
|
||||
}
|
||||
else {
|
||||
@@ -801,7 +816,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
|
||||
pushScopedEntityCandidate(entityCandidates, candidate, groundedFollowupEntity);
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, predecomposeEntities.counterparty, groundedFollowupEntity);
|
||||
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
|
||||
if (!groundedFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.counterparty, null);
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
|
||||
@@ -812,13 +827,23 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
pushUnique(entityCandidates, rawMetadataScopeHint);
|
||||
}
|
||||
if (valueFlowSignal && !predecomposeEntities.counterparty && !followupSeed.counterparty) {
|
||||
const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !normalizedPredecomposeCounterparty && !followupSeed.counterparty;
|
||||
const valueFlowOrganizationStaysScope = openScopeValueFlowWithoutCounterparty &&
|
||||
(bidirectionalValueFlowSignal || hasValueRankingSignal(rawText));
|
||||
if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) {
|
||||
pushUnique(entityCandidates, predecomposeEntities.organization);
|
||||
pushUnique(entityCandidates, followupSeed.organization);
|
||||
}
|
||||
const explicitOrganizationScope = valueFlowSignal && !predecomposeEntities.counterparty && !followupSeed.counterparty
|
||||
? null
|
||||
: predecomposeEntities.organization ?? assistantTurnMeaningOrganizationScope ?? followupSeed.organization;
|
||||
const explicitOrganizationScope = valueFlowOrganizationStaysScope || !openScopeValueFlowWithoutCounterparty
|
||||
? predecomposeEntities.organization ?? assistantTurnMeaningOrganizationScope ?? followupSeed.organization
|
||||
: null;
|
||||
if (valueFlowOrganizationStaysScope && explicitOrganizationScope) {
|
||||
for (let index = entityCandidates.length - 1; index >= 0; index -= 1) {
|
||||
if (entityCandidates[index] === explicitOrganizationScope) {
|
||||
entityCandidates.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
const explicitDateScope = assistantTurnMeaningDateScope ?? predecomposeDateScope ?? rawDateScope ?? followupSeed.dateScope;
|
||||
const turnMeaning = {
|
||||
asked_domain_family: lifecycleSignal
|
||||
@@ -1054,7 +1079,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (unsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||||
}
|
||||
if (predecomposeEntities.counterparty) {
|
||||
if (!(valueFlowOrganizationStaysScope && normalizedPredecomposeCounterparty === explicitOrganizationScope) &&
|
||||
normalizedPredecomposeCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||||
}
|
||||
if (followupSeed.counterparty) {
|
||||
@@ -1072,12 +1098,23 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (runDiscovery && !hasTurnMeaning) {
|
||||
pushReason(reasonCodes, "mcp_discovery_turn_meaning_missing");
|
||||
}
|
||||
const dataNeedGraph = runDiscovery && hasTurnMeaning
|
||||
? (0, assistantMcpDiscoveryDataNeedGraph_1.buildAssistantMcpDiscoveryDataNeedGraph)({
|
||||
semanticDataNeed,
|
||||
rawUtterance: rawSignalSourceText,
|
||||
turnMeaning: cleanTurnMeaning
|
||||
})
|
||||
: null;
|
||||
if (dataNeedGraph) {
|
||||
pushReason(reasonCodes, "mcp_discovery_data_need_graph_built");
|
||||
}
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryTurnInputAdapter",
|
||||
adapter_status: !runDiscovery ? "not_applicable" : hasTurnMeaning ? "ready" : "needs_more_context",
|
||||
should_run_discovery: runDiscovery,
|
||||
semantic_data_need: runDiscovery ? semanticDataNeed : null,
|
||||
data_need_graph: dataNeedGraph,
|
||||
turn_meaning_ref: runDiscovery && hasTurnMeaning ? cleanTurnMeaning : null,
|
||||
source_signal: sourceSignal,
|
||||
reason_codes: reasonCodes
|
||||
|
||||
Reference in New Issue
Block a user