NODEDC_1C/llm_normalizer/backend/src/services/assistantTransitionPolicy.ts

568 lines
27 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @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
};
}