Закрепить selected-object закупки в 1С ассистенте
This commit is contained in:
@@ -77,7 +77,7 @@ function hasSelectedObjectInventoryProvenanceSignal(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)) {
|
||||
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+подтвержден[ао]?\s+закупк|какой\s+документ\s+подтвержда[её]т\s+закупк|документ[а-яё]*\s+подтвержд[а-яё]*\s+закупк|закупк[а-яё]*\s+подтвержден[а-яё]*\s+документ|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\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) ||
|
||||
|
||||
@@ -583,6 +583,10 @@ function shouldRestoreInventoryRootFrame(userMessage, intent, extractedFilters,
|
||||
return hasTemporalPatch;
|
||||
}
|
||||
function hasSelectedObjectInventorySignal(text) {
|
||||
const repairedSelectedObjectText = textWithRepairedVariant(String(text ?? ""));
|
||||
if (/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(repairedSelectedObjectText)) {
|
||||
return true;
|
||||
}
|
||||
if (/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(String(text ?? ""))) {
|
||||
return true;
|
||||
}
|
||||
@@ -612,8 +616,9 @@ function hasInventorySupplierFollowupCue(text) {
|
||||
return (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(String(text ?? ""));
|
||||
}
|
||||
function hasInventoryPurchaseDocumentsFollowupCue(text) {
|
||||
const value = String(text ?? "");
|
||||
return (/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+(?:это|его|её|ее|она|он|этот\s+товар|эта\s+позиция|эту\s+позицию)?\s*(?:подтвержден[оа]?|доказан[оа]?|закрыт[оа]?)|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(value) ||
|
||||
const value = textWithRepairedVariant(String(text ?? ""));
|
||||
return (/(?:по\s+каким\s+документам\s+(?:это|его|ее|её|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+(?:это|его|ее|её|она|он|этот\s+товар|эта\s+позиция|эту\s+позицию)?\s*(?:подтвержден[оа]?|подтверждена\s+закупка|доказан[оа]?|закрыт[оа]?)|закупк[аи]\s+подтвержден[аы]?\s+документ|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему))/iu.test(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+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(value) ||
|
||||
/(?:(?:покажи|показать|выведи|дай)?[\s\S]{0,30}док(?:и|умент[а-яё]*)[\s\S]{0,80}(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару)|операци)|(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару))[\s\S]{0,80}док(?:и|умент[а-яё]*))/iu.test(value));
|
||||
}
|
||||
function hasInventoryProfitabilityFollowupCue(text) {
|
||||
@@ -692,10 +697,13 @@ function hasInventoryMarginRankingFollowupCue(text) {
|
||||
return wantsFoundRows || asksMarginBasis || account41Not01;
|
||||
}
|
||||
function hasAddressFollowupContextSignal(text) {
|
||||
const normalized = String(text ?? "").trim();
|
||||
const normalized = textWithRepairedVariant(String(text ?? "")).trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
if (/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (hasInventoryMarginRankingFollowupCue(normalized)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+4
-1
@@ -57,6 +57,9 @@ function inventoryRequestedPartyMatches(requested, actualParties) {
|
||||
function inventoryPartyListOrUnknown(parties) {
|
||||
return parties.length > 0 ? parties.slice(0, 4).join("; ") : "не выделен отдельным полем";
|
||||
}
|
||||
function omitAmountFromInventoryTraceEvidence(lines) {
|
||||
return lines.map((line) => String(line ?? "").replace(/\s+\|\s+сумма:\s+[^|]+(?=\s+\||$)/gu, ""));
|
||||
}
|
||||
function normalizeInventoryReplyEntityToken(value) {
|
||||
const normalized = String(value ?? "")
|
||||
.trim()
|
||||
@@ -383,7 +386,7 @@ function composeInventoryReply(intent, rows, options, deps) {
|
||||
lines.push(`- Для ответа проверены закупочные документы не позже ${boundedAsOfLabel}.`);
|
||||
}
|
||||
if (summary.documents.length > 0) {
|
||||
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Опорные документы:", deps.formatInventoryTraceRows(purchaseRows, INVENTORY_TRACE_EVIDENCE_ROW_LIMIT));
|
||||
(0, inventoryReplyPresentation_1.appendInventorySection)(lines, "Опорные документы:", omitAmountFromInventoryTraceEvidence(deps.formatInventoryTraceRows(purchaseRows, INVENTORY_TRACE_EVIDENCE_ROW_LIMIT)));
|
||||
if (purchaseRows.length > INVENTORY_TRACE_EVIDENCE_ROW_LIMIT) {
|
||||
lines.push(`- Показаны первые ${INVENTORY_TRACE_EVIDENCE_ROW_LIMIT} из ${deps.formatNumberWithDots(purchaseRows.length)} найденных строк; полный след остается в подтвержденном срезе.`);
|
||||
}
|
||||
|
||||
@@ -3298,6 +3298,11 @@ function hasPredecomposeExplicitDrilldownSignal(text) {
|
||||
return ADDRESS_DOCS_SIGNAL_PATTERN.test(source) || ADDRESS_BANK_SIGNAL_PATTERN.test(source) || ADDRESS_CONTRACT_SIGNAL_PATTERN.test(source);
|
||||
}
|
||||
function hasSelectedObjectInventoryFollowupSignalForPredecompose(text) {
|
||||
const source = String(text ?? "");
|
||||
const repaired = repairAddressMojibake(source);
|
||||
if (/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(`${source} ${repaired}`)) {
|
||||
return true;
|
||||
}
|
||||
return /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(text ?? ""));
|
||||
}
|
||||
function isInventorySelectedObjectFollowupIntent(intent) {
|
||||
@@ -3583,6 +3588,30 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
|
||||
}, userMessage);
|
||||
}
|
||||
const candidateAnchorQuality = evaluateAddressAnchorQuality(candidate);
|
||||
const candidateHasSemanticSelectedObjectScope = candidateMeta?.semanticHints?.selected_object_scope_detected === true ||
|
||||
candidateMeta?.semanticHints?.scope_target_kind === "selected_object";
|
||||
const candidateTextForSelectedObjectCounterpartyDegradation = compactWhitespace(repairAddressMojibake(String(candidate ?? "")).toLowerCase());
|
||||
const candidateUsesSelectedObjectAsCounterpartyAnchor = /(?:^|\s)по\s+контрагент\p{L}*\s+(?:выбран\p{L}*|selected(?:\s+object)?|object|объект\p{L}*)/iu.test(candidateTextForSelectedObjectCounterpartyDegradation) ||
|
||||
/(?:^|\s)counterparty\s+(?:selected(?:\s+object)?|object)/iu.test(candidateTextForSelectedObjectCounterpartyDegradation);
|
||||
const candidateTurnsSelectedObjectIntoCounterparty = sourceHasSelectedObjectInventoryFollowup &&
|
||||
(candidateInjectsGenericDocsIntent || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
|
||||
(candidateHasSemanticSelectedObjectScope || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
|
||||
(candidateAnchorQuality.anchorType === "counterparty" || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
|
||||
(candidateAnchorQuality.quality < 2 || candidateUsesSelectedObjectAsCounterpartyAnchor);
|
||||
if (candidateTurnsSelectedObjectIntoCounterparty) {
|
||||
return attachAddressPredecomposeContract({
|
||||
...baseMeta,
|
||||
attempted: true,
|
||||
applied: false,
|
||||
traceId: normalized?.trace_id ?? null,
|
||||
llmCanonicalCandidateDetected: true,
|
||||
effectiveMessage: userMessage,
|
||||
reason: "normalized_fragment_rejected_selected_object_counterparty_degradation",
|
||||
fallbackRuleHit: null,
|
||||
sanitizedUserMessage,
|
||||
semanticHints: candidateMeta?.semanticHints ?? null
|
||||
}, userMessage);
|
||||
}
|
||||
const sameIntentForAnchorSafety = sourceAnchorQuality.intent !== "unknown" && sourceAnchorQuality.intent === candidateAnchorQuality.intent;
|
||||
const sourceSelectedObjectItemAnchorValue = toNonEmptyString((0, addressFilterExtractor_1.extractSelectedObjectQuotedValue)(userMessage)) ??
|
||||
toNonEmptyString((0, addressFilterExtractor_1.extractSelectedObjectQuotedValue)(repairedSourceMessage || userMessage));
|
||||
|
||||
@@ -118,6 +118,12 @@ function createAssistantTransitionPolicy(deps) {
|
||||
if (samples.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (samples.some((sample) => /(?:где\s+(?:мы\s+)?взял|откуда\s+(?:мы\s+)?взял|у\s+кого\s+купил|кто\s+постав|поставщик|источник\s+поступл|от\s+кого\s+поступ)/iu.test(sample))) {
|
||||
return "inventory_purchase_provenance_for_item";
|
||||
}
|
||||
if (samples.some((sample) => /(?:документ|накладн|счет|счёт|акт|поступл|подтвержден|подтверждена\s+закупка|закупк[аи]\s+подтвержден)/iu.test(sample))) {
|
||||
return "inventory_purchase_documents_for_item";
|
||||
}
|
||||
if (suggestedIntents.includes("bank_operations_by_counterparty") &&
|
||||
samples.some((sample) => hasBankOperationsPivotCue(sample) || hasReadableBankOperationsPivotCue(sample))) {
|
||||
return "bank_operations_by_counterparty";
|
||||
@@ -145,10 +151,17 @@ function createAssistantTransitionPolicy(deps) {
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
if (/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(normalized)) {
|
||||
return true;
|
||||
}
|
||||
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(normalized);
|
||||
}
|
||||
function extractSelectedObjectLabel(text) {
|
||||
const repaired = deps.repairAddressMojibake(String(text ?? ""));
|
||||
const unicodeQuotedMatch = repaired.match(/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)\s*[:,-]?\s*[«"]([^"»]+?)["»]/iu);
|
||||
if (unicodeQuotedMatch?.[1]) {
|
||||
return deps.toNonEmptyString(unicodeQuotedMatch[1]);
|
||||
}
|
||||
const quotedMatch = repaired.match(/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)\s*[:,-]?\s*[«"]([^"»]+?)["»]/iu);
|
||||
if (quotedMatch?.[1]) {
|
||||
return deps.toNonEmptyString(quotedMatch[1]);
|
||||
|
||||
Reference in New Issue
Block a user