361 lines
27 KiB
JavaScript
361 lines
27 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.createAssistantLivingModePolicy = createAssistantLivingModePolicy;
|
||
function createAssistantLivingModePolicy(deps) {
|
||
const { featureAssistantLivingChatRouterV1, compactWhitespace, repairAddressMojibake, toNonEmptyString, normalizeOrganizationScopeValue, hasReferentialPointer, hasSmallTalkSignal, hasAssistantCapabilityQuestionSignal, hasOperationalAdminActionRequestSignal, resolveProviderExecutionState } = deps;
|
||
function hasStrongDataIntentSignal(text) {
|
||
const lower = String(text ?? "").toLowerCase();
|
||
return /(база|док|документ|проводк|контрагент|договор|контракт|счет|сч[её]т|остат|сальдо|хвост|платеж|плат[её]ж|операц|поставщик|клиент|заказчик|дебитор|кредитор|оборот|баланс|период|месяц|год|инн|аванс|предоплат|отгруз|задолж|долг|склад|товар|номенклат|материал|mcp|bank|counterparty|contract|document|ledger|posting|account|organization|company|advance|prepay|shipment|receivab|payab|warehouse|inventory|stock|item|организац|компан|контор|фирм)/i.test(lower);
|
||
}
|
||
function hasDataRetrievalRequestSignal(text) {
|
||
const lower = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
||
if (!lower) {
|
||
return false;
|
||
}
|
||
const hasBroadInterrogative = /(?:\u0433\u0434\u0435|\u0432\s+\u043a\u0430\u043a\u0438\u0445|\u043f\u043e\s+\u043a\u0430\u043a\u0438\u043c|\u043f\u043e\s+\u043a\u043e\u043c\u0443|\u043a\u0430\u043a\u0438\u0435|\u043a\u0430\u043a\u043e\u0439|\u043a\u0442\u043e|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|where|which|who|how\s+many)/iu.test(lower);
|
||
const hasBroadBusinessObject = /(?:\u0430\u0432\u0430\u043d\u0441|\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442|\u043e\u0442\u0433\u0440\u0443\u0437|\u0437\u0430\u0434\u043e\u043b\u0436|\u0434\u043e\u043b\u0433|\u0441\u0430\u043b\u044c\u0434\u043e|\u043e\u043f\u043b\u0430\u0442|\u043f\u043b\u0430\u0442(?:\u0435|\u0451)\u0436|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0441\u0447(?:\u0435|\u0451)\u0442|\u043e\u0431\u043e\u0440\u043e\u0442|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446|\u0433\u043e\u0434|\u0441\u043a\u043b\u0430\u0434|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442|\u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b|advance|prepay|shipment|receivab|payab|counterparty|contract|document|account|balance|turnover|warehouse|inventory|stock|item)/iu.test(lower);
|
||
if (hasBroadInterrogative && hasBroadBusinessObject) {
|
||
return true;
|
||
}
|
||
const hasRussianRetrievalAction = /(?:^|\s)(?:\u043f\u043e\u043a\u0430\u0436\u0438|\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c|\u043d\u0430\u0439\u0434\u0438|\u0432\u044b\u0432\u0435\u0434\u0438|\u0434\u0430\u0439|\u0440\u0430\u0441\u043a\u0440\u043e\u0439|\u0441\u043f\u0438\u0441\u043e\u043a|\u043f\u0440\u043e\u0432\u0435\u0440\u044c|\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c)(?:$|[\s,.!?;:])/iu.test(lower);
|
||
const hasRussianRetrievalObject = /(?:\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u0441\u0447(?:\u0435|\u0451)\u0442|\u043e\u0441\u0442\u0430\u0442|\u0441\u0430\u043b\u044c\u0434\u043e|\u043e\u0431\u043e\u0440\u043e\u0442|\u043f\u043b\u0430\u0442(?:\u0435|\u0451)\u0436|\u043e\u043f\u0435\u0440\u0430\u0446|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043a\u043b\u0438\u0435\u043d\u0442|\u0433\u043e\u0434|\u043f\u0435\u0440\u0438\u043e\u0434|\u043c\u0435\u0441\u044f\u0446|\u0430\u0432\u0430\u043d\u0441|\u043f\u0440\u0435\u0434\u043e\u043f\u043b\u0430\u0442|\u043e\u0442\u0433\u0440\u0443\u0437|\u0437\u0430\u0434\u043e\u043b\u0436|\u0434\u043e\u043b\u0433|\u0441\u043a\u043b\u0430\u0434|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442|\u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b)/iu.test(lower);
|
||
if (hasRussianRetrievalAction && hasRussianRetrievalObject) {
|
||
return true;
|
||
}
|
||
const hasExplicitRetrievalAction = /(?:\bпокажи\b|\bпоказать\b|\bвыведи\b|\bнайди\b|\bсписок\b|\bдай\b|\bраскрой\b|\bshow\b|\blist\b|\bfind\b|\bcount\b)/i.test(lower);
|
||
const hasInterrogativeRetrievalAction = /(?:\bсколько\b|\bкакой\b|\bкакая\b|\bкакое\b|\bкакую\b|\bкакие\b|\bкто\b|\bгде\b|\bпо\s+каким\b|\bпо\s+кому\b|\bу\s+кого\b|\bwhich\b|\bwho\b|\bwhere\b)/i.test(lower);
|
||
if (!hasExplicitRetrievalAction && !hasInterrogativeRetrievalAction) {
|
||
return false;
|
||
}
|
||
const hasRetrievalObject = /(1с|база|док|документ|контрагент|договор|контракт|счет|сч[её]т|остат|сальдо|хвост|платеж|плат[её]ж|операц|поставщик|клиент|заказчик|дебитор|кредитор|период|месяц|год|инн|аванс|предоплат|отгруз|задолж|долг|склад|товар|номенклат|материал|bank|counterparty|contract|document|account|balance|ledger|posting|advance|prepay|shipment|receivab|payab|warehouse|inventory|stock|item|организац|компан|контор|фирм|возраст|дата\s+регистрац|регистрац|основан)/i.test(lower);
|
||
if (!hasRetrievalObject) {
|
||
return false;
|
||
}
|
||
if (hasExplicitRetrievalAction) {
|
||
return true;
|
||
}
|
||
const hasMetaCapabilityShape = /(?:мож(?:ем|ешь|ете|но)|уме(?:ешь|ете)|доступ|подключ|чья|как\s+называ(?:ет|ется)|работ(?:ать|аем|аешь|аете)|в\s+тебе|у\s+тебя)/i.test(lower);
|
||
return !hasMetaCapabilityShape;
|
||
}
|
||
function hasOrganizationFactLookupSignal(text) {
|
||
const repaired = repairAddressMojibake(String(text ?? ""));
|
||
const normalized = compactWhitespace(repaired.toLowerCase()).replace(/ё/g, "е");
|
||
if (!normalized) {
|
||
return false;
|
||
}
|
||
const hasFactCue = /(?:возраст|сколько\s+лет|дата\s+регистрац|когда\s+(?:зарегистр|создан|основан)|год\s+регистрац|год\s+основан|с\s+какого\s+года|when\s+was\s+(?:it\s+)?(?:registered|founded|created))/i.test(normalized);
|
||
if (!hasFactCue) {
|
||
return false;
|
||
}
|
||
return /(?:организац|компан|контор|фирм|ооо|ао|зао|ип|альтернатив|лайсвуд|райм|organization|company)/i.test(normalized);
|
||
}
|
||
function findLastAssistantLivingChatDebug(items) {
|
||
if (!Array.isArray(items)) {
|
||
return null;
|
||
}
|
||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||
const item = items[index];
|
||
if (!item || item.role !== "assistant") {
|
||
continue;
|
||
}
|
||
if (item.debug && typeof item.debug === "object") {
|
||
return item.debug;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function hasMetaAnswerFollowupSignal(userMessage) {
|
||
const rawText = compactWhitespace(String(userMessage ?? "").toLowerCase());
|
||
const repairedText = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||
const samples = [rawText, repairedText]
|
||
.filter((item) => item.length > 0)
|
||
.map((item) => item.replace(/ё/g, "е"));
|
||
if (samples.length === 0) {
|
||
return false;
|
||
}
|
||
const hasReflectionCue = samples.some((sample) => sample.includes("дума") ||
|
||
sample.includes("скаж") ||
|
||
sample.includes("мнение") ||
|
||
sample.includes("как тебе") ||
|
||
sample.includes("норм") ||
|
||
sample.includes("стран") ||
|
||
sample.includes("логич") ||
|
||
sample.includes("смуща") ||
|
||
sample.includes("выгляд"));
|
||
const hasTopicPointerCue = samples.some((sample) => sample.includes("на эту тему") ||
|
||
sample.includes("по этому поводу") ||
|
||
sample.includes("об этом") ||
|
||
(sample.includes("это") && hasReferentialPointer(sample)));
|
||
const hasEvaluationCue = samples.some((sample) => /\b(?:много|мало|нормально|хорошо|плохо|критично|перебор|слабо)\b/iu.test(sample));
|
||
if (!((hasReflectionCue || hasEvaluationCue) &&
|
||
(hasTopicPointerCue || (hasEvaluationCue && samples.some((sample) => /^(?:это|ну это)\b/iu.test(sample)))))) {
|
||
return false;
|
||
}
|
||
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
|
||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
|
||
hasDataRetrievalRequestSignal(sample) ||
|
||
hasStrongDataIntentSignal(sample));
|
||
}
|
||
function hasConversationMemoryRecallFollowupSignal(userMessage) {
|
||
const rawText = compactWhitespace(String(userMessage ?? "").toLowerCase());
|
||
const repairedText = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||
const samples = [rawText, repairedText]
|
||
.filter((item) => item.length > 0)
|
||
.map((item) => item.replace(/ё/g, "е"));
|
||
if (samples.length === 0) {
|
||
return false;
|
||
}
|
||
const hasMemoryCue = samples.some((sample) => /(?:помни(?:шь|те|м)?|remember|recall)/iu.test(sample));
|
||
const hasDiscussionCue = samples.some((sample) => /(?:обсуждал[аи]?|говорил[аи]?|смотрел[аи]?|разбирал[аи]?|спрашивал[аи]?)/iu.test(sample));
|
||
const hasExplicitRecapPrompt = samples.some((sample) => /(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|напомни\s+что\s+мы|what\s+we\s+already\s+(?:discussed|figured\s+out))/iu.test(sample));
|
||
if (!(hasExplicitRecapPrompt || (hasMemoryCue && hasDiscussionCue))) {
|
||
return false;
|
||
}
|
||
if (hasExplicitRecapPrompt) {
|
||
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
|
||
shouldHandleAsAssistantCapabilityMetaQuery(sample));
|
||
}
|
||
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
|
||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
|
||
hasDataRetrievalRequestSignal(sample) ||
|
||
hasStrongDataIntentSignal(sample));
|
||
}
|
||
function hasHistoricalCapabilityFollowupSignal(text) {
|
||
const repaired = repairAddressMojibake(String(text ?? ""));
|
||
const normalized = compactWhitespace(repaired.toLowerCase()).replace(/ё/g, "е");
|
||
if (!normalized) {
|
||
return false;
|
||
}
|
||
const hasHistoryCue = /(?:историческ|история|архив|прошл(?:ый|ые|ую|ых)?|раньше|ретро|старые\s+данные)/iu.test(normalized);
|
||
if (!hasHistoryCue) {
|
||
return false;
|
||
}
|
||
return /(?:мож(?:ешь|ете|но)|уме(?:ешь|ете)|показ|вывед|дай|раскрой)/iu.test(normalized);
|
||
}
|
||
function hasOrganizationFactFollowupSignal(userMessage, items) {
|
||
const repaired = repairAddressMojibake(String(userMessage ?? ""));
|
||
const normalized = compactWhitespace(repaired.toLowerCase()).replace(/ё/g, "е");
|
||
if (!normalized) {
|
||
return false;
|
||
}
|
||
if (hasOrganizationFactLookupSignal(normalized)) {
|
||
return false;
|
||
}
|
||
const hasFollowupCue = /(?:^|\s)(?:давай|го|погнали|ок(?:ей)?|хорошо|принято|подтверждаю|запрашивай|запроси|проверь|продолжай|ну\s+давай|да\s+давай)(?=$|[\s,.!?;:])/iu.test(normalized);
|
||
if (!hasFollowupCue) {
|
||
return false;
|
||
}
|
||
const lastDebug = findLastAssistantLivingChatDebug(items);
|
||
const lastSource = toNonEmptyString(lastDebug?.living_chat_response_source);
|
||
const lastGuardReason = toNonEmptyString(lastDebug?.living_chat_grounding_guard_reason);
|
||
const inOrganizationFactBoundary = lastSource === "deterministic_organization_fact_boundary" ||
|
||
lastSource === "deterministic_organization_fact_boundary_followup" ||
|
||
lastGuardReason === "organization_fact_without_live_source_blocked";
|
||
return inOrganizationFactBoundary;
|
||
}
|
||
function shouldEmitOrganizationSelectionReply(userMessage, selectedOrganization) {
|
||
const selected = normalizeOrganizationScopeValue(selectedOrganization);
|
||
if (!selected) {
|
||
return false;
|
||
}
|
||
const repaired = repairAddressMojibake(String(userMessage ?? ""));
|
||
const normalized = compactWhitespace(repaired.toLowerCase()).replace(/ё/g, "е");
|
||
if (!normalized) {
|
||
return false;
|
||
}
|
||
if (hasOrganizationFactLookupSignal(normalized) || hasDataRetrievalRequestSignal(normalized) || hasStrongDataIntentSignal(normalized)) {
|
||
return false;
|
||
}
|
||
const hasAnalyticalCue = /(?:какой|какая|какие|когда|сколько|кто|почему|зачем|возраст|дата|регистрац|ндс|налог|контракт|договор|документ|операц|оборот|сумм|остат|сальдо|founded|registered|created)/i.test(normalized);
|
||
if (hasAnalyticalCue) {
|
||
return false;
|
||
}
|
||
const hasSelectionCue = /(?:давай|го|погнали|ок(?:ей)?|хорошо|отлично|берем|выберем|выбираем|переключ(?:им|аем|ай)|фиксир|работаем|обсудим|тогда)\b/i.test(normalized);
|
||
if (hasSelectionCue) {
|
||
return true;
|
||
}
|
||
const hasAffectiveReactionCue = /(?:^|[\s,.;:!?()\-])(?:ну|мда|ох|ах|офигеть|офигенно|ахуеть|охуеть|пиздец|пизда|нихуя|хуево|хуёво|ебать|ебан|бля|блять|fuck|shit|damn)(?=$|[\s,.;:!?()\-])/iu.test(normalized) ||
|
||
normalized.includes("\u0430\u0445\u0443") ||
|
||
normalized.includes("\u043e\u0445\u0443") ||
|
||
normalized.includes("\u043f\u0438\u0437\u0434") ||
|
||
normalized.includes("\u0431\u043b\u044f");
|
||
if (hasAffectiveReactionCue) {
|
||
return false;
|
||
}
|
||
return normalized.length <= 36 && !/[?]/.test(String(userMessage ?? ""));
|
||
}
|
||
function hasAssistantDataScopeMetaQuestionSignal(text) {
|
||
const repaired = repairAddressMojibake(String(text ?? ""));
|
||
const lower = compactWhitespace(repaired.toLowerCase()).replace(/ё/g, "е");
|
||
const normalized = lower.replace(/\b1\s*[cс]\b/giu, "1с");
|
||
if (!normalized) {
|
||
return false;
|
||
}
|
||
const hasDirectSlangScopeLead = /(?:по\s+каким\s+(?:контор(?:ам|ы|а)?|кантор(?:ам|ы|а)?|компан(?:иям|ии|ию|ия)|организац(?:иям|ии|ию|ия))\s+мож(?:ем|но)\s+(?:общат|работ)|база\s+какой\s+(?:контор|компан|организац|фирм)|какая\s+база\s+(?:подключ|подруб|актив))/iu.test(normalized);
|
||
if (hasDirectSlangScopeLead) {
|
||
return true;
|
||
}
|
||
const hasSlangScopeQuestion = /(?:\u043f\u043e\s+\u043a\u0430\u043a\u0438\u043c\s+(?:\u043a\u043e\u043d\u0442\u043e\u0440(?:\u0430\u043c|\u044b|\u0430)?|\u043a\u043e\u043c\u043f\u0430\u043d(?:\u0438\u044f\u043c|\u0438\u0438|\u0438\u044e|\u0438\u044f)|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446(?:\u0438\u044f\u043c|\u0438\u0438|\u0438\u044e|\u0438\u044f)|\u0444\u0438\u0440\u043c(?:\u0430\u043c|\u0435|\u0443|\u0430)).*(?:\u043c\u043e\u0436(?:\u0435\u043c|\u043d\u043e)|\u0440\u0430\u0431\u043e\u0442|\u043e\u0431\u0449\u0430\u0442|\u043f\u043e\u0434\u0440\u0443\u0431|\u043f\u043e\u0434\u043a\u043b\u044e\u0447)|(?:\u0431\u0430\u0437\u0430\s+\u043a\u0430\u043a\u043e\u0439\s+(?:\u043a\u043e\u043d\u0442\u043e\u0440|\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u0444\u0438\u0440\u043c))|(?:\u043a\u0430\u043a\u0430\u044f\s+\u0431\u0430\u0437\u0430\s+(?:\u043f\u043e\u0434\u043a\u043b\u044e\u0447|\u0430\u043a\u0442\u0438\u0432)))/iu.test(normalized);
|
||
if (hasSlangScopeQuestion) {
|
||
return true;
|
||
}
|
||
const hasBaseOrTenantObject = /(?:баз(?:а|е|у|ы)?|тенант|tenant|контур)/i.test(normalized);
|
||
const hasCompanyObject = /(?:компан(?:ия|ии|ию|ией)|компин(?:ия|ии|ию|ией)?|компини(?:я|и|ю|ей)?|компани[яеию]|организац(?:ия|ии|ию|ией)|контор(?:а|ы|у|ой)?|фирм(?:а|ы|у|ой)?)/i.test(normalized);
|
||
const hasConnectionCue = /(?:подключен(?:а|о|ы)?|подруб|воткнут|активн(?:ый|ая)\s+канал|mcp-?канал|канал)/i.test(normalized);
|
||
const hasNamingCue = /(?:как\s+называ(?:ет|ется)|что\s+за\s+(?:контор|компан|организац|фирм))/i.test(normalized);
|
||
const hasWorkabilityCue = /(?:мож(?:ем|ешь|ете|но)\s+работ|работ(?:ать|аем|аешь|аете))/i.test(normalized);
|
||
const hasScopeObject = hasBaseOrTenantObject || hasCompanyObject || hasConnectionCue;
|
||
if (!hasScopeObject) {
|
||
return false;
|
||
}
|
||
const hasMetaPerspective = /(?:ты|тебе|твой|у\s+тебя|в\s+тебе|мы|нам|наш(?:а|е|и|у|ей)?|сейчас|щас)/i.test(normalized);
|
||
const hasScopedInterrogativePair = /(?:^|\s)(?:по\s+какой|с\s+какой|какая|какой|какие)\s+(?:баз|компан|компин|компини|компани|организац|контор|фирм|тенант|контур)/i.test(normalized);
|
||
const hasScopeQuestion = /(?:чья|чье|чьи|доступн|подключен|подруб|воткнут|какая\s+баз|какой\s+баз)/i.test(normalized) ||
|
||
hasNamingCue ||
|
||
hasWorkabilityCue ||
|
||
hasScopedInterrogativePair;
|
||
const hasInterrogativeScopeLead = /(?:^|\s)(?:по\s+какой|с\s+какой|чья|чье|чьи|which|who|what)/i.test(normalized);
|
||
const isQuestionLike = /[?]/.test(String(text ?? "")) || hasInterrogativeScopeLead || hasScopedInterrogativePair;
|
||
const hasExplicitScopeContext = hasBaseOrTenantObject || hasConnectionCue || hasWorkabilityCue || hasNamingCue;
|
||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(normalized);
|
||
const hasContractAnalyticsCue = /(?:договор|контракт|contract).*(?:топ|сам(?:ый|ая|ое|ые)|крупн|жирн|оборот|бюджет|сумм|стоим|value|turnover|all\s+time|всю\s+истори|за\s+вс[её]\s+время)/iu.test(normalized);
|
||
if (hasContractAnalyticsCue) {
|
||
return false;
|
||
}
|
||
if (hasRetrievalSignal && !hasExplicitScopeContext) {
|
||
return false;
|
||
}
|
||
const hasEligibleScopeObject = hasBaseOrTenantObject || (hasCompanyObject && (hasConnectionCue || hasWorkabilityCue || hasNamingCue || hasMetaPerspective));
|
||
return hasEligibleScopeObject && hasScopeQuestion && (hasMetaPerspective || isQuestionLike || hasExplicitScopeContext);
|
||
}
|
||
function shouldHandleAsAssistantCapabilityMetaQuery(text) {
|
||
const raw = String(text ?? "");
|
||
const repaired = repairAddressMojibake(raw);
|
||
const hasScopeMetaSignal = hasAssistantDataScopeMetaQuestionSignal(raw) || hasAssistantDataScopeMetaQuestionSignal(repaired);
|
||
if (hasScopeMetaSignal) {
|
||
return true;
|
||
}
|
||
const hasExplicitDeltaCapabilityMetaSignal = /(?:мож(?:ешь|ете)|уме(?:ешь|ете)).*(?:считать|рассчитывать|посчитать).*(?:дельт|delta|маржинальн|margin|рентабельн)/iu.test(raw) ||
|
||
/(?:мож(?:ешь|ете)|уме(?:ешь|ете)).*(?:считать|рассчитывать|посчитать).*(?:дельт|delta|маржинальн|margin|рентабельн)/iu.test(repaired);
|
||
if (hasExplicitDeltaCapabilityMetaSignal) {
|
||
return true;
|
||
}
|
||
const hasCapabilitySignal = hasAssistantCapabilityQuestionSignal(raw) ||
|
||
hasAssistantCapabilityQuestionSignal(repaired) ||
|
||
hasOperationalAdminActionRequestSignal(raw) ||
|
||
hasOperationalAdminActionRequestSignal(repaired);
|
||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(raw) || hasDataRetrievalRequestSignal(repaired);
|
||
return hasCapabilitySignal && !hasRetrievalSignal;
|
||
}
|
||
function hasLivingChatSignal(text) {
|
||
const rawLower = compactWhitespace(String(text ?? "").toLowerCase());
|
||
const repairedLower = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase());
|
||
const variants = [rawLower, repairedLower].filter((value, index, items) => value.length > 0 && items.indexOf(value) === index);
|
||
if (variants.length === 0) {
|
||
return false;
|
||
}
|
||
const hasDirectionalReply = variants.some((value) => /^(?:\u0430\s+)?(?:\u0442\u0443\u0442|\u0437\u0434\u0435\u0441\u044c|\u0442\u0430\u043c|\u0441\u044e\u0434\u0430|\u0442\u0443\u0434\u0430)[\s!?.,:;\-]*$/iu.test(value));
|
||
if (hasDirectionalReply) {
|
||
return true;
|
||
}
|
||
const hasShortGreeting = variants.some((value) => /^(?:\u0430\u0433\u0430|\u0443\u0433\u0443|\u043e\u043a|\u043e\u043a\u0435\u0439|\u044f\u0441\u043d\u043e|\u043f\u043e\u043d\u044f\u043b(?:\u0430)?|\u043f\u0440\u0438\u043d\u044f\u0442\u043e|\u0441\u043f\u0430\u0441\u0438\u0431\u043e|\u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e|\u0441\u0443\u043f\u0435\u0440|\u043a\u043b\u0430\u0441\u0441|\u043d\u043e\u0440\u043c|\u0433\u043e|\u0434\u0430\u0432\u0430\u0439|\u043f\u043e\u0433\u043d\u0430\u043b\u0438|\u043f\u0440\u0438\u0432\u0435\u0442(?:\u0438\u043a)?|\u0445\u0430\u0439|\u0439\u043e|yo|\u0447[её]\s+\u0442\u0430\u043c|\u0447[её]\s+\u043a\u0430\u043a|hello|hi|thanks?)$/iu.test(value));
|
||
if (hasShortGreeting) {
|
||
return true;
|
||
}
|
||
const hasGreetingWithFollowup = variants.some((value) => /(?:^|[\s,.!?;:()\-])(?:\u043f\u0440\u0438\u0432\u0435\u0442\p{L}*|\u0445\u0430\u0439|\u0439\u043e|hello|hi|yo)(?=$|[\s,.!?;:()\-]).*(?:^|[\s,.!?;:()\-])(?:\u043a\u0430\u043a|\u0434\u0435\u043b\u0430|\u0442\u0430\u043c|\u0447[её])(?=$|[\s,.!?;:()\-])/iu.test(value));
|
||
if (hasGreetingWithFollowup) {
|
||
return true;
|
||
}
|
||
const hasColloquialFollowup = variants.some((value) => /(?:^|[\s,.!?;:()\-])(?:\u0447[её]\s+\u043a\u0430\u043a|\u0447[её]\s+\u0442\u0430\u043c|\u043a\u0430\u043a\s+\u0442\u0430\u043c(?:\s+\u0434\u0435\u043b\u0430)?)(?=$|[\s,.!?;:()\-])/iu.test(value));
|
||
if (hasColloquialFollowup) {
|
||
return true;
|
||
}
|
||
const hasOpenSmalltalkPrompt = variants.some((value) => /(?:\u043a\u0430\u043a\s+\u0434\u0435\u043b\u0430|\u043a\u0430\u043a\s+\u0442\u044b|\u0447\u0442\u043e\s+\u043d\u043e\u0432\u043e\u0433\u043e|\u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0438\s+\u043e\s+\u0441\u0435\u0431\u0435|\u0447\u0435\u043c\s+\u043c\u043e\u0436\u0435\u0448\u044c\s+\u043f\u043e\u043c\u043e\u0447\u044c|\u0434\u0430\u0432\u0430\u0439\s+\u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c|\u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c|\u043e\u0431\u0441\u0443\u0434\u0438\u043c|\u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u0439|\u0447\u0442\u043e\s+\u0434\u0443\u043c\u0430\u0435\u0448\u044c)/iu.test(value));
|
||
if (hasOpenSmalltalkPrompt) {
|
||
return true;
|
||
}
|
||
return variants.some((value) => hasSmallTalkSignal(value));
|
||
}
|
||
function resolveLivingAssistantModeDecision(input) {
|
||
const userMessage = String(input?.userMessage ?? "");
|
||
if (input?.addressLaneTriggered) {
|
||
return {
|
||
mode: "address_data",
|
||
reason: "address_lane_triggered"
|
||
};
|
||
}
|
||
if (!featureAssistantLivingChatRouterV1) {
|
||
return {
|
||
mode: "deep_analysis",
|
||
reason: "living_chat_router_disabled"
|
||
};
|
||
}
|
||
const providerExecution = resolveProviderExecutionState({
|
||
llmProvider: input?.llmProvider,
|
||
useMock: input?.useMock
|
||
});
|
||
if (providerExecution.living_mode_forced_deep) {
|
||
return {
|
||
mode: "deep_analysis",
|
||
reason: providerExecution.living_mode_forced_reason ?? "mock_mode_keeps_deep_pipeline"
|
||
};
|
||
}
|
||
if (hasAssistantDataScopeMetaQuestionSignal(userMessage)) {
|
||
return {
|
||
mode: "chat",
|
||
reason: "assistant_data_scope_query_detected"
|
||
};
|
||
}
|
||
if (shouldHandleAsAssistantCapabilityMetaQuery(userMessage)) {
|
||
return {
|
||
mode: "chat",
|
||
reason: "assistant_capability_query_detected"
|
||
};
|
||
}
|
||
if (hasOrganizationFactLookupSignal(userMessage) || hasOrganizationFactFollowupSignal(userMessage)) {
|
||
return {
|
||
mode: "chat",
|
||
reason: "organization_fact_lookup_signal_detected"
|
||
};
|
||
}
|
||
if (hasStrongDataIntentSignal(userMessage)) {
|
||
return {
|
||
mode: "deep_analysis",
|
||
reason: "strong_data_signal_detected"
|
||
};
|
||
}
|
||
if (hasLivingChatSignal(userMessage)) {
|
||
return {
|
||
mode: "chat",
|
||
reason: "living_chat_signal_detected"
|
||
};
|
||
}
|
||
const predecomposeMode = toNonEmptyString(input?.predecomposeMode);
|
||
const predecomposeConfidence = toNonEmptyString(input?.predecomposeModeConfidence);
|
||
if (predecomposeMode === "unsupported" && (predecomposeConfidence === "low" || predecomposeConfidence === "medium")) {
|
||
return {
|
||
mode: "deep_analysis",
|
||
reason: "predecompose_unsupported_mode_fallback_to_deep"
|
||
};
|
||
}
|
||
return {
|
||
mode: "deep_analysis",
|
||
reason: "default_deep_pipeline"
|
||
};
|
||
}
|
||
return {
|
||
hasStrongDataIntentSignal,
|
||
hasDataRetrievalRequestSignal,
|
||
hasOrganizationFactLookupSignal,
|
||
hasMetaAnswerFollowupSignal,
|
||
hasConversationMemoryRecallFollowupSignal,
|
||
hasHistoricalCapabilityFollowupSignal,
|
||
hasOrganizationFactFollowupSignal,
|
||
shouldEmitOrganizationSelectionReply,
|
||
hasAssistantDataScopeMetaQuestionSignal,
|
||
shouldHandleAsAssistantCapabilityMetaQuery,
|
||
hasLivingChatSignal,
|
||
resolveLivingAssistantModeDecision
|
||
};
|
||
}
|