АРЧ АП11 - Вынести transition carryover policy из assistantService в отдельный модуль
This commit is contained in:
parent
44f1c1e11e
commit
6b14946f7e
|
|
@ -69,6 +69,7 @@ const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assista
|
|||
const assistantBoundaryPolicy_1 = __importStar(require("./assistantBoundaryPolicy"));
|
||||
const assistantLivingModePolicy_1 = __importStar(require("./assistantLivingModePolicy"));
|
||||
const assistantRoutePolicy_1 = __importStar(require("./assistantRoutePolicy"));
|
||||
const assistantTransitionPolicy_1 = __importStar(require("./assistantTransitionPolicy"));
|
||||
const assistantOrganizationScopeRuntimeAdapter_1 = __importStar(require("./assistantOrganizationScopeRuntimeAdapter"));
|
||||
const assistantTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantTurnAttemptRuntimeAdapter"));
|
||||
const assistantTurnRuntimeDepsAdapter_1 = __importStar(require("./assistantTurnRuntimeDepsAdapter"));
|
||||
|
|
@ -4790,6 +4791,40 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
|||
hasDeepSessionContinuationSignal,
|
||||
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision
|
||||
});
|
||||
const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({
|
||||
compactWhitespace,
|
||||
repairAddressMojibake,
|
||||
countTokens,
|
||||
findLastAddressAssistantItem,
|
||||
findLastOrganizationClarificationAddressDebug,
|
||||
mergeKnownOrganizations,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
toNonEmptyString,
|
||||
buildAddressFollowupOffer,
|
||||
isImplicitAddressContinuationByLlm,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
resolveDebtRoleSwapFollowupIntent,
|
||||
hasAddressFollowupContextSignal,
|
||||
extractDisplayedEntityIndexMention,
|
||||
findRecentInventoryRootFrame,
|
||||
hasInventoryRootTemporalFollowupSignal,
|
||||
hasFollowupMarker,
|
||||
hasReferentialPointer,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
|
||||
resolveAddressIntentFamily,
|
||||
readAddressFilterString,
|
||||
normalizeOrganizationScopeValue,
|
||||
isInventoryDrilldownFrameIntent,
|
||||
isInventoryRootFrameIntent,
|
||||
findRecentAddressFilterValue,
|
||||
hasForeignAccountingPivotOverInventoryMessage,
|
||||
buildRootScopedCarryoverFilters,
|
||||
inferDisplayedEntityTypeFromIntent,
|
||||
extractDisplayedAddressEntityCandidates,
|
||||
resolveDisplayedAddressEntityMention
|
||||
});
|
||||
function normalizeOrganizationScopeSearchText(value) {
|
||||
const source = normalizeScopeKey(value);
|
||||
return source
|
||||
|
|
@ -5643,9 +5678,9 @@ class AssistantService {
|
|||
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
||||
sanitizeAddressMessageForFallback,
|
||||
toNonEmptyString,
|
||||
resolveAddressFollowupCarryoverContext,
|
||||
resolveAddressFollowupCarryoverContext: assistantTransitionPolicy.resolveAddressFollowupCarryoverContext,
|
||||
resolveAssistantOrchestrationDecision,
|
||||
buildAddressDialogContinuationContractV2,
|
||||
buildAddressDialogContinuationContractV2: assistantTransitionPolicy.buildAddressDialogContinuationContractV2,
|
||||
mergeFollowupContextWithOrganizationScope,
|
||||
isRetryableAddressLimitedResult,
|
||||
mergeKnownOrganizations,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,487 @@
|
|||
"use strict";
|
||||
// @ts-nocheck
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAssistantTransitionPolicy = createAssistantTransitionPolicy;
|
||||
function createAssistantTransitionPolicy(deps) {
|
||||
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
||||
const normalized = deps.compactWhitespace(deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||||
if (!normalized || deps.countTokens(normalized) > 4) {
|
||||
return false;
|
||||
}
|
||||
if (sourceIntent !== "list_documents_by_counterparty" && sourceIntent !== "list_documents_by_contract") {
|
||||
return false;
|
||||
}
|
||||
if (/(?:банк|выписк|плат[её]ж|оплат|списан|поступлен|bank|payment|wire|statement)/iu.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
return /^(?:Р°|Рё|РЅСѓ)?\s*РїРѕ\s+[a-zР°-СЏС‘0-9._-]{2,}(?:\s+[a-zР°-СЏС‘0-9._-]{2,})?$/iu.test(normalized);
|
||||
}
|
||||
function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMessage = null, llmPreDecomposeMeta = null, addressNavigationState = null) {
|
||||
const previousAddressItem = deps.findLastAddressAssistantItem(items);
|
||||
const previousAddressDebug = previousAddressItem?.debug ?? null;
|
||||
const lastOrganizationClarificationDebug = deps.findLastOrganizationClarificationAddressDebug(items);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? deps.mergeKnownOrganizations(lastOrganizationClarificationDebug.organization_candidates)
|
||||
: [];
|
||||
const organizationClarificationSelection = deps.resolveOrganizationSelectionFromMessage(userMessage, organizationClarificationCandidates) ??
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveOrganizationSelectionFromMessage(String(alternateMessage ?? ""), organizationClarificationCandidates)
|
||||
: null);
|
||||
const hasOrganizationClarificationContinuation = Boolean(lastOrganizationClarificationDebug && organizationClarificationSelection);
|
||||
const followupOffer = previousAddressDebug ? deps.buildAddressFollowupOffer(previousAddressDebug) : null;
|
||||
const hasImplicitContinuationSignal = Boolean(previousAddressDebug) &&
|
||||
Boolean(followupOffer?.enabled) &&
|
||||
(deps.isImplicitAddressContinuationByLlm(userMessage, llmPreDecomposeMeta) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||
: false));
|
||||
const sourceIntentHint = deps.toNonEmptyString(previousAddressDebug?.detected_intent);
|
||||
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(deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
||||
deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint)));
|
||||
let inventoryShortFollowupPrimary = (deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
|
||||
deps.hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
let inventoryShortFollowupAlternate = (deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
|
||||
deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""))
|
||||
: false;
|
||||
const debtRoleSwapPrimary = sourceIntentHint
|
||||
? deps.resolveDebtRoleSwapFollowupIntent(userMessage, sourceIntentHint)
|
||||
: null;
|
||||
const debtRoleSwapAlternate = sourceIntentHint && deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
||||
: null;
|
||||
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
||||
let hasPrimaryFollowupSignal = deps.hasAddressFollowupContextSignal(userMessage) || Boolean(debtRoleSwapPrimary) || inventoryShortFollowupPrimary;
|
||||
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
Boolean(debtRoleSwapAlternate) ||
|
||||
inventoryShortFollowupAlternate
|
||||
: false;
|
||||
const hasPrimaryIndexReferenceSignal = deps.extractDisplayedEntityIndexMention(userMessage) !== null;
|
||||
const hasAlternateIndexReferenceSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.extractDisplayedEntityIndexMention(String(alternateMessage ?? "")) !== null
|
||||
: false;
|
||||
const hasIndexReferenceSignal = hasPrimaryIndexReferenceSignal || hasAlternateIndexReferenceSignal;
|
||||
const recentInventoryRootFrame = deps.findRecentInventoryRootFrame(items);
|
||||
const hasInventoryRootTemporalFollowupPrimary = deps.hasInventoryRootTemporalFollowupSignal(userMessage, sourceIntentHint, Boolean(recentInventoryRootFrame));
|
||||
const hasInventoryRootTemporalFollowupAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasInventoryRootTemporalFollowupSignal(String(alternateMessage ?? ""), sourceIntentHint, Boolean(recentInventoryRootFrame))
|
||||
: false;
|
||||
let hasStrongFollowupReference = hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
deps.hasFollowupMarker(userMessage) ||
|
||||
deps.hasReferentialPointer(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
|
||||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
|
||||
: false);
|
||||
const hasStandaloneAddressTopic = deps.hasStandaloneAddressTopicSignal(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage) ? deps.hasStandaloneAddressTopicSignal(alternateMessage) : false);
|
||||
if (hasStandaloneAddressTopic &&
|
||||
!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal) {
|
||||
return null;
|
||||
}
|
||||
if (!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal) {
|
||||
return null;
|
||||
}
|
||||
if (!previousAddressDebug) {
|
||||
return null;
|
||||
}
|
||||
const sourceIntent = deps.toNonEmptyString(previousAddressDebug.detected_intent);
|
||||
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const resolvedPrimaryIntent = deps.resolveAddressIntent(deps.repairAddressMojibake(String(userMessage ?? ""))).intent;
|
||||
const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent
|
||||
: null;
|
||||
const explicitIntent = llmExplicitIntent && llmExplicitIntent !== "unknown"
|
||||
? llmExplicitIntent
|
||||
: resolvedPrimaryIntent && resolvedPrimaryIntent !== "unknown"
|
||||
? resolvedPrimaryIntent
|
||||
: resolvedAlternateIntent && resolvedAlternateIntent !== "unknown"
|
||||
? resolvedAlternateIntent
|
||||
: null;
|
||||
const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent);
|
||||
const explicitIntentFamily = deps.resolveAddressIntentFamily(explicitIntent);
|
||||
if (sourceIntentFamily && explicitIntentFamily && sourceIntentFamily !== explicitIntentFamily && !hasStrongFollowupReference) {
|
||||
return null;
|
||||
}
|
||||
let previousIntent = sourceIntent;
|
||||
let followupSelectionMode = "carry_previous_intent";
|
||||
if (debtRoleSwapIntent) {
|
||||
previousIntent = debtRoleSwapIntent;
|
||||
}
|
||||
if (hasImplicitContinuationSignal) {
|
||||
const suggestedIntent = Array.isArray(followupOffer?.suggested_intents)
|
||||
? deps.toNonEmptyString(followupOffer.suggested_intents[0])
|
||||
: null;
|
||||
const keepPreviousIntent = shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent);
|
||||
if (suggestedIntent && !keepPreviousIntent) {
|
||||
previousIntent = suggestedIntent;
|
||||
followupSelectionMode = "switch_to_suggested_intent";
|
||||
}
|
||||
}
|
||||
let previousAnchorType = deps.toNonEmptyString(previousAddressDebug.anchor_type);
|
||||
let previousAnchor = deps.toNonEmptyString(previousAddressDebug.anchor_value_resolved) ??
|
||||
deps.toNonEmptyString(previousAddressDebug.anchor_value_raw) ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "item") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "counterparty") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "account") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "contract");
|
||||
const navigationSessionContext = addressNavigationState && typeof addressNavigationState === "object"
|
||||
? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object"
|
||||
? addressNavigationState.session_context
|
||||
: null
|
||||
: null;
|
||||
const navigationDateScope = navigationSessionContext && typeof navigationSessionContext.date_scope === "object"
|
||||
? navigationSessionContext.date_scope
|
||||
: null;
|
||||
const navigationOrganization = deps.normalizeOrganizationScopeValue(navigationSessionContext?.organization_scope);
|
||||
const navigationFocusObject = navigationSessionContext && typeof navigationSessionContext.active_focus_object === "object"
|
||||
? navigationSessionContext.active_focus_object
|
||||
: null;
|
||||
const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.object_type);
|
||||
const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
||||
const hasInventoryItemFocusCarryover = navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint));
|
||||
if (!inventoryShortFollowupPrimary && hasInventoryItemFocusCarryover) {
|
||||
inventoryShortFollowupPrimary = deps.hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
}
|
||||
if (!inventoryShortFollowupAlternate && hasInventoryItemFocusCarryover && deps.toNonEmptyString(alternateMessage)) {
|
||||
inventoryShortFollowupAlternate = deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""));
|
||||
}
|
||||
hasPrimaryFollowupSignal =
|
||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||
Boolean(debtRoleSwapPrimary) ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupPrimary;
|
||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
Boolean(debtRoleSwapAlternate) ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupAlternate
|
||||
: false;
|
||||
hasStrongFollowupReference =
|
||||
hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
deps.hasFollowupMarker(userMessage) ||
|
||||
deps.hasReferentialPointer(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
|
||||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
|
||||
: false);
|
||||
const hasSelectedObjectInventorySignalPrimary = /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(userMessage ?? ""));
|
||||
const hasSelectedObjectInventorySignalAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(alternateMessage ?? ""))
|
||||
: false;
|
||||
let inventoryRootFrame = deps.findRecentInventoryRootFrame(items);
|
||||
if (inventoryRootFrame && navigationOrganization && !deps.toNonEmptyString(inventoryRootFrame.filters?.organization)) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
organization: navigationOrganization
|
||||
}
|
||||
};
|
||||
}
|
||||
if (inventoryRootFrame && navigationDateScope) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
as_of_date: deps.toNonEmptyString(inventoryRootFrame.filters?.as_of_date) ??
|
||||
deps.toNonEmptyString(navigationDateScope.as_of_date) ??
|
||||
undefined,
|
||||
period_from: deps.toNonEmptyString(inventoryRootFrame.filters?.period_from) ??
|
||||
deps.toNonEmptyString(navigationDateScope.period_from) ??
|
||||
undefined,
|
||||
period_to: deps.toNonEmptyString(inventoryRootFrame.filters?.period_to) ??
|
||||
deps.toNonEmptyString(navigationDateScope.period_to) ??
|
||||
undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
let currentFrameKind = inventoryRootFrame
|
||||
? deps.isInventoryDrilldownFrameIntent(sourceIntent)
|
||||
? "inventory_drilldown"
|
||||
: deps.isInventoryRootFrameIntent(sourceIntent)
|
||||
? "inventory_root"
|
||||
: "generic"
|
||||
: null;
|
||||
let resolvedCounterpartyFromDisplay = false;
|
||||
const previousFiltersRaw = previousAddressDebug.extracted_filters;
|
||||
let previousFilters = previousFiltersRaw && typeof previousFiltersRaw === "object" ? { ...previousFiltersRaw } : {};
|
||||
const shouldBackfillHistoricalPartyAnchors = sourceIntentHint === "list_contracts_by_counterparty" ||
|
||||
sourceIntentHint === "list_documents_by_counterparty" ||
|
||||
sourceIntentHint === "bank_operations_by_counterparty" ||
|
||||
sourceIntentHint === "list_documents_by_contract" ||
|
||||
sourceIntentHint === "bank_operations_by_contract" ||
|
||||
sourceIntentHint === "open_items_by_counterparty_or_contract";
|
||||
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.contract)) {
|
||||
const historicalContract = deps.findRecentAddressFilterValue(items, "contract");
|
||||
if (historicalContract) {
|
||||
previousFilters.contract = historicalContract;
|
||||
}
|
||||
}
|
||||
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.counterparty)) {
|
||||
const historicalCounterparty = deps.findRecentAddressFilterValue(items, "counterparty");
|
||||
if (historicalCounterparty) {
|
||||
previousFilters.counterparty = historicalCounterparty;
|
||||
}
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization)) {
|
||||
const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization");
|
||||
if (historicalOrganization) {
|
||||
previousFilters.organization = historicalOrganization;
|
||||
}
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization) && navigationOrganization) {
|
||||
previousFilters.organization = navigationOrganization;
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) {
|
||||
previousFilters.organization = organizationClarificationSelection;
|
||||
}
|
||||
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 &&
|
||||
!deps.toNonEmptyString(previousFilters.as_of_date) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.as_of_date)) {
|
||||
previousFilters.as_of_date = deps.toNonEmptyString(navigationDateScope?.as_of_date);
|
||||
}
|
||||
if (shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!deps.toNonEmptyString(previousFilters.period_from) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.period_from)) {
|
||||
previousFilters.period_from = deps.toNonEmptyString(navigationDateScope?.period_from);
|
||||
}
|
||||
if (shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!deps.toNonEmptyString(previousFilters.period_to) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.period_to)) {
|
||||
previousFilters.period_to = deps.toNonEmptyString(navigationDateScope?.period_to);
|
||||
}
|
||||
const rootContextOnlyPivot = Boolean((deps.isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
const inventoryRootTemporalPivot = Boolean(inventoryRootFrame &&
|
||||
(deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
|
||||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
currentFrameKind === "inventory_drilldown" ||
|
||||
currentFrameKind === "inventory_root") &&
|
||||
(hasInventoryRootTemporalFollowupPrimary || hasInventoryRootTemporalFollowupAlternate) &&
|
||||
!deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
const rootScopedPivot = rootContextOnlyPivot || inventoryRootTemporalPivot;
|
||||
if (rootScopedPivot) {
|
||||
previousIntent = null;
|
||||
previousAnchorType = null;
|
||||
previousAnchor = null;
|
||||
previousFilters = deps.buildRootScopedCarryoverFilters(previousFilters, inventoryRootFrame);
|
||||
currentFrameKind = inventoryRootFrame ? "inventory_root" : currentFrameKind;
|
||||
followupSelectionMode = "carry_root_context";
|
||||
}
|
||||
const displayedEntityType = deps.inferDisplayedEntityTypeFromIntent(sourceIntent);
|
||||
const displayedEntities = deps.extractDisplayedAddressEntityCandidates(deps.toNonEmptyString(previousAddressItem?.text) ?? "", displayedEntityType);
|
||||
const resolvedEntityFromFollowup = deps.resolveDisplayedAddressEntityMention(userMessage, displayedEntities) ??
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveDisplayedAddressEntityMention(String(alternateMessage ?? ""), displayedEntities)
|
||||
: null);
|
||||
if (resolvedEntityFromFollowup && !rootScopedPivot) {
|
||||
if (resolvedEntityFromFollowup.entityType === "counterparty") {
|
||||
previousFilters.counterparty = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "counterparty";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
resolvedCounterpartyFromDisplay = true;
|
||||
}
|
||||
else if (resolvedEntityFromFollowup.entityType === "contract") {
|
||||
previousFilters.contract = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "contract";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
}
|
||||
else if (resolvedEntityFromFollowup.entityType === "item") {
|
||||
previousFilters.item = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
}
|
||||
if (followupSelectionMode !== "switch_to_suggested_intent") {
|
||||
followupSelectionMode = "carry_referenced_entity";
|
||||
}
|
||||
}
|
||||
if (!rootScopedPivot &&
|
||||
!deps.toNonEmptyString(previousFilters.item) &&
|
||||
navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
(sourceIntentHint === "inventory_on_hand_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" ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate)) {
|
||||
previousFilters.item = navigationFocusObjectLabel;
|
||||
if (!previousAnchor) {
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = navigationFocusObjectLabel;
|
||||
}
|
||||
}
|
||||
if (organizationClarificationSelection && !previousAnchor) {
|
||||
previousAnchorType = "organization";
|
||||
previousAnchor = organizationClarificationSelection;
|
||||
}
|
||||
if (inventoryRootFrame &&
|
||||
organizationClarificationSelection &&
|
||||
!deps.toNonEmptyString(inventoryRootFrame.filters?.organization)) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
organization: organizationClarificationSelection
|
||||
}
|
||||
};
|
||||
}
|
||||
if (!previousIntent && !previousAnchor && Object.keys(previousFilters).length === 0) {
|
||||
return null;
|
||||
}
|
||||
const shouldAttachInventoryRootFrame = Boolean(inventoryRootFrame &&
|
||||
(rootScopedPivot ||
|
||||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
|
||||
hasNavigationInventoryItemFocusHint ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate));
|
||||
const carryoverTargetIntent = followupSelectionMode === "carry_root_context"
|
||||
? inventoryRootFrame?.intent ?? explicitIntent ?? previousIntent ?? undefined
|
||||
: explicitIntent ?? previousIntent ?? undefined;
|
||||
return {
|
||||
followupContext: {
|
||||
previous_intent: previousIntent ?? undefined,
|
||||
target_intent: carryoverTargetIntent,
|
||||
previous_filters: previousFilters,
|
||||
previous_anchor_type: previousAnchorType ?? undefined,
|
||||
previous_anchor_value: previousAnchor,
|
||||
resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined,
|
||||
root_context_only: rootScopedPivot || undefined,
|
||||
root_intent: shouldAttachInventoryRootFrame ? inventoryRootFrame?.intent ?? undefined : undefined,
|
||||
root_filters: shouldAttachInventoryRootFrame ? inventoryRootFrame?.filters ?? undefined : undefined,
|
||||
root_anchor_type: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorType ?? undefined : undefined,
|
||||
root_anchor_value: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorValue ?? undefined : undefined,
|
||||
current_frame_kind: shouldAttachInventoryRootFrame ? currentFrameKind ?? undefined : undefined
|
||||
},
|
||||
previousAddressIntent: previousIntent,
|
||||
previousAddressAnchor: previousAnchor,
|
||||
previousSourceIntent: sourceIntent,
|
||||
followupSelectionMode,
|
||||
hasImplicitContinuationSignal
|
||||
};
|
||||
}
|
||||
function buildAddressDialogContinuationContractV2(userMessage, effectiveMessage, carryoverMeta, llmPreDecomposeMeta) {
|
||||
const sourceMessage = String(userMessage ?? "");
|
||||
const canonicalMessage = String(effectiveMessage ?? sourceMessage);
|
||||
const hasFollowupContext = Boolean(carryoverMeta?.followupContext);
|
||||
const previousIntent = deps.toNonEmptyString(carryoverMeta?.previousSourceIntent) ?? null;
|
||||
const selectionMode = deps.toNonEmptyString(carryoverMeta?.followupSelectionMode) ?? null;
|
||||
const rootContextOnly = selectionMode === "carry_root_context";
|
||||
const explicitIntentRaw = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const explicitIntent = explicitIntentRaw === "unknown" ? null : explicitIntentRaw;
|
||||
const rootIntent = deps.toNonEmptyString(carryoverMeta?.followupContext?.root_intent) ?? null;
|
||||
const targetIntent = selectionMode === "switch_to_suggested_intent"
|
||||
? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null
|
||||
: rootContextOnly
|
||||
? rootIntent ?? explicitIntent ?? null
|
||||
: explicitIntent ?? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null;
|
||||
const hasImplicitContinuationSignal = Boolean(carryoverMeta?.hasImplicitContinuationSignal);
|
||||
const rewrittenByPredecompose = deps.compactWhitespace(sourceMessage.toLowerCase()) !== deps.compactWhitespace(canonicalMessage.toLowerCase());
|
||||
const hasExplicitIntent = Boolean(explicitIntent);
|
||||
const decision = !hasFollowupContext
|
||||
? "new_topic"
|
||||
: selectionMode === "switch_to_suggested_intent"
|
||||
? "switch_to_suggested"
|
||||
: "continue_previous";
|
||||
const reasons = [];
|
||||
if (hasFollowupContext) {
|
||||
reasons.push("followup_context_detected");
|
||||
}
|
||||
if (hasImplicitContinuationSignal) {
|
||||
reasons.push("implicit_continuation_by_llm");
|
||||
}
|
||||
if (rewrittenByPredecompose) {
|
||||
reasons.push("effective_message_rewritten_by_predecompose");
|
||||
}
|
||||
if (hasExplicitIntent) {
|
||||
reasons.push("llm_contract_intent_available");
|
||||
}
|
||||
if (selectionMode === "carry_referenced_entity" && explicitIntent && previousIntent && explicitIntent !== previousIntent) {
|
||||
reasons.push("operation_intent_from_current_message");
|
||||
}
|
||||
if (rootContextOnly) {
|
||||
reasons.push("root_context_only_carryover");
|
||||
}
|
||||
return {
|
||||
schema_version: "address_dialog_continuation_contract_v2",
|
||||
source_message: sourceMessage,
|
||||
effective_message: canonicalMessage,
|
||||
decision,
|
||||
decision_reasons: reasons,
|
||||
followup_context_applied: hasFollowupContext,
|
||||
previous_intent: previousIntent,
|
||||
target_intent: targetIntent,
|
||||
intent_selection_mode: selectionMode,
|
||||
anchor_type: carryoverMeta?.followupContext?.previous_anchor_type ?? null,
|
||||
anchor_value: carryoverMeta?.followupContext?.previous_anchor_value ?? null,
|
||||
implicit_continuation_signal: hasImplicitContinuationSignal
|
||||
};
|
||||
}
|
||||
return {
|
||||
resolveAddressFollowupCarryoverContext,
|
||||
buildAddressDialogContinuationContractV2
|
||||
};
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAt
|
|||
import * as assistantBoundaryPolicy_1 from "./assistantBoundaryPolicy";
|
||||
import * as assistantLivingModePolicy_1 from "./assistantLivingModePolicy";
|
||||
import * as assistantRoutePolicy_1 from "./assistantRoutePolicy";
|
||||
import * as assistantTransitionPolicy_1 from "./assistantTransitionPolicy";
|
||||
import * as assistantOrganizationScopeRuntimeAdapter_1 from "./assistantOrganizationScopeRuntimeAdapter";
|
||||
import * as assistantOrganizationMatcher_1 from "./assistantOrganizationMatcher";
|
||||
import * as assistantTurnAttemptRuntimeAdapter_1 from "./assistantTurnAttemptRuntimeAdapter";
|
||||
|
|
@ -4751,6 +4752,40 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
|||
hasDeepSessionContinuationSignal,
|
||||
resolveLivingAssistantModeDecision: assistantLivingModePolicy.resolveLivingAssistantModeDecision
|
||||
});
|
||||
const assistantTransitionPolicy = (0, assistantTransitionPolicy_1.createAssistantTransitionPolicy)({
|
||||
compactWhitespace,
|
||||
repairAddressMojibake,
|
||||
countTokens,
|
||||
findLastAddressAssistantItem,
|
||||
findLastOrganizationClarificationAddressDebug,
|
||||
mergeKnownOrganizations,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
toNonEmptyString,
|
||||
buildAddressFollowupOffer,
|
||||
isImplicitAddressContinuationByLlm,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
resolveDebtRoleSwapFollowupIntent,
|
||||
hasAddressFollowupContextSignal,
|
||||
extractDisplayedEntityIndexMention,
|
||||
findRecentInventoryRootFrame,
|
||||
hasInventoryRootTemporalFollowupSignal,
|
||||
hasFollowupMarker,
|
||||
hasReferentialPointer,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
resolveAddressIntent: addressIntentResolver_1.resolveAddressIntent,
|
||||
resolveAddressIntentFamily,
|
||||
readAddressFilterString,
|
||||
normalizeOrganizationScopeValue,
|
||||
isInventoryDrilldownFrameIntent,
|
||||
isInventoryRootFrameIntent,
|
||||
findRecentAddressFilterValue,
|
||||
hasForeignAccountingPivotOverInventoryMessage,
|
||||
buildRootScopedCarryoverFilters,
|
||||
inferDisplayedEntityTypeFromIntent,
|
||||
extractDisplayedAddressEntityCandidates,
|
||||
resolveDisplayedAddressEntityMention
|
||||
});
|
||||
function normalizeOrganizationScopeSearchText(value) {
|
||||
const source = normalizeScopeKey(value);
|
||||
return source
|
||||
|
|
@ -5603,9 +5638,9 @@ export class AssistantService {
|
|||
buildAddressLlmPredecomposeContractV1: predecomposeContract_1.buildAddressLlmPredecomposeContractV1,
|
||||
sanitizeAddressMessageForFallback,
|
||||
toNonEmptyString,
|
||||
resolveAddressFollowupCarryoverContext,
|
||||
resolveAddressFollowupCarryoverContext: assistantTransitionPolicy.resolveAddressFollowupCarryoverContext,
|
||||
resolveAssistantOrchestrationDecision,
|
||||
buildAddressDialogContinuationContractV2,
|
||||
buildAddressDialogContinuationContractV2: assistantTransitionPolicy.buildAddressDialogContinuationContractV2,
|
||||
mergeFollowupContextWithOrganizationScope,
|
||||
isRetryableAddressLimitedResult,
|
||||
mergeKnownOrganizations,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,567 @@
|
|||
// @ts-nocheck
|
||||
|
||||
export function createAssistantTransitionPolicy(deps) {
|
||||
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
||||
const normalized = deps.compactWhitespace(
|
||||
deps.repairAddressMojibake(String(userMessage ?? "")).toLowerCase()
|
||||
);
|
||||
if (!normalized || deps.countTokens(normalized) > 4) {
|
||||
return false;
|
||||
}
|
||||
if (sourceIntent !== "list_documents_by_counterparty" && sourceIntent !== "list_documents_by_contract") {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
/(?:банк|выписк|плат[её]ж|оплат|списан|поступлен|bank|payment|wire|statement)/iu.test(
|
||||
normalized
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return /^(?:Р°|Рё|РЅСѓ)?\s*РїРѕ\s+[a-zР°-СЏС‘0-9._-]{2,}(?:\s+[a-zР°-СЏС‘0-9._-]{2,})?$/iu.test(normalized);
|
||||
}
|
||||
|
||||
function resolveAddressFollowupCarryoverContext(
|
||||
userMessage,
|
||||
items,
|
||||
alternateMessage = null,
|
||||
llmPreDecomposeMeta = null,
|
||||
addressNavigationState = null
|
||||
) {
|
||||
const previousAddressItem = deps.findLastAddressAssistantItem(items);
|
||||
const previousAddressDebug = previousAddressItem?.debug ?? null;
|
||||
const lastOrganizationClarificationDebug = deps.findLastOrganizationClarificationAddressDebug(items);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? deps.mergeKnownOrganizations(lastOrganizationClarificationDebug.organization_candidates)
|
||||
: [];
|
||||
const organizationClarificationSelection =
|
||||
deps.resolveOrganizationSelectionFromMessage(userMessage, organizationClarificationCandidates) ??
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveOrganizationSelectionFromMessage(String(alternateMessage ?? ""), organizationClarificationCandidates)
|
||||
: null);
|
||||
const hasOrganizationClarificationContinuation = Boolean(
|
||||
lastOrganizationClarificationDebug && organizationClarificationSelection
|
||||
);
|
||||
const followupOffer = previousAddressDebug ? deps.buildAddressFollowupOffer(previousAddressDebug) : null;
|
||||
const hasImplicitContinuationSignal =
|
||||
Boolean(previousAddressDebug) &&
|
||||
Boolean(followupOffer?.enabled) &&
|
||||
(deps.isImplicitAddressContinuationByLlm(userMessage, llmPreDecomposeMeta) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.isImplicitAddressContinuationByLlm(alternateMessage, llmPreDecomposeMeta)
|
||||
: false));
|
||||
const sourceIntentHint = deps.toNonEmptyString(previousAddressDebug?.detected_intent);
|
||||
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(
|
||||
deps.toNonEmptyString(navigationFocusObjectHint?.label) &&
|
||||
deps.toNonEmptyString(navigationFocusObjectHint?.object_type) === "item" &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint))
|
||||
);
|
||||
let inventoryShortFollowupPrimary =
|
||||
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
|
||||
deps.hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
let inventoryShortFollowupAlternate =
|
||||
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || hasNavigationInventoryItemFocusHint) &&
|
||||
deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""))
|
||||
: false;
|
||||
const debtRoleSwapPrimary = sourceIntentHint
|
||||
? deps.resolveDebtRoleSwapFollowupIntent(userMessage, sourceIntentHint)
|
||||
: null;
|
||||
const debtRoleSwapAlternate =
|
||||
sourceIntentHint && deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveDebtRoleSwapFollowupIntent(String(alternateMessage ?? ""), sourceIntentHint)
|
||||
: null;
|
||||
const debtRoleSwapIntent = debtRoleSwapPrimary ?? debtRoleSwapAlternate ?? null;
|
||||
let hasPrimaryFollowupSignal =
|
||||
deps.hasAddressFollowupContextSignal(userMessage) || Boolean(debtRoleSwapPrimary) || inventoryShortFollowupPrimary;
|
||||
let hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
Boolean(debtRoleSwapAlternate) ||
|
||||
inventoryShortFollowupAlternate
|
||||
: false;
|
||||
const hasPrimaryIndexReferenceSignal = deps.extractDisplayedEntityIndexMention(userMessage) !== null;
|
||||
const hasAlternateIndexReferenceSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.extractDisplayedEntityIndexMention(String(alternateMessage ?? "")) !== null
|
||||
: false;
|
||||
const hasIndexReferenceSignal = hasPrimaryIndexReferenceSignal || hasAlternateIndexReferenceSignal;
|
||||
const recentInventoryRootFrame = deps.findRecentInventoryRootFrame(items);
|
||||
const hasInventoryRootTemporalFollowupPrimary = deps.hasInventoryRootTemporalFollowupSignal(
|
||||
userMessage,
|
||||
sourceIntentHint,
|
||||
Boolean(recentInventoryRootFrame)
|
||||
);
|
||||
const hasInventoryRootTemporalFollowupAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasInventoryRootTemporalFollowupSignal(
|
||||
String(alternateMessage ?? ""),
|
||||
sourceIntentHint,
|
||||
Boolean(recentInventoryRootFrame)
|
||||
)
|
||||
: false;
|
||||
let hasStrongFollowupReference =
|
||||
hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
deps.hasFollowupMarker(userMessage) ||
|
||||
deps.hasReferentialPointer(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
|
||||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
|
||||
: false);
|
||||
const hasStandaloneAddressTopic =
|
||||
deps.hasStandaloneAddressTopicSignal(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage) ? deps.hasStandaloneAddressTopicSignal(alternateMessage) : false);
|
||||
if (
|
||||
hasStandaloneAddressTopic &&
|
||||
!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
!hasPrimaryFollowupSignal &&
|
||||
!hasAlternateFollowupSignal &&
|
||||
!hasInventoryRootTemporalFollowupPrimary &&
|
||||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasImplicitContinuationSignal &&
|
||||
!hasOrganizationClarificationContinuation &&
|
||||
!hasIndexReferenceSignal
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (!previousAddressDebug) {
|
||||
return null;
|
||||
}
|
||||
const sourceIntent = deps.toNonEmptyString(previousAddressDebug.detected_intent);
|
||||
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const resolvedPrimaryIntent = deps.resolveAddressIntent(deps.repairAddressMojibake(String(userMessage ?? ""))).intent;
|
||||
const resolvedAlternateIntent = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveAddressIntent(deps.repairAddressMojibake(String(alternateMessage ?? ""))).intent
|
||||
: null;
|
||||
const explicitIntent =
|
||||
llmExplicitIntent && llmExplicitIntent !== "unknown"
|
||||
? llmExplicitIntent
|
||||
: resolvedPrimaryIntent && resolvedPrimaryIntent !== "unknown"
|
||||
? resolvedPrimaryIntent
|
||||
: resolvedAlternateIntent && resolvedAlternateIntent !== "unknown"
|
||||
? resolvedAlternateIntent
|
||||
: null;
|
||||
const sourceIntentFamily = deps.resolveAddressIntentFamily(sourceIntent);
|
||||
const explicitIntentFamily = deps.resolveAddressIntentFamily(explicitIntent);
|
||||
if (sourceIntentFamily && explicitIntentFamily && sourceIntentFamily !== explicitIntentFamily && !hasStrongFollowupReference) {
|
||||
return null;
|
||||
}
|
||||
let previousIntent = sourceIntent;
|
||||
let followupSelectionMode = "carry_previous_intent";
|
||||
if (debtRoleSwapIntent) {
|
||||
previousIntent = debtRoleSwapIntent;
|
||||
}
|
||||
if (hasImplicitContinuationSignal) {
|
||||
const suggestedIntent = Array.isArray(followupOffer?.suggested_intents)
|
||||
? deps.toNonEmptyString(followupOffer.suggested_intents[0])
|
||||
: null;
|
||||
const keepPreviousIntent = shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent);
|
||||
if (suggestedIntent && !keepPreviousIntent) {
|
||||
previousIntent = suggestedIntent;
|
||||
followupSelectionMode = "switch_to_suggested_intent";
|
||||
}
|
||||
}
|
||||
let previousAnchorType = deps.toNonEmptyString(previousAddressDebug.anchor_type);
|
||||
let previousAnchor =
|
||||
deps.toNonEmptyString(previousAddressDebug.anchor_value_resolved) ??
|
||||
deps.toNonEmptyString(previousAddressDebug.anchor_value_raw) ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "item") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "counterparty") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "account") ??
|
||||
deps.readAddressFilterString(previousAddressDebug, "contract");
|
||||
const navigationSessionContext =
|
||||
addressNavigationState && typeof addressNavigationState === "object"
|
||||
? addressNavigationState.session_context && typeof addressNavigationState.session_context === "object"
|
||||
? addressNavigationState.session_context
|
||||
: null
|
||||
: null;
|
||||
const navigationDateScope =
|
||||
navigationSessionContext && typeof navigationSessionContext.date_scope === "object"
|
||||
? navigationSessionContext.date_scope
|
||||
: null;
|
||||
const navigationOrganization = deps.normalizeOrganizationScopeValue(navigationSessionContext?.organization_scope);
|
||||
const navigationFocusObject =
|
||||
navigationSessionContext && typeof navigationSessionContext.active_focus_object === "object"
|
||||
? navigationSessionContext.active_focus_object
|
||||
: null;
|
||||
const navigationFocusObjectType = deps.toNonEmptyString(navigationFocusObject?.object_type);
|
||||
const navigationFocusObjectLabel = deps.toNonEmptyString(navigationFocusObject?.label);
|
||||
const hasInventoryItemFocusCarryover =
|
||||
navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_supplier_stock_overlap_as_of_date" ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint));
|
||||
if (!inventoryShortFollowupPrimary && hasInventoryItemFocusCarryover) {
|
||||
inventoryShortFollowupPrimary = deps.hasShortInventoryObjectFollowupSignal(userMessage);
|
||||
}
|
||||
if (!inventoryShortFollowupAlternate && hasInventoryItemFocusCarryover && deps.toNonEmptyString(alternateMessage)) {
|
||||
inventoryShortFollowupAlternate = deps.hasShortInventoryObjectFollowupSignal(String(alternateMessage ?? ""));
|
||||
}
|
||||
hasPrimaryFollowupSignal =
|
||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||
Boolean(debtRoleSwapPrimary) ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupPrimary;
|
||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
Boolean(debtRoleSwapAlternate) ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupAlternate
|
||||
: false;
|
||||
hasStrongFollowupReference =
|
||||
hasPrimaryIndexReferenceSignal ||
|
||||
hasAlternateIndexReferenceSignal ||
|
||||
hasOrganizationClarificationContinuation ||
|
||||
hasImplicitContinuationSignal ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
deps.hasFollowupMarker(userMessage) ||
|
||||
deps.hasReferentialPointer(userMessage) ||
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasFollowupMarker(String(alternateMessage ?? "")) ||
|
||||
deps.hasReferentialPointer(String(alternateMessage ?? ""))
|
||||
: false);
|
||||
const hasSelectedObjectInventorySignalPrimary = /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(
|
||||
String(userMessage ?? "")
|
||||
);
|
||||
const hasSelectedObjectInventorySignalAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(
|
||||
String(alternateMessage ?? "")
|
||||
)
|
||||
: false;
|
||||
let inventoryRootFrame = deps.findRecentInventoryRootFrame(items);
|
||||
if (inventoryRootFrame && navigationOrganization && !deps.toNonEmptyString(inventoryRootFrame.filters?.organization)) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
organization: navigationOrganization
|
||||
}
|
||||
};
|
||||
}
|
||||
if (inventoryRootFrame && navigationDateScope) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
as_of_date:
|
||||
deps.toNonEmptyString(inventoryRootFrame.filters?.as_of_date) ??
|
||||
deps.toNonEmptyString(navigationDateScope.as_of_date) ??
|
||||
undefined,
|
||||
period_from:
|
||||
deps.toNonEmptyString(inventoryRootFrame.filters?.period_from) ??
|
||||
deps.toNonEmptyString(navigationDateScope.period_from) ??
|
||||
undefined,
|
||||
period_to:
|
||||
deps.toNonEmptyString(inventoryRootFrame.filters?.period_to) ??
|
||||
deps.toNonEmptyString(navigationDateScope.period_to) ??
|
||||
undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
let currentFrameKind = inventoryRootFrame
|
||||
? deps.isInventoryDrilldownFrameIntent(sourceIntent)
|
||||
? "inventory_drilldown"
|
||||
: deps.isInventoryRootFrameIntent(sourceIntent)
|
||||
? "inventory_root"
|
||||
: "generic"
|
||||
: null;
|
||||
let resolvedCounterpartyFromDisplay = false;
|
||||
const previousFiltersRaw = previousAddressDebug.extracted_filters;
|
||||
let previousFilters =
|
||||
previousFiltersRaw && typeof previousFiltersRaw === "object" ? { ...previousFiltersRaw } : {};
|
||||
const shouldBackfillHistoricalPartyAnchors =
|
||||
sourceIntentHint === "list_contracts_by_counterparty" ||
|
||||
sourceIntentHint === "list_documents_by_counterparty" ||
|
||||
sourceIntentHint === "bank_operations_by_counterparty" ||
|
||||
sourceIntentHint === "list_documents_by_contract" ||
|
||||
sourceIntentHint === "bank_operations_by_contract" ||
|
||||
sourceIntentHint === "open_items_by_counterparty_or_contract";
|
||||
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.contract)) {
|
||||
const historicalContract = deps.findRecentAddressFilterValue(items, "contract");
|
||||
if (historicalContract) {
|
||||
previousFilters.contract = historicalContract;
|
||||
}
|
||||
}
|
||||
if (shouldBackfillHistoricalPartyAnchors && !deps.toNonEmptyString(previousFilters.counterparty)) {
|
||||
const historicalCounterparty = deps.findRecentAddressFilterValue(items, "counterparty");
|
||||
if (historicalCounterparty) {
|
||||
previousFilters.counterparty = historicalCounterparty;
|
||||
}
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization)) {
|
||||
const historicalOrganization = deps.findRecentAddressFilterValue(items, "organization");
|
||||
if (historicalOrganization) {
|
||||
previousFilters.organization = historicalOrganization;
|
||||
}
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization) && navigationOrganization) {
|
||||
previousFilters.organization = navigationOrganization;
|
||||
}
|
||||
if (!deps.toNonEmptyString(previousFilters.organization) && organizationClarificationSelection) {
|
||||
previousFilters.organization = organizationClarificationSelection;
|
||||
}
|
||||
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 &&
|
||||
!deps.toNonEmptyString(previousFilters.as_of_date) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.as_of_date)
|
||||
) {
|
||||
previousFilters.as_of_date = deps.toNonEmptyString(navigationDateScope?.as_of_date);
|
||||
}
|
||||
if (
|
||||
shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!deps.toNonEmptyString(previousFilters.period_from) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.period_from)
|
||||
) {
|
||||
previousFilters.period_from = deps.toNonEmptyString(navigationDateScope?.period_from);
|
||||
}
|
||||
if (
|
||||
shouldBackfillPreviousDateScopeFromNavigation &&
|
||||
!deps.toNonEmptyString(previousFilters.period_to) &&
|
||||
deps.toNonEmptyString(navigationDateScope?.period_to)
|
||||
) {
|
||||
previousFilters.period_to = deps.toNonEmptyString(navigationDateScope?.period_to);
|
||||
}
|
||||
const rootContextOnlyPivot = Boolean(
|
||||
(deps.isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage)
|
||||
);
|
||||
const inventoryRootTemporalPivot = Boolean(
|
||||
inventoryRootFrame &&
|
||||
(deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
|
||||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
currentFrameKind === "inventory_drilldown" ||
|
||||
currentFrameKind === "inventory_root") &&
|
||||
(hasInventoryRootTemporalFollowupPrimary || hasInventoryRootTemporalFollowupAlternate) &&
|
||||
!deps.hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage)
|
||||
);
|
||||
const rootScopedPivot = rootContextOnlyPivot || inventoryRootTemporalPivot;
|
||||
if (rootScopedPivot) {
|
||||
previousIntent = null;
|
||||
previousAnchorType = null;
|
||||
previousAnchor = null;
|
||||
previousFilters = deps.buildRootScopedCarryoverFilters(previousFilters, inventoryRootFrame);
|
||||
currentFrameKind = inventoryRootFrame ? "inventory_root" : currentFrameKind;
|
||||
followupSelectionMode = "carry_root_context";
|
||||
}
|
||||
const displayedEntityType = deps.inferDisplayedEntityTypeFromIntent(sourceIntent);
|
||||
const displayedEntities = deps.extractDisplayedAddressEntityCandidates(
|
||||
deps.toNonEmptyString(previousAddressItem?.text) ?? "",
|
||||
displayedEntityType
|
||||
);
|
||||
const resolvedEntityFromFollowup =
|
||||
deps.resolveDisplayedAddressEntityMention(userMessage, displayedEntities) ??
|
||||
(deps.toNonEmptyString(alternateMessage)
|
||||
? deps.resolveDisplayedAddressEntityMention(String(alternateMessage ?? ""), displayedEntities)
|
||||
: null);
|
||||
if (resolvedEntityFromFollowup && !rootScopedPivot) {
|
||||
if (resolvedEntityFromFollowup.entityType === "counterparty") {
|
||||
previousFilters.counterparty = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "counterparty";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
resolvedCounterpartyFromDisplay = true;
|
||||
} else if (resolvedEntityFromFollowup.entityType === "contract") {
|
||||
previousFilters.contract = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "contract";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
} else if (resolvedEntityFromFollowup.entityType === "item") {
|
||||
previousFilters.item = resolvedEntityFromFollowup.value;
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = resolvedEntityFromFollowup.value;
|
||||
}
|
||||
if (followupSelectionMode !== "switch_to_suggested_intent") {
|
||||
followupSelectionMode = "carry_referenced_entity";
|
||||
}
|
||||
}
|
||||
if (
|
||||
!rootScopedPivot &&
|
||||
!deps.toNonEmptyString(previousFilters.item) &&
|
||||
navigationFocusObjectType === "item" &&
|
||||
navigationFocusObjectLabel &&
|
||||
(sourceIntentHint === "inventory_on_hand_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" ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate)
|
||||
) {
|
||||
previousFilters.item = navigationFocusObjectLabel;
|
||||
if (!previousAnchor) {
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = navigationFocusObjectLabel;
|
||||
}
|
||||
}
|
||||
if (organizationClarificationSelection && !previousAnchor) {
|
||||
previousAnchorType = "organization";
|
||||
previousAnchor = organizationClarificationSelection;
|
||||
}
|
||||
if (
|
||||
inventoryRootFrame &&
|
||||
organizationClarificationSelection &&
|
||||
!deps.toNonEmptyString(inventoryRootFrame.filters?.organization)
|
||||
) {
|
||||
inventoryRootFrame = {
|
||||
...inventoryRootFrame,
|
||||
filters: {
|
||||
...(inventoryRootFrame.filters ?? {}),
|
||||
organization: organizationClarificationSelection
|
||||
}
|
||||
};
|
||||
}
|
||||
if (!previousIntent && !previousAnchor && Object.keys(previousFilters).length === 0) {
|
||||
return null;
|
||||
}
|
||||
const shouldAttachInventoryRootFrame = Boolean(
|
||||
inventoryRootFrame &&
|
||||
(rootScopedPivot ||
|
||||
deps.isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
deps.isInventorySelectedObjectIntent(sourceIntentHint) ||
|
||||
hasNavigationInventoryItemFocusHint ||
|
||||
inventoryShortFollowupPrimary ||
|
||||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate)
|
||||
);
|
||||
const carryoverTargetIntent =
|
||||
followupSelectionMode === "carry_root_context"
|
||||
? inventoryRootFrame?.intent ?? explicitIntent ?? previousIntent ?? undefined
|
||||
: explicitIntent ?? previousIntent ?? undefined;
|
||||
return {
|
||||
followupContext: {
|
||||
previous_intent: previousIntent ?? undefined,
|
||||
target_intent: carryoverTargetIntent,
|
||||
previous_filters: previousFilters,
|
||||
previous_anchor_type: previousAnchorType ?? undefined,
|
||||
previous_anchor_value: previousAnchor,
|
||||
resolved_counterparty_from_display: resolvedCounterpartyFromDisplay || undefined,
|
||||
root_context_only: rootScopedPivot || undefined,
|
||||
root_intent: shouldAttachInventoryRootFrame ? inventoryRootFrame?.intent ?? undefined : undefined,
|
||||
root_filters: shouldAttachInventoryRootFrame ? inventoryRootFrame?.filters ?? undefined : undefined,
|
||||
root_anchor_type: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorType ?? undefined : undefined,
|
||||
root_anchor_value: shouldAttachInventoryRootFrame ? inventoryRootFrame?.anchorValue ?? undefined : undefined,
|
||||
current_frame_kind: shouldAttachInventoryRootFrame ? currentFrameKind ?? undefined : undefined
|
||||
},
|
||||
previousAddressIntent: previousIntent,
|
||||
previousAddressAnchor: previousAnchor,
|
||||
previousSourceIntent: sourceIntent,
|
||||
followupSelectionMode,
|
||||
hasImplicitContinuationSignal
|
||||
};
|
||||
}
|
||||
|
||||
function buildAddressDialogContinuationContractV2(
|
||||
userMessage,
|
||||
effectiveMessage,
|
||||
carryoverMeta,
|
||||
llmPreDecomposeMeta
|
||||
) {
|
||||
const sourceMessage = String(userMessage ?? "");
|
||||
const canonicalMessage = String(effectiveMessage ?? sourceMessage);
|
||||
const hasFollowupContext = Boolean(carryoverMeta?.followupContext);
|
||||
const previousIntent = deps.toNonEmptyString(carryoverMeta?.previousSourceIntent) ?? null;
|
||||
const selectionMode = deps.toNonEmptyString(carryoverMeta?.followupSelectionMode) ?? null;
|
||||
const rootContextOnly = selectionMode === "carry_root_context";
|
||||
const explicitIntentRaw = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const explicitIntent = explicitIntentRaw === "unknown" ? null : explicitIntentRaw;
|
||||
const rootIntent = deps.toNonEmptyString(carryoverMeta?.followupContext?.root_intent) ?? null;
|
||||
const targetIntent =
|
||||
selectionMode === "switch_to_suggested_intent"
|
||||
? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null
|
||||
: rootContextOnly
|
||||
? rootIntent ?? explicitIntent ?? null
|
||||
: explicitIntent ?? deps.toNonEmptyString(carryoverMeta?.previousAddressIntent) ?? null;
|
||||
const hasImplicitContinuationSignal = Boolean(carryoverMeta?.hasImplicitContinuationSignal);
|
||||
const rewrittenByPredecompose =
|
||||
deps.compactWhitespace(sourceMessage.toLowerCase()) !== deps.compactWhitespace(canonicalMessage.toLowerCase());
|
||||
const hasExplicitIntent = Boolean(explicitIntent);
|
||||
const decision = !hasFollowupContext
|
||||
? "new_topic"
|
||||
: selectionMode === "switch_to_suggested_intent"
|
||||
? "switch_to_suggested"
|
||||
: "continue_previous";
|
||||
const reasons = [];
|
||||
if (hasFollowupContext) {
|
||||
reasons.push("followup_context_detected");
|
||||
}
|
||||
if (hasImplicitContinuationSignal) {
|
||||
reasons.push("implicit_continuation_by_llm");
|
||||
}
|
||||
if (rewrittenByPredecompose) {
|
||||
reasons.push("effective_message_rewritten_by_predecompose");
|
||||
}
|
||||
if (hasExplicitIntent) {
|
||||
reasons.push("llm_contract_intent_available");
|
||||
}
|
||||
if (selectionMode === "carry_referenced_entity" && explicitIntent && previousIntent && explicitIntent !== previousIntent) {
|
||||
reasons.push("operation_intent_from_current_message");
|
||||
}
|
||||
if (rootContextOnly) {
|
||||
reasons.push("root_context_only_carryover");
|
||||
}
|
||||
return {
|
||||
schema_version: "address_dialog_continuation_contract_v2",
|
||||
source_message: sourceMessage,
|
||||
effective_message: canonicalMessage,
|
||||
decision,
|
||||
decision_reasons: reasons,
|
||||
followup_context_applied: hasFollowupContext,
|
||||
previous_intent: previousIntent,
|
||||
target_intent: targetIntent,
|
||||
intent_selection_mode: selectionMode,
|
||||
anchor_type: carryoverMeta?.followupContext?.previous_anchor_type ?? null,
|
||||
anchor_value: carryoverMeta?.followupContext?.previous_anchor_value ?? null,
|
||||
implicit_continuation_signal: hasImplicitContinuationSignal
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
resolveAddressFollowupCarryoverContext,
|
||||
buildAddressDialogContinuationContractV2
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { createAssistantTransitionPolicy } from "../src/services/assistantTransitionPolicy";
|
||||
|
||||
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 buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
return createAssistantTransitionPolicy({
|
||||
compactWhitespace: (value: string) => String(value ?? "").replace(/\s+/g, " ").trim(),
|
||||
repairAddressMojibake: (value: string) => value,
|
||||
countTokens: (value: string) => String(value ?? "").split(/\s+/).filter(Boolean).length,
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "1. Рабочая станция",
|
||||
debug: {
|
||||
detected_intent: "inventory_purchase_documents_for_item",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция"
|
||||
},
|
||||
anchor_type: "item",
|
||||
anchor_value_resolved: "Рабочая станция"
|
||||
}
|
||||
}),
|
||||
findLastOrganizationClarificationAddressDebug: () => null,
|
||||
mergeKnownOrganizations: (values: unknown[]) => values,
|
||||
resolveOrganizationSelectionFromMessage: () => null,
|
||||
toNonEmptyString,
|
||||
buildAddressFollowupOffer: () => null,
|
||||
isImplicitAddressContinuationByLlm: () => false,
|
||||
isInventorySelectedObjectIntent: (intent: unknown) =>
|
||||
[
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date"
|
||||
].includes(String(intent ?? "")),
|
||||
hasShortInventoryObjectFollowupSignal: () => false,
|
||||
resolveDebtRoleSwapFollowupIntent: () => null,
|
||||
hasAddressFollowupContextSignal: () => false,
|
||||
extractDisplayedEntityIndexMention: () => null,
|
||||
findRecentInventoryRootFrame: () => ({
|
||||
intent: "inventory_on_hand_as_of_date",
|
||||
filters: {
|
||||
as_of_date: "2020-03-31",
|
||||
organization: 'ООО "Альтернатива Плюс"'
|
||||
},
|
||||
anchorType: "organization",
|
||||
anchorValue: 'ООО "Альтернатива Плюс"'
|
||||
}),
|
||||
hasInventoryRootTemporalFollowupSignal: (message: string) => /март 2020/i.test(message),
|
||||
hasFollowupMarker: () => false,
|
||||
hasReferentialPointer: () => false,
|
||||
hasStandaloneAddressTopicSignal: () => false,
|
||||
resolveAddressIntent: () => ({ intent: "unknown" }),
|
||||
resolveAddressIntentFamily: (intent: unknown) => (intent ? String(intent) : null),
|
||||
readAddressFilterString: (debug: Record<string, unknown>, key: string) =>
|
||||
debug?.extracted_filters && typeof debug.extracted_filters === "object"
|
||||
? toNonEmptyString((debug.extracted_filters as Record<string, unknown>)[key])
|
||||
: null,
|
||||
normalizeOrganizationScopeValue: (value: unknown) => toNonEmptyString(value),
|
||||
isInventoryDrilldownFrameIntent: (intent: unknown) =>
|
||||
[
|
||||
"inventory_purchase_provenance_for_item",
|
||||
"inventory_purchase_documents_for_item",
|
||||
"inventory_sale_trace_for_item",
|
||||
"inventory_profitability_for_item",
|
||||
"inventory_purchase_to_sale_chain",
|
||||
"inventory_aging_by_purchase_date"
|
||||
].includes(String(intent ?? "")),
|
||||
isInventoryRootFrameIntent: (intent: unknown) => String(intent ?? "") === "inventory_on_hand_as_of_date",
|
||||
findRecentAddressFilterValue: () => null,
|
||||
hasForeignAccountingPivotOverInventoryMessage: () => false,
|
||||
buildRootScopedCarryoverFilters: (_previousFilters: Record<string, unknown>, inventoryRootFrame: Record<string, unknown>) => ({
|
||||
...(inventoryRootFrame?.filters ?? {})
|
||||
}),
|
||||
inferDisplayedEntityTypeFromIntent: () => "item",
|
||||
extractDisplayedAddressEntityCandidates: () => [],
|
||||
resolveDisplayedAddressEntityMention: () => null,
|
||||
...overrides
|
||||
});
|
||||
}
|
||||
|
||||
describe("assistantTransitionPolicy", () => {
|
||||
it("promotes inventory temporal follow-up into root-scoped carryover", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"остатки на март 2020",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
{
|
||||
session_context: {
|
||||
active_focus_object: {
|
||||
object_type: "item",
|
||||
label: "Рабочая станция"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupSelectionMode).toBe("carry_root_context");
|
||||
expect(carryover?.followupContext?.root_context_only).toBe(true);
|
||||
expect(carryover?.followupContext?.target_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.root_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.previous_filters).toEqual({
|
||||
as_of_date: "2020-03-31",
|
||||
organization: 'ООО "Альтернатива Плюс"'
|
||||
});
|
||||
});
|
||||
|
||||
it("builds continuation contract from extracted root carryover", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
const contract = policy.buildAddressDialogContinuationContractV2(
|
||||
"остатки на эту дату",
|
||||
"остатки на эту дату",
|
||||
{
|
||||
followupContext: {
|
||||
root_intent: "inventory_on_hand_as_of_date",
|
||||
previous_anchor_type: "item",
|
||||
previous_anchor_value: "Рабочая станция"
|
||||
},
|
||||
previousSourceIntent: "inventory_purchase_documents_for_item",
|
||||
previousAddressIntent: null,
|
||||
followupSelectionMode: "carry_root_context",
|
||||
hasImplicitContinuationSignal: true
|
||||
},
|
||||
{
|
||||
predecomposeContract: {
|
||||
intent: "unknown"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(contract.decision).toBe("continue_previous");
|
||||
expect(contract.target_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(contract.decision_reasons).toContain("root_context_only_carryover");
|
||||
expect(contract.decision_reasons).toContain("implicit_continuation_by_llm");
|
||||
expect(contract.anchor_type).toBe("item");
|
||||
expect(contract.anchor_value).toBe("Рабочая станция");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue