Post-F: укрепить inventory selected-object provenance

This commit is contained in:
dctouch 2026-04-24 08:18:02 +03:00
parent 0158419b65
commit 972db24779
6 changed files with 90 additions and 3 deletions

View File

@ -50,12 +50,21 @@ function hasInventoryOnHandSignal(text) {
(hasRequestCue || hasBalanceLexeme || hasColloquialStockSnapshotCue || hasStockStateCue);
}
function hasSelectedObjectInventoryCue(text) {
const value = String(text ?? "");
if (/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+нему|по\s+ней|по\s+ним|по\s+нему\s+же|по\s+ней\s+же|selected\s+object)/iu.test(value)) {
return true;
}
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+нему|по\s+ней|по\s+ним|по\s+нему\s+же|по\s+ней\s+же|selected\s+object)/iu.test(String(text ?? ""));
}
function hasSelectedObjectInventoryProvenanceSignal(text) {
return hasSelectedObjectInventoryCue(text) && (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(text);
}
function hasSelectedObjectInventoryPurchaseDocumentsSignal(text) {
const value = String(text ?? "");
if (hasSelectedObjectInventoryCue(value) &&
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(value)) {
return true;
}
const hasPurchaseDocumentsCue = /(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(text) ||
/(?:(?:по\s+каким|какими)\s+док[а-яё]*[\s\S]{0,80}(?:купил|куплен)|док(?:и|умент[а-яё]*)[\s\S]{0,80}(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару)|операци)|(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару))[\s\S]{0,80}док(?:и|умент[а-яё]*))/iu.test(text);
return hasSelectedObjectInventoryCue(text) && hasPurchaseDocumentsCue;
@ -67,6 +76,13 @@ function hasSelectedObjectInventoryProfitabilitySignal(text) {
return hasSelectedObjectInventoryCue(text) && (0, inventoryLifecycleCueHelpers_1.hasInventoryProfitabilityCue)(text);
}
function hasInventoryProvenanceSignalV2(text) {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|sku|item|product|остат|склад)/iu.test(value);
const hasPlainSupplierCue = (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(value) || /(?:кем\s+поставлен|кто\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*поставил)/iu.test(value);
const hasPlainPurchaseCue = /(?:куплен(?:ы|а|о)?|закупк|происхожд|откуда|где\s+(?:мы\s+)?купили|поставлен(?:ы|а)?|purchase\s+provenance|purchase\s+date)/iu.test(value) || (0, inventoryLifecycleCueHelpers_1.hasInventoryPurchaseStem)(value);
if (hasPlainItemCue && hasPlainSupplierCue && hasPlainPurchaseCue) {
return true;
}
const hasItemCue = /(?:товар|номенклатур|sku|item|product|остат(?:ок|ки)|склад)/iu.test(text);
const hasSupplierCue = (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(text) || /кем\s+поставлен/iu.test(text);
const hasPurchaseCue = /(?:куплен(?:С|Р°|Рѕ)?|закупк|происхождени|откуда|РіРґРµ\s+(?:РјС\s+)?купили(?:\s+(?:это|его|товар|позицию))?|РіРґРµ\s+куплено|РєРѕРіРґР°\s+был\s+куплен|РєРѕРіРґР°\s+куплен|дата\s+закупк|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставлен(?:С|Р°)?|purchase\s+provenance|purchase\s+date)/iu.test(text) || (0, inventoryLifecycleCueHelpers_1.hasInventoryPurchaseStem)(text);
@ -79,6 +95,12 @@ function hasInventoryPurchaseDateSignal(text) {
return hasItemCue && hasPurchaseDateCue;
}
function hasInventoryPurchaseDocumentsSignalV2(text) {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(value);
const hasPlainPurchaseDocCue = /(?:по\s+каким\s+документам\s+(?:был\s+)?куплен|по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|документ[а-яё]*\s+закупк|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(value);
if (hasPlainItemCue && hasPlainPurchaseDocCue) {
return true;
}
const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text);
const hasPurchaseDocCue = /(?:РїРѕ\s+каким\s+документам\s+был\s+куплен|РїРѕ\s+каким\s+документам\s+куплен|какими\s+документами\s+был\s+куплен|документ(?:ам|С)\s+закупк|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(text);
return hasItemCue && hasPurchaseDocCue;

View File

@ -1446,7 +1446,7 @@ function buildAddressRecipePlan(recipe, filters) {
: recipe.query_template === "inventory_purchase_provenance_profile"
? buildInventoryPurchaseDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_purchase_documents_profile"
? buildInventoryPurchaseDocumentQuery(filters, resolvedLimit)
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_supplier_stock_overlap_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_sale_trace_profile"

View File

@ -1168,6 +1168,7 @@ function deriveIntentWithFollowupContext(detectedIntent, userMessage, followupCo
followupContext.current_frame_kind === "inventory_drilldown";
const inventorySelectedObjectFollowup = inventoryLineageActive &&
(hasSelectedObjectInventorySignal(normalizedMessage) || (previousIsInventoryFamily && hasFollowupSignal));
const hasExplicitInventoryItemReference = /(?:товар|номенклатур|позици|склад|остат|sku|item|product|товар|номенклатур|позици|склад|остат)/iu.test(normalizedMessage) || hasSelectedObjectInlineSnapshotMetadata(normalizedMessage);
const inventoryPurchaseDateVatBridge = inventorySelectedObjectFollowup && hasInventoryPurchaseDateVatBridgeCue(normalizedMessage);
if (inventoryPurchaseDateVatBridge &&
(detectedIntent.intent === "unknown" ||
@ -1191,6 +1192,16 @@ function deriveIntentWithFollowupContext(detectedIntent, userMessage, followupCo
reasons: [...detectedIntent.reasons, "intent_adjusted_to_vat_followup_context"]
};
}
if (!inventoryLineageActive &&
hasAnyPartyAnchor &&
!hasExplicitInventoryItemReference &&
detectedIntent.intent === "inventory_purchase_documents_for_item") {
return {
intent: hasPreviousContract && !hasPreviousCounterparty ? "list_documents_by_contract" : "list_documents_by_counterparty",
confidence: "low",
reasons: [...detectedIntent.reasons, "intent_adjusted_from_non_inventory_followup_context"]
};
}
const allowOpenItemsFollowupFallback = detectedIntent.intent === "unknown" && !isVatFollowup;
if (allowOpenItemsFollowupFallback &&
!inventorySelectedObjectFollowup &&

View File

@ -79,6 +79,14 @@ function hasInventoryOnHandSignal(text: string): boolean {
}
function hasSelectedObjectInventoryCue(text: string): boolean {
const value = String(text ?? "");
if (
/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+нему|по\s+ней|по\s+ним|по\s+нему\s+же|по\s+ней\s+же|selected\s+object)/iu.test(
value
)
) {
return true;
}
return /(?:РїРѕ\s+РІСбранному\s+объекССѓ|РїРѕ\s+РІСбранноР\s+РїРѕР·РёСРёРё|РїРѕ\s+СЌСРѕР\s+РїРѕР·РёСРёРё|РїРѕ\s+СЌСРѕРјСѓ\s+Совару|РїРѕ\s+нему|РїРѕ\s+РЅРµР|РїРѕ\s+РЅРёРј|РїРѕ\s+нему\s+РРµ|РїРѕ\s+РЅРµР\s+РРµ|selected\s+object)/iu.test(
String(text ?? "")
);
@ -89,6 +97,15 @@ function hasSelectedObjectInventoryProvenanceSignal(text: string): boolean {
}
function hasSelectedObjectInventoryPurchaseDocumentsSignal(text: string): boolean {
const value = String(text ?? "");
if (
hasSelectedObjectInventoryCue(value) &&
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
value
)
) {
return true;
}
const hasPurchaseDocumentsCue =
/(?:РїРѕ\s+каким\s+докуменСам\s+(?:СЌСРѕ|его|СЌСРѕС\s+Совар|СЌССѓ\s+РїРѕР·РёСРёСЋ)\s+купили|РїРѕ\s+каким\s+докуменСам\s+(?:Р±СР»\s+)?куплен|какими\s+докуменСами\s+(?:СЌСРѕ|его|СЌСРѕС\s+Совар|СЌССѓ\s+РїРѕР·РёСРёСЋ)\s+купили|какими\s+докуменСами\s+(?:Р±СР»\s+)?куплен|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
text
@ -108,6 +125,17 @@ function hasSelectedObjectInventoryProfitabilitySignal(text: string): boolean {
}
function hasInventoryProvenanceSignalV2(text: string): boolean {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|sku|item|product|остат|склад)/iu.test(value);
const hasPlainSupplierCue =
hasInventorySupplierCue(value) || /(?:кем\s+поставлен|кто\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*поставил)/iu.test(value);
const hasPlainPurchaseCue =
/(?:куплен(?:ы|а|о)?|закупк|происхожд|откуда|где\s+(?:мы\s+)?купили|поставлен(?:ы|а)?|purchase\s+provenance|purchase\s+date)/iu.test(
value
) || hasInventoryPurchaseStem(value);
if (hasPlainItemCue && hasPlainSupplierCue && hasPlainPurchaseCue) {
return true;
}
const hasItemCue = /(?:Совар|номенклаССѓСЂ|sku|item|product|РѕСЃСР°С(?:РѕРє|РєРё)|склад)/iu.test(text);
const hasSupplierCue = hasInventorySupplierCue(text) || /кем\s+РїРѕСЃСавлен/iu.test(text);
const hasPurchaseCue =
@ -129,6 +157,15 @@ function hasInventoryPurchaseDateSignal(text: string): boolean {
}
function hasInventoryPurchaseDocumentsSignalV2(text: string): boolean {
const value = String(text ?? "");
const hasPlainItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(value);
const hasPlainPurchaseDocCue =
/(?:по\s+каким\s+документам\s+(?:был\s+)?куплен|по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|документ[а-яё]*\s+закупк|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
value
);
if (hasPlainItemCue && hasPlainPurchaseDocCue) {
return true;
}
const hasItemCue = /(?:Совар|номенклаССѓСЂ|sku|item|product)/iu.test(text);
const hasPurchaseDocCue = /(?:РїРѕ\s+каким\s+докуменСам\s+Р±СР»\s+куплен|РїРѕ\s+каким\s+докуменСам\s+куплен|какими\s+докуменСами\s+Р±СР»\s+куплен|докуменС(?:ам|С)\s+закупк|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
text

View File

@ -1,4 +1,4 @@
import type {
import type {
AddressFilterSet,
AddressIntent,
AddressRecipeDefinition,
@ -1610,7 +1610,7 @@ export function buildAddressRecipePlan(
: recipe.query_template === "inventory_purchase_provenance_profile"
? buildInventoryPurchaseDocumentQuery(filters, resolvedLimit)
: recipe.query_template === "inventory_purchase_documents_profile"
? buildInventoryPurchaseDocumentQuery(filters, resolvedLimit)
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_supplier_stock_overlap_profile"
? buildInventoryMovementQuery(filters, resolvedLimit, "dt")
: recipe.query_template === "inventory_sale_trace_profile"

View File

@ -1461,6 +1461,10 @@ function deriveIntentWithFollowupContext(
const inventorySelectedObjectFollowup =
inventoryLineageActive &&
(hasSelectedObjectInventorySignal(normalizedMessage) || (previousIsInventoryFamily && hasFollowupSignal));
const hasExplicitInventoryItemReference =
/(?:товар|номенклатур|позици|склад|остат|sku|item|product|Совар|номенклаССѓСЂ|РїРѕР·РёСРё|склад|РѕСЃСР°С)/iu.test(
normalizedMessage
) || hasSelectedObjectInlineSnapshotMetadata(normalizedMessage);
const inventoryPurchaseDateVatBridge =
inventorySelectedObjectFollowup && hasInventoryPurchaseDateVatBridgeCue(normalizedMessage);
@ -1490,6 +1494,19 @@ function deriveIntentWithFollowupContext(
};
}
if (
!inventoryLineageActive &&
hasAnyPartyAnchor &&
!hasExplicitInventoryItemReference &&
detectedIntent.intent === "inventory_purchase_documents_for_item"
) {
return {
intent: hasPreviousContract && !hasPreviousCounterparty ? "list_documents_by_contract" : "list_documents_by_counterparty",
confidence: "low",
reasons: [...detectedIntent.reasons, "intent_adjusted_from_non_inventory_followup_context"]
};
}
const allowOpenItemsFollowupFallback = detectedIntent.intent === "unknown" && !isVatFollowup;
if (
allowOpenItemsFollowupFallback &&