ДОМЕНЫ - ВОПРОСЫ - СКЛАД - Систематизировать ответы по поставщику без поставок через анализ оплат и возвратов
This commit is contained in:
@@ -2440,6 +2440,12 @@ function findRecentAddressFilterValue(items, key) {
|
||||
if (!isAddressLaneDebugPayload(debug)) {
|
||||
continue;
|
||||
}
|
||||
const replyType = toNonEmptyString(item.reply_type);
|
||||
const limitedReasonCategory = toNonEmptyString(debug.limited_reason_category);
|
||||
if ((replyType && replyType !== "factual" && replyType !== "factual_with_explanation") ||
|
||||
limitedReasonCategory) {
|
||||
continue;
|
||||
}
|
||||
const directFilterValue = readAddressFilterString(debug, key);
|
||||
if (directFilterValue) {
|
||||
return directFilterValue;
|
||||
@@ -2735,8 +2741,10 @@ function hasShortDebtMirrorFollowupSignal(userMessage) {
|
||||
return false;
|
||||
}
|
||||
return samples.some((sample) => /^(?:а|a|и|i)\s+(?:нам\s+)?кто(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/^(?:а|a|и|i)\s+нам(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/^(?:а|a|и|i)\s+(?:мы\s+)?кому(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/^(?:р°|a|рё|i)\s+(?:рЅр°рј\s+)?рєс‚рѕ(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/^(?:р°|a|рё|i)\s+рЅр°рј(?=$|[\s,.;:!?])/iu.test(sample) ||
|
||||
/^(?:р°|a|рё|i)\s+(?:рјс‹\s+)?рєрѕрјсѓ(?=$|[\s,.;:!?])/iu.test(sample));
|
||||
}
|
||||
function isInventorySelectedObjectIntent(intent) {
|
||||
@@ -2854,6 +2862,7 @@ function resolveDebtRoleSwapFollowupIntent(userMessage, previousIntent) {
|
||||
}
|
||||
const hasReceivablesCue = /(?:нам\s+кто\s+долж|кто\s+нам\s+долж|кто\s+долж[а-яё]*\s+нам|дебитор|к\s+получению|к\s+взысканию|receivable)/iu.test(normalized) ||
|
||||
/^(?:а|a|и|i)\s+(?:нам\s+)?кто(?=$|[\s,.;:!?])/iu.test(normalized) ||
|
||||
/^(?:а|a|и|i)\s+нам(?=$|[\s,.;:!?])/iu.test(normalized) ||
|
||||
/^(?:р°|a|рё|i)\s+(?:рЅр°рј\s+)?рєс‚рѕ(?=$|[\s,.;:!?])/iu.test(normalized);
|
||||
const hasPayablesCue = /(?:мы\s+кому\s+долж|кому\s+мы\s+долж|кому\s+долж[а-яё]*\s+мы|кредитор|к\s+уплате|payable)/iu.test(normalized) ||
|
||||
/^(?:а|a|и|i)\s+(?:мы\s+)?кому(?=$|[\s,.;:!?])/iu.test(normalized) ||
|
||||
@@ -2868,6 +2877,49 @@ function resolveDebtRoleSwapFollowupIntent(userMessage, previousIntent) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function hasExplicitOrganizationScopeCue(text) {
|
||||
return /(?:(?:^|[\s"'«»„“()\\\/])(?:ооо|ао|пао|зао|оао|ип|гку|муп|гуп|llc|ltd|inc|corp)(?=$|[\s"'«»„“()\\\/.,;:]))|организац|компан|контор|фирм/u.test(String(text ?? "").toLowerCase());
|
||||
}
|
||||
function looksLikeBareCounterpartyScopeTarget(text) {
|
||||
const normalized = compactWhitespace(String(text ?? "").toLowerCase());
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
if (hasExplicitOrganizationScopeCue(normalized)) {
|
||||
return false;
|
||||
}
|
||||
if (/[0-9]/u.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
const tokens = normalized.split(/\s+/u).filter(Boolean);
|
||||
if (tokens.length === 0 || tokens.length > 4) {
|
||||
return false;
|
||||
}
|
||||
return tokens.every((token) => /^[a-zа-яё._-]{2,}$/iu.test(token));
|
||||
}
|
||||
function shouldDowngradeOrganizationSemanticHint(scopeTargetText, fragmentText) {
|
||||
const target = toNonEmptyString(scopeTargetText);
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
if (hasExplicitOrganizationScopeCue(target) || hasExplicitOrganizationScopeCue(fragmentText)) {
|
||||
return false;
|
||||
}
|
||||
return looksLikeBareCounterpartyScopeTarget(target);
|
||||
}
|
||||
function shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent) {
|
||||
const normalized = compactWhitespace(repairAddressMojibake(String(userMessage ?? "")).toLowerCase());
|
||||
if (!normalized || 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 = findLastAddressAssistantItem(items);
|
||||
const previousAddressDebug = previousAddressItem?.debug ?? null;
|
||||
@@ -2988,7 +3040,8 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
const suggestedIntent = Array.isArray(followupOffer?.suggested_intents)
|
||||
? toNonEmptyString(followupOffer.suggested_intents[0])
|
||||
: null;
|
||||
if (suggestedIntent) {
|
||||
const keepPreviousIntent = shouldKeepPreviousIntentForShortCounterpartyRetarget(userMessage, sourceIntent);
|
||||
if (suggestedIntent && !keepPreviousIntent) {
|
||||
previousIntent = suggestedIntent;
|
||||
followupSelectionMode = "switch_to_suggested_intent";
|
||||
}
|
||||
@@ -3086,13 +3139,20 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
let previousFilters = previousFiltersRaw && typeof previousFiltersRaw === "object"
|
||||
? { ...previousFiltersRaw }
|
||||
: {};
|
||||
if (!toNonEmptyString(previousFilters.contract)) {
|
||||
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 && !toNonEmptyString(previousFilters.contract)) {
|
||||
const historicalContract = findRecentAddressFilterValue(items, "contract");
|
||||
if (historicalContract) {
|
||||
previousFilters.contract = historicalContract;
|
||||
}
|
||||
}
|
||||
if (!toNonEmptyString(previousFilters.counterparty)) {
|
||||
if (shouldBackfillHistoricalPartyAnchors && !toNonEmptyString(previousFilters.counterparty)) {
|
||||
const historicalCounterparty = findRecentAddressFilterValue(items, "counterparty");
|
||||
if (historicalCounterparty) {
|
||||
previousFilters.counterparty = historicalCounterparty;
|
||||
@@ -3138,7 +3198,10 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
const rootContextOnlyPivot = Boolean((isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
const inventoryRootTemporalPivot = Boolean(inventoryRootFrame &&
|
||||
(isInventorySelectedObjectIntent(sourceIntentHint) || currentFrameKind === "inventory_drilldown") &&
|
||||
(isInventorySelectedObjectIntent(sourceIntentHint) ||
|
||||
isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
currentFrameKind === "inventory_drilldown" ||
|
||||
currentFrameKind === "inventory_root") &&
|
||||
(hasInventoryRootTemporalFollowupPrimary || hasInventoryRootTemporalFollowupAlternate) &&
|
||||
!hasForeignAccountingPivotOverInventoryMessage(userMessage, alternateMessage));
|
||||
const rootScopedPivot = rootContextOnlyPivot || inventoryRootTemporalPivot;
|
||||
@@ -3212,19 +3275,34 @@ function resolveAddressFollowupCarryoverContext(userMessage, items, alternateMes
|
||||
if (!previousIntent && !previousAnchor && Object.keys(previousFilters).length === 0) {
|
||||
return null;
|
||||
}
|
||||
const shouldAttachInventoryRootFrame = Boolean(inventoryRootFrame &&
|
||||
(rootScopedPivot ||
|
||||
isInventoryRootFrameIntent(sourceIntentHint) ||
|
||||
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: inventoryRootFrame?.intent ?? undefined,
|
||||
root_filters: inventoryRootFrame?.filters ?? undefined,
|
||||
root_anchor_type: inventoryRootFrame?.anchorType ?? undefined,
|
||||
root_anchor_value: inventoryRootFrame?.anchorValue ?? undefined,
|
||||
current_frame_kind: currentFrameKind ?? 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,
|
||||
@@ -3298,7 +3376,7 @@ function isRetryableAddressLimitedResult(addressLane) {
|
||||
return false;
|
||||
}
|
||||
const category = String(addressLane?.debug?.limited_reason_category ?? "").trim().toLowerCase();
|
||||
return category === "missing_anchor" || category === "empty_match";
|
||||
return category === "missing_anchor" || category === "empty_match" || category === "unsupported";
|
||||
}
|
||||
function isAddressLlmPreDecomposeCandidate(userMessage) {
|
||||
const repaired = repairAddressMojibake(String(userMessage ?? ""));
|
||||
@@ -3317,13 +3395,19 @@ function normalizeAddressSemanticHintsFromFragment(fragment) {
|
||||
return null;
|
||||
}
|
||||
const scopeTargetKind = toNonEmptyString(hints.scope_target_kind);
|
||||
const scopeTargetText = toNonEmptyString(hints.scope_target_text);
|
||||
const fragmentText = toNonEmptyString(fragment.raw_fragment_text) ?? toNonEmptyString(fragment.normalized_fragment_text) ?? "";
|
||||
const normalizedScopeTargetKind = scopeTargetKind === "organization" &&
|
||||
shouldDowngradeOrganizationSemanticHint(scopeTargetText, fragmentText)
|
||||
? "counterparty"
|
||||
: scopeTargetKind;
|
||||
const dateScopeKind = toNonEmptyString(hints.date_scope_kind);
|
||||
return {
|
||||
scope_target_kind: scopeTargetKind ?? "none",
|
||||
scope_target_text: toNonEmptyString(hints.scope_target_text),
|
||||
scope_target_kind: normalizedScopeTargetKind ?? "none",
|
||||
scope_target_text: scopeTargetText,
|
||||
date_scope_kind: dateScopeKind ?? "missing",
|
||||
self_scope_detected: hints.self_scope_detected === true || scopeTargetKind === "self_scope",
|
||||
selected_object_scope_detected: hints.selected_object_scope_detected === true || scopeTargetKind === "selected_object"
|
||||
self_scope_detected: hints.self_scope_detected === true || normalizedScopeTargetKind === "self_scope",
|
||||
selected_object_scope_detected: hints.selected_object_scope_detected === true || normalizedScopeTargetKind === "selected_object"
|
||||
};
|
||||
}
|
||||
function extractAddressPredecomposeCandidateFromFragments(fragments) {
|
||||
|
||||
Reference in New Issue
Block a user