458 lines
16 KiB
JavaScript
458 lines
16 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.detectAddressQuestionMode = detectAddressQuestionMode;
|
||
const inventoryLifecycleCueHelpers_1 = require("./inventoryLifecycleCueHelpers");
|
||
const ADDRESS_ACTION_TOKENS = [
|
||
"show",
|
||
"list",
|
||
"find",
|
||
"get",
|
||
"lookup",
|
||
"open",
|
||
"balance",
|
||
"debt",
|
||
"owe",
|
||
"покажи",
|
||
"покаж",
|
||
"показ",
|
||
"проверь",
|
||
"провер",
|
||
"чекни",
|
||
"чекн",
|
||
"глянь",
|
||
"глян",
|
||
"посмотри",
|
||
"смотри",
|
||
"список",
|
||
"найди",
|
||
"найд",
|
||
"выведи",
|
||
"вывед",
|
||
"кто",
|
||
"кому",
|
||
"какой",
|
||
"какая",
|
||
"какое",
|
||
"какую",
|
||
"какие",
|
||
"каких",
|
||
"что по",
|
||
"че по",
|
||
"чё по",
|
||
"остаток",
|
||
"скока",
|
||
"сколько",
|
||
"долг",
|
||
"задолж",
|
||
"хвост",
|
||
"незакрыт"
|
||
];
|
||
const ADDRESS_ENTITY_TOKENS = [
|
||
"counterparty",
|
||
"counterparties",
|
||
"company",
|
||
"organization",
|
||
"supplier",
|
||
"vendor",
|
||
"customer",
|
||
"client",
|
||
"partner",
|
||
"contract",
|
||
"contracts",
|
||
"account",
|
||
"accounts",
|
||
"document",
|
||
"documents",
|
||
"balance",
|
||
"payable",
|
||
"payables",
|
||
"receivable",
|
||
"receivables",
|
||
"owe",
|
||
"owes",
|
||
"owed",
|
||
"контрагент",
|
||
"контра",
|
||
"компан",
|
||
"организац",
|
||
"поставщик",
|
||
"заказчик",
|
||
"клиент",
|
||
"покупател",
|
||
"партнер",
|
||
"контракт",
|
||
"банк",
|
||
"выписк",
|
||
"операц",
|
||
"транзак",
|
||
"договор",
|
||
"счет",
|
||
"счёт",
|
||
"документ",
|
||
"доки",
|
||
"док",
|
||
"остаток",
|
||
"дебитор",
|
||
"кредитор",
|
||
"аванс",
|
||
"оплат",
|
||
"приход",
|
||
"чек",
|
||
"доход",
|
||
"выруч",
|
||
"сделк",
|
||
"бюджет",
|
||
"топ",
|
||
"самый",
|
||
"самые",
|
||
"поступлен",
|
||
"поступлени",
|
||
"списан",
|
||
"списани",
|
||
"склад",
|
||
"складе",
|
||
"складу",
|
||
"товар",
|
||
"товары",
|
||
"товарн",
|
||
"номенклат",
|
||
"материал",
|
||
"долг",
|
||
"должен",
|
||
"должны",
|
||
"должна"
|
||
];
|
||
const DEEP_REASONING_TOKENS = [
|
||
"why",
|
||
"because",
|
||
"root cause",
|
||
"mechanism",
|
||
"prove",
|
||
"chain",
|
||
"почему",
|
||
"причин",
|
||
"механизм",
|
||
"докажи",
|
||
"цепоч",
|
||
"разрыв",
|
||
"ошибк"
|
||
];
|
||
function hasManagementProfileSignal(text) {
|
||
if (/(?:за\s+какие\s+год[а-яё]*).*(?:баз[аы].*жив|период|данн)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+год[а-яё]*).*(?:по\s+док|докам|документам)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+месяц[а-яё]*).*(?:пик|по\s+операц)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:месяц[\s-]*пик).*(?:операц|ops?|operation)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:top\s*year|top\s*month|years?\/top\s*year|years?\s*top\s*year)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:каких?\s+док(?:ов|и)?).*(?:больше\s+всего|чаще\s+всего|крут)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:сводк[ауи].*тип[а-яё]*\s+док(?:умент|ов|и)?).*(?:дол[ья]|объем|объ[её]м)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:за\s+какие\s+год[а-яё]*\s+в\s+баз[еы]\s+есть\s+данн)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+год[а-яё]*\s+сам(?:ый|ая|ое)\s+актив)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+год[а-яё]*\s+сам(?:ый|ая|ое)\s+пассив|какой\s+год[а-яё]*\s+наименее\s+актив|год\s+с\s+минимальн)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+месяц[а-яё]*\s+сам(?:ый|ая|ое)\s+актив)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какой\s+месяц[а-яё]*\s+сам(?:ый|ая|ое)\s+пассив|какой\s+месяц[а-яё]*\s+наименее\s+актив|месяц\s+с\s+минимальн)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:профил[ья]\s+данн|покрыт(?:ие|ия)\s+период|диапазон\s+лет)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какие\s+тип[аы]\s+док(?:умент|ов|и)?\s+(?:использ|чаще|больш))/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какие\s+тип[аы]\s+док(?:умент|ов|и)?\s+(?:реже|редк|наименее|миним))/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:типы?\s+док(?:умент|ов|и)?\s+и\s+их\s+дол[ья])/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какие\s+раздел[ыа]\s+уч[её]та\s+(?:наибол|наимен|заполн|почти\s+не))/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:раздел[ыа]\s+уч[её]та).*(?:жирн|мертв|пуст|использ)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:(?:сколько|скока|скок)\s+(?:всего\s+)?(?:уникальн(?:ых|ые|ого)?\s+)?контрагент(?:ов|а)?(?:\s+в\s+баз[еы])?)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?заказчик(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?поставщик(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?клиент(?:ов|а)?|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?покупател(?:ей|я)|(?:сколько|скока|скок)\s+(?:у\s+нас\s+)?смешан(?:ных|ые)\s+контрагент(?:ов|а)?|разбей\s+контр)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:покажи|выведи|список|какие|кто).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:за\s+вс[её]\s+время|all\s+time|(?:^|[^\d])(19|20)\d{2}(?:[^\d]|$)|(?:^|[^\d])\d{2}\s*(?:г(?:од|ода)?|г)(?:[^\p{L}\p{N}]|$)|за\s+год|в\s+году)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:какие|кто|покажи|выведи|список).*(?:заказчик(?:ов|а|и)?|клиент(?:ов|а|ы)?|покупател(?:ей|я|и)?).*(?:работал(?:и)?|активн(?:ые|ых|а|о)?).*(?:за\s+вс[её]\s+время|(?:19|20)\d{2}|за\s+год|в\s+году)|(?:active\s+customers?|customer\s+activity)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:сколько\s+(?:всего\s+)?договор(?:ов|а)?(?:\s+заведен[оы])?|договорн(?:ая|ой)\s+баз[аы]|total\s+vs\s+used).*(?:использ|used|договор|contract)?/iu.test(text)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function hasLooseByAnchorMention(text) {
|
||
const match = text.match(/(?:^|\s)по\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu);
|
||
if (!match) {
|
||
return false;
|
||
}
|
||
const token = String(match[1] ?? "").toLowerCase();
|
||
if (!token) {
|
||
return false;
|
||
}
|
||
const stopWords = new Set([
|
||
"контрагенту",
|
||
"контрагента",
|
||
"контре",
|
||
"компании",
|
||
"компанию",
|
||
"организации",
|
||
"организацию",
|
||
"поставщику",
|
||
"поставщика",
|
||
"клиенту",
|
||
"клиента",
|
||
"покупателю",
|
||
"покупателя",
|
||
"партнеру",
|
||
"партнера",
|
||
"договору",
|
||
"договора",
|
||
"счету",
|
||
"счёту",
|
||
"дате",
|
||
"периоду",
|
||
"период",
|
||
"документам",
|
||
"докам",
|
||
"взаиморасчетам",
|
||
"взаиморасчётам",
|
||
"количество",
|
||
"количеству",
|
||
"количества",
|
||
"активности",
|
||
"пассивности",
|
||
"наименее",
|
||
"минимум",
|
||
"запрос",
|
||
"запросу",
|
||
"запроса",
|
||
"запросом",
|
||
"запросе",
|
||
"вопрос",
|
||
"вопросу",
|
||
"вопроса",
|
||
"вопросом",
|
||
"вопросе"
|
||
]);
|
||
return !stopWords.has(token);
|
||
}
|
||
function hasAddressFollowupSignal(text) {
|
||
if (/(?:за\s+любой\s+период|за\s+вс[её]\s+время|for\s+all\s+time|all\s+time)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
if (/(?:\bесть\s+что(?:-|\s)?то\b|\bесть\s+ли\b|\bчто\s+есть\b)/iu.test(text)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function hasSelectedObjectInventoryFollowupSignal(text) {
|
||
if (!/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)/iu.test(text)) {
|
||
return false;
|
||
}
|
||
return ((0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(text) ||
|
||
(0, inventoryLifecycleCueHelpers_1.hasInventoryProfitabilityCue)(text) ||
|
||
(0, inventoryLifecycleCueHelpers_1.hasInventorySaleCue)(text) ||
|
||
/(?:кто\s+(?:поставил|продал)|по\s+каким\s+документам\s+.*купили)/iu.test(text) ||
|
||
/(?:к[оа]му|куда)[\s\S]{0,80}(?:поставил|поставили|поставлен|поставлена|поставлено|отгрузил|отгрузили|отгружен|отгружена|отгружено)/iu.test(text) ||
|
||
/(?:док(?:и|умент[а-яё]*)[\s\S]{0,80}(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару)|операци)|(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару))[\s\S]{0,80}док(?:и|умент[а-яё]*))/iu.test(text) ||
|
||
(/\bкогда\b/iu.test(text) && (0, inventoryLifecycleCueHelpers_1.hasInventoryPurchaseStem)(text)));
|
||
}
|
||
function hasDocsOrBankSignal(text) {
|
||
return /(?:док(?:и|умент|ументы|ументов)|docs?|documents?|банк|выписк|платеж|платёж|оплат|поступлен|списан|транзак|transactions?|bank\s+ops|bank\s+operations?)/iu.test(text);
|
||
}
|
||
function hasAccountCodeAnchor(text) {
|
||
return /(?<![\d-])\d{2}(?:[.,]\d{1,2})(?![\d-])/u.test(text);
|
||
}
|
||
function hasLikelyCounterpartyToken(text) {
|
||
const stopWords = new Set([
|
||
"за",
|
||
"с",
|
||
"по",
|
||
"на",
|
||
"и",
|
||
"или",
|
||
"док",
|
||
"доки",
|
||
"документ",
|
||
"документы",
|
||
"документов",
|
||
"банк",
|
||
"банковские",
|
||
"операции",
|
||
"платежи",
|
||
"платеж",
|
||
"платёж",
|
||
"контрагент",
|
||
"контрагенту",
|
||
"контрагента",
|
||
"компания",
|
||
"компании",
|
||
"организация",
|
||
"организации",
|
||
"год",
|
||
"года",
|
||
"г",
|
||
"плс",
|
||
"pls",
|
||
"пж",
|
||
"пжлст",
|
||
"пожалуйста",
|
||
"бля",
|
||
"блять",
|
||
"епт",
|
||
"ёпт",
|
||
"епта",
|
||
"нах",
|
||
"нахуй",
|
||
"покеж",
|
||
"покажи",
|
||
"показать",
|
||
"покаж",
|
||
"выведи",
|
||
"show",
|
||
"list",
|
||
"please",
|
||
"all",
|
||
"vse",
|
||
"количество",
|
||
"количеству",
|
||
"количества",
|
||
"активный",
|
||
"пассивный",
|
||
"наименее",
|
||
"минимум",
|
||
"реже",
|
||
"запрос",
|
||
"запросу",
|
||
"запроса",
|
||
"запросом",
|
||
"запросе",
|
||
"вопрос",
|
||
"вопросу",
|
||
"вопроса",
|
||
"вопросом",
|
||
"вопросе"
|
||
]);
|
||
const tokens = String(text ?? "")
|
||
.split(/[^a-zа-яё0-9._-]+/iu)
|
||
.map((token) => token.trim())
|
||
.filter((token) => token.length >= 2);
|
||
return tokens.some((token) => {
|
||
const lowered = token.toLowerCase();
|
||
if (stopWords.has(lowered)) {
|
||
return false;
|
||
}
|
||
if (/^\d+$/.test(lowered)) {
|
||
return false;
|
||
}
|
||
if (/^(?:19|20)\d{2}$/.test(lowered)) {
|
||
return false;
|
||
}
|
||
return true;
|
||
});
|
||
}
|
||
function hasAnyToken(text, tokens) {
|
||
return tokens.some((token) => text.includes(token));
|
||
}
|
||
function detectAddressQuestionMode(userMessage) {
|
||
const text = String(userMessage ?? "").trim().toLowerCase();
|
||
if (!text) {
|
||
return {
|
||
mode: "unsupported",
|
||
confidence: "low",
|
||
reasons: ["empty_message"]
|
||
};
|
||
}
|
||
const hasAddressAction = hasAnyToken(text, ADDRESS_ACTION_TOKENS);
|
||
const hasAddressEntity = hasAnyToken(text, ADDRESS_ENTITY_TOKENS);
|
||
const hasDeepReasoning = hasAnyToken(text, DEEP_REASONING_TOKENS);
|
||
const hasManagementSignal = hasManagementProfileSignal(text);
|
||
const hasLooseByAnchor = hasLooseByAnchorMention(text);
|
||
const hasFollowupSignal = hasAddressFollowupSignal(text);
|
||
const hasSelectedObjectInventoryFollowup = hasSelectedObjectInventoryFollowupSignal(text);
|
||
const hasAccountCode = hasAccountCodeAnchor(text);
|
||
if (hasAddressAction && (hasAddressEntity || hasAccountCode) && !hasDeepReasoning) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "high",
|
||
reasons: ["address_action_detected", "address_entity_detected"]
|
||
};
|
||
}
|
||
if (hasSelectedObjectInventoryFollowup && !hasDeepReasoning) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "medium",
|
||
reasons: ["selected_object_inventory_followup_detected"]
|
||
};
|
||
}
|
||
if (hasManagementSignal && !hasDeepReasoning) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "medium",
|
||
reasons: ["management_profile_signal_detected"]
|
||
};
|
||
}
|
||
if (hasLooseByAnchor && (hasAddressAction || hasAddressEntity || hasFollowupSignal || hasAccountCode) && !hasDeepReasoning) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "medium",
|
||
reasons: ["loose_by_anchor_detected", ...(hasFollowupSignal ? ["address_followup_signal_detected"] : [])]
|
||
};
|
||
}
|
||
if (hasAccountCode && !hasDeepReasoning) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "medium",
|
||
reasons: ["account_code_detected"]
|
||
};
|
||
}
|
||
if (!hasDeepReasoning && hasDocsOrBankSignal(text) && (hasLooseByAnchor || hasLikelyCounterpartyToken(text))) {
|
||
return {
|
||
mode: "address_query",
|
||
confidence: "medium",
|
||
reasons: ["docs_or_bank_signal_detected", "anchor_like_token_detected"]
|
||
};
|
||
}
|
||
if (hasDeepReasoning) {
|
||
return {
|
||
mode: "deep_analysis",
|
||
confidence: "high",
|
||
reasons: ["deep_reasoning_signal_detected"]
|
||
};
|
||
}
|
||
return {
|
||
mode: "unsupported",
|
||
confidence: "low",
|
||
reasons: ["no_address_or_deep_signal"]
|
||
};
|
||
}
|