АРЧ АП11 - Вынести политику оркестрационной маршрутизации из assistantService в отдельный модуль
This commit is contained in:
parent
606654641b
commit
f1333c457e
|
|
@ -0,0 +1,333 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAssistantLivingModePolicy = createAssistantLivingModePolicy;
|
||||
function createAssistantLivingModePolicy(deps) {
|
||||
const { featureAssistantLivingChatRouterV1, compactWhitespace, repairAddressMojibake, toNonEmptyString, normalizeOrganizationScopeValue, hasReferentialPointer, hasSmallTalkSignal, hasAssistantCapabilityQuestionSignal, hasOperationalAdminActionRequestSignal } = 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(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));
|
||||
if (!hasMemoryCue || !hasDiscussionCue) {
|
||||
return false;
|
||||
}
|
||||
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 hasCapabilitySignal = hasAssistantCapabilityQuestionSignal(raw) ||
|
||||
hasAssistantCapabilityQuestionSignal(repaired) ||
|
||||
hasOperationalAdminActionRequestSignal(raw) ||
|
||||
hasOperationalAdminActionRequestSignal(repaired);
|
||||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(raw) || hasDataRetrievalRequestSignal(repaired);
|
||||
return hasCapabilitySignal && !hasRetrievalSignal;
|
||||
}
|
||||
function hasLivingChatSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase());
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
if (/^(?:а\s+)?(?:тут|здесь|там|сюда|туда)[\s!?.,:;\-]*$/iu.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|yo|че\s+там|ч[её]\s+как|че\s+как|hello|hi|thanks?)$/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/(как дела|как ты|что нового|расскажи о себе|чем можешь помочь|давай поговорим|поговорим|обсудим|посоветуй|что думаешь)/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
return hasSmallTalkSignal(lower);
|
||||
}
|
||||
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"
|
||||
};
|
||||
}
|
||||
if (Boolean(input?.useMock)) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
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
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,582 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAssistantRoutePolicy = createAssistantRoutePolicy;
|
||||
// @ts-nocheck
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
"document_type_and_account_section_profile",
|
||||
"counterparty_population_and_roles",
|
||||
"counterparty_activity_lifecycle",
|
||||
"customer_revenue_and_payments",
|
||||
"supplier_payouts_profile",
|
||||
"open_contracts_confirmed_as_of_date",
|
||||
"list_open_contracts",
|
||||
"open_items_by_counterparty_or_contract",
|
||||
"list_payables_counterparties",
|
||||
"list_receivables_counterparties",
|
||||
"inventory_on_hand_as_of_date",
|
||||
"payables_confirmed_as_of_date",
|
||||
"receivables_confirmed_as_of_date",
|
||||
"list_documents_by_contract",
|
||||
"bank_operations_by_contract",
|
||||
"list_documents_by_counterparty",
|
||||
"bank_operations_by_counterparty",
|
||||
"list_contracts_by_counterparty",
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_supplier_stock_overlap_as_of_date",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date",
|
||||
"contract_usage_overview",
|
||||
"contract_usage_and_value",
|
||||
"vat_payable_forecast",
|
||||
"vat_liability_confirmed_for_tax_period",
|
||||
"vat_payable_confirmed_as_of_date"
|
||||
]);
|
||||
const ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS = new Set([
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain"
|
||||
]);
|
||||
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||
}
|
||||
function createAssistantRoutePolicy(deps) {
|
||||
const { repairAddressMojibake, findLastGroundedAddressAnswerDebug, findLastOrganizationClarificationAddressDebug, mergeKnownOrganizations, normalizeOrganizationScopeValue, resolveOrganizationSelectionFromMessage, hasAssistantDataScopeMetaQuestionSignal, shouldHandleAsAssistantCapabilityMetaQuery, hasDataRetrievalRequestSignal, hasAggregateBusinessAnalyticsSignal, hasStandaloneAddressTopicSignal, hasOpenContractsAddressSignal, detectAddressQuestionMode, resolveAddressIntent, toNonEmptyString, hasStrictDeepInvestigationCue, hasStrongDataIntentSignal, hasAccountingSignal, hasDangerOrCoercionSignal, hasAddressFollowupContextSignal, hasShortDebtMirrorFollowupSignal, isInventorySelectedObjectIntent, hasShortInventoryObjectFollowupSignal, hasHistoricalCapabilityFollowupSignal, isGroundedInventoryContextDebug, hasConversationMemoryRecallFollowupSignal, findLastAddressAssistantItem, hasMetaAnswerFollowupSignal, resolveAddressToolGateDecision, hasSameDateAccountFollowupSignalForPredecompose, hasLooseAllTimeAddressLookupSignal, hasDeepAnalysisPreferenceSignal, hasDirectDeepAnalysisSignal, compactWhitespace, hasDeepSessionContinuationSignal, resolveLivingAssistantModeDecision } = deps;
|
||||
function resolveAssistantOrchestrationDecision(input) {
|
||||
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
|
||||
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
|
||||
const repairedRawUserMessage = repairAddressMojibake(rawUserMessage);
|
||||
const repairedEffectiveAddressUserMessage = repairAddressMojibake(effectiveAddressUserMessage);
|
||||
const followupContext = input?.followupContext ?? null;
|
||||
const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null;
|
||||
const useMock = Boolean(input?.useMock);
|
||||
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : null;
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: []))
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedEffectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
(organizationClarificationSelectionFromScope &&
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedEffectiveAddressUserMessage);
|
||||
const aggregateBusinessAnalyticsSignal = hasAggregateBusinessAnalyticsSignal(rawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedRawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(effectiveAddressUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedEffectiveAddressUserMessage);
|
||||
const standaloneAddressTopicSignal = hasStandaloneAddressTopicSignal(rawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedRawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(effectiveAddressUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedEffectiveAddressUserMessage);
|
||||
const openContractsAddressSignal = hasOpenContractsAddressSignal(rawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedRawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(effectiveAddressUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedEffectiveAddressUserMessage);
|
||||
const modeSample = repairedEffectiveAddressUserMessage || effectiveAddressUserMessage;
|
||||
const modeDetection = detectAddressQuestionMode(modeSample);
|
||||
const modeDetectionRaw = detectAddressQuestionMode(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
|
||||
const intentResolution = resolveAddressIntent(modeSample);
|
||||
const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
|
||||
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||
const llmRuntimeUnavailableDetected = Boolean(llmPreDecomposeReason &&
|
||||
/(?:openai\s+api\s+key\s+is\s+missing|api\s+key\s+is\s+missing|missing\s+api\s+key|authentication)/iu.test(llmPreDecomposeReason));
|
||||
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
|
||||
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
|
||||
? llmPreDecomposeMeta.semanticExtractionContract
|
||||
: null;
|
||||
const semanticContractValid = semanticExtractionContract?.valid !== false;
|
||||
const semanticApplyCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
|
||||
const semanticReasonCodes = Array.isArray(semanticExtractionContract?.reason_codes)
|
||||
? semanticExtractionContract.reason_codes
|
||||
: [];
|
||||
const strictDeepInvestigationCueDetected = hasStrictDeepInvestigationCue(rawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedRawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(effectiveAddressUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedEffectiveAddressUserMessage);
|
||||
const strictDeepInvestigationBypassAllowed = shouldBypassStrictDeepInvestigationCueForAddressIntent(resolvedIntentResolution.intent) ||
|
||||
shouldBypassStrictDeepInvestigationCueForAddressIntent(llmContractIntent);
|
||||
const keepAddressLaneByIntent = semanticApplyCanonicalRecommended &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal) &&
|
||||
(!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed);
|
||||
const strongDataSignal = hasStrongDataIntentSignal(rawUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedRawUserMessage) ||
|
||||
hasStrongDataIntentSignal(effectiveAddressUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(rawUserMessage) ||
|
||||
hasAccountingSignal(repairedRawUserMessage) ||
|
||||
hasAccountingSignal(effectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedRawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(effectiveAddressUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedEffectiveAddressUserMessage);
|
||||
const explicitAddressFollowupSignal = hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const protectedInventoryShortFollowup = Boolean(followupContext &&
|
||||
isInventorySelectedObjectIntent(toNonEmptyString(followupContext.previous_intent)) &&
|
||||
(hasShortInventoryObjectFollowupSignal(rawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
||||
lastOrganizationClarificationDebug &&
|
||||
organizationClarificationSelection &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal);
|
||||
const effectiveAddressFollowupSignal = explicitAddressFollowupSignal && !dangerOrCoercionSignal;
|
||||
const deterministicNonDomainGuard = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!effectiveAddressFollowupSignal &&
|
||||
resolvedModeDetection.mode === "unsupported" &&
|
||||
resolvedIntentResolution.intent === "unknown");
|
||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||
deterministicNonDomainGuard &&
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_data_scope_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_data_scope_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "data_scope",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_data_scope_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_data_scope_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hardMetaMode === "capability") {
|
||||
if (contextualHistoricalCapabilityFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "inventory_history_capability_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "inventory_history_capability_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "inventory_history_capability_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "inventory_history_capability_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_capability_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_capability_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_capability_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_capability_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (nonDomainQueryIndexed) {
|
||||
if (contextualMemoryRecapFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "memory_recap_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "memory_recap_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "memory_recap_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "memory_recap_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "non_domain_query_indexed",
|
||||
livingMode: "chat",
|
||||
livingReason: "non_domain_query_indexed",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "non_domain_query_indexed",
|
||||
living_mode: "chat",
|
||||
living_reason: "non_domain_query_indexed"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
llmContractMode === "address_query") ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(rawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(effectiveAddressUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedRawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedEffectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(rawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(effectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedRawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage));
|
||||
const supportedAddressIntentDetected = (!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed) &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal);
|
||||
const semanticGuardHints = semanticExtractionContract?.guard_hints &&
|
||||
typeof semanticExtractionContract.guard_hints === "object"
|
||||
? semanticExtractionContract.guard_hints
|
||||
: null;
|
||||
const semanticExtraction = semanticExtractionContract?.extraction &&
|
||||
typeof semanticExtractionContract.extraction === "object"
|
||||
? semanticExtractionContract.extraction
|
||||
: null;
|
||||
const semanticDeepInvestigationHintDetected = semanticGuardHints?.deep_investigation_signal_detected === true;
|
||||
const semanticAggregateShapeDetected = semanticExtraction?.query_shape === "AGGREGATE_LOOKUP" ||
|
||||
semanticExtraction?.aggregation_profile === "management_profile";
|
||||
const rootContextOnlyFollowup = Boolean(followupContext && followupContext.root_context_only === true);
|
||||
const followupSemanticOverrideToDeepAllowed = Boolean(followupContext &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
!semanticApplyCanonicalRecommended));
|
||||
const unsupportedIntentOrMode = (resolvedModeDetection.mode !== "address_query" && resolvedIntentResolution.intent === "unknown") ||
|
||||
llmContractMode === "unsupported" ||
|
||||
(rootContextOnlyFollowup &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
unsupportedIntentOrMode &&
|
||||
strongDataSignal &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "deep_analysis" ||
|
||||
!dataRetrievalSignal ||
|
||||
strictDeepInvestigationCueDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
aggregateBusinessAnalyticsSignal) &&
|
||||
!preserveAddressLaneSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext || followupSemanticOverrideToDeepAllowed));
|
||||
const deepAnalysisPreferenceDetected = Boolean(hasDeepAnalysisPreferenceSignal(rawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedRawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(effectiveAddressUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(rawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedRawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(effectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedEffectiveAddressUserMessage));
|
||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const vatEvaluativeFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:^|\s)(?:это\s+)?много\s+или\s+мало(?:\?|$)|(?:^|\s)(?:это\s+)?нормально(?:\?|$)|(?:^|\s)(?:это\s+)?плохо(?:\?|$)|(?:^|\s)(?:это\s+)?хорошо(?:\?|$)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const deepAnalysisSignalFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
(deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected) &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
!vatExplainFollowupSignal &&
|
||||
(!followupContext || !dataRetrievalSignal || followupSemanticOverrideToDeepAllowed));
|
||||
const aggregateAnalyticsFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
aggregateBusinessAnalyticsSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
!semanticApplyCanonicalRecommended ||
|
||||
standaloneAddressTopicSignal));
|
||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
||||
baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
hasDeepSessionContinuationSignal({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "address_signal_unsupported_intent_fallback_to_deep";
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_analysis_signal_fallback_to_deep";
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "aggregate_analytics_signal_fallback_to_deep";
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_session_continuation_fallback_to_deep";
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "meta_followup_over_grounded_answer";
|
||||
}
|
||||
let livingDecision = resolveLivingAssistantModeDecision({
|
||||
userMessage: rawUserMessage,
|
||||
addressLaneTriggered: runAddressLane,
|
||||
useMock,
|
||||
predecomposeMode: llmPreDecomposeMeta?.predecomposeContract?.mode ?? null,
|
||||
predecomposeModeConfidence: llmPreDecomposeMeta?.predecomposeContract?.mode_confidence ?? null
|
||||
});
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "unsupported_address_intent_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_analysis_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "aggregate_analytics_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_session_continuation_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
livingDecision = {
|
||||
mode: "chat",
|
||||
reason: "meta_followup_over_grounded_answer"
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane,
|
||||
toolGateDecision,
|
||||
toolGateReason,
|
||||
livingMode: livingDecision.mode,
|
||||
livingReason: livingDecision.reason,
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: null,
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
semantic_contract_valid: semanticContractValid,
|
||||
semantic_apply_canonical_recommended: semanticApplyCanonicalRecommended,
|
||||
semantic_reason_codes: semanticReasonCodes,
|
||||
semantic_route_arbitration: {
|
||||
supported_address_intent_detected: supportedAddressIntentDetected,
|
||||
strict_deep_investigation_bypass_allowed: strictDeepInvestigationBypassAllowed,
|
||||
semantic_deep_investigation_hint_detected: semanticDeepInvestigationHintDetected,
|
||||
semantic_aggregate_shape_detected: semanticAggregateShapeDetected,
|
||||
followup_semantic_override_to_deep_allowed: followupSemanticOverrideToDeepAllowed
|
||||
},
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep,
|
||||
deep_analysis_signal_fallback_to_deep: deepAnalysisSignalFallbackToDeep,
|
||||
aggregate_analytics_signal_fallback_to_deep: aggregateAnalyticsFallbackToDeep,
|
||||
deep_session_continuation_fallback_to_deep: deepSessionContinuationFallbackToDeep,
|
||||
final_decision: {
|
||||
run_address_lane: runAddressLane,
|
||||
tool_gate_decision: toolGateDecision,
|
||||
tool_gate_reason: toolGateReason,
|
||||
living_mode: livingDecision.mode,
|
||||
living_reason: livingDecision.reason
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
resolveAssistantOrchestrationDecision
|
||||
};
|
||||
}
|
||||
|
|
@ -67,6 +67,8 @@ const assistantAddressAttemptRuntimeAdapter_1 = __importStar(require("./assistan
|
|||
const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGrounding"));
|
||||
const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAttemptRuntimeAdapter"));
|
||||
const assistantBoundaryPolicy_1 = __importStar(require("./assistantBoundaryPolicy"));
|
||||
const assistantLivingModePolicy_1 = __importStar(require("./assistantLivingModePolicy"));
|
||||
const assistantRoutePolicy_1 = __importStar(require("./assistantRoutePolicy"));
|
||||
const assistantOrganizationScopeRuntimeAdapter_1 = __importStar(require("./assistantOrganizationScopeRuntimeAdapter"));
|
||||
const assistantTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantTurnAttemptRuntimeAdapter"));
|
||||
const assistantTurnRuntimeDepsAdapter_1 = __importStar(require("./assistantTurnRuntimeDepsAdapter"));
|
||||
|
|
@ -4252,623 +4254,17 @@ function hasOpenContractsAddressSignal(text) {
|
|||
const hasTemporalCue = hasPeriodLiteral(normalized) || /\b\d{4}[-/.]\d{2}[-/.]\d{2}\b/.test(normalized);
|
||||
return hasRequestCue || hasTemporalCue;
|
||||
}
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
"document_type_and_account_section_profile",
|
||||
"counterparty_population_and_roles",
|
||||
"counterparty_activity_lifecycle",
|
||||
"customer_revenue_and_payments",
|
||||
"supplier_payouts_profile",
|
||||
"open_contracts_confirmed_as_of_date",
|
||||
"list_open_contracts",
|
||||
"open_items_by_counterparty_or_contract",
|
||||
"list_payables_counterparties",
|
||||
"list_receivables_counterparties",
|
||||
"inventory_on_hand_as_of_date",
|
||||
"payables_confirmed_as_of_date",
|
||||
"receivables_confirmed_as_of_date",
|
||||
"list_documents_by_contract",
|
||||
"bank_operations_by_contract",
|
||||
"list_documents_by_counterparty",
|
||||
"bank_operations_by_counterparty",
|
||||
"list_contracts_by_counterparty",
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_supplier_stock_overlap_as_of_date",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date",
|
||||
"contract_usage_overview",
|
||||
"contract_usage_and_value",
|
||||
"vat_payable_forecast",
|
||||
"vat_liability_confirmed_for_tax_period",
|
||||
"vat_payable_confirmed_as_of_date"
|
||||
]);
|
||||
const ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS = new Set([
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain"
|
||||
]);
|
||||
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||
}
|
||||
function resolveAssistantOrchestrationDecision(input) {
|
||||
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
|
||||
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
|
||||
const repairedRawUserMessage = repairAddressMojibake(rawUserMessage);
|
||||
const repairedEffectiveAddressUserMessage = repairAddressMojibake(effectiveAddressUserMessage);
|
||||
const followupContext = input?.followupContext ?? null;
|
||||
const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null;
|
||||
const useMock = Boolean(input?.useMock);
|
||||
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : null;
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: []))
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedEffectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
(organizationClarificationSelectionFromScope &&
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedEffectiveAddressUserMessage);
|
||||
const aggregateBusinessAnalyticsSignal = hasAggregateBusinessAnalyticsSignal(rawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedRawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(effectiveAddressUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedEffectiveAddressUserMessage);
|
||||
const standaloneAddressTopicSignal = hasStandaloneAddressTopicSignal(rawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedRawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(effectiveAddressUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedEffectiveAddressUserMessage);
|
||||
const openContractsAddressSignal = hasOpenContractsAddressSignal(rawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedRawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(effectiveAddressUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedEffectiveAddressUserMessage);
|
||||
const modeSample = repairedEffectiveAddressUserMessage || effectiveAddressUserMessage;
|
||||
const modeDetection = (0, addressQueryClassifier_1.detectAddressQuestionMode)(modeSample);
|
||||
const modeDetectionRaw = (0, addressQueryClassifier_1.detectAddressQuestionMode)(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
|
||||
const intentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(modeSample);
|
||||
const intentResolutionRaw = (0, addressIntentResolver_1.resolveAddressIntent)(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
|
||||
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||
const llmRuntimeUnavailableDetected = Boolean(llmPreDecomposeReason &&
|
||||
/(?:openai\s+api\s+key\s+is\s+missing|api\s+key\s+is\s+missing|missing\s+api\s+key|authentication)/iu.test(llmPreDecomposeReason));
|
||||
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
|
||||
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
|
||||
? llmPreDecomposeMeta.semanticExtractionContract
|
||||
: null;
|
||||
const semanticContractValid = semanticExtractionContract?.valid !== false;
|
||||
const semanticApplyCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
|
||||
const semanticReasonCodes = Array.isArray(semanticExtractionContract?.reason_codes)
|
||||
? semanticExtractionContract.reason_codes
|
||||
: [];
|
||||
const strictDeepInvestigationCueDetected = hasStrictDeepInvestigationCue(rawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedRawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(effectiveAddressUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedEffectiveAddressUserMessage);
|
||||
const strictDeepInvestigationBypassAllowed = shouldBypassStrictDeepInvestigationCueForAddressIntent(resolvedIntentResolution.intent) ||
|
||||
shouldBypassStrictDeepInvestigationCueForAddressIntent(llmContractIntent);
|
||||
const keepAddressLaneByIntent = semanticApplyCanonicalRecommended &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal) &&
|
||||
(!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed);
|
||||
const strongDataSignal = hasStrongDataIntentSignal(rawUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedRawUserMessage) ||
|
||||
hasStrongDataIntentSignal(effectiveAddressUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(rawUserMessage) ||
|
||||
hasAccountingSignal(repairedRawUserMessage) ||
|
||||
hasAccountingSignal(effectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedRawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(effectiveAddressUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedEffectiveAddressUserMessage);
|
||||
const explicitAddressFollowupSignal = hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const protectedInventoryShortFollowup = Boolean(followupContext &&
|
||||
isInventorySelectedObjectIntent(toNonEmptyString(followupContext.previous_intent)) &&
|
||||
(hasShortInventoryObjectFollowupSignal(rawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
||||
lastOrganizationClarificationDebug &&
|
||||
organizationClarificationSelection &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal);
|
||||
const effectiveAddressFollowupSignal = explicitAddressFollowupSignal && !dangerOrCoercionSignal;
|
||||
const deterministicNonDomainGuard = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!effectiveAddressFollowupSignal &&
|
||||
resolvedModeDetection.mode === "unsupported" &&
|
||||
resolvedIntentResolution.intent === "unknown");
|
||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||
deterministicNonDomainGuard &&
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_data_scope_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_data_scope_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "data_scope",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_data_scope_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_data_scope_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hardMetaMode === "capability") {
|
||||
if (contextualHistoricalCapabilityFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "inventory_history_capability_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "inventory_history_capability_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "inventory_history_capability_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "inventory_history_capability_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_capability_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_capability_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_capability_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_capability_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (nonDomainQueryIndexed) {
|
||||
if (contextualMemoryRecapFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "memory_recap_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "memory_recap_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "memory_recap_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "memory_recap_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "non_domain_query_indexed",
|
||||
livingMode: "chat",
|
||||
livingReason: "non_domain_query_indexed",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "non_domain_query_indexed",
|
||||
living_mode: "chat",
|
||||
living_reason: "non_domain_query_indexed"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
llmContractMode === "address_query") ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(rawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(effectiveAddressUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedRawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedEffectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(rawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(effectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedRawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage));
|
||||
const supportedAddressIntentDetected = (!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed) &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal);
|
||||
const semanticGuardHints = semanticExtractionContract?.guard_hints &&
|
||||
typeof semanticExtractionContract.guard_hints === "object"
|
||||
? semanticExtractionContract.guard_hints
|
||||
: null;
|
||||
const semanticExtraction = semanticExtractionContract?.extraction &&
|
||||
typeof semanticExtractionContract.extraction === "object"
|
||||
? semanticExtractionContract.extraction
|
||||
: null;
|
||||
const semanticDeepInvestigationHintDetected = semanticGuardHints?.deep_investigation_signal_detected === true;
|
||||
const semanticAggregateShapeDetected = semanticExtraction?.query_shape === "AGGREGATE_LOOKUP" ||
|
||||
semanticExtraction?.aggregation_profile === "management_profile";
|
||||
const rootContextOnlyFollowup = Boolean(followupContext && followupContext.root_context_only === true);
|
||||
const followupSemanticOverrideToDeepAllowed = Boolean(followupContext &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
!semanticApplyCanonicalRecommended));
|
||||
const unsupportedIntentOrMode = (resolvedModeDetection.mode !== "address_query" && resolvedIntentResolution.intent === "unknown") ||
|
||||
llmContractMode === "unsupported" ||
|
||||
(rootContextOnlyFollowup &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
unsupportedIntentOrMode &&
|
||||
strongDataSignal &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "deep_analysis" ||
|
||||
!dataRetrievalSignal ||
|
||||
strictDeepInvestigationCueDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
aggregateBusinessAnalyticsSignal) &&
|
||||
!preserveAddressLaneSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext || followupSemanticOverrideToDeepAllowed));
|
||||
const deepAnalysisPreferenceDetected = Boolean(hasDeepAnalysisPreferenceSignal(rawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedRawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(effectiveAddressUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(rawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedRawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(effectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedEffectiveAddressUserMessage));
|
||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const vatEvaluativeFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:^|\s)(?:это\s+)?много\s+или\s+мало(?:\?|$)|(?:^|\s)(?:это\s+)?нормально(?:\?|$)|(?:^|\s)(?:это\s+)?плохо(?:\?|$)|(?:^|\s)(?:это\s+)?хорошо(?:\?|$)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const deepAnalysisSignalFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
(deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected) &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
!vatExplainFollowupSignal &&
|
||||
(!followupContext || !dataRetrievalSignal || followupSemanticOverrideToDeepAllowed));
|
||||
const aggregateAnalyticsFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
aggregateBusinessAnalyticsSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
!semanticApplyCanonicalRecommended ||
|
||||
standaloneAddressTopicSignal));
|
||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
||||
baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
hasDeepSessionContinuationSignal({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "address_signal_unsupported_intent_fallback_to_deep";
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_analysis_signal_fallback_to_deep";
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "aggregate_analytics_signal_fallback_to_deep";
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_session_continuation_fallback_to_deep";
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "meta_followup_over_grounded_answer";
|
||||
}
|
||||
let livingDecision = resolveLivingAssistantModeDecision({
|
||||
userMessage: rawUserMessage,
|
||||
addressLaneTriggered: runAddressLane,
|
||||
useMock,
|
||||
predecomposeMode: llmPreDecomposeMeta?.predecomposeContract?.mode ?? null,
|
||||
predecomposeModeConfidence: llmPreDecomposeMeta?.predecomposeContract?.mode_confidence ?? null
|
||||
});
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "unsupported_address_intent_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_analysis_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "aggregate_analytics_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_session_continuation_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
livingDecision = {
|
||||
mode: "chat",
|
||||
reason: "meta_followup_over_grounded_answer"
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane,
|
||||
toolGateDecision,
|
||||
toolGateReason,
|
||||
livingMode: livingDecision.mode,
|
||||
livingReason: livingDecision.reason,
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: null,
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
semantic_contract_valid: semanticContractValid,
|
||||
semantic_apply_canonical_recommended: semanticApplyCanonicalRecommended,
|
||||
semantic_reason_codes: semanticReasonCodes,
|
||||
semantic_route_arbitration: {
|
||||
supported_address_intent_detected: supportedAddressIntentDetected,
|
||||
strict_deep_investigation_bypass_allowed: strictDeepInvestigationBypassAllowed,
|
||||
semantic_deep_investigation_hint_detected: semanticDeepInvestigationHintDetected,
|
||||
semantic_aggregate_shape_detected: semanticAggregateShapeDetected,
|
||||
followup_semantic_override_to_deep_allowed: followupSemanticOverrideToDeepAllowed
|
||||
},
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep,
|
||||
deep_analysis_signal_fallback_to_deep: deepAnalysisSignalFallbackToDeep,
|
||||
aggregate_analytics_signal_fallback_to_deep: aggregateAnalyticsFallbackToDeep,
|
||||
deep_session_continuation_fallback_to_deep: deepSessionContinuationFallbackToDeep,
|
||||
final_decision: {
|
||||
run_address_lane: runAddressLane,
|
||||
tool_gate_decision: toolGateDecision,
|
||||
tool_gate_reason: toolGateReason,
|
||||
living_mode: livingDecision.mode,
|
||||
living_reason: livingDecision.reason
|
||||
}
|
||||
}
|
||||
};
|
||||
return assistantRoutePolicy.resolveAssistantOrchestrationDecision(input);
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasStrongDataIntentSignal(text);
|
||||
}
|
||||
function hasDataRetrievalRequestSignal(text) {
|
||||
const lower = compactWhitespace(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;
|
||||
return assistantLivingModePolicy.hasDataRetrievalRequestSignal(text);
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasOrganizationFactLookupSignal(text);
|
||||
}
|
||||
function findLastAssistantLivingChatDebug(items) {
|
||||
if (!Array.isArray(items)) {
|
||||
|
|
@ -4929,67 +4325,13 @@ function findLastOrganizationClarificationAddressDebug(items) {
|
|||
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));
|
||||
return assistantLivingModePolicy.hasMetaAnswerFollowupSignal(userMessage);
|
||||
}
|
||||
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));
|
||||
if (!hasMemoryCue || !hasDiscussionCue) {
|
||||
return false;
|
||||
}
|
||||
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
|
||||
hasDataRetrievalRequestSignal(sample) ||
|
||||
hasStrongDataIntentSignal(sample));
|
||||
return assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal(userMessage);
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal(text);
|
||||
}
|
||||
function isGroundedInventoryContextDebug(debug) {
|
||||
if (!debug || typeof debug !== "object") {
|
||||
|
|
@ -5006,56 +4348,10 @@ function isGroundedInventoryContextDebug(debug) {
|
|||
rootIntent === "inventory_on_hand_as_of_date";
|
||||
}
|
||||
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;
|
||||
return assistantLivingModePolicy.hasOrganizationFactFollowupSignal(userMessage, items);
|
||||
}
|
||||
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 ?? ""));
|
||||
return assistantLivingModePolicy.shouldEmitOrganizationSelectionReply(userMessage, selectedOrganization);
|
||||
}
|
||||
function hasOperationalAdminActionRequestSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase()).replace(/ё/g, "е");
|
||||
|
|
@ -5131,78 +4427,13 @@ function hasAssistantCapabilityQuestionSignal(text) {
|
|||
return false;
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal(text);
|
||||
}
|
||||
function shouldHandleAsAssistantCapabilityMetaQuery(text) {
|
||||
const raw = String(text ?? "");
|
||||
const repaired = repairAddressMojibake(raw);
|
||||
const hasScopeMetaSignal = hasAssistantDataScopeMetaQuestionSignal(raw) || hasAssistantDataScopeMetaQuestionSignal(repaired);
|
||||
if (hasScopeMetaSignal) {
|
||||
return true;
|
||||
}
|
||||
const hasCapabilitySignal = hasAssistantCapabilityQuestionSignal(raw) ||
|
||||
hasAssistantCapabilityQuestionSignal(repaired) ||
|
||||
hasOperationalAdminActionRequestSignal(raw) ||
|
||||
hasOperationalAdminActionRequestSignal(repaired);
|
||||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(raw) || hasDataRetrievalRequestSignal(repaired);
|
||||
return hasCapabilitySignal && !hasRetrievalSignal;
|
||||
return assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery(text);
|
||||
}
|
||||
function hasLivingChatSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase());
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
if (/^(?:а\s+)?(?:тут|здесь|там|сюда|туда)[\s!?.,:;\-]*$/iu.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|yo|че\s+там|ч[её]\s+как|че\s+как|hello|hi|thanks?)$/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/(как дела|как ты|что нового|расскажи о себе|чем можешь помочь|давай поговорим|поговорим|обсудим|посоветуй|что думаешь)/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
return hasSmallTalkSignal(lower);
|
||||
return assistantLivingModePolicy.hasLivingChatSignal(text);
|
||||
}
|
||||
function buildAssistantCapabilityContractReply() {
|
||||
return (0, capabilitiesRegistry_1.buildCapabilityContractReplyFromRegistry)();
|
||||
|
|
@ -5306,6 +4537,55 @@ function normalizeOrganizationScopeValue(value) {
|
|||
.trim();
|
||||
return unwrapped ? unwrapped : null;
|
||||
}
|
||||
const assistantLivingModePolicy = (0, assistantLivingModePolicy_1.createAssistantLivingModePolicy)({
|
||||
featureAssistantLivingChatRouterV1: config_1.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1,
|
||||
compactWhitespace,
|
||||
repairAddressMojibake,
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
hasReferentialPointer,
|
||||
hasSmallTalkSignal,
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
hasOperationalAdminActionRequestSignal
|
||||
});
|
||||
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
|
||||
repairAddressMojibake,
|
||||
findLastGroundedAddressAnswerDebug,
|
||||
findLastOrganizationClarificationAddressDebug,
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDataRetrievalRequestSignal: assistantLivingModePolicy.hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
hasOpenContractsAddressSignal,
|
||||
detectAddressQuestionMode: addressQueryClassifier_1.detectAddressQuestionMode,
|
||||
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
|
||||
toNonEmptyString,
|
||||
hasStrictDeepInvestigationCue,
|
||||
hasStrongDataIntentSignal: assistantLivingModePolicy.hasStrongDataIntentSignal,
|
||||
hasAccountingSignal,
|
||||
hasDangerOrCoercionSignal,
|
||||
hasAddressFollowupContextSignal,
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
hasDeepAnalysisPreferenceSignal,
|
||||
hasDirectDeepAnalysisSignal,
|
||||
compactWhitespace,
|
||||
hasDeepSessionContinuationSignal,
|
||||
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision
|
||||
});
|
||||
function normalizeOrganizationScopeSearchText(value) {
|
||||
const source = normalizeScopeKey(value);
|
||||
return source
|
||||
|
|
@ -6105,67 +5385,7 @@ function applyLivingChatGroundingGuard(input) {
|
|||
};
|
||||
}
|
||||
function resolveLivingAssistantModeDecision(input) {
|
||||
const userMessage = String(input?.userMessage ?? "");
|
||||
if (input?.addressLaneTriggered) {
|
||||
return {
|
||||
mode: "address_data",
|
||||
reason: "address_lane_triggered"
|
||||
};
|
||||
}
|
||||
if (!config_1.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
reason: "living_chat_router_disabled"
|
||||
};
|
||||
}
|
||||
if (Boolean(input?.useMock)) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
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 assistantLivingModePolicy.resolveLivingAssistantModeDecision(input);
|
||||
}
|
||||
class AssistantService {
|
||||
normalizerService;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,395 @@
|
|||
// @ts-nocheck
|
||||
export interface ResolveLivingAssistantModeDecisionInput {
|
||||
userMessage?: unknown;
|
||||
addressLaneTriggered?: boolean;
|
||||
useMock?: boolean;
|
||||
predecomposeMode?: unknown;
|
||||
predecomposeModeConfidence?: unknown;
|
||||
}
|
||||
|
||||
export interface AssistantLivingModePolicyDeps {
|
||||
featureAssistantLivingChatRouterV1: boolean;
|
||||
compactWhitespace: (text: string) => string;
|
||||
repairAddressMojibake: (text: string) => string;
|
||||
toNonEmptyString: (value: unknown) => string | null;
|
||||
normalizeOrganizationScopeValue: (value: unknown) => string | null;
|
||||
hasReferentialPointer: (text: string) => boolean;
|
||||
hasSmallTalkSignal: (text: string) => boolean;
|
||||
hasAssistantCapabilityQuestionSignal: (text: string) => boolean;
|
||||
hasOperationalAdminActionRequestSignal: (text: string) => boolean;
|
||||
}
|
||||
|
||||
export interface AssistantLivingModeDecision {
|
||||
mode: "address_data" | "deep_analysis" | "chat";
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface AssistantLivingModePolicy {
|
||||
hasStrongDataIntentSignal: (text: unknown) => boolean;
|
||||
hasDataRetrievalRequestSignal: (text: unknown) => boolean;
|
||||
hasOrganizationFactLookupSignal: (text: unknown) => boolean;
|
||||
hasMetaAnswerFollowupSignal: (text: unknown) => boolean;
|
||||
hasConversationMemoryRecallFollowupSignal: (text: unknown) => boolean;
|
||||
hasHistoricalCapabilityFollowupSignal: (text: unknown) => boolean;
|
||||
hasOrganizationFactFollowupSignal: (userMessage: unknown, items: unknown[]) => boolean;
|
||||
shouldEmitOrganizationSelectionReply: (userMessage: unknown, selectedOrganization: unknown) => boolean;
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: unknown) => boolean;
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: unknown) => boolean;
|
||||
hasLivingChatSignal: (text: unknown) => boolean;
|
||||
resolveLivingAssistantModeDecision: (input: ResolveLivingAssistantModeDecisionInput) => AssistantLivingModeDecision;
|
||||
}
|
||||
|
||||
export function createAssistantLivingModePolicy(deps: AssistantLivingModePolicyDeps): AssistantLivingModePolicy {
|
||||
const {
|
||||
featureAssistantLivingChatRouterV1,
|
||||
compactWhitespace,
|
||||
repairAddressMojibake,
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
hasReferentialPointer,
|
||||
hasSmallTalkSignal,
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
hasOperationalAdminActionRequestSignal
|
||||
} = 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(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));
|
||||
if (!hasMemoryCue || !hasDiscussionCue) {
|
||||
return false;
|
||||
}
|
||||
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 hasCapabilitySignal = hasAssistantCapabilityQuestionSignal(raw) ||
|
||||
hasAssistantCapabilityQuestionSignal(repaired) ||
|
||||
hasOperationalAdminActionRequestSignal(raw) ||
|
||||
hasOperationalAdminActionRequestSignal(repaired);
|
||||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(raw) || hasDataRetrievalRequestSignal(repaired);
|
||||
return hasCapabilitySignal && !hasRetrievalSignal;
|
||||
}
|
||||
|
||||
function hasLivingChatSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase());
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
if (/^(?:а\s+)?(?:тут|здесь|там|сюда|туда)[\s!?.,:;\-]*$/iu.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|yo|че\s+там|ч[её]\s+как|че\s+как|hello|hi|thanks?)$/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/(как дела|как ты|что нового|расскажи о себе|чем можешь помочь|давай поговорим|поговорим|обсудим|посоветуй|что думаешь)/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
return hasSmallTalkSignal(lower);
|
||||
}
|
||||
|
||||
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"
|
||||
};
|
||||
}
|
||||
if (Boolean(input?.useMock)) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
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
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,616 @@
|
|||
// @ts-nocheck
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
"document_type_and_account_section_profile",
|
||||
"counterparty_population_and_roles",
|
||||
"counterparty_activity_lifecycle",
|
||||
"customer_revenue_and_payments",
|
||||
"supplier_payouts_profile",
|
||||
"open_contracts_confirmed_as_of_date",
|
||||
"list_open_contracts",
|
||||
"open_items_by_counterparty_or_contract",
|
||||
"list_payables_counterparties",
|
||||
"list_receivables_counterparties",
|
||||
"inventory_on_hand_as_of_date",
|
||||
"payables_confirmed_as_of_date",
|
||||
"receivables_confirmed_as_of_date",
|
||||
"list_documents_by_contract",
|
||||
"bank_operations_by_contract",
|
||||
"list_documents_by_counterparty",
|
||||
"bank_operations_by_counterparty",
|
||||
"list_contracts_by_counterparty",
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_supplier_stock_overlap_as_of_date",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date",
|
||||
"contract_usage_overview",
|
||||
"contract_usage_and_value",
|
||||
"vat_payable_forecast",
|
||||
"vat_liability_confirmed_for_tax_period",
|
||||
"vat_payable_confirmed_as_of_date"
|
||||
]);
|
||||
const ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS = new Set([
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain"
|
||||
]);
|
||||
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||
}
|
||||
export function createAssistantRoutePolicy(deps) {
|
||||
const {
|
||||
repairAddressMojibake,
|
||||
findLastGroundedAddressAnswerDebug,
|
||||
findLastOrganizationClarificationAddressDebug,
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
hasOpenContractsAddressSignal,
|
||||
detectAddressQuestionMode,
|
||||
resolveAddressIntent,
|
||||
toNonEmptyString,
|
||||
hasStrictDeepInvestigationCue,
|
||||
hasStrongDataIntentSignal,
|
||||
hasAccountingSignal,
|
||||
hasDangerOrCoercionSignal,
|
||||
hasAddressFollowupContextSignal,
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
hasDeepAnalysisPreferenceSignal,
|
||||
hasDirectDeepAnalysisSignal,
|
||||
compactWhitespace,
|
||||
hasDeepSessionContinuationSignal,
|
||||
resolveLivingAssistantModeDecision
|
||||
} = deps;
|
||||
function resolveAssistantOrchestrationDecision(input) {
|
||||
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
|
||||
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
|
||||
const repairedRawUserMessage = repairAddressMojibake(rawUserMessage);
|
||||
const repairedEffectiveAddressUserMessage = repairAddressMojibake(effectiveAddressUserMessage);
|
||||
const followupContext = input?.followupContext ?? null;
|
||||
const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null;
|
||||
const useMock = Boolean(input?.useMock);
|
||||
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : null;
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: []))
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedEffectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
(organizationClarificationSelectionFromScope &&
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedEffectiveAddressUserMessage);
|
||||
const aggregateBusinessAnalyticsSignal = hasAggregateBusinessAnalyticsSignal(rawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedRawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(effectiveAddressUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedEffectiveAddressUserMessage);
|
||||
const standaloneAddressTopicSignal = hasStandaloneAddressTopicSignal(rawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedRawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(effectiveAddressUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedEffectiveAddressUserMessage);
|
||||
const openContractsAddressSignal = hasOpenContractsAddressSignal(rawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedRawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(effectiveAddressUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedEffectiveAddressUserMessage);
|
||||
const modeSample = repairedEffectiveAddressUserMessage || effectiveAddressUserMessage;
|
||||
const modeDetection = detectAddressQuestionMode(modeSample);
|
||||
const modeDetectionRaw = detectAddressQuestionMode(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
|
||||
const intentResolution = resolveAddressIntent(modeSample);
|
||||
const intentResolutionRaw = resolveAddressIntent(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
|
||||
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||
const llmRuntimeUnavailableDetected = Boolean(llmPreDecomposeReason &&
|
||||
/(?:openai\s+api\s+key\s+is\s+missing|api\s+key\s+is\s+missing|missing\s+api\s+key|authentication)/iu.test(llmPreDecomposeReason));
|
||||
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
|
||||
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
|
||||
? llmPreDecomposeMeta.semanticExtractionContract
|
||||
: null;
|
||||
const semanticContractValid = semanticExtractionContract?.valid !== false;
|
||||
const semanticApplyCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
|
||||
const semanticReasonCodes = Array.isArray(semanticExtractionContract?.reason_codes)
|
||||
? semanticExtractionContract.reason_codes
|
||||
: [];
|
||||
const strictDeepInvestigationCueDetected = hasStrictDeepInvestigationCue(rawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedRawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(effectiveAddressUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedEffectiveAddressUserMessage);
|
||||
const strictDeepInvestigationBypassAllowed = shouldBypassStrictDeepInvestigationCueForAddressIntent(resolvedIntentResolution.intent) ||
|
||||
shouldBypassStrictDeepInvestigationCueForAddressIntent(llmContractIntent);
|
||||
const keepAddressLaneByIntent = semanticApplyCanonicalRecommended &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal) &&
|
||||
(!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed);
|
||||
const strongDataSignal = hasStrongDataIntentSignal(rawUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedRawUserMessage) ||
|
||||
hasStrongDataIntentSignal(effectiveAddressUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(rawUserMessage) ||
|
||||
hasAccountingSignal(repairedRawUserMessage) ||
|
||||
hasAccountingSignal(effectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedRawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(effectiveAddressUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedEffectiveAddressUserMessage);
|
||||
const explicitAddressFollowupSignal = hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const protectedInventoryShortFollowup = Boolean(followupContext &&
|
||||
isInventorySelectedObjectIntent(toNonEmptyString(followupContext.previous_intent)) &&
|
||||
(hasShortInventoryObjectFollowupSignal(rawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
||||
lastOrganizationClarificationDebug &&
|
||||
organizationClarificationSelection &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal);
|
||||
const effectiveAddressFollowupSignal = explicitAddressFollowupSignal && !dangerOrCoercionSignal;
|
||||
const deterministicNonDomainGuard = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!effectiveAddressFollowupSignal &&
|
||||
resolvedModeDetection.mode === "unsupported" &&
|
||||
resolvedIntentResolution.intent === "unknown");
|
||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||
deterministicNonDomainGuard &&
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_data_scope_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_data_scope_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "data_scope",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_data_scope_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_data_scope_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hardMetaMode === "capability") {
|
||||
if (contextualHistoricalCapabilityFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "inventory_history_capability_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "inventory_history_capability_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "inventory_history_capability_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "inventory_history_capability_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_capability_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_capability_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_capability_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_capability_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (nonDomainQueryIndexed) {
|
||||
if (contextualMemoryRecapFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "memory_recap_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "memory_recap_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "memory_recap_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "memory_recap_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "non_domain_query_indexed",
|
||||
livingMode: "chat",
|
||||
livingReason: "non_domain_query_indexed",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "non_domain_query_indexed",
|
||||
living_mode: "chat",
|
||||
living_reason: "non_domain_query_indexed"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
llmContractMode === "address_query") ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(rawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(effectiveAddressUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedRawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedEffectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(rawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(effectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedRawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage));
|
||||
const supportedAddressIntentDetected = (!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed) &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal);
|
||||
const semanticGuardHints = semanticExtractionContract?.guard_hints &&
|
||||
typeof semanticExtractionContract.guard_hints === "object"
|
||||
? semanticExtractionContract.guard_hints
|
||||
: null;
|
||||
const semanticExtraction = semanticExtractionContract?.extraction &&
|
||||
typeof semanticExtractionContract.extraction === "object"
|
||||
? semanticExtractionContract.extraction
|
||||
: null;
|
||||
const semanticDeepInvestigationHintDetected = semanticGuardHints?.deep_investigation_signal_detected === true;
|
||||
const semanticAggregateShapeDetected = semanticExtraction?.query_shape === "AGGREGATE_LOOKUP" ||
|
||||
semanticExtraction?.aggregation_profile === "management_profile";
|
||||
const rootContextOnlyFollowup = Boolean(followupContext && followupContext.root_context_only === true);
|
||||
const followupSemanticOverrideToDeepAllowed = Boolean(followupContext &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
!semanticApplyCanonicalRecommended));
|
||||
const unsupportedIntentOrMode = (resolvedModeDetection.mode !== "address_query" && resolvedIntentResolution.intent === "unknown") ||
|
||||
llmContractMode === "unsupported" ||
|
||||
(rootContextOnlyFollowup &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
unsupportedIntentOrMode &&
|
||||
strongDataSignal &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "deep_analysis" ||
|
||||
!dataRetrievalSignal ||
|
||||
strictDeepInvestigationCueDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
aggregateBusinessAnalyticsSignal) &&
|
||||
!preserveAddressLaneSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext || followupSemanticOverrideToDeepAllowed));
|
||||
const deepAnalysisPreferenceDetected = Boolean(hasDeepAnalysisPreferenceSignal(rawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedRawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(effectiveAddressUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(rawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedRawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(effectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedEffectiveAddressUserMessage));
|
||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const vatEvaluativeFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:^|\s)(?:это\s+)?много\s+или\s+мало(?:\?|$)|(?:^|\s)(?:это\s+)?нормально(?:\?|$)|(?:^|\s)(?:это\s+)?плохо(?:\?|$)|(?:^|\s)(?:это\s+)?хорошо(?:\?|$)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const deepAnalysisSignalFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
(deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected) &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
!vatExplainFollowupSignal &&
|
||||
(!followupContext || !dataRetrievalSignal || followupSemanticOverrideToDeepAllowed));
|
||||
const aggregateAnalyticsFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
aggregateBusinessAnalyticsSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
!semanticApplyCanonicalRecommended ||
|
||||
standaloneAddressTopicSignal));
|
||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
||||
baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
hasDeepSessionContinuationSignal({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "address_signal_unsupported_intent_fallback_to_deep";
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_analysis_signal_fallback_to_deep";
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "aggregate_analytics_signal_fallback_to_deep";
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_session_continuation_fallback_to_deep";
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "meta_followup_over_grounded_answer";
|
||||
}
|
||||
let livingDecision = resolveLivingAssistantModeDecision({
|
||||
userMessage: rawUserMessage,
|
||||
addressLaneTriggered: runAddressLane,
|
||||
useMock,
|
||||
predecomposeMode: llmPreDecomposeMeta?.predecomposeContract?.mode ?? null,
|
||||
predecomposeModeConfidence: llmPreDecomposeMeta?.predecomposeContract?.mode_confidence ?? null
|
||||
});
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "unsupported_address_intent_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_analysis_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "aggregate_analytics_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_session_continuation_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
livingDecision = {
|
||||
mode: "chat",
|
||||
reason: "meta_followup_over_grounded_answer"
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane,
|
||||
toolGateDecision,
|
||||
toolGateReason,
|
||||
livingMode: livingDecision.mode,
|
||||
livingReason: livingDecision.reason,
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: null,
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
semantic_contract_valid: semanticContractValid,
|
||||
semantic_apply_canonical_recommended: semanticApplyCanonicalRecommended,
|
||||
semantic_reason_codes: semanticReasonCodes,
|
||||
semantic_route_arbitration: {
|
||||
supported_address_intent_detected: supportedAddressIntentDetected,
|
||||
strict_deep_investigation_bypass_allowed: strictDeepInvestigationBypassAllowed,
|
||||
semantic_deep_investigation_hint_detected: semanticDeepInvestigationHintDetected,
|
||||
semantic_aggregate_shape_detected: semanticAggregateShapeDetected,
|
||||
followup_semantic_override_to_deep_allowed: followupSemanticOverrideToDeepAllowed
|
||||
},
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep,
|
||||
deep_analysis_signal_fallback_to_deep: deepAnalysisSignalFallbackToDeep,
|
||||
aggregate_analytics_signal_fallback_to_deep: aggregateAnalyticsFallbackToDeep,
|
||||
deep_session_continuation_fallback_to_deep: deepSessionContinuationFallbackToDeep,
|
||||
final_decision: {
|
||||
run_address_lane: runAddressLane,
|
||||
tool_gate_decision: toolGateDecision,
|
||||
tool_gate_reason: toolGateReason,
|
||||
living_mode: livingDecision.mode,
|
||||
living_reason: livingDecision.reason
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
resolveAssistantOrchestrationDecision
|
||||
};
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ import * as assistantAddressAttemptRuntimeAdapter_1 from "./assistantAddressAtte
|
|||
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
||||
import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAttemptRuntimeAdapter";
|
||||
import * as assistantBoundaryPolicy_1 from "./assistantBoundaryPolicy";
|
||||
import * as assistantLivingModePolicy_1 from "./assistantLivingModePolicy";
|
||||
import * as assistantRoutePolicy_1 from "./assistantRoutePolicy";
|
||||
import * as assistantOrganizationScopeRuntimeAdapter_1 from "./assistantOrganizationScopeRuntimeAdapter";
|
||||
import * as assistantOrganizationMatcher_1 from "./assistantOrganizationMatcher";
|
||||
import * as assistantTurnAttemptRuntimeAdapter_1 from "./assistantTurnAttemptRuntimeAdapter";
|
||||
|
|
@ -4211,623 +4213,18 @@ function hasOpenContractsAddressSignal(text) {
|
|||
const hasTemporalCue = hasPeriodLiteral(normalized) || /\b\d{4}[-/.]\d{2}[-/.]\d{2}\b/.test(normalized);
|
||||
return hasRequestCue || hasTemporalCue;
|
||||
}
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
"document_type_and_account_section_profile",
|
||||
"counterparty_population_and_roles",
|
||||
"counterparty_activity_lifecycle",
|
||||
"customer_revenue_and_payments",
|
||||
"supplier_payouts_profile",
|
||||
"open_contracts_confirmed_as_of_date",
|
||||
"list_open_contracts",
|
||||
"open_items_by_counterparty_or_contract",
|
||||
"list_payables_counterparties",
|
||||
"list_receivables_counterparties",
|
||||
"inventory_on_hand_as_of_date",
|
||||
"payables_confirmed_as_of_date",
|
||||
"receivables_confirmed_as_of_date",
|
||||
"list_documents_by_contract",
|
||||
"bank_operations_by_contract",
|
||||
"list_documents_by_counterparty",
|
||||
"bank_operations_by_counterparty",
|
||||
"list_contracts_by_counterparty",
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_supplier_stock_overlap_as_of_date",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date",
|
||||
"contract_usage_overview",
|
||||
"contract_usage_and_value",
|
||||
"vat_payable_forecast",
|
||||
"vat_liability_confirmed_for_tax_period",
|
||||
"vat_payable_confirmed_as_of_date"
|
||||
]);
|
||||
const ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS = new Set([
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain"
|
||||
]);
|
||||
function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||
}
|
||||
export function resolveAssistantOrchestrationDecision(input) {
|
||||
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
|
||||
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
|
||||
const repairedRawUserMessage = repairAddressMojibake(rawUserMessage);
|
||||
const repairedEffectiveAddressUserMessage = repairAddressMojibake(effectiveAddressUserMessage);
|
||||
const followupContext = input?.followupContext ?? null;
|
||||
const llmPreDecomposeMeta = input?.llmPreDecomposeMeta ?? null;
|
||||
const useMock = Boolean(input?.useMock);
|
||||
const sessionItems = Array.isArray(input?.sessionItems) ? input.sessionItems : null;
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: []))
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedEffectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
(organizationClarificationSelectionFromScope &&
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedEffectiveAddressUserMessage);
|
||||
const aggregateBusinessAnalyticsSignal = hasAggregateBusinessAnalyticsSignal(rawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedRawUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(effectiveAddressUserMessage) ||
|
||||
hasAggregateBusinessAnalyticsSignal(repairedEffectiveAddressUserMessage);
|
||||
const standaloneAddressTopicSignal = hasStandaloneAddressTopicSignal(rawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedRawUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(effectiveAddressUserMessage) ||
|
||||
hasStandaloneAddressTopicSignal(repairedEffectiveAddressUserMessage);
|
||||
const openContractsAddressSignal = hasOpenContractsAddressSignal(rawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedRawUserMessage) ||
|
||||
hasOpenContractsAddressSignal(effectiveAddressUserMessage) ||
|
||||
hasOpenContractsAddressSignal(repairedEffectiveAddressUserMessage);
|
||||
const modeSample = repairedEffectiveAddressUserMessage || effectiveAddressUserMessage;
|
||||
const modeDetection = (0, addressQueryClassifier_1.detectAddressQuestionMode)(modeSample);
|
||||
const modeDetectionRaw = (0, addressQueryClassifier_1.detectAddressQuestionMode)(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedModeDetection = modeDetection.mode === "address_query" ? modeDetection : modeDetectionRaw;
|
||||
const intentResolution = (0, addressIntentResolver_1.resolveAddressIntent)(modeSample);
|
||||
const intentResolutionRaw = (0, addressIntentResolver_1.resolveAddressIntent)(repairedRawUserMessage || rawUserMessage);
|
||||
const resolvedIntentResolution = intentResolution.intent !== "unknown" ? intentResolution : intentResolutionRaw;
|
||||
const llmContractIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmPreDecomposeReason = toNonEmptyString(llmPreDecomposeMeta?.reason);
|
||||
const llmRuntimeUnavailableDetected = Boolean(llmPreDecomposeReason &&
|
||||
/(?:openai\s+api\s+key\s+is\s+missing|api\s+key\s+is\s+missing|missing\s+api\s+key|authentication)/iu.test(llmPreDecomposeReason));
|
||||
const semanticExtractionContract = llmPreDecomposeMeta?.semanticExtractionContract &&
|
||||
typeof llmPreDecomposeMeta.semanticExtractionContract === "object"
|
||||
? llmPreDecomposeMeta.semanticExtractionContract
|
||||
: null;
|
||||
const semanticContractValid = semanticExtractionContract?.valid !== false;
|
||||
const semanticApplyCanonicalRecommended = semanticExtractionContract?.apply_canonical_recommended !== false;
|
||||
const semanticReasonCodes = Array.isArray(semanticExtractionContract?.reason_codes)
|
||||
? semanticExtractionContract.reason_codes
|
||||
: [];
|
||||
const strictDeepInvestigationCueDetected = hasStrictDeepInvestigationCue(rawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedRawUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(effectiveAddressUserMessage) ||
|
||||
hasStrictDeepInvestigationCue(repairedEffectiveAddressUserMessage);
|
||||
const strictDeepInvestigationBypassAllowed = shouldBypassStrictDeepInvestigationCueForAddressIntent(resolvedIntentResolution.intent) ||
|
||||
shouldBypassStrictDeepInvestigationCueForAddressIntent(llmContractIntent);
|
||||
const keepAddressLaneByIntent = semanticApplyCanonicalRecommended &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal) &&
|
||||
(!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed);
|
||||
const strongDataSignal = hasStrongDataIntentSignal(rawUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedRawUserMessage) ||
|
||||
hasStrongDataIntentSignal(effectiveAddressUserMessage) ||
|
||||
hasStrongDataIntentSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(rawUserMessage) ||
|
||||
hasAccountingSignal(repairedRawUserMessage) ||
|
||||
hasAccountingSignal(effectiveAddressUserMessage) ||
|
||||
hasAccountingSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage);
|
||||
const llmContractMode = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.mode);
|
||||
const llmFirstAddressCandidate = Boolean(llmContractMode === "address_query" && llmContractIntent && llmContractIntent !== "unknown");
|
||||
const llmFirstUnsupportedCandidate = Boolean(llmContractMode === "unsupported" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const dangerOrCoercionSignal = hasDangerOrCoercionSignal(rawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedRawUserMessage) ||
|
||||
hasDangerOrCoercionSignal(effectiveAddressUserMessage) ||
|
||||
hasDangerOrCoercionSignal(repairedEffectiveAddressUserMessage);
|
||||
const explicitAddressFollowupSignal = hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const protectedInventoryShortFollowup = Boolean(followupContext &&
|
||||
isInventorySelectedObjectIntent(toNonEmptyString(followupContext.previous_intent)) &&
|
||||
(hasShortInventoryObjectFollowupSignal(rawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
||||
lastOrganizationClarificationDebug &&
|
||||
organizationClarificationSelection &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal);
|
||||
const effectiveAddressFollowupSignal = explicitAddressFollowupSignal && !dangerOrCoercionSignal;
|
||||
const deterministicNonDomainGuard = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!effectiveAddressFollowupSignal &&
|
||||
resolvedModeDetection.mode === "unsupported" &&
|
||||
resolvedIntentResolution.intent === "unknown");
|
||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||
deterministicNonDomainGuard &&
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_data_scope_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_data_scope_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "data_scope",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_data_scope_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_data_scope_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (hardMetaMode === "capability") {
|
||||
if (contextualHistoricalCapabilityFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "inventory_history_capability_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "inventory_history_capability_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "inventory_history_capability_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "inventory_history_capability_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "assistant_capability_query_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "assistant_capability_query_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "capability",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "assistant_capability_query_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "assistant_capability_query_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (nonDomainQueryIndexed) {
|
||||
if (contextualMemoryRecapFollowupDetected) {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "memory_recap_followup_detected",
|
||||
livingMode: "chat",
|
||||
livingReason: "memory_recap_followup_detected",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "memory_recap_followup_detected",
|
||||
living_mode: "chat",
|
||||
living_reason: "memory_recap_followup_detected"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane: false,
|
||||
toolGateDecision: "skip_address_lane",
|
||||
toolGateReason: "non_domain_query_indexed",
|
||||
livingMode: "chat",
|
||||
livingReason: "non_domain_query_indexed",
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: "non_domain",
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: false,
|
||||
final_decision: {
|
||||
run_address_lane: false,
|
||||
tool_gate_decision: "skip_address_lane",
|
||||
tool_gate_reason: "non_domain_query_indexed",
|
||||
living_mode: "chat",
|
||||
living_reason: "non_domain_query_indexed"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
llmContractMode === "address_query") ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(rawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(effectiveAddressUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedRawUserMessage) ||
|
||||
hasSameDateAccountFollowupSignalForPredecompose(repairedEffectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(rawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(effectiveAddressUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedRawUserMessage) ||
|
||||
hasLooseAllTimeAddressLookupSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(rawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(effectiveAddressUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedRawUserMessage) ||
|
||||
hasAddressFollowupContextSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(rawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage));
|
||||
const supportedAddressIntentDetected = (!strictDeepInvestigationCueDetected || strictDeepInvestigationBypassAllowed) &&
|
||||
Boolean((resolvedIntentResolution.intent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(resolvedIntentResolution.intent)) ||
|
||||
(llmContractIntent && ADDRESS_INTENTS_KEEP_ADDRESS_LANE.has(llmContractIntent)) ||
|
||||
openContractsAddressSignal);
|
||||
const semanticGuardHints = semanticExtractionContract?.guard_hints &&
|
||||
typeof semanticExtractionContract.guard_hints === "object"
|
||||
? semanticExtractionContract.guard_hints
|
||||
: null;
|
||||
const semanticExtraction = semanticExtractionContract?.extraction &&
|
||||
typeof semanticExtractionContract.extraction === "object"
|
||||
? semanticExtractionContract.extraction
|
||||
: null;
|
||||
const semanticDeepInvestigationHintDetected = semanticGuardHints?.deep_investigation_signal_detected === true;
|
||||
const semanticAggregateShapeDetected = semanticExtraction?.query_shape === "AGGREGATE_LOOKUP" ||
|
||||
semanticExtraction?.aggregation_profile === "management_profile";
|
||||
const rootContextOnlyFollowup = Boolean(followupContext && followupContext.root_context_only === true);
|
||||
const followupSemanticOverrideToDeepAllowed = Boolean(followupContext &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
!semanticApplyCanonicalRecommended));
|
||||
const unsupportedIntentOrMode = (resolvedModeDetection.mode !== "address_query" && resolvedIntentResolution.intent === "unknown") ||
|
||||
llmContractMode === "unsupported" ||
|
||||
(rootContextOnlyFollowup &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown"));
|
||||
const unsupportedAddressIntentFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
unsupportedIntentOrMode &&
|
||||
strongDataSignal &&
|
||||
(rootContextOnlyFollowup ||
|
||||
llmContractMode === "deep_analysis" ||
|
||||
!dataRetrievalSignal ||
|
||||
strictDeepInvestigationCueDetected ||
|
||||
semanticDeepInvestigationHintDetected ||
|
||||
aggregateBusinessAnalyticsSignal) &&
|
||||
!preserveAddressLaneSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext || followupSemanticOverrideToDeepAllowed));
|
||||
const deepAnalysisPreferenceDetected = Boolean(hasDeepAnalysisPreferenceSignal(rawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedRawUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(effectiveAddressUserMessage) ||
|
||||
hasDeepAnalysisPreferenceSignal(repairedEffectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(rawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedRawUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(effectiveAddressUserMessage) ||
|
||||
hasDirectDeepAnalysisSignal(repairedEffectiveAddressUserMessage));
|
||||
const vatExplainFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:\u043f\u043e\u0447\u0435\u043c\u0443|why).*(?:\u043f\u0440\u043e\u0433\u043d\u043e\u0437|forecast).*(?:\u0443\u043f\u043b\u0430\u0442|payable|\b0\b)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const vatEvaluativeFollowupSignal = Boolean(followupContext &&
|
||||
toNonEmptyString(followupContext.previous_intent) === "vat_payable_forecast" &&
|
||||
/(?:^|\s)(?:это\s+)?много\s+или\s+мало(?:\?|$)|(?:^|\s)(?:это\s+)?нормально(?:\?|$)|(?:^|\s)(?:это\s+)?плохо(?:\?|$)|(?:^|\s)(?:это\s+)?хорошо(?:\?|$)/iu.test(compactWhitespace(`${repairedRawUserMessage} ${repairedEffectiveAddressUserMessage}`)));
|
||||
const deepAnalysisSignalFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
(deepAnalysisPreferenceDetected || semanticDeepInvestigationHintDetected) &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
!vatExplainFollowupSignal &&
|
||||
(!followupContext || !dataRetrievalSignal || followupSemanticOverrideToDeepAllowed));
|
||||
const aggregateAnalyticsFallbackToDeep = Boolean(baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
aggregateBusinessAnalyticsSignal &&
|
||||
!keepAddressLaneByIntent &&
|
||||
!supportedAddressIntentDetected &&
|
||||
(!followupContext ||
|
||||
llmContractMode === "unsupported" ||
|
||||
semanticAggregateShapeDetected ||
|
||||
!semanticApplyCanonicalRecommended ||
|
||||
standaloneAddressTopicSignal));
|
||||
const deepSessionContinuationFallbackToDeep = Boolean(!followupContext &&
|
||||
baseToolGate?.runAddressLane &&
|
||||
!llmRuntimeUnavailableDetected &&
|
||||
hasDeepSessionContinuationSignal({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "address_signal_unsupported_intent_fallback_to_deep";
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_analysis_signal_fallback_to_deep";
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "aggregate_analytics_signal_fallback_to_deep";
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "deep_session_continuation_fallback_to_deep";
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
runAddressLane = false;
|
||||
toolGateDecision = "skip_address_lane";
|
||||
toolGateReason = "meta_followup_over_grounded_answer";
|
||||
}
|
||||
let livingDecision = resolveLivingAssistantModeDecision({
|
||||
userMessage: rawUserMessage,
|
||||
addressLaneTriggered: runAddressLane,
|
||||
useMock,
|
||||
predecomposeMode: llmPreDecomposeMeta?.predecomposeContract?.mode ?? null,
|
||||
predecomposeModeConfidence: llmPreDecomposeMeta?.predecomposeContract?.mode_confidence ?? null
|
||||
});
|
||||
if (unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "unsupported_address_intent_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepAnalysisSignalFallbackToDeep && !unsupportedAddressIntentFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_analysis_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (aggregateAnalyticsFallbackToDeep &&
|
||||
!unsupportedAddressIntentFallbackToDeep &&
|
||||
!deepAnalysisSignalFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "aggregate_analytics_signal_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (deepSessionContinuationFallbackToDeep) {
|
||||
livingDecision = {
|
||||
mode: "deep_analysis",
|
||||
reason: "deep_session_continuation_fallback_to_deep"
|
||||
};
|
||||
}
|
||||
if (metaFollowupOverGroundedAnswer) {
|
||||
livingDecision = {
|
||||
mode: "chat",
|
||||
reason: "meta_followup_over_grounded_answer"
|
||||
};
|
||||
}
|
||||
return {
|
||||
runAddressLane,
|
||||
toolGateDecision,
|
||||
toolGateReason,
|
||||
livingMode: livingDecision.mode,
|
||||
livingReason: livingDecision.reason,
|
||||
orchestrationContract: {
|
||||
schema_version: "assistant_orchestration_contract_v1",
|
||||
hard_meta_mode: null,
|
||||
address_mode: resolvedModeDetection.mode,
|
||||
address_mode_confidence: resolvedModeDetection.confidence,
|
||||
address_intent: resolvedIntentResolution.intent,
|
||||
address_intent_confidence: resolvedIntentResolution.confidence,
|
||||
strong_data_signal_detected: strongDataSignal,
|
||||
data_retrieval_signal_detected: dataRetrievalSignal,
|
||||
semantic_contract_valid: semanticContractValid,
|
||||
semantic_apply_canonical_recommended: semanticApplyCanonicalRecommended,
|
||||
semantic_reason_codes: semanticReasonCodes,
|
||||
semantic_route_arbitration: {
|
||||
supported_address_intent_detected: supportedAddressIntentDetected,
|
||||
strict_deep_investigation_bypass_allowed: strictDeepInvestigationBypassAllowed,
|
||||
semantic_deep_investigation_hint_detected: semanticDeepInvestigationHintDetected,
|
||||
semantic_aggregate_shape_detected: semanticAggregateShapeDetected,
|
||||
followup_semantic_override_to_deep_allowed: followupSemanticOverrideToDeepAllowed
|
||||
},
|
||||
followup_context_detected: Boolean(followupContext),
|
||||
unsupported_address_intent_fallback_to_deep: unsupportedAddressIntentFallbackToDeep,
|
||||
deep_analysis_signal_fallback_to_deep: deepAnalysisSignalFallbackToDeep,
|
||||
aggregate_analytics_signal_fallback_to_deep: aggregateAnalyticsFallbackToDeep,
|
||||
deep_session_continuation_fallback_to_deep: deepSessionContinuationFallbackToDeep,
|
||||
final_decision: {
|
||||
run_address_lane: runAddressLane,
|
||||
tool_gate_decision: toolGateDecision,
|
||||
tool_gate_reason: toolGateReason,
|
||||
living_mode: livingDecision.mode,
|
||||
living_reason: livingDecision.reason
|
||||
}
|
||||
}
|
||||
};
|
||||
return assistantRoutePolicy.resolveAssistantOrchestrationDecision(input);
|
||||
}
|
||||
|
||||
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);
|
||||
return assistantLivingModePolicy.hasStrongDataIntentSignal(text);
|
||||
}
|
||||
function hasDataRetrievalRequestSignal(text) {
|
||||
const lower = compactWhitespace(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;
|
||||
return assistantLivingModePolicy.hasDataRetrievalRequestSignal(text);
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasOrganizationFactLookupSignal(text);
|
||||
}
|
||||
function findLastAssistantLivingChatDebug(items) {
|
||||
if (!Array.isArray(items)) {
|
||||
|
|
@ -4888,67 +4285,13 @@ function findLastOrganizationClarificationAddressDebug(items) {
|
|||
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));
|
||||
return assistantLivingModePolicy.hasMetaAnswerFollowupSignal(userMessage);
|
||||
}
|
||||
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));
|
||||
if (!hasMemoryCue || !hasDiscussionCue) {
|
||||
return false;
|
||||
}
|
||||
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
|
||||
hasDataRetrievalRequestSignal(sample) ||
|
||||
hasStrongDataIntentSignal(sample));
|
||||
return assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal(userMessage);
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal(text);
|
||||
}
|
||||
function isGroundedInventoryContextDebug(debug) {
|
||||
if (!debug || typeof debug !== "object") {
|
||||
|
|
@ -4965,56 +4308,10 @@ function isGroundedInventoryContextDebug(debug) {
|
|||
rootIntent === "inventory_on_hand_as_of_date";
|
||||
}
|
||||
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;
|
||||
return assistantLivingModePolicy.hasOrganizationFactFollowupSignal(userMessage, items);
|
||||
}
|
||||
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 ?? ""));
|
||||
return assistantLivingModePolicy.shouldEmitOrganizationSelectionReply(userMessage, selectedOrganization);
|
||||
}
|
||||
function hasOperationalAdminActionRequestSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase()).replace(/ё/g, "е");
|
||||
|
|
@ -5090,78 +4387,13 @@ function hasAssistantCapabilityQuestionSignal(text) {
|
|||
return false;
|
||||
}
|
||||
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);
|
||||
return assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal(text);
|
||||
}
|
||||
function shouldHandleAsAssistantCapabilityMetaQuery(text) {
|
||||
const raw = String(text ?? "");
|
||||
const repaired = repairAddressMojibake(raw);
|
||||
const hasScopeMetaSignal = hasAssistantDataScopeMetaQuestionSignal(raw) || hasAssistantDataScopeMetaQuestionSignal(repaired);
|
||||
if (hasScopeMetaSignal) {
|
||||
return true;
|
||||
}
|
||||
const hasCapabilitySignal = hasAssistantCapabilityQuestionSignal(raw) ||
|
||||
hasAssistantCapabilityQuestionSignal(repaired) ||
|
||||
hasOperationalAdminActionRequestSignal(raw) ||
|
||||
hasOperationalAdminActionRequestSignal(repaired);
|
||||
const hasRetrievalSignal = hasDataRetrievalRequestSignal(raw) || hasDataRetrievalRequestSignal(repaired);
|
||||
return hasCapabilitySignal && !hasRetrievalSignal;
|
||||
return assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery(text);
|
||||
}
|
||||
function hasLivingChatSignal(text) {
|
||||
const lower = compactWhitespace(String(text ?? "").toLowerCase());
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
if (/^(?:а\s+)?(?:тут|здесь|там|сюда|туда)[\s!?.,:;\-]*$/iu.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(ага|угу|ок|окей|ясно|понял|поняла|принято|спасибо|благодарю|супер|класс|норм|го|давай|погнали|привет|хай|йо|yo|че\s+там|ч[её]\s+как|че\s+как|hello|hi|thanks?)$/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
if (/(как дела|как ты|что нового|расскажи о себе|чем можешь помочь|давай поговорим|поговорим|обсудим|посоветуй|что думаешь)/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
return hasSmallTalkSignal(lower);
|
||||
return assistantLivingModePolicy.hasLivingChatSignal(text);
|
||||
}
|
||||
function buildAssistantCapabilityContractReply() {
|
||||
return (0, capabilitiesRegistry_1.buildCapabilityContractReplyFromRegistry)();
|
||||
|
|
@ -5265,6 +4497,55 @@ function normalizeOrganizationScopeValue(value) {
|
|||
.trim();
|
||||
return unwrapped ? unwrapped : null;
|
||||
}
|
||||
const assistantLivingModePolicy = (0, assistantLivingModePolicy_1.createAssistantLivingModePolicy)({
|
||||
featureAssistantLivingChatRouterV1: config_1.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1,
|
||||
compactWhitespace,
|
||||
repairAddressMojibake,
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
hasReferentialPointer,
|
||||
hasSmallTalkSignal,
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
hasOperationalAdminActionRequestSignal
|
||||
});
|
||||
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
|
||||
repairAddressMojibake,
|
||||
findLastGroundedAddressAnswerDebug,
|
||||
findLastOrganizationClarificationAddressDebug,
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDataRetrievalRequestSignal: assistantLivingModePolicy.hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
hasOpenContractsAddressSignal,
|
||||
detectAddressQuestionMode: addressQueryClassifier_1.detectAddressQuestionMode,
|
||||
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
|
||||
toNonEmptyString,
|
||||
hasStrictDeepInvestigationCue,
|
||||
hasStrongDataIntentSignal: assistantLivingModePolicy.hasStrongDataIntentSignal,
|
||||
hasAccountingSignal,
|
||||
hasDangerOrCoercionSignal,
|
||||
hasAddressFollowupContextSignal,
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
hasDeepAnalysisPreferenceSignal,
|
||||
hasDirectDeepAnalysisSignal,
|
||||
compactWhitespace,
|
||||
hasDeepSessionContinuationSignal,
|
||||
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision
|
||||
});
|
||||
function normalizeOrganizationScopeSearchText(value) {
|
||||
const source = normalizeScopeKey(value);
|
||||
return source
|
||||
|
|
@ -6063,67 +5344,7 @@ function applyLivingChatGroundingGuard(input) {
|
|||
};
|
||||
}
|
||||
export function resolveLivingAssistantModeDecision(input) {
|
||||
const userMessage = String(input?.userMessage ?? "");
|
||||
if (input?.addressLaneTriggered) {
|
||||
return {
|
||||
mode: "address_data",
|
||||
reason: "address_lane_triggered"
|
||||
};
|
||||
}
|
||||
if (!config_1.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
reason: "living_chat_router_disabled"
|
||||
};
|
||||
}
|
||||
if (Boolean(input?.useMock)) {
|
||||
return {
|
||||
mode: "deep_analysis",
|
||||
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 assistantLivingModePolicy.resolveLivingAssistantModeDecision(input);
|
||||
}
|
||||
export class AssistantService {
|
||||
normalizerService;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { createAssistantLivingModePolicy } from "../src/services/assistantLivingModePolicy";
|
||||
|
||||
function buildPolicy() {
|
||||
return createAssistantLivingModePolicy({
|
||||
featureAssistantLivingChatRouterV1: true,
|
||||
compactWhitespace: (text: string) => text.replace(/\s+/g, " ").trim(),
|
||||
repairAddressMojibake: (text: string) => text,
|
||||
toNonEmptyString: (value: unknown) => {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
const text = String(value).trim();
|
||||
return text.length > 0 ? text : null;
|
||||
},
|
||||
normalizeOrganizationScopeValue: (value: unknown) => {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
const text = String(value).trim().replace(/^"+|"+$/g, "").replace(/^'+|'+$/g, "");
|
||||
return text.length > 0 ? text : null;
|
||||
},
|
||||
hasReferentialPointer: (text: string) =>
|
||||
/(по этому|по тому|это же|этой|этим|этому|этого|этот|эту|этом|это|эти|этих|из этого|из них|из этих|из тех|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(
|
||||
text.toLowerCase()
|
||||
),
|
||||
hasSmallTalkSignal: (text: string) => /(привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase()),
|
||||
hasAssistantCapabilityQuestionSignal: (text: string) =>
|
||||
/(?:кто ты|что ты можешь|какие фичи|полный список возможностей|чем ты можешь помочь|что ты умеешь)/i.test(text),
|
||||
hasOperationalAdminActionRequestSignal: (text: string) =>
|
||||
/(?:настро|установ|подключ|обнов|почин|исправ|удал|снеси|delete\s+database|drop\s+database)/i.test(text)
|
||||
});
|
||||
}
|
||||
|
||||
describe("assistantLivingModePolicy", () => {
|
||||
it("routes data-scope question to chat mode", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const decision = policy.resolveLivingAssistantModeDecision({
|
||||
userMessage: "по какой компании мы можем работать?",
|
||||
addressLaneTriggered: false,
|
||||
useMock: false,
|
||||
predecomposeMode: "unsupported",
|
||||
predecomposeModeConfidence: "low"
|
||||
});
|
||||
|
||||
expect(decision.mode).toBe("chat");
|
||||
expect(decision.reason).toBe("assistant_data_scope_query_detected");
|
||||
});
|
||||
|
||||
it("keeps explicit accounting question in deep mode", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const decision = policy.resolveLivingAssistantModeDecision({
|
||||
userMessage: "покажи документы по сверке за 2020",
|
||||
addressLaneTriggered: false,
|
||||
useMock: false,
|
||||
predecomposeMode: "unsupported",
|
||||
predecomposeModeConfidence: "low"
|
||||
});
|
||||
|
||||
expect(decision.mode).toBe("deep_analysis");
|
||||
expect(decision.reason).toBe("strong_data_signal_detected");
|
||||
});
|
||||
|
||||
it("detects organization fact follow-up after prior boundary reply", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const detected = policy.hasOrganizationFactFollowupSignal("давай", [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
living_chat_response_source: "deterministic_organization_fact_boundary",
|
||||
living_chat_grounding_guard_reason: null
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
expect(detected).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { createAssistantRoutePolicy } from "../src/services/assistantRoutePolicy";
|
||||
|
||||
function toNonEmptyString(value: unknown): string | null {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
const text = String(value).trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
|
||||
function normalizeOrganizationScopeValue(value: unknown): string | null {
|
||||
const text = toNonEmptyString(value);
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
return text.replace(/^"+|"+$/g, "").replace(/^'+|'+$/g, "");
|
||||
}
|
||||
|
||||
function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
return createAssistantRoutePolicy({
|
||||
repairAddressMojibake: (text: string) => text,
|
||||
findLastGroundedAddressAnswerDebug: () => null,
|
||||
findLastOrganizationClarificationAddressDebug: () => null,
|
||||
mergeKnownOrganizations: (values: unknown[]) =>
|
||||
Array.from(
|
||||
new Set(
|
||||
(Array.isArray(values) ? values : [])
|
||||
.map((item) => normalizeOrganizationScopeValue(item))
|
||||
.filter((item): item is string => Boolean(item))
|
||||
)
|
||||
),
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage: () => null,
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: string) => /по какой компании|какая база|по каким конторам/i.test(text),
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: string) => /что ты можешь|что ты умеешь/i.test(text),
|
||||
hasDataRetrievalRequestSignal: () => false,
|
||||
hasAggregateBusinessAnalyticsSignal: () => false,
|
||||
hasStandaloneAddressTopicSignal: () => false,
|
||||
hasOpenContractsAddressSignal: () => false,
|
||||
detectAddressQuestionMode: () => ({ mode: "unsupported", confidence: "low" }),
|
||||
resolveAddressIntent: () => ({ intent: "unknown", confidence: "low" }),
|
||||
toNonEmptyString,
|
||||
hasStrictDeepInvestigationCue: () => false,
|
||||
hasStrongDataIntentSignal: () => false,
|
||||
hasAccountingSignal: () => false,
|
||||
hasDangerOrCoercionSignal: () => false,
|
||||
hasAddressFollowupContextSignal: () => false,
|
||||
hasShortDebtMirrorFollowupSignal: () => false,
|
||||
isInventorySelectedObjectIntent: (intent: unknown) => /inventory/i.test(String(intent ?? "")),
|
||||
hasShortInventoryObjectFollowupSignal: () => false,
|
||||
hasHistoricalCapabilityFollowupSignal: () => false,
|
||||
isGroundedInventoryContextDebug: (debug: unknown) => Boolean(debug),
|
||||
hasConversationMemoryRecallFollowupSignal: () => false,
|
||||
findLastAddressAssistantItem: () => null,
|
||||
hasMetaAnswerFollowupSignal: () => false,
|
||||
resolveAddressToolGateDecision: () => ({
|
||||
runAddressLane: false,
|
||||
decision: "skip_address_lane",
|
||||
reason: "no_address_signal_after_l0"
|
||||
}),
|
||||
hasSameDateAccountFollowupSignalForPredecompose: () => false,
|
||||
hasLooseAllTimeAddressLookupSignal: () => false,
|
||||
hasDeepAnalysisPreferenceSignal: () => false,
|
||||
hasDirectDeepAnalysisSignal: () => false,
|
||||
compactWhitespace: (text: string) => String(text ?? "").replace(/\s+/g, " ").trim(),
|
||||
hasDeepSessionContinuationSignal: () => false,
|
||||
resolveLivingAssistantModeDecision: (input: { addressLaneTriggered?: boolean }) =>
|
||||
input.addressLaneTriggered
|
||||
? { mode: "address_data", reason: "address_lane_triggered" }
|
||||
: { mode: "chat", reason: "living_chat_signal_detected" },
|
||||
...overrides
|
||||
});
|
||||
}
|
||||
|
||||
describe("assistantRoutePolicy", () => {
|
||||
it("routes data-scope meta question to chat contract", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "по какой компании мы можем работать?",
|
||||
effectiveAddressUserMessage: "по какой компании мы можем работать?",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateReason).toBe("assistant_data_scope_query_detected");
|
||||
expect(decision.livingMode).toBe("chat");
|
||||
expect(decision.orchestrationContract?.hard_meta_mode).toBe("data_scope");
|
||||
});
|
||||
|
||||
it("keeps supported address intent in address lane", () => {
|
||||
const policy = buildPolicy({
|
||||
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
|
||||
resolveAddressIntent: () => ({ intent: "inventory_on_hand_as_of_date", confidence: "high" }),
|
||||
resolveAddressToolGateDecision: () => ({
|
||||
runAddressLane: true,
|
||||
decision: "run_address_lane",
|
||||
reason: "address_mode_classifier_detected"
|
||||
})
|
||||
});
|
||||
|
||||
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "какие товары сейчас лежат на складе",
|
||||
effectiveAddressUserMessage: "какие товары сейчас лежат на складе",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: null,
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(true);
|
||||
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
|
||||
expect(decision.livingMode).toBe("address_data");
|
||||
expect(decision.orchestrationContract?.semantic_route_arbitration?.supported_address_intent_detected).toBe(true);
|
||||
});
|
||||
|
||||
it("routes memory recap follow-up over grounded answer to chat", () => {
|
||||
const policy = buildPolicy({
|
||||
hasConversationMemoryRecallFollowupSignal: () => true,
|
||||
findLastGroundedAddressAnswerDebug: () => ({ execution_lane: "address_query" })
|
||||
});
|
||||
|
||||
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||
rawUserMessage: "а ты помнишь что мы обсуждали?",
|
||||
effectiveAddressUserMessage: "а ты помнишь что мы обсуждали?",
|
||||
followupContext: null,
|
||||
llmPreDecomposeMeta: {
|
||||
applied: false,
|
||||
reason: "normalized_fragment_rejected_semantic_guard",
|
||||
predecomposeContract: {
|
||||
mode: "unsupported",
|
||||
mode_confidence: "low",
|
||||
intent: "unknown",
|
||||
intent_confidence: "low"
|
||||
}
|
||||
},
|
||||
useMock: false
|
||||
});
|
||||
|
||||
expect(decision.runAddressLane).toBe(false);
|
||||
expect(decision.toolGateReason).toBe("memory_recap_followup_detected");
|
||||
expect(decision.livingMode).toBe("chat");
|
||||
expect(decision.livingReason).toBe("memory_recap_followup_detected");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue