193 lines
12 KiB
JavaScript
193 lines
12 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.buildAssistantAddressOrchestrationRuntime = buildAssistantAddressOrchestrationRuntime;
|
||
const assistantRoutePolicyRuntimeAdapter_1 = require("./assistantRoutePolicyRuntimeAdapter");
|
||
const assistantMcpDiscoveryRuntimeEntryPoint_1 = require("./assistantMcpDiscoveryRuntimeEntryPoint");
|
||
const inventoryLifecycleCueHelpers_1 = require("./inventoryLifecycleCueHelpers");
|
||
function toRecordObject(value) {
|
||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||
return null;
|
||
}
|
||
return value;
|
||
}
|
||
function hasSelectedObjectInventorySignal(text) {
|
||
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ним|selected\s+object)/iu.test(String(text ?? ""));
|
||
}
|
||
function hasSelectedObjectInventoryActionCue(text) {
|
||
const value = String(text ?? "");
|
||
return (/(?:кому[\s\S]{0,80}(?:продал[аи]?|реализова[нлт][а-я]*|поставил[аи]?|поставлен[а-я]*|отгрузил[аи]?|отгружен[а-я]*)|кому\s+был\s+продан|куда[\s\S]{0,80}(?:продал[аи]?|реализова[нлт][а-я]*|поставил[аи]?|поставлен[а-я]*|отгрузил[аи]?|отгружен[а-я]*)|кто[\s\S]{0,40}купил|кто\s+это\s+поставил|кто\s+поставил|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+мы\s+купили|где\s+куплено|по\s+каким\s+документам|какими\s+документами|покажи\s+документы|документы[\s\S]{0,80}(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару)|операци)|документы\s+закупки|buyer|sale\s+trace|supplier|vendor|purchase\s+documents|purchase[\s-]?to[\s-]?sale|old\s+purchase|aged\s+stock)/iu.test(value) || (0, inventoryLifecycleCueHelpers_1.hasInventoryProfitabilityCue)(value));
|
||
}
|
||
function hasShortInventoryPurchaseFollowupCue(text) {
|
||
return /(?:^|[\s,.;:!?])(а\s+)?(?:купили\s+у\s+кого|у\s+кого\s+купили|поставщик|продавец|seller)(?:[\s,.;:!?]|$)/iu.test(String(text ?? ""));
|
||
}
|
||
function isInventorySelectedObjectOrRootIntent(intent) {
|
||
return (intent === "inventory_on_hand_as_of_date" ||
|
||
intent === "inventory_purchase_provenance_for_item" ||
|
||
intent === "inventory_purchase_documents_for_item" ||
|
||
intent === "inventory_sale_trace_for_item" ||
|
||
intent === "inventory_profitability_for_item" ||
|
||
intent === "inventory_purchase_to_sale_chain" ||
|
||
intent === "inventory_aging_by_purchase_date");
|
||
}
|
||
function isGenericCanonicalDriftIntent(intent) {
|
||
return (intent === "open_items_by_counterparty_or_contract" ||
|
||
intent === "customer_revenue_and_payments" ||
|
||
intent === "list_documents_by_counterparty" ||
|
||
intent === "list_documents_by_contract" ||
|
||
intent === "bank_operations_by_counterparty" ||
|
||
intent === "bank_operations_by_contract" ||
|
||
intent === "documents_forming_balance");
|
||
}
|
||
function hasSameDateFollowupSignal(text) {
|
||
return /(?:эту\s+же\s+дат(?:у|е|ой)|ту\s+же\s+дат(?:у|е|ой)|same\s+date)/iu.test(String(text ?? ""));
|
||
}
|
||
function hasExplicitCurrentDateSignal(text) {
|
||
return /(?:текущ(?:ую|ая|ий|ее|ей)\s+дат(?:у|а|е|ой)|сегодняшн(?:юю|ий|ей)\s+дат(?:у|а|е|ой)|today|current\s+date)/iu.test(String(text ?? ""));
|
||
}
|
||
function hasInventoryTemporalRootFollowupCue(text) {
|
||
const value = String(text ?? "").trim().toLowerCase();
|
||
if (!value) {
|
||
return false;
|
||
}
|
||
const tokenCount = value.split(/\s+/).filter(Boolean).length;
|
||
const hasMonthYearCue = /(?:январ(?:ь|е)|феврал(?:ь|е)|март(?:е)?|апрел(?:ь|е)|ма(?:й|е)|июн(?:ь|е)|июл(?:ь|е)|август(?:е)?|сентябр(?:ь|е)|октябр(?:ь|е)|ноябр(?:ь|е)|декабр(?:ь|е))(?:\s+\d{4})?/iu.test(value) || /\b(?:19|20)\d{2}\b/u.test(value);
|
||
if (tokenCount <= 3 && hasMonthYearCue) {
|
||
return true;
|
||
}
|
||
const hasInventoryLexeme = /(?:остат|склад|товар|позици|номенклатур)/iu.test(value);
|
||
return hasInventoryLexeme && (hasMonthYearCue || hasSameDateFollowupSignal(value));
|
||
}
|
||
function shouldPreferRawFollowupMessage(userMessage, addressInputMessage, carryover, addressPreDecompose, toNonEmptyString) {
|
||
if (!carryover?.followupContext || typeof carryover.followupContext !== "object") {
|
||
return false;
|
||
}
|
||
const rawMessage = toNonEmptyString(userMessage);
|
||
const canonicalMessage = toNonEmptyString(addressInputMessage);
|
||
if (!rawMessage || !canonicalMessage || rawMessage === canonicalMessage) {
|
||
return false;
|
||
}
|
||
const predecomposeContract = addressPreDecompose?.predecomposeContract && typeof addressPreDecompose.predecomposeContract === "object"
|
||
? addressPreDecompose.predecomposeContract
|
||
: null;
|
||
const mode = toNonEmptyString(predecomposeContract?.mode) ?? "unknown";
|
||
const intent = toNonEmptyString(predecomposeContract?.intent) ?? "unknown";
|
||
const followupContext = carryover.followupContext && typeof carryover.followupContext === "object"
|
||
? carryover.followupContext
|
||
: null;
|
||
const previousIntent = toNonEmptyString(followupContext?.previous_intent);
|
||
const rootIntent = toNonEmptyString(followupContext?.root_intent);
|
||
const previousAnchorType = toNonEmptyString(followupContext?.previous_anchor_type);
|
||
const hasReferentialDocumentExclusionFollowupCue = /(?:\u043a\u0440\u043e\u043c\u0435|\u043f\u043e\u043c\u0438\u043c\u043e)\s+(?:\u044d\u0442\u043e\u0433\u043e|\u044d\u0442\u043e\u0439|\u044d\u0442\u043e\u0442|\u044d\u0442\u0443|\u044d\u0442\u0438\u0445)(?:\s+(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430|\u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0430|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430))?/iu.test(rawMessage);
|
||
const hasInventoryItemCarryover = previousAnchorType === "item" && isInventorySelectedObjectOrRootIntent(previousIntent);
|
||
const hasInventoryFrameCarryover = isInventorySelectedObjectOrRootIntent(previousIntent) ||
|
||
isInventorySelectedObjectOrRootIntent(rootIntent);
|
||
const hasDocumentCarryover = previousIntent === "list_documents_by_counterparty" || previousIntent === "list_documents_by_contract";
|
||
if (mode === "unsupported" && intent === "unknown") {
|
||
return true;
|
||
}
|
||
if (hasDocumentCarryover && hasReferentialDocumentExclusionFollowupCue) {
|
||
return true;
|
||
}
|
||
if (hasSameDateFollowupSignal(rawMessage) && hasExplicitCurrentDateSignal(canonicalMessage)) {
|
||
return true;
|
||
}
|
||
if (hasInventoryFrameCarryover &&
|
||
hasInventoryTemporalRootFollowupCue(rawMessage) &&
|
||
(intent === "account_balance_snapshot" || intent === "documents_forming_balance" || intent === "unknown")) {
|
||
return true;
|
||
}
|
||
return ((hasSelectedObjectInventorySignal(rawMessage) || hasInventoryItemCarryover) &&
|
||
(hasSelectedObjectInventoryActionCue(rawMessage) || hasShortInventoryPurchaseFollowupCue(rawMessage)) &&
|
||
(isGenericCanonicalDriftIntent(intent) || intent === "unknown"));
|
||
}
|
||
function fallbackAddressPreDecompose(userMessage, llmProvider, buildAddressLlmPredecomposeContractV1, sanitizeAddressMessageForFallback) {
|
||
const provider = llmProvider === "local" ? "local" : llmProvider === "openai" ? "openai" : null;
|
||
return {
|
||
attempted: false,
|
||
applied: false,
|
||
provider,
|
||
traceId: null,
|
||
effectiveMessage: userMessage,
|
||
reason: "disabled_by_feature_flag",
|
||
llmCanonicalCandidateDetected: false,
|
||
predecomposeContract: buildAddressLlmPredecomposeContractV1({
|
||
sourceMessage: userMessage,
|
||
canonicalMessage: userMessage
|
||
}),
|
||
fallbackRuleHit: null,
|
||
sanitizedUserMessage: sanitizeAddressMessageForFallback(userMessage),
|
||
toolGateDecision: null,
|
||
toolGateReason: null
|
||
};
|
||
}
|
||
async function buildAssistantAddressOrchestrationRuntime(input) {
|
||
const initialAddressPreDecompose = input.featureAddressLlmPredecomposeV1
|
||
? await input.runAddressLlmPreDecompose()
|
||
: fallbackAddressPreDecompose(input.userMessage, input.llmProvider, input.buildAddressLlmPredecomposeContractV1, input.sanitizeAddressMessageForFallback);
|
||
let addressPreDecompose = initialAddressPreDecompose;
|
||
let addressInputMessage = input.toNonEmptyString(addressPreDecompose?.effectiveMessage) ?? input.userMessage;
|
||
let carryover = input.resolveAddressFollowupCarryoverContext(input.userMessage, input.sessionItems, addressInputMessage, addressPreDecompose, input.sessionAddressNavigationState);
|
||
if (shouldPreferRawFollowupMessage(input.userMessage, addressInputMessage, carryover, addressPreDecompose, input.toNonEmptyString)) {
|
||
addressInputMessage = input.userMessage;
|
||
addressPreDecompose = {
|
||
...addressPreDecompose,
|
||
applied: false,
|
||
effectiveMessage: input.userMessage,
|
||
reason: "followup_raw_message_preferred_over_llm_rewrite",
|
||
predecomposeContract: input.buildAddressLlmPredecomposeContractV1({
|
||
sourceMessage: input.userMessage,
|
||
canonicalMessage: input.userMessage
|
||
})
|
||
};
|
||
carryover = input.resolveAddressFollowupCarryoverContext(input.userMessage, input.sessionItems, addressInputMessage, addressPreDecompose, input.sessionAddressNavigationState);
|
||
}
|
||
const followupContext = toRecordObject(carryover?.followupContext);
|
||
const routePolicyRuntime = (0, assistantRoutePolicyRuntimeAdapter_1.runAssistantRoutePolicyRuntime)({
|
||
rawUserMessage: input.userMessage,
|
||
effectiveAddressUserMessage: addressInputMessage,
|
||
followupContext,
|
||
llmPreDecomposeMeta: addressPreDecompose,
|
||
sessionItems: input.sessionItems,
|
||
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
|
||
useMock: input.useMock,
|
||
resolveAssistantOrchestrationDecision: input.resolveAssistantOrchestrationDecision
|
||
});
|
||
const orchestrationDecision = routePolicyRuntime.orchestrationDecision;
|
||
const orchestrationContract = toRecordObject(orchestrationDecision.orchestrationContract);
|
||
const predecomposeContract = toRecordObject(addressPreDecompose.predecomposeContract);
|
||
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(input.userMessage, addressInputMessage, carryover, addressPreDecompose);
|
||
const runDiscoveryEntryPoint = input.runMcpDiscoveryRuntimeEntryPoint ?? assistantMcpDiscoveryRuntimeEntryPoint_1.runAssistantMcpDiscoveryRuntimeEntryPoint;
|
||
let mcpDiscoveryRuntimeEntryPoint = null;
|
||
let mcpDiscoveryRuntimeEntryPointError = null;
|
||
try {
|
||
mcpDiscoveryRuntimeEntryPoint = (await runDiscoveryEntryPoint({
|
||
userMessage: input.userMessage,
|
||
effectiveMessage: addressInputMessage,
|
||
assistantTurnMeaning: toRecordObject(orchestrationContract?.assistant_turn_meaning),
|
||
predecomposeContract,
|
||
followupContext
|
||
}));
|
||
}
|
||
catch (error) {
|
||
mcpDiscoveryRuntimeEntryPointError = String(error instanceof Error ? error.message : error ?? "unknown_error").slice(0, 280);
|
||
}
|
||
const addressRuntimeMeta = {
|
||
...addressPreDecompose,
|
||
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
|
||
toolGateReason: orchestrationDecision.toolGateReason ?? null,
|
||
dialogContinuationContract,
|
||
orchestrationContract: orchestrationContract ?? null,
|
||
routePolicyContract: routePolicyRuntime.routePolicyContract,
|
||
mcpDiscoveryRuntimeEntryPoint,
|
||
mcpDiscoveryRuntimeEntryPointError
|
||
};
|
||
return {
|
||
addressPreDecompose,
|
||
addressInputMessage,
|
||
carryover,
|
||
orchestrationDecision,
|
||
addressRuntimeMeta,
|
||
livingModeDecision: routePolicyRuntime.livingModeDecision
|
||
};
|
||
}
|