NODEDC_1C/llm_normalizer/backend/dist/services/assistantAddressOrchestrati...

233 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 sessionOrganizationName(sessionOrganizationScope, toNonEmptyString) {
const scope = toRecordObject(sessionOrganizationScope);
return toNonEmptyString(scope?.selectedOrganization) ?? toNonEmptyString(scope?.activeOrganization);
}
function predecomposeOrganizationName(predecomposeContract, toNonEmptyString) {
const entities = toRecordObject(predecomposeContract?.entities);
return (toNonEmptyString(entities?.organization) ??
toNonEmptyString(predecomposeContract?.organization));
}
function mergeOrganizationIntoDiscoveryFollowupContext(followupContext, organization) {
if (!organization) {
return followupContext;
}
const base = followupContext ? { ...followupContext } : {};
const previousFilters = toRecordObject(base.previous_filters)
? { ...base.previous_filters }
: {};
if (!previousFilters.organization) {
previousFilters.organization = organization;
}
base.previous_filters = previousFilters;
const rootFilters = toRecordObject(base.root_filters)
? { ...base.root_filters }
: {};
if (!rootFilters.organization) {
rootFilters.organization = organization;
}
base.root_filters = rootFilters;
if (!base.previous_anchor_type) {
base.previous_anchor_type = "organization";
}
if (!base.previous_anchor_value) {
base.previous_anchor_value = organization;
}
return base;
}
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 explicitPredecomposeOrganization = predecomposeOrganizationName(predecomposeContract, input.toNonEmptyString);
const discoveryFollowupContext = mergeOrganizationIntoDiscoveryFollowupContext(followupContext, explicitPredecomposeOrganization
? null
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString));
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: discoveryFollowupContext
}));
}
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
};
}