277 lines
12 KiB
JavaScript
277 lines
12 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION = void 0;
|
||
exports.buildAssistantMcpDiscoveryTurnInput = buildAssistantMcpDiscoveryTurnInput;
|
||
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)) {
|
||
return null;
|
||
}
|
||
return value;
|
||
}
|
||
function toNonEmptyString(value) {
|
||
if (value === null || value === undefined) {
|
||
return null;
|
||
}
|
||
const text = String(value).trim();
|
||
return text.length > 0 ? text : null;
|
||
}
|
||
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 compactLower(value) {
|
||
return String(value ?? "")
|
||
.toLowerCase()
|
||
.replace(/\s+/g, " ")
|
||
.trim();
|
||
}
|
||
function candidateValue(value) {
|
||
const direct = toNonEmptyString(value);
|
||
if (direct && direct !== "[object Object]") {
|
||
return direct;
|
||
}
|
||
const record = toRecordObject(value);
|
||
if (!record) {
|
||
return null;
|
||
}
|
||
return (toNonEmptyString(record.value) ??
|
||
toNonEmptyString(record.name) ??
|
||
toNonEmptyString(record.ref) ??
|
||
toNonEmptyString(record.text));
|
||
}
|
||
function collectEntityCandidates(value) {
|
||
const result = [];
|
||
if (Array.isArray(value)) {
|
||
for (const item of value) {
|
||
pushUnique(result, candidateValue(item));
|
||
}
|
||
return result;
|
||
}
|
||
pushUnique(result, candidateValue(value));
|
||
return result;
|
||
}
|
||
function collectPredecomposeEntities(predecompose) {
|
||
const entities = toRecordObject(predecompose?.entities);
|
||
return {
|
||
counterparty: toNonEmptyString(entities?.counterparty),
|
||
organization: toNonEmptyString(entities?.organization)
|
||
};
|
||
}
|
||
function collectDateScope(predecompose) {
|
||
const period = toRecordObject(predecompose?.period);
|
||
const asOfDate = toNonEmptyString(period?.as_of_date);
|
||
const periodFrom = toNonEmptyString(period?.period_from);
|
||
const periodTo = toNonEmptyString(period?.period_to);
|
||
if (asOfDate) {
|
||
return asOfDate;
|
||
}
|
||
const yearFrom = periodFrom?.match(/^(\d{4})-01-01$/);
|
||
const yearTo = periodTo?.match(/^(\d{4})-12-31$/);
|
||
if (yearFrom && yearTo && yearFrom[1] === yearTo[1]) {
|
||
return yearFrom[1];
|
||
}
|
||
if (periodFrom && periodTo) {
|
||
return `${periodFrom}..${periodTo}`;
|
||
}
|
||
return periodFrom ?? periodTo ?? null;
|
||
}
|
||
function hasLifecycleSignal(text) {
|
||
return /(?:сколько\s+лет|как\s+давно|давно\s+ли|возраст|перв(?:ая|ый)\s+актив|когда\s+начал|когда\s+появ|lifecycle|activity\s+duration|business\s+age|how\s+long)/iu.test(text);
|
||
}
|
||
function hasValueFlowSignal(text) {
|
||
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(text);
|
||
}
|
||
function hasPayoutSignal(text) {
|
||
return /(?:\bмы\s+(?:за)?плат|(?:за)?платил|оплатил|перечисл|списан|расход|поставщик|исходящ|supplier|payout|outflow|paid\s+to|payment\s+to)/iu.test(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 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);
|
||
}
|
||
function semanticNeedFor(input) {
|
||
const combined = compactLower(`${input.domain ?? ""} ${input.action ?? ""} ${input.unsupported ?? ""}`);
|
||
if (input.lifecycleSignal || /(?:lifecycle|activity|duration|age)/iu.test(combined)) {
|
||
return "counterparty lifecycle evidence";
|
||
}
|
||
if (input.valueFlowSignal || /(?:turnover|revenue|payment|payout|value|net|netting|balance|cashflow)/iu.test(combined)) {
|
||
return "counterparty value-flow evidence";
|
||
}
|
||
if (/(?:document|documents|list_documents)/iu.test(combined)) {
|
||
return "document evidence";
|
||
}
|
||
if (/(?:metadata|schema|catalog)/iu.test(combined)) {
|
||
return "1C metadata evidence";
|
||
}
|
||
return null;
|
||
}
|
||
function shouldRunDiscovery(input) {
|
||
if (input.lifecycleSignal || input.unsupported) {
|
||
return true;
|
||
}
|
||
if (input.valueFlowSignal && !input.explicitIntentCandidate) {
|
||
return true;
|
||
}
|
||
if (!input.explicitIntentCandidate && input.semanticDataNeed) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function buildAssistantMcpDiscoveryTurnInput(input) {
|
||
const assistantTurnMeaning = toRecordObject(input.assistantTurnMeaning);
|
||
const predecomposeContract = toRecordObject(input.predecomposeContract);
|
||
const predecomposeEntities = collectPredecomposeEntities(predecomposeContract);
|
||
const reasonCodes = [];
|
||
const rawText = compactLower(`${input.userMessage ?? ""} ${input.effectiveMessage ?? ""}`);
|
||
const lifecycleSignal = hasLifecycleSignal(rawText);
|
||
const bidirectionalValueFlowSignal = !lifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
||
const valueFlowSignal = !lifecycleSignal && (hasValueFlowSignal(rawText) || bidirectionalValueFlowSignal);
|
||
const payoutSignal = valueFlowSignal && !bidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||
const monthlyAggregationSignal = valueFlowSignal && hasMonthlyAggregationSignal(rawText);
|
||
const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family);
|
||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
||
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
||
const explicitIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
|
||
const semanticDataNeed = semanticNeedFor({
|
||
domain: rawDomain,
|
||
action: rawAction,
|
||
unsupported,
|
||
lifecycleSignal,
|
||
valueFlowSignal
|
||
});
|
||
const entityCandidates = collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
||
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
||
if (valueFlowSignal && !predecomposeEntities.counterparty) {
|
||
pushUnique(entityCandidates, predecomposeEntities.organization);
|
||
}
|
||
const explicitOrganizationScope = valueFlowSignal && !predecomposeEntities.counterparty ? null : predecomposeEntities.organization;
|
||
const turnMeaning = {
|
||
asked_domain_family: lifecycleSignal ? "counterparty_lifecycle" : valueFlowSignal ? "counterparty_value" : rawDomain,
|
||
asked_action_family: lifecycleSignal
|
||
? "activity_duration"
|
||
: valueFlowSignal
|
||
? bidirectionalValueFlowSignal
|
||
? "net_value_flow"
|
||
: payoutSignal
|
||
? "payout"
|
||
: "turnover"
|
||
: rawAction,
|
||
asked_aggregation_axis: monthlyAggregationSignal ? "month" : rawAggregationAxis,
|
||
explicit_entity_candidates: entityCandidates,
|
||
explicit_organization_scope: explicitOrganizationScope,
|
||
explicit_date_scope: collectDateScope(predecomposeContract),
|
||
unsupported_but_understood_family: unsupported ??
|
||
(lifecycleSignal
|
||
? "counterparty_lifecycle"
|
||
: valueFlowSignal
|
||
? bidirectionalValueFlowSignal
|
||
? "counterparty_bidirectional_value_flow_or_netting"
|
||
: payoutSignal
|
||
? "counterparty_payouts_or_outflow"
|
||
: "counterparty_value_or_turnover"
|
||
: null),
|
||
stale_replay_forbidden: Boolean(assistantTurnMeaning?.stale_replay_forbidden || unsupported || lifecycleSignal || valueFlowSignal)
|
||
};
|
||
const cleanTurnMeaning = {};
|
||
if (toNonEmptyString(turnMeaning.asked_domain_family)) {
|
||
cleanTurnMeaning.asked_domain_family = turnMeaning.asked_domain_family;
|
||
}
|
||
if (toNonEmptyString(turnMeaning.asked_action_family)) {
|
||
cleanTurnMeaning.asked_action_family = turnMeaning.asked_action_family;
|
||
}
|
||
if (toNonEmptyString(turnMeaning.asked_aggregation_axis)) {
|
||
cleanTurnMeaning.asked_aggregation_axis = turnMeaning.asked_aggregation_axis;
|
||
}
|
||
if ((turnMeaning.explicit_entity_candidates?.length ?? 0) > 0) {
|
||
cleanTurnMeaning.explicit_entity_candidates = turnMeaning.explicit_entity_candidates;
|
||
}
|
||
if (toNonEmptyString(turnMeaning.explicit_organization_scope)) {
|
||
cleanTurnMeaning.explicit_organization_scope = turnMeaning.explicit_organization_scope;
|
||
}
|
||
if (toNonEmptyString(turnMeaning.explicit_date_scope)) {
|
||
cleanTurnMeaning.explicit_date_scope = turnMeaning.explicit_date_scope;
|
||
}
|
||
if (toNonEmptyString(turnMeaning.unsupported_but_understood_family)) {
|
||
cleanTurnMeaning.unsupported_but_understood_family = turnMeaning.unsupported_but_understood_family;
|
||
}
|
||
if (turnMeaning.stale_replay_forbidden) {
|
||
cleanTurnMeaning.stale_replay_forbidden = true;
|
||
}
|
||
const runDiscovery = shouldRunDiscovery({
|
||
unsupported,
|
||
lifecycleSignal,
|
||
valueFlowSignal,
|
||
semanticDataNeed,
|
||
explicitIntentCandidate
|
||
});
|
||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||
const sourceSignal = assistantTurnMeaning
|
||
? "assistant_turn_meaning"
|
||
: predecomposeContract
|
||
? "predecompose_contract"
|
||
: lifecycleSignal
|
||
? "raw_text"
|
||
: valueFlowSignal
|
||
? "raw_text"
|
||
: "none";
|
||
if (lifecycleSignal) {
|
||
pushReason(reasonCodes, "mcp_discovery_lifecycle_signal_detected");
|
||
}
|
||
if (valueFlowSignal) {
|
||
pushReason(reasonCodes, "mcp_discovery_value_flow_signal_detected");
|
||
}
|
||
if (payoutSignal) {
|
||
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
||
}
|
||
if (bidirectionalValueFlowSignal) {
|
||
pushReason(reasonCodes, "mcp_discovery_bidirectional_value_flow_signal_detected");
|
||
}
|
||
if (monthlyAggregationSignal) {
|
||
pushReason(reasonCodes, "mcp_discovery_monthly_aggregation_signal_detected");
|
||
}
|
||
if (unsupported) {
|
||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||
}
|
||
if (predecomposeEntities.counterparty) {
|
||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||
}
|
||
if (entityCandidates.length > 0) {
|
||
pushReason(reasonCodes, "mcp_discovery_entity_scope_available");
|
||
}
|
||
if (!runDiscovery) {
|
||
pushReason(reasonCodes, "mcp_discovery_not_applicable_for_supported_exact_turn");
|
||
}
|
||
if (runDiscovery && !hasTurnMeaning) {
|
||
pushReason(reasonCodes, "mcp_discovery_turn_meaning_missing");
|
||
}
|
||
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,
|
||
turn_meaning_ref: runDiscovery && hasTurnMeaning ? cleanTurnMeaning : null,
|
||
source_signal: sourceSignal,
|
||
reason_codes: reasonCodes
|
||
};
|
||
}
|