ЮИ - Перенести настройки ассистента в управление автопрогонами и убрать верхний режим Ассистент
This commit is contained in:
@@ -1189,6 +1189,12 @@ function isTemporalWarehousePhrase(candidate: string): boolean {
|
||||
.toLowerCase()
|
||||
.replace(/ё/g, "е")
|
||||
.trim();
|
||||
if (/^(?:на\s+)?(?:эту|ту|текущ(?:ую|ая|ий|ее|ей)|сегодняшн(?:юю|ая|ий|ее|ей))(?:\s+же)?\s+дат(?:у|а|е|ой)$/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (/^(?:нет|не\s+было|не\s+было\s+ли)(?:\s+на\s+(?:эту|ту|такую)\s+дат(?:у|е|ой))?$/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
return /^(?:в|на)\s+(?:январ(?:е|ь)|феврал(?:е|ь)|март(?:е)?|апрел(?:е|ь)|ма(?:й|е)|июн(?:е|ь)|июл(?:е|ь)|август(?:е)?|сентябр(?:е|ь)|октябр(?:е|ь)|ноябр(?:е|ь)|декабр(?:е|ь))(?:\s+\d{4}(?:\s+г(?:\.|ода)?)?)?$/iu.test(
|
||||
normalized
|
||||
);
|
||||
@@ -1244,7 +1250,8 @@ function isLowQualityWarehouseAnchorValue(rawValue: string): boolean {
|
||||
"охуеть",
|
||||
"пиздец",
|
||||
"блять",
|
||||
"бля"
|
||||
"бля",
|
||||
"нет"
|
||||
]);
|
||||
const tokens = value
|
||||
.split(/[^a-zа-я0-9]+/iu)
|
||||
@@ -1279,7 +1286,9 @@ function hasImplicitSelfScopeSignal(text: string): boolean {
|
||||
|
||||
function isImplicitSelfScopeWarehouseAnchor(candidate: string): boolean {
|
||||
const normalized = normalizeSemanticAnchorCandidate(candidate);
|
||||
return /^(?:у\s+нас|у\s+себя|у\s+меня|наш(?:ем|ей|его|их|а|е)?|сво(?:ем|ей|его|их|я|е)?)$/iu.test(normalized);
|
||||
return /^(?:у\s+нас|у\s+себя|у\s+меня|наш(?:ем|ей|его|их|а|е)?|сво(?:ем|ей|его|их|я|е)?)(?:\s+(?:висит|висят|висело|висели|лежит|лежат|лежало|лежали))?$/iu.test(
|
||||
normalized
|
||||
);
|
||||
}
|
||||
|
||||
function hasSelectedObjectScopeSignal(text: string): boolean {
|
||||
|
||||
@@ -1286,11 +1286,23 @@ function toNormalizedRows(rows: Array<Record<string, unknown>>): NormalizedAddre
|
||||
}
|
||||
|
||||
function rowSearchableText(row: NormalizedAddressRow): string {
|
||||
return [row.registrator, row.item ?? "", row.warehouse ?? "", row.account_dt ?? "", row.account_kt ?? "", ...row.analytics]
|
||||
return [
|
||||
row.registrator,
|
||||
row.item ?? "",
|
||||
row.warehouse ?? "",
|
||||
row.organization ?? "",
|
||||
row.account_dt ?? "",
|
||||
row.account_kt ?? "",
|
||||
...row.analytics
|
||||
]
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function rowOrganizationSearchableText(row: NormalizedAddressRow): string {
|
||||
return [row.organization ?? "", row.registrator, ...row.analytics].join(" ").toLowerCase();
|
||||
}
|
||||
|
||||
function rowMatchesAnyAccount(row: NormalizedAddressRow, accountScope: string[]): boolean {
|
||||
if (accountScope.length === 0) {
|
||||
return true;
|
||||
@@ -1366,9 +1378,12 @@ function applyAddressFilters(rows: NormalizedAddressRow[], filters: AddressFilte
|
||||
if (filters.organization && String(filters.organization).trim()) {
|
||||
const needle = String(filters.organization);
|
||||
const before = filtered.length;
|
||||
filtered = filtered.filter((row) => matchesAnchorText(rowSearchableText(row), needle));
|
||||
if (before > 0 && filtered.length === 0 && mismatchReason === null) {
|
||||
mismatchReason = "organization_anchor_not_matched_in_materialized_rows";
|
||||
const organizationMaterialized = filtered.some((row) => Boolean(String(row.organization ?? "").trim()));
|
||||
if (organizationMaterialized) {
|
||||
filtered = filtered.filter((row) => matchesAnchorText(rowOrganizationSearchableText(row), needle));
|
||||
if (before > 0 && filtered.length === 0 && mismatchReason === null) {
|
||||
mismatchReason = "organization_anchor_not_matched_in_materialized_rows";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1181,9 +1181,30 @@ function buildInventoryMovementQuery(
|
||||
: side === "kt"
|
||||
? creditPredicate
|
||||
: `(${debitPredicate} ИЛИ ${creditPredicate})`;
|
||||
const itemFieldPaths =
|
||||
side === "dt"
|
||||
? ["Движения.СубконтоДт1", "Движения.СубконтоДт2", "Движения.СубконтоДт3"]
|
||||
: side === "kt"
|
||||
? ["Движения.СубконтоКт1", "Движения.СубконтоКт2", "Движения.СубконтоКт3"]
|
||||
: [
|
||||
"Движения.СубконтоДт1",
|
||||
"Движения.СубконтоДт2",
|
||||
"Движения.СубконтоДт3",
|
||||
"Движения.СубконтоКт1",
|
||||
"Движения.СубконтоКт2",
|
||||
"Движения.СубконтоКт3"
|
||||
];
|
||||
const itemCondition = buildInventoryItemReferenceCondition(filters, itemFieldPaths);
|
||||
return INVENTORY_MOVEMENTS_QUERY_TEMPLATE
|
||||
.replace("__LIMIT__", String(resolvedLimit))
|
||||
.replace("__WHERE_CLAUSE__", buildWhereClause(filters, "Движения.Период", [inventoryCondition]))
|
||||
.replace(
|
||||
"__WHERE_CLAUSE__",
|
||||
buildWhereClause(
|
||||
filters,
|
||||
"Движения.Период",
|
||||
[inventoryCondition, itemCondition].filter((item): item is string => Boolean(item))
|
||||
)
|
||||
)
|
||||
.replaceAll("__ORDER_DIRECTION__", resolveOrderDirection(filters.sort));
|
||||
}
|
||||
|
||||
@@ -1469,7 +1490,7 @@ export function buildAddressRecipePlan(
|
||||
: recipe.query_template === "inventory_supplier_stock_overlap_profile"
|
||||
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
|
||||
: recipe.query_template === "inventory_sale_trace_profile"
|
||||
? buildInventorySaleDocumentQuery(filters, resolvedLimit)
|
||||
? buildInventoryMovementQuery(filters, resolvedLimit, "kt")
|
||||
: recipe.query_template === "inventory_purchase_to_sale_chain_profile"
|
||||
? buildInventoryMovementQuery(filters, resolvedLimit, "either")
|
||||
: recipe.query_template === "inventory_aging_by_purchase_date_profile"
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface AddressFollowupContext {
|
||||
| "unknown"
|
||||
| null;
|
||||
root_anchor_value?: string | null;
|
||||
root_context_only?: boolean;
|
||||
current_frame_kind?: "generic" | "inventory_root" | "inventory_drilldown";
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ function hasAllTimeHint(text: string): boolean {
|
||||
}
|
||||
|
||||
function hasSameDateHint(text: string): boolean {
|
||||
return /(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|на\s+эту\s+дат[ауеы]|эту\s+дат[ауеы]|та\s+же\s+дата|same\s+date|as\s+of\s+same\s+date|the\s+same\s+date)/iu.test(
|
||||
return /(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|на\s+эту\s+дат[ауеы]|эту\s+дат[ауеы]|та\s+же\s+дата|дат[ауеы],?\s+котор(?:ую|ая)\s+(?:до\s+этого|раньше|ранее)\s+(?:рассматривали|смотрели)|дат[ауеы],?\s+которая\s+был[ао]?\s+ранее\s+рассмотрен[ао]?|same\s+date|as\s+of\s+same\s+date|the\s+same\s+date|date\s+we\s+looked\s+at\s+before|previously\s+considered\s+date)/iu.test(
|
||||
String(text ?? "")
|
||||
);
|
||||
}
|
||||
@@ -487,6 +488,41 @@ function resolveRelativeMonthPeriodFromInventoryRoot(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveRelativeMonthPeriodFromFollowupYear(
|
||||
userMessage: string,
|
||||
followupContext: AddressFollowupContext | null
|
||||
): { period_from: string; period_to: string; as_of_date: string } | null {
|
||||
if (!followupContext) {
|
||||
return null;
|
||||
}
|
||||
const month = resolveMonthNumberFromText(userMessage);
|
||||
if (!month) {
|
||||
return null;
|
||||
}
|
||||
const normalized = String(userMessage ?? "");
|
||||
if (hasExplicitPeriodLiteral(normalized) || hasExplicitCurrentDateHint(normalized) || hasSameDateHint(normalized)) {
|
||||
return null;
|
||||
}
|
||||
const shortTemporalPatch = getTokenCount(normalized) <= 8 || hasRelativeYearHint(normalized);
|
||||
if (!shortTemporalPatch) {
|
||||
return null;
|
||||
}
|
||||
const year =
|
||||
resolveYearFromFilters(followupContext.previous_filters) ??
|
||||
resolveYearFromFilters(followupContext.root_filters);
|
||||
if (!year) {
|
||||
return null;
|
||||
}
|
||||
const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate();
|
||||
const periodFrom = `${year}-${String(month).padStart(2, "0")}-01`;
|
||||
const periodTo = `${year}-${String(month).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`;
|
||||
return {
|
||||
period_from: periodFrom,
|
||||
period_to: periodTo,
|
||||
as_of_date: periodTo
|
||||
};
|
||||
}
|
||||
|
||||
function shouldRestoreInventoryRootFrame(
|
||||
userMessage: string,
|
||||
intent: AddressIntent,
|
||||
@@ -498,6 +534,7 @@ function shouldRestoreInventoryRootFrame(
|
||||
}
|
||||
const currentFrameKind = followupContext.current_frame_kind ?? null;
|
||||
const previousIntent = followupContext.previous_intent;
|
||||
const rootContextOnly = followupContext.root_context_only === true;
|
||||
const comingFromInventoryDrilldown =
|
||||
currentFrameKind === "inventory_drilldown" || isInventoryDrilldownFrameIntent(previousIntent);
|
||||
const normalized = String(userMessage ?? "");
|
||||
@@ -508,6 +545,7 @@ function shouldRestoreInventoryRootFrame(
|
||||
);
|
||||
const canReenterInventoryRoot =
|
||||
comingFromInventoryDrilldown ||
|
||||
rootContextOnly ||
|
||||
(currentFrameKind === "inventory_root" && hasSamePeriodHint(normalized)) ||
|
||||
(currentFrameKind === "generic" && hasInventoryRootRestatementCue && hasSamePeriodHint(normalized));
|
||||
if (!canReenterInventoryRoot) {
|
||||
@@ -517,7 +555,7 @@ function shouldRestoreInventoryRootFrame(
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
hasSelectedObjectInventorySignal(normalized) ||
|
||||
(hasSelectedObjectInventorySignal(normalized) && !hasInventoryRootRestatementCue) ||
|
||||
hasInventorySupplierFollowupCue(normalized) ||
|
||||
hasInventoryPurchaseDocumentsFollowupCue(normalized) ||
|
||||
hasInventoryPurchaseDateFollowupCue(normalized) ||
|
||||
@@ -533,6 +571,7 @@ function shouldRestoreInventoryRootFrame(
|
||||
const hasTemporalPatch =
|
||||
hasExplicitPeriodWindow(extractedFilters) ||
|
||||
Boolean(toNonEmptyString(extractedFilters.as_of_date)) ||
|
||||
hasSameDateHint(normalized) ||
|
||||
hasSamePeriodHint(normalized) ||
|
||||
hasExplicitPeriodLiteral(normalized) ||
|
||||
Boolean(resolveRelativeMonthPeriodFromInventoryRoot(normalized, followupContext));
|
||||
@@ -709,9 +748,11 @@ function mergeFollowupFilters(
|
||||
const previousPeriodFrom = toNonEmptyString(previous.period_from);
|
||||
const previousPeriodTo = toNonEmptyString(previous.period_to);
|
||||
const relativeMonthFromInventoryRoot = resolveRelativeMonthPeriodFromInventoryRoot(userMessage, followupContext);
|
||||
const relativeMonthFromFollowupYear = resolveRelativeMonthPeriodFromFollowupYear(userMessage, followupContext);
|
||||
const allTimeRequested = hasAllTimeHint(userMessage);
|
||||
const sameDateRequested = hasSameDateHint(userMessage);
|
||||
const samePeriodRequested = hasSamePeriodHint(userMessage);
|
||||
const explicitQuotedItem = extractSelectedObjectItemFromFollowupText(userMessage);
|
||||
if (!toNonEmptyString(merged.organization) && previousOrganization) {
|
||||
merged.organization = previousOrganization;
|
||||
reasons.push("organization_from_followup_context");
|
||||
@@ -837,6 +878,17 @@ function mergeFollowupFilters(
|
||||
merged.counterparty = inheritedCounterparty;
|
||||
reasons.push(currentCounterparty ? "counterparty_replaced_from_followup_context" : "counterparty_from_followup_context");
|
||||
}
|
||||
if (intent === "inventory_on_hand_as_of_date" && explicitQuotedItem) {
|
||||
const currentItem = toNonEmptyString(merged.item);
|
||||
if (
|
||||
!currentItem ||
|
||||
currentItem !== explicitQuotedItem ||
|
||||
isInventoryItemAnchorDegradation(explicitQuotedItem, currentItem)
|
||||
) {
|
||||
merged.item = explicitQuotedItem;
|
||||
reasons.push(currentItem ? "item_replaced_from_explicit_quote" : "item_from_explicit_quote");
|
||||
}
|
||||
}
|
||||
if (
|
||||
(intent === "inventory_purchase_provenance_for_item" ||
|
||||
intent === "inventory_purchase_documents_for_item" ||
|
||||
@@ -846,7 +898,6 @@ function mergeFollowupFilters(
|
||||
intent === "inventory_aging_by_purchase_date")
|
||||
) {
|
||||
const inheritedItem = previousItem ?? previousAnchorItem;
|
||||
const explicitQuotedItem = extractSelectedObjectItemFromFollowupText(userMessage);
|
||||
const currentItem = toNonEmptyString(merged.item);
|
||||
const shouldAdoptExplicitQuotedItem =
|
||||
Boolean(explicitQuotedItem) &&
|
||||
@@ -983,6 +1034,7 @@ function mergeFollowupFilters(
|
||||
if (
|
||||
!sameDateRequested &&
|
||||
hasFollowupSignalForConfirmed &&
|
||||
!isInventoryLifecycleHistoryIntent(intent) &&
|
||||
!hasExplicitPeriodLiteral(userMessage) &&
|
||||
!hasExplicitCurrentDateHint(userMessage)
|
||||
) {
|
||||
@@ -1040,12 +1092,29 @@ function mergeFollowupFilters(
|
||||
intent === "vat_payable_confirmed_as_of_date";
|
||||
const currentHasPeriod = hasExplicitPeriodWindow(merged);
|
||||
const previousHasPeriod = hasExplicitPeriodWindow(previous);
|
||||
const vatRelativeMonthFollowup =
|
||||
relativeMonthFromFollowupYear &&
|
||||
(intent === "vat_payable_confirmed_as_of_date" ||
|
||||
intent === "vat_payable_forecast" ||
|
||||
intent === "vat_liability_confirmed_for_tax_period");
|
||||
|
||||
if (vatRelativeMonthFollowup) {
|
||||
merged.period_from = relativeMonthFromFollowupYear.period_from;
|
||||
merged.period_to = relativeMonthFromFollowupYear.period_to;
|
||||
if (intent === "vat_payable_confirmed_as_of_date") {
|
||||
merged.as_of_date = relativeMonthFromFollowupYear.as_of_date;
|
||||
} else if (toNonEmptyString(merged.as_of_date)) {
|
||||
delete merged.as_of_date;
|
||||
}
|
||||
reasons.push("period_derived_from_followup_context_year");
|
||||
}
|
||||
|
||||
if (
|
||||
(intent === "vat_payable_forecast" || intent === "vat_liability_confirmed_for_tax_period") &&
|
||||
previousHasPeriod &&
|
||||
hasFollowupSignal &&
|
||||
!hasExplicitPeriodInMessage
|
||||
!hasExplicitPeriodInMessage &&
|
||||
!vatRelativeMonthFollowup
|
||||
) {
|
||||
const currentPeriodFrom = toNonEmptyString(merged.period_from);
|
||||
const currentPeriodTo = toNonEmptyString(merged.period_to);
|
||||
@@ -1067,7 +1136,8 @@ function mergeFollowupFilters(
|
||||
previousHasPeriod &&
|
||||
hasFollowupSignal &&
|
||||
!hasExplicitPeriodInMessage &&
|
||||
!inventoryLifecycleHistoryIntent
|
||||
!inventoryLifecycleHistoryIntent &&
|
||||
!vatRelativeMonthFollowup
|
||||
) {
|
||||
if (previousPeriodFrom) {
|
||||
merged.period_from = previousPeriodFrom;
|
||||
|
||||
@@ -77,6 +77,24 @@ function hasSelectedObjectInventoryActionCue(text: string | null): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasShortInventoryPurchaseFollowupCue(text: string | null): boolean {
|
||||
return /(?:^|[\s,.;:!?])(а\s+)?(?:купили\s+у\s+кого|у\s+кого\s+купили|поставщик|продавец|seller)(?:[\s,.;:!?]|$)/iu.test(
|
||||
String(text ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
function isInventorySelectedObjectOrRootIntent(intent: string | null): boolean {
|
||||
return (
|
||||
intent === "inventory_on_hand_as_of_date" ||
|
||||
intent === "inventory_purchase_provenance_for_item" ||
|
||||
intent === "inventory_purchase_documents_for_item" ||
|
||||
intent === "inventory_sale_trace_for_item" ||
|
||||
intent === "inventory_profitability_for_item" ||
|
||||
intent === "inventory_purchase_to_sale_chain" ||
|
||||
intent === "inventory_aging_by_purchase_date"
|
||||
);
|
||||
}
|
||||
|
||||
function isGenericCanonicalDriftIntent(intent: string | null): boolean {
|
||||
return (
|
||||
intent === "open_items_by_counterparty_or_contract" ||
|
||||
@@ -89,6 +107,33 @@ function isGenericCanonicalDriftIntent(intent: string | null): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasSameDateFollowupSignal(text: string | null): boolean {
|
||||
return /(?:эту\s+же\s+дат(?:у|е|ой)|ту\s+же\s+дат(?:у|е|ой)|same\s+date)/iu.test(String(text ?? ""));
|
||||
}
|
||||
|
||||
function hasExplicitCurrentDateSignal(text: string | null): boolean {
|
||||
return /(?:текущ(?:ую|ая|ий|ее|ей)\s+дат(?:у|а|е|ой)|сегодняшн(?:юю|ий|ей)\s+дат(?:у|а|е|ой)|today|current\s+date)/iu.test(
|
||||
String(text ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
function hasInventoryTemporalRootFollowupCue(text: string | null): boolean {
|
||||
const value = String(text ?? "").trim().toLowerCase();
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
const tokenCount = value.split(/\s+/).filter(Boolean).length;
|
||||
const hasMonthYearCue =
|
||||
/(?:январ(?:ь|е)|феврал(?:ь|е)|март(?:е)?|апрел(?:ь|е)|ма(?:й|е)|июн(?:ь|е)|июл(?:ь|е)|август(?:е)?|сентябр(?:ь|е)|октябр(?:ь|е)|ноябр(?:ь|е)|декабр(?:ь|е))(?:\s+\d{4})?/iu.test(
|
||||
value
|
||||
) || /\b(?:19|20)\d{2}\b/u.test(value);
|
||||
if (tokenCount <= 3 && hasMonthYearCue) {
|
||||
return true;
|
||||
}
|
||||
const hasInventoryLexeme = /(?:остат|склад|товар|позици|номенклатур)/iu.test(value);
|
||||
return hasInventoryLexeme && (hasMonthYearCue || hasSameDateFollowupSignal(value));
|
||||
}
|
||||
|
||||
function shouldPreferRawFollowupMessage(
|
||||
userMessage: string,
|
||||
addressInputMessage: string,
|
||||
@@ -112,15 +157,39 @@ function shouldPreferRawFollowupMessage(
|
||||
: null;
|
||||
const mode = toNonEmptyString(predecomposeContract?.mode) ?? "unknown";
|
||||
const intent = toNonEmptyString(predecomposeContract?.intent) ?? "unknown";
|
||||
const followupContext =
|
||||
carryover.followupContext && typeof carryover.followupContext === "object"
|
||||
? (carryover.followupContext as Record<string, unknown>)
|
||||
: null;
|
||||
const previousIntent = toNonEmptyString(followupContext?.previous_intent);
|
||||
const rootIntent = toNonEmptyString(followupContext?.root_intent);
|
||||
const previousAnchorType = toNonEmptyString(followupContext?.previous_anchor_type);
|
||||
const hasInventoryItemCarryover =
|
||||
previousAnchorType === "item" && isInventorySelectedObjectOrRootIntent(previousIntent);
|
||||
const hasInventoryFrameCarryover =
|
||||
isInventorySelectedObjectOrRootIntent(previousIntent) ||
|
||||
isInventorySelectedObjectOrRootIntent(rootIntent);
|
||||
|
||||
if (mode === "unsupported" && intent === "unknown") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasSameDateFollowupSignal(rawMessage) && hasExplicitCurrentDateSignal(canonicalMessage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
hasInventoryFrameCarryover &&
|
||||
hasInventoryTemporalRootFollowupCue(rawMessage) &&
|
||||
(intent === "account_balance_snapshot" || intent === "documents_forming_balance" || intent === "unknown")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
hasSelectedObjectInventorySignal(rawMessage) &&
|
||||
hasSelectedObjectInventoryActionCue(rawMessage) &&
|
||||
isGenericCanonicalDriftIntent(intent)
|
||||
(hasSelectedObjectInventorySignal(rawMessage) || hasInventoryItemCarryover) &&
|
||||
(hasSelectedObjectInventoryActionCue(rawMessage) || hasShortInventoryPurchaseFollowupCue(rawMessage)) &&
|
||||
(isGenericCanonicalDriftIntent(intent) || intent === "unknown")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +193,16 @@ export function createAssistantRoutePolicy(deps) {
|
||||
hasShortDebtMirrorFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasShortDebtMirrorFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const followupPreviousIntent = toNonEmptyString(followupContext?.previous_intent);
|
||||
const followupPreviousFilters = followupContext?.previous_filters && typeof followupContext.previous_filters === "object"
|
||||
? followupContext.previous_filters
|
||||
: null;
|
||||
const protectedInventoryShortFollowup = Boolean(followupContext &&
|
||||
isInventorySelectedObjectIntent(toNonEmptyString(followupContext.previous_intent)) &&
|
||||
(isInventorySelectedObjectIntent(followupPreviousIntent) ||
|
||||
(followupPreviousIntent === "inventory_on_hand_as_of_date" &&
|
||||
(toNonEmptyString(followupContext.previous_anchor_type) === "item" ||
|
||||
toNonEmptyString(followupContext.previous_anchor_value) ||
|
||||
toNonEmptyString(followupPreviousFilters?.item)))) &&
|
||||
(hasShortInventoryObjectFollowupSignal(rawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||
|
||||
@@ -2757,8 +2757,11 @@ function hasShortInventoryObjectFollowupSignal(userMessage) {
|
||||
if (minTokens > 8) {
|
||||
return false;
|
||||
}
|
||||
const hasDirectPurchaseFollowupCue = (sample) => /(?:^|[\s,.;:!?])(?:а\s+)?(?:у|от)\s+кого(?:\s+\S+){0,5}\s+купил(?:и|о)?(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/(?:^|[\s,.;:!?])(?:а\s+)?купил(?:и|о)?(?:\s+\S+){0,5}\s+(?:у|от)\s+кого(?=$|[\s,.;:!?])/iu.test(sample);
|
||||
const hasDirectSaleFollowupCue = (sample) => /(?:кому|каму|куда)(?:\s+\S+){0,4}\s+(?:продали|продано|продан(?:о|а|ы)?|реализовали|реализован(?:о|а|ы)?)|(?:продали|продано|реализовали|реализован(?:о|а|ы)?)(?:\s+\S+){0,4}\s+(?:кому|каму|куда)|(?:^|\s)(?:продано|продали|реализовано|реализовали)(?=$|[\s,.;:!?])/iu.test(sample);
|
||||
return samples.some((sample) => /^(?:кто|когда|документы|сумма|поставщик|покупатель)(?:\?)?$/iu.test(sample) ||
|
||||
return samples.some((sample) => /^(?:кто|когда|документы|сумма|поставщик|покупатель|продавец|seller)(?:\?)?$/iu.test(sample) ||
|
||||
hasDirectPurchaseFollowupCue(sample) ||
|
||||
hasDirectSaleFollowupCue(sample) ||
|
||||
(0, decomposeStage_1.hasInventorySupplierFollowupCue)(sample) ||
|
||||
(0, decomposeStage_1.hasInventoryPurchaseDocumentsFollowupCue)(sample) ||
|
||||
@@ -2767,6 +2770,41 @@ function hasShortInventoryObjectFollowupSignal(userMessage) {
|
||||
(0, decomposeStage_1.hasInventorySaleFollowupCue)(sample) ||
|
||||
(0, decomposeStage_1.hasInventoryPurchaseToSaleChainFollowupCue)(sample));
|
||||
}
|
||||
function hasInventoryRootTemporalFollowupSignal(userMessage, sourceIntentHint, hasInventoryRootFrame) {
|
||||
if (!hasInventoryRootFrame) {
|
||||
return false;
|
||||
}
|
||||
if (!(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
isInventorySelectedObjectIntent(sourceIntentHint))) {
|
||||
return false;
|
||||
}
|
||||
const rawText = compactWhitespace(String(userMessage ?? "").toLowerCase());
|
||||
const repairedText = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||||
const samples = [rawText, repairedText].filter((item) => item.length > 0);
|
||||
if (samples.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const minTokens = samples.reduce((min, sample) => Math.min(min, countTokens(sample)), Number.POSITIVE_INFINITY);
|
||||
if (minTokens > 8) {
|
||||
return false;
|
||||
}
|
||||
const hasMonthYearCue = samples.some((sample) => /(?:январ|феврал|март|апрел|ма(?:й|е|я)|июн|июл|август|сентябр|октябр|ноябр|декабр)(?:\s+\d{4})?/iu.test(sample) ||
|
||||
/\b(?:19|20)\d{2}\b/u.test(sample));
|
||||
const hasTemporalCue = samples.some((sample) => hasPeriodLiteral(sample) ||
|
||||
hasShortNamedPeriodFollowupLiteral(sample) ||
|
||||
hasShortCurrentDateFollowupLiteral(sample) ||
|
||||
/(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|same\s+date|the\s+same\s+date)/iu.test(sample));
|
||||
if (!hasTemporalCue && !hasMonthYearCue) {
|
||||
return false;
|
||||
}
|
||||
if (samples.some((sample) => hasForeignAccountingPivotOverInventoryMessage(sample))) {
|
||||
return false;
|
||||
}
|
||||
const hasInventoryLexeme = samples.some((sample) => /(?:остат|склад|товар|номенклат|позиц)/iu.test(sample));
|
||||
const hasPlainInventoryLexeme = samples.some((sample) => /(?:остат|склад|товар|номенклатур|позиц)/iu.test(sample));
|
||||
return hasInventoryLexeme || hasPlainInventoryLexeme || minTokens <= 3;
|
||||
}
|
||||
function hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage = null) {
|
||||
const samples = [
|
||||
compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase()),
|
||||
@@ -2848,8 +2886,22 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
(isImplicitAddressContinuationByLlm(userMessage, llmPreDecomposeMeta) ||
|
||||
(toNonEmptyString(alternateMessage) ? isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta) : false));
|
||||
const sourceIntentHint = toNonEmptyString(previousAddressDebug?.detected_intent);
|
||||
const inventoryShortFollowupPrimary = isInventorySelectedObjectIntent(sourceIntentHint) && hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
const inventoryShortFollowupAlternate = isInventorySelectedObjectIntent(sourceIntentHint) && toNonEmptyString(alternateMessage)
|
||||
const navigationFocusObjectHint = addressNavigationState &&
|
||||
typeof addressNavigationState === "object" &&
|
||||
addressNavigationState.session_context &&
|
||||
typeof addressNavigationState.session_context === "object" &&
|
||||
addressNavigationState.session_context.active_focus_object &&
|
||||
typeof addressNavigationState.session_context.active_focus_object === "object"
|
||||
? addressNavigationState.session_context.active_focus_object
|
||||
: null;
|
||||
const hasNavigationInventoryItemFocusHint = Boolean(toNonEmptyString(navigationFocusObjectHint?.label) &&
|
||||
toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
isInventorySelectedObjectIntent(sourceIntentHint)));
|
||||
let inventoryShortFollowupPrimary = (isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
|
||||
hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
let inventoryShortFollowupAlternate = (isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) && toNonEmptyString(alternateMessage)
|
||||
? hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""))
|
||||
: false;
|
||||
const debtRoleSwapPrimary = sourceIntentHint ? resolveDebtRoleSwapFollowupIntent(userMessage, sourceIntentHint) : null;
|
||||
@@ -2857,8 +2909,8 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
? resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
||||
: null;
|
||||
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
||||
const hasPrimaryFollowupSignal = hasAddressFollowupContextSignal(userMessage) || Boolean(debtRoleSwapPrimary) || inventoryShortFollowupPrimary;
|
||||
const hasAlternateFollowupSignal = toNonEmptyString(alternateMessage)
|
||||
let hasPrimaryFollowupSignal = hasAddressFollowupContextSignal(userMessage) || Boolean(debtRoleSwapPrimary) || inventoryShortFollowupPrimary;
|
||||
let hasAlternateFollowupSignal = toNonEmptyString(alternateMessage)
|
||||
? hasAddressFollowupContextSignal(alternateMessage) || Boolean(debtRoleSwapAlternate) || inventoryShortFollowupAlternate
|
||||
: false;
|
||||
const hasPrimaryIndexReferenceSignal = extractDisplayedEntityIndexMention(userMessage) !== null;
|
||||
@@ -2866,12 +2918,19 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
? extractDisplayedEntityIndexMention(String(alternateMessage ?? "")) !== null
|
||||
: false;
|
||||
const hasIndexReferenceSignal = hasPrimaryIndexReferenceSignal || hasAlternateIndexReferenceSignal;
|
||||
const hasStrongFollowupReference = hasPrimaryIndexReferenceSignal ||
|
||||
const recentInventoryRootFrame = findRecentInventoryRootFrame(items);
|
||||
const hasInventoryRootTemporalFollowupPrimary = hasInventoryRootTemporalFollowupSignal(userMessage, sourceIntentHint, Boolean(recentInventoryRootFrame));
|
||||
const hasInventoryRootTemporalFollowupAlternate = toNonEmptyString(alternateMessage)
|
||||
? hasInventoryRootTemporalFollowupSignal(String(alternateMessage ?? ""), sourceIntentHint, Boolean(recentInventoryRootFrame))
|
||||
: false;
|
||||
let hasStrongFollowupReference = hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
hasFollowupMarker(userMessage) ||
|
||||
hasReferentialPointer(userMessage) ||
|
||||
@@ -2883,6 +2942,8 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
if (hasStandaloneAddressTopic &&
|
||||
!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal) {
|
||||
@@ -2890,6 +2951,8 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
}
|
||||
if (!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal) {
|
||||
@@ -2951,6 +3014,41 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
: null;
|
||||
const navigationFocusObjectType = toNonEmptyString(navigationFocusObject?.object_type);
|
||||
const navigationFocusObjectLabel = toNonEmptyString(navigationFocusObject?.label);
|
||||
const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
isInventorySelectedObjectIntent(sourceIntentHint));
|
||||
if (!inventoryShortFollowupPrimary && hasInventoryItemFocusCarryover) {
|
||||
inventoryShortFollowupPrimary = hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
}
|
||||
if (!inventoryShortFollowupAlternate && hasInventoryItemFocusCarryover && toNonEmptyString(alternateMessage)) {
|
||||
inventoryShortFollowupAlternate = hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""));
|
||||
}
|
||||
hasPrimaryFollowupSignal = hasAddressFollowupContextSignal(userMessage) ||
|
||||
Boolean(debtRoleSwapPrimary) ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupPrimary;
|
||||
hasAlternateFollowupSignal = toNonEmptyString(alternateMessage)
|
||||
? hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
Boolean(debtRoleSwapAlternate) ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupAlternate
|
||||
: false;
|
||||
hasStrongFollowupReference = hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
hasFollowupMarker(userMessage) ||
|
||||
hasReferentialPointer(userMessage) ||
|
||||
(toNonEmptyString(alternateMessage)
|
||||
? hasFollowupMarker(String(alternateMessage ?? "")) || hasReferentialPointer(String(alternateMessage ?? ""))
|
||||
: false);
|
||||
const hasSelectedObjectInventorySignalPrimary = /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(userMessage ?? ""));
|
||||
const hasSelectedObjectInventorySignalAlternate = toNonEmptyString(alternateMessage)
|
||||
? /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(alternateMessage ?? ""))
|
||||
@@ -3012,18 +3110,39 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
if (!toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) {
|
||||
previousFilters.organization = organizationClarificationSelection;
|
||||
}
|
||||
if (!toNonEmptyString(previousFilters.as_of_date) && toNonEmptyString(navigationDateScope?.as_of_date)) {
|
||||
const shouldBackfillPreviousDateScopeFromNavigation = sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
|
||||
sourceIntentHint === "inventory_sale_trace_for_item" ||
|
||||
sourceIntentHint === "inventory_profitability_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
|
||||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
|
||||
sourceIntentHint === "account_balance_snapshot" ||
|
||||
sourceIntentHint === "documents_forming_balance";
|
||||
if (shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!toNonEmptyString(previousFilters.as_of_date) &&
|
||||
toNonEmptyString(navigationDateScope?.as_of_date)) {
|
||||
previousFilters.as_of_date = toNonEmptyString(navigationDateScope?.as_of_date);
|
||||
}
|
||||
if (!toNonEmptyString(previousFilters.period_from) && toNonEmptyString(navigationDateScope?.period_from)) {
|
||||
if (shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!toNonEmptyString(previousFilters.period_from) &&
|
||||
toNonEmptyString(navigationDateScope?.period_from)) {
|
||||
previousFilters.period_from = toNonEmptyString(navigationDateScope?.period_from);
|
||||
}
|
||||
if (!toNonEmptyString(previousFilters.period_to) && toNonEmptyString(navigationDateScope?.period_to)) {
|
||||
if (shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!toNonEmptyString(previousFilters.period_to) &&
|
||||
toNonEmptyString(navigationDateScope?.period_to)) {
|
||||
previousFilters.period_to = toNonEmptyString(navigationDateScope?.period_to);
|
||||
}
|
||||
const rootContextOnlyPivot = Boolean((isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
if (rootContextOnlyPivot) {
|
||||
const inventoryRootTemporalPivot = Boolean(inventoryRootFrame &&
|
||||
(isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
(hasInventoryRootTemporalFollowupPrimary || hasInventoryRootTemporalFollowupAlternate) &&
|
||||
!hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
const rootScopedPivot = rootContextOnlyPivot || inventoryRootTemporalPivot;
|
||||
if (rootScopedPivot) {
|
||||
previousIntent = null;
|
||||
previousAnchorType = null;
|
||||
previousAnchor = null;
|
||||
@@ -3037,7 +3156,7 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
(toNonEmptyString(alternateMessage)
|
||||
? resolveDisplayedAddressEntityMention(String(alternateMessage ?? ""), displayedEntities)
|
||||
: null);
|
||||
if (resolvedEntityFromFollowup && !rootContextOnlyPivot) {
|
||||
if (resolvedEntityFromFollowup && !rootScopedPivot) {
|
||||
if (resolvedEntityFromFollowup.entityType === "counterparty") {
|
||||
previousFilters.counterparty = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "counterparty";
|
||||
@@ -3058,7 +3177,7 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
followupSelectionMode = "carry_referenced_entity";
|
||||
}
|
||||
}
|
||||
if (!rootContextOnlyPivot &&
|
||||
if (!rootScopedPivot &&
|
||||
!toNonEmptyString(previousFilters.item) &&
|
||||
navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
@@ -3100,7 +3219,7 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
previous_anchor_type: previousAnchorType ?? undefined,
|
||||
previous_anchor_value: previousAnchor,
|
||||
resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined,
|
||||
root_context_only: rootContextOnlyPivot || undefined,
|
||||
root_context_only: rootScopedPivot || undefined,
|
||||
root_intent: inventoryRootFrame?.intent ?? undefined,
|
||||
root_filters: inventoryRootFrame?.filters ?? undefined,
|
||||
root_anchor_type: inventoryRootFrame?.anchorType ?? undefined,
|
||||
@@ -3121,11 +3240,13 @@ function buildAddressDialogContinuationContractV2(userMessage, effectiveMessage,
|
||||
const previousIntent = toNonEmptyString(carryoverMeta?.previousSourceIntent) ?? null;
|
||||
const selectionMode = toNonEmptyString(carryoverMeta?.followupSelectionMode) ?? null;
|
||||
const rootContextOnly = selectionMode === "carry_root_context";
|
||||
const explicitIntent = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const explicitIntentRaw = toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const explicitIntent = explicitIntentRaw === "unknown" ? null : explicitIntentRaw;
|
||||
const rootIntent = toNonEmptyString(carryoverMeta?.followupContext?.root_intent) ?? null;
|
||||
const targetIntent = selectionMode === "switch_to_suggested_intent"
|
||||
? toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null
|
||||
: rootContextOnly
|
||||
? explicitIntent ?? null
|
||||
? rootIntent ?? explicitIntent ?? null
|
||||
: explicitIntent ?? toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null;
|
||||
const hasImplicitContinuationSignal = Boolean(carryoverMeta?.hasImplicitContinuationSignal);
|
||||
const rewrittenByPredecompose = compactWhitespace(sourceMessage.toLowerCase()) !== compactWhitespace(canonicalMessage.toLowerCase());
|
||||
|
||||
@@ -11,6 +11,9 @@ export function hasInventorySupplierCue(text: string): boolean {
|
||||
if (/(?:купил(?:и|о)?\s+у\s+кого|куплен(?:о)?\s+у\s+кого|купил(?:и|о)?\s+от\s+кого|куплен(?:о)?\s+от\s+кого)/iu.test(value)) {
|
||||
return true;
|
||||
}
|
||||
if (/(?:кто\s+(?:был\s+)?продавец|кто\s+нам\s+продал|кто\s+продал\s+нам|(?:^|[\s,.;:!?()\-])(?:продавец|seller)(?=$|[\s,.;:!?()\-]))/iu.test(value)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|откуда\s+(?:мы\s+)?взяли(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(
|
||||
value
|
||||
|
||||
Reference in New Issue
Block a user