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

624 lines
34 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 sessionKnownOrganizations(sessionOrganizationScope) {
const scope = toRecordObject(sessionOrganizationScope);
const knownOrganizations = scope?.knownOrganizations;
return Array.isArray(knownOrganizations) ? knownOrganizations : [];
}
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 compactLower(value) {
return String(value ?? "")
.toLowerCase()
.replace(/\s+/g, " ")
.trim();
}
function hasCompactCashflowFollowupSignal(text) {
const value = compactLower(text);
if (!value) {
return false;
}
return /(?:\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u043f\u0440\u0438\u0448\p{L}*[\s\S]{0,80}\u0443\u0448\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b)/iu.test(value);
}
function dateScopeToFilterWindow(dateScope) {
if (!dateScope) {
return null;
}
if (/^(?:19|20)\d{2}$/.test(dateScope)) {
return {
period_from: `${dateScope}-01-01`,
period_to: `${dateScope}-12-31`
};
}
if (/^(?:19|20)\d{2}-\d{2}-\d{2}$/.test(dateScope)) {
return { as_of_date: dateScope };
}
return null;
}
function looksLikeReliableOrganizationScope(value) {
const text = compactLower(value);
if (!text) {
return false;
}
if (/(?:\u0440\u0430\u0437\u0431\u0438\u0432|\u0440\u0430\u0437\u0440\u0435\u0437|\u0432\u0445\u043e\u0434\u044f\u0449|\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u043d\u0435\u0442\u0442\u043e|\u0442\u043e\u043f)/iu.test(text)) {
return false;
}
return /(?:^|[\s"'\u00ab])(?:\u043e\u043e\u043e|\u0438\u043f|\u043f\u0430\u043e|\u0430\u043e|\u0437\u0430\u043e)(?:[\s"'\u00bb]|$)|\u043e\u0431\u0449\u0435\u0441\u0442\u0432\p{L}*\s+\u0441\s+\u043e\u0433\u0440\u0430\u043d\u0438\u0447\p{L}*\s+\u043e\u0442\u0432\p{L}*|\b(?:llc|inc|corp)\b/iu.test(text);
}
function inferBusinessOverviewDiscoveryContextFromSessionItems(sessionItems, toNonEmptyString) {
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
const item = toRecordObject(sessionItems[index]);
if (toNonEmptyString(item?.role) !== "assistant") {
continue;
}
const debug = toRecordObject(item?.debug);
const entryPoint = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1);
const turnInput = toRecordObject(entryPoint?.turn_input);
const dataNeedGraph = toRecordObject(turnInput?.data_need_graph);
if (toNonEmptyString(dataNeedGraph?.business_fact_family) !== "business_overview") {
continue;
}
const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref);
const filterWindow = dateScopeToFilterWindow(toNonEmptyString(turnMeaningRef?.explicit_date_scope));
if (!filterWindow) {
continue;
}
const previousFilters = { ...filterWindow };
const organization = toNonEmptyString(turnMeaningRef?.explicit_organization_scope);
if (looksLikeReliableOrganizationScope(organization)) {
previousFilters.organization = organization;
}
const rankingNeed = toNonEmptyString(dataNeedGraph?.ranking_need);
return {
previous_discovery_pilot_scope: "business_overview_route_template_v1",
previous_filters: previousFilters,
root_filters: { ...previousFilters },
previous_seeded_ranking_need: rankingNeed,
previous_intent: "business_overview",
root_intent: "business_overview"
};
}
return null;
}
function mergeBusinessOverviewDateContextForCompactCashflow(input) {
if (!hasCompactCashflowFollowupSignal(input.userMessage)) {
return input.followupContext;
}
const existingFilters = toRecordObject(input.followupContext?.previous_filters);
if (input.toNonEmptyString(existingFilters?.period_from) ||
input.toNonEmptyString(existingFilters?.period_to) ||
input.toNonEmptyString(existingFilters?.as_of_date)) {
return input.followupContext;
}
const inferred = inferBusinessOverviewDiscoveryContextFromSessionItems(input.sessionItems, input.toNonEmptyString);
if (!inferred) {
return input.followupContext;
}
return {
...inferred,
...(input.followupContext ?? {}),
previous_filters: {
...(toRecordObject(inferred.previous_filters) ?? {}),
...(toRecordObject(input.followupContext?.previous_filters) ?? {})
},
root_filters: {
...(toRecordObject(inferred.root_filters) ?? {}),
...(toRecordObject(input.followupContext?.root_filters) ?? {})
}
};
}
function firstString(values, toNonEmptyString) {
for (const value of values) {
const text = toNonEmptyString(value);
if (text) {
return text;
}
}
return null;
}
function comparableEntityName(value) {
const text = compactLower(value);
return text ? text.replace(/["'«»„“”]+/g, "") : null;
}
function sameEntityHint(expected, actual) {
const left = comparableEntityName(expected);
const right = comparableEntityName(actual);
if (!left || !right) {
return true;
}
return left === right || left.includes(right) || right.includes(left);
}
function isBusinessOverviewDiscoveryFollowup(followupContext, toNonEmptyString) {
if (!followupContext) {
return false;
}
return [
followupContext.previous_discovery_pilot_scope,
followupContext.previous_discovery_loop_selected_chain_id,
followupContext.previous_discovery_loop_asked_domain_family,
followupContext.previous_intent,
followupContext.target_intent
]
.map((value) => toNonEmptyString(value))
.some((value) => value === "business_overview" || value === "business_overview_route_template_v1");
}
function businessOverviewCounterpartyHint(followupContext, toNonEmptyString) {
const previousFilters = toRecordObject(followupContext.previous_filters);
const rootFilters = toRecordObject(followupContext.root_filters);
return (toNonEmptyString(followupContext.previous_discovery_loop_metadata_scope_hint) ??
(toNonEmptyString(followupContext.previous_anchor_type) === "counterparty"
? toNonEmptyString(followupContext.previous_anchor_value)
: null) ??
toNonEmptyString(previousFilters?.counterparty) ??
toNonEmptyString(rootFilters?.counterparty));
}
function turnMeaningCounterpartyName(turnMeaningRef, valueBundle, documentBundle, toNonEmptyString) {
const separateEntities = Array.isArray(turnMeaningRef.business_overview_separate_entity_candidates)
? turnMeaningRef.business_overview_separate_entity_candidates
: [];
return (toNonEmptyString(valueBundle?.counterparty) ??
toNonEmptyString(documentBundle?.counterparty) ??
toNonEmptyString(turnMeaningRef.metadata_scope_hint) ??
firstString(separateEntities, toNonEmptyString));
}
function parseBusinessOverviewProofBundlesFromText(value, toNonEmptyString) {
const text = toNonEmptyString(value);
if (!text) {
return null;
}
const valueMatch = text.match(/Отдельно\s+по\s+контрагенту\s+([^:\n]+):\s*подтверждено\s+получили\s+([^,\n]+?руб\.?),\s*заплатили\s+([^,\n]+?руб\.?),\s*расчетное\s+нетто\s+в\s+нашу\s+сторону\s+([^.\n]+?руб\.?)/iu);
const directDocumentMatch = text.match(/Контрагент:\s*([^.\n]+)\.\s*Найдено\s+документов:\s*(\d+)/iu);
const summaryDocumentMatch = text.match(/документы\s+по\s+цепочке:\s*найдено\s*(\d+)/iu);
const counterparty = toNonEmptyString(valueMatch?.[1]) ?? toNonEmptyString(directDocumentMatch?.[1]);
if (!counterparty) {
return null;
}
const valueBundle = valueMatch
? {
counterparty,
incoming_customer_revenue: {
total_amount_human_ru: toNonEmptyString(valueMatch[2])
},
outgoing_supplier_payout: {
total_amount_human_ru: toNonEmptyString(valueMatch[3])
},
net_amount_human_ru: toNonEmptyString(valueMatch[4]),
net_direction: "net_incoming",
inference_basis: "parsed_from_previous_business_overview_summary"
}
: null;
const documentCount = Number(directDocumentMatch?.[2] ?? summaryDocumentMatch?.[1]);
const documentBundle = Number.isFinite(documentCount) && documentCount > 0
? {
counterparty,
document_count: documentCount
}
: null;
return valueBundle || documentBundle ? { valueBundle, documentBundle } : null;
}
function parseBusinessOverviewProofBundlesFromTextV2(value, counterpartyHint, toNonEmptyString) {
const text = toNonEmptyString(value);
if (!text) {
return null;
}
const comparableHint = comparableEntityName(counterpartyHint);
const lines = text
.split(/\r?\n/u)
.map((line) => line.trim())
.filter(Boolean);
const candidateLines = comparableHint
? lines.filter((line) => comparableEntityName(line)?.includes(comparableHint))
: lines;
const rubAmountPattern = /[0-9][0-9\s.,]*\s*\u0440\u0443\u0431\.?/giu;
const valueLine = candidateLines.find((line) => (line.match(rubAmountPattern) ?? []).length >= 3) ?? null;
const valueAmounts = valueLine?.match(rubAmountPattern) ?? [];
const directDocumentMatch = candidateLines
.map((line) => line.match(/\u041a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442:\s*([^.\n]+)\.\s*\u041d\u0430\u0439\u0434\u0435\u043d\u043e\s+\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432:\s*(\d+)/iu))
.find(Boolean);
const summaryDocumentMatch = candidateLines
.map((line) => line.match(/\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b\s+\u043f\u043e\s+\u0446\u0435\u043f\u043e\u0447\u043a\u0435:\s*\u043d\u0430\u0439\u0434\u0435\u043d\u043e\s*(\d+)/iu))
.find(Boolean);
const counterparty = counterpartyHint ?? toNonEmptyString(directDocumentMatch?.[1]);
if (!counterparty) {
return null;
}
const valueBundle = valueAmounts.length >= 3
? {
counterparty,
incoming_customer_revenue: {
total_amount_human_ru: toNonEmptyString(valueAmounts[0])
},
outgoing_supplier_payout: {
total_amount_human_ru: toNonEmptyString(valueAmounts[1])
},
net_amount_human_ru: toNonEmptyString(valueAmounts[2]),
net_direction: "net_incoming",
inference_basis: "parsed_from_previous_business_overview_summary"
}
: null;
const documentCount = Number(directDocumentMatch?.[2] ?? summaryDocumentMatch?.[1]);
const documentBundle = Number.isFinite(documentCount) && documentCount > 0
? {
counterparty,
document_count: documentCount
}
: null;
return valueBundle || documentBundle ? { valueBundle, documentBundle } : null;
}
function findRecentBusinessOverviewProofBundles(input) {
if (!input.counterpartyHint) {
return null;
}
for (let index = input.sessionItems.length - 1; index >= 0; index -= 1) {
const item = toRecordObject(input.sessionItems[index]);
if (input.toNonEmptyString(item?.role) !== "assistant") {
continue;
}
const debug = toRecordObject(item?.debug);
const entryPoint = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1);
const turnInput = toRecordObject(entryPoint?.turn_input);
const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref);
if (turnMeaningRef) {
const valueBundle = toRecordObject(turnMeaningRef.previous_counterparty_value_flow_bundle);
const documentBundle = toRecordObject(turnMeaningRef.previous_counterparty_document_bundle);
if (valueBundle || documentBundle) {
const bundleCounterparty = turnMeaningCounterpartyName(turnMeaningRef, valueBundle, documentBundle, input.toNonEmptyString);
if (sameEntityHint(input.counterpartyHint, bundleCounterparty)) {
return { valueBundle, documentBundle };
}
}
}
const parsedBundles = parseBusinessOverviewProofBundlesFromTextV2(item?.text, input.counterpartyHint, input.toNonEmptyString) ??
parseBusinessOverviewProofBundlesFromText(item?.text, input.toNonEmptyString);
if (!parsedBundles) {
continue;
}
const parsedCounterparty = input.toNonEmptyString(parsedBundles.valueBundle?.counterparty) ??
input.toNonEmptyString(parsedBundles.documentBundle?.counterparty);
if (!sameEntityHint(input.counterpartyHint, parsedCounterparty)) {
continue;
}
return parsedBundles;
}
return null;
}
function mergeBusinessOverviewProofBundlesFromNavigationState(input) {
const currentValueBundle = toRecordObject(input.followupContext?.previous_discovery_bidirectional_value_flow);
const currentDocumentBundle = toRecordObject(input.followupContext?.previous_discovery_document_summary);
if (currentValueBundle && currentDocumentBundle) {
return input.followupContext;
}
const state = toRecordObject(input.sessionAddressNavigationState);
const sessionContext = toRecordObject(state?.session_context);
const comparisonScope = toRecordObject(sessionContext?.comparison_scope);
const proofBundles = toRecordObject(comparisonScope?.proof_bundles);
const valueBundle = toRecordObject(proofBundles?.counterparty_value_flow_bundle);
const documentBundle = toRecordObject(proofBundles?.counterparty_document_bundle);
if (!valueBundle && !documentBundle) {
return input.followupContext;
}
const entities = toRecordObject(input.predecomposeContract?.entities);
const hasCurrentOrganizationSelection = Boolean(input.toNonEmptyString(entities?.organization));
const businessOverviewFollowup = isBusinessOverviewDiscoveryFollowup(input.followupContext, input.toNonEmptyString);
if (!businessOverviewFollowup && !hasCurrentOrganizationSelection) {
return input.followupContext;
}
const comparisonCounterparty = toRecordObject(comparisonScope?.counterparty);
const counterpartyHint = (input.followupContext ? businessOverviewCounterpartyHint(input.followupContext, input.toNonEmptyString) : null) ??
input.toNonEmptyString(comparisonCounterparty?.label);
const bundleCounterparty = input.toNonEmptyString(valueBundle?.counterparty) ?? input.toNonEmptyString(documentBundle?.counterparty);
if (!counterpartyHint || !sameEntityHint(counterpartyHint, bundleCounterparty)) {
return input.followupContext;
}
return {
...(input.followupContext ?? {}),
previous_intent: input.toNonEmptyString(input.followupContext?.previous_intent) ?? "business_overview",
target_intent: input.toNonEmptyString(input.followupContext?.target_intent) ?? "business_overview",
previous_discovery_pilot_scope: input.toNonEmptyString(input.followupContext?.previous_discovery_pilot_scope) ??
"business_overview_route_template_v1",
previous_discovery_loop_status: input.toNonEmptyString(input.followupContext?.previous_discovery_loop_status) ?? "awaiting_clarification",
previous_discovery_loop_selected_chain_id: input.toNonEmptyString(input.followupContext?.previous_discovery_loop_selected_chain_id) ?? "business_overview",
previous_discovery_loop_pending_axes: Array.isArray(input.followupContext?.previous_discovery_loop_pending_axes)
? input.followupContext?.previous_discovery_loop_pending_axes
: ["organization"],
previous_discovery_loop_asked_domain_family: input.toNonEmptyString(input.followupContext?.previous_discovery_loop_asked_domain_family) ?? "business_overview",
previous_discovery_loop_asked_action_family: input.toNonEmptyString(input.followupContext?.previous_discovery_loop_asked_action_family) ?? "broad_evaluation",
previous_discovery_loop_metadata_scope_hint: input.toNonEmptyString(input.followupContext?.previous_discovery_loop_metadata_scope_hint) ?? counterpartyHint,
previous_anchor_type: input.toNonEmptyString(input.followupContext?.previous_anchor_type) ?? "counterparty",
previous_anchor_value: input.toNonEmptyString(input.followupContext?.previous_anchor_value) ?? counterpartyHint,
previous_filters: toRecordObject(input.followupContext?.previous_filters) ?? {},
previous_discovery_bidirectional_value_flow: currentValueBundle ?? valueBundle ?? undefined,
previous_discovery_document_summary: currentDocumentBundle ?? documentBundle ?? undefined
};
}
function mergeBusinessOverviewProofBundlesFromSessionItems(input) {
if (!isBusinessOverviewDiscoveryFollowup(input.followupContext, input.toNonEmptyString)) {
return input.followupContext;
}
const currentValueBundle = toRecordObject(input.followupContext?.previous_discovery_bidirectional_value_flow);
const currentDocumentBundle = toRecordObject(input.followupContext?.previous_discovery_document_summary);
if (currentValueBundle && currentDocumentBundle) {
return input.followupContext;
}
const counterpartyHint = input.followupContext
? businessOverviewCounterpartyHint(input.followupContext, input.toNonEmptyString)
: null;
const proofBundles = findRecentBusinessOverviewProofBundles({
sessionItems: input.sessionItems,
counterpartyHint,
toNonEmptyString: input.toNonEmptyString
});
if (!proofBundles) {
return input.followupContext;
}
return {
...(input.followupContext ?? {}),
previous_discovery_bidirectional_value_flow: currentValueBundle ?? proofBundles.valueBundle ?? undefined,
previous_discovery_document_summary: currentDocumentBundle ?? proofBundles.documentBundle ?? undefined
};
}
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_margin_ranking_for_nomenclature" ||
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 hasInventoryMarginRankingAccountCorrectionCue(text) {
const value = String(text ?? "").toLowerCase();
if (!value.trim()) {
return false;
}
return (/\b41(?:[.,]\d{1,2})?\b/iu.test(value) &&
/\b01(?:[.,]\d{1,2})?\b/iu.test(value) &&
/(?:\u0430\s+\u043d\u0435|\u043d\u0435|\u0432\u043c\u0435\u0441\u0442\u043e|not|instead)/iu.test(value));
}
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 hasInventoryMarginRankingCarryover = previousIntent === "inventory_margin_ranking_for_nomenclature" ||
rootIntent === "inventory_margin_ranking_for_nomenclature";
const hasInventoryMarginRankingAccountCorrection = hasInventoryMarginRankingCarryover &&
[rawMessage, canonicalMessage].some((message) => hasInventoryMarginRankingAccountCorrectionCue(message));
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;
}
if (hasInventoryMarginRankingAccountCorrection &&
(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 discoveryFollowupContextWithOrganization = mergeOrganizationIntoDiscoveryFollowupContext(followupContext, explicitPredecomposeOrganization
? null
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString));
const discoveryFollowupContext = mergeBusinessOverviewDateContextForCompactCashflow({
userMessage: input.userMessage,
followupContext: discoveryFollowupContextWithOrganization,
sessionItems: input.sessionItems,
toNonEmptyString: input.toNonEmptyString
});
const discoveryFollowupContextWithProofBundles = mergeBusinessOverviewProofBundlesFromSessionItems({
followupContext: discoveryFollowupContext,
sessionItems: input.sessionItems,
toNonEmptyString: input.toNonEmptyString
});
const discoveryFollowupContextWithStateProofBundles = mergeBusinessOverviewProofBundlesFromNavigationState({
followupContext: discoveryFollowupContextWithProofBundles,
sessionAddressNavigationState: input.sessionAddressNavigationState,
predecomposeContract,
toNonEmptyString: 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: discoveryFollowupContextWithStateProofBundles,
knownOrganizations: sessionKnownOrganizations(input.sessionOrganizationScope ?? null)
}));
}
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
};
}