АРЧ - Ассистент: отделить meta-followup по прошлому ответу от повторного запуска address lane

This commit is contained in:
2026-04-15 12:38:44 +03:00
parent 8866176be6
commit 70cc5a99f1
61 changed files with 4023 additions and 298 deletions
@@ -6,6 +6,7 @@ const addressQueryClassifier_1 = require("../addressQueryClassifier");
const addressQueryShapeClassifier_1 = require("../addressQueryShapeClassifier");
const addressIntentResolver_1 = require("../addressIntentResolver");
const addressFilterExtractor_1 = require("../addressFilterExtractor");
const semanticHintOverlay_1 = require("./semanticHintOverlay");
function hasExplicitPeriodWindow(filters) {
return ((typeof filters.period_from === "string" && filters.period_from.trim().length > 0) ||
(typeof filters.period_to === "string" && filters.period_to.trim().length > 0));
@@ -253,6 +254,144 @@ function isInventoryIntent(intent) {
intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date");
}
function isInventoryRootFrameIntent(intent) {
return intent === "inventory_on_hand_as_of_date";
}
function isInventoryDrilldownFrameIntent(intent) {
return (intent === "inventory_purchase_provenance_for_item" ||
intent === "inventory_purchase_documents_for_item" ||
intent === "inventory_sale_trace_for_item" ||
intent === "inventory_purchase_to_sale_chain" ||
intent === "inventory_aging_by_purchase_date");
}
function buildInventoryRootFollowupContext(followupContext) {
if (!followupContext || !followupContext.root_intent || !followupContext.root_filters) {
return followupContext;
}
return {
...followupContext,
previous_intent: followupContext.root_intent,
previous_filters: { ...followupContext.root_filters },
previous_anchor_type: followupContext.root_anchor_type ?? followupContext.previous_anchor_type,
previous_anchor_value: followupContext.root_anchor_value ?? followupContext.previous_anchor_value,
current_frame_kind: "inventory_root"
};
}
function getTokenCount(text) {
return String(text ?? "")
.trim()
.split(/\s+/)
.filter(Boolean).length;
}
function resolveMonthNumberFromText(text) {
const normalized = String(text ?? "").toLowerCase();
if (!normalized) {
return null;
}
if (/январ|january|jan/iu.test(normalized))
return 1;
if (/феврал|february|feb/iu.test(normalized))
return 2;
if (/март|march|mar/iu.test(normalized))
return 3;
if (/апрел|april|apr/iu.test(normalized))
return 4;
if (/(?:^|[\s,.;:!?()\-])ма(?:й|е|я)(?=$|[\s,.;:!?()\-])|may/iu.test(normalized))
return 5;
if (/июн|june|jun/iu.test(normalized))
return 6;
if (/июл|july|jul/iu.test(normalized))
return 7;
if (/август|august|aug/iu.test(normalized))
return 8;
if (/сентябр|september|sep/iu.test(normalized))
return 9;
if (/октябр|october|oct/iu.test(normalized))
return 10;
if (/ноябр|november|nov/iu.test(normalized))
return 11;
if (/декабр|december|dec/iu.test(normalized))
return 12;
return null;
}
function resolveYearFromFilters(filters) {
const candidates = [
toNonEmptyString(filters?.as_of_date),
toNonEmptyString(filters?.period_to),
toNonEmptyString(filters?.period_from)
];
for (const candidate of candidates) {
const match = candidate?.match(/\b((?:19|20)\d{2})\b/u);
if (match) {
const year = Number(match[1]);
if (Number.isFinite(year)) {
return year;
}
}
}
return null;
}
function hasRelativeYearHint(text) {
return /(?:эт(?:от|ого)(?:\s+же)?\s+год|этого\s+же\s+года|того\s+же\s+года|this\s+year|same\s+year|that\s+year)/iu.test(String(text ?? ""));
}
function resolveRelativeMonthPeriodFromInventoryRoot(userMessage, followupContext) {
if (!followupContext || !isInventoryRootFrameIntent(followupContext.root_intent)) {
return null;
}
const month = resolveMonthNumberFromText(userMessage);
if (!month) {
return null;
}
const normalized = String(userMessage ?? "");
if (hasExplicitPeriodLiteral(normalized) || hasExplicitCurrentDateHint(normalized)) {
return null;
}
const shortTemporalPatch = getTokenCount(normalized) <= 8 || hasRelativeYearHint(normalized);
if (!shortTemporalPatch) {
return null;
}
const year = resolveYearFromFilters(followupContext.root_filters);
if (!year) {
return null;
}
const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate();
const periodFrom = `${year}-${String(month).padStart(2, "0")}-01`;
const periodTo = `${year}-${String(month).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`;
return {
period_from: periodFrom,
period_to: periodTo,
as_of_date: periodTo
};
}
function shouldRestoreInventoryRootFrame(userMessage, intent, extractedFilters, followupContext) {
if (!followupContext || !isInventoryRootFrameIntent(followupContext.root_intent)) {
return false;
}
const currentFrameKind = followupContext.current_frame_kind ?? null;
const previousIntent = followupContext.previous_intent;
const comingFromInventoryDrilldown = currentFrameKind === "inventory_drilldown" || isInventoryDrilldownFrameIntent(previousIntent);
if (!comingFromInventoryDrilldown) {
return false;
}
const normalized = String(userMessage ?? "");
if (hasSelectedObjectInventorySignal(normalized) ||
hasInventorySupplierFollowupCue(normalized) ||
hasInventoryPurchaseDocumentsFollowupCue(normalized) ||
hasInventoryPurchaseDateFollowupCue(normalized) ||
hasBareInventoryPurchaseDateFollowupCue(normalized) ||
hasInventorySaleFollowupCue(normalized) ||
hasInventoryPurchaseToSaleChainFollowupCue(normalized)) {
return false;
}
if (intent === "inventory_on_hand_as_of_date") {
return true;
}
const hasTemporalPatch = hasExplicitPeriodWindow(extractedFilters) ||
Boolean(toNonEmptyString(extractedFilters.as_of_date)) ||
hasExplicitPeriodLiteral(normalized) ||
Boolean(resolveRelativeMonthPeriodFromInventoryRoot(normalized, followupContext));
return hasTemporalPatch;
}
function hasSelectedObjectInventorySignal(text) {
return /(?:по\s+выбранному\s+объекту|for\s+selected\s+object)/iu.test(String(text ?? ""));
}
@@ -350,6 +489,7 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
const previousAsOfDate = toNonEmptyString(previous.as_of_date);
const previousPeriodFrom = toNonEmptyString(previous.period_from);
const previousPeriodTo = toNonEmptyString(previous.period_to);
const relativeMonthFromInventoryRoot = resolveRelativeMonthPeriodFromInventoryRoot(userMessage, followupContext);
const allTimeRequested = hasAllTimeHint(userMessage);
const sameDateRequested = hasSameDateHint(userMessage);
if (!toNonEmptyString(merged.organization) && previousOrganization) {
@@ -516,6 +656,13 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
reasons.push("as_of_date_from_open_items_followup_context");
}
}
if (relativeMonthFromInventoryRoot &&
(intent === "inventory_on_hand_as_of_date" || intent === "inventory_supplier_stock_overlap_as_of_date")) {
merged.period_from = relativeMonthFromInventoryRoot.period_from;
merged.period_to = relativeMonthFromInventoryRoot.period_to;
merged.as_of_date = relativeMonthFromInventoryRoot.as_of_date;
reasons.push("period_derived_from_inventory_root_frame_year");
}
if (intent === "inventory_aging_by_purchase_date") {
const explicitItemMention = /(?:^|[\s,.;:!?()\-\u2014])(?:товар(?:у|а|ом)?|позици(?:и|я|ю)|item|row|line)(?=$|[\s,.;:!?()\-\u2014])/iu.test(String(userMessage ?? ""));
if (toNonEmptyString(merged.item) && !explicitItemMention) {
@@ -822,7 +969,7 @@ function deriveIntentWithFollowupContext(detectedIntent, userMessage, followupCo
reasons: [...detectedIntent.reasons, "intent_from_followup_context"]
};
}
function runAddressDecomposeStage(userMessage, followupContext) {
function runAddressDecomposeStage(userMessage, followupContext, llmSemanticHints = null) {
const detectedMode = (0, addressQueryClassifier_1.detectAddressQuestionMode)(userMessage);
const shape = (0, addressQueryShapeClassifier_1.classifyAddressQueryShape)(userMessage);
const allowExplainAsFollowup = shape.shape === "EXPLAIN_OR_REASON" &&
@@ -850,17 +997,29 @@ function runAddressDecomposeStage(userMessage, followupContext) {
if (mode.mode !== "address_query") {
return null;
}
const intent = deriveIntentWithFollowupContext(detectedIntent, userMessage, followupContext);
const extractedFilters = (0, addressFilterExtractor_1.extractAddressFilters)(userMessage, intent.intent);
const followupMerged = mergeFollowupFilters(extractedFilters.extracted_filters, intent.intent, userMessage, followupContext);
let effectiveFollowupContext = followupContext;
let intent = deriveIntentWithFollowupContext(detectedIntent, userMessage, effectiveFollowupContext);
let extractedFilters = (0, semanticHintOverlay_1.applyAddressLlmSemanticHintsToExtraction)((0, addressFilterExtractor_1.extractAddressFilters)(userMessage, intent.intent), llmSemanticHints);
if (shouldRestoreInventoryRootFrame(userMessage, intent.intent, extractedFilters.extracted_filters, effectiveFollowupContext)) {
effectiveFollowupContext = buildInventoryRootFollowupContext(effectiveFollowupContext);
intent = {
intent: effectiveFollowupContext?.root_intent ?? "inventory_on_hand_as_of_date",
confidence: "low",
reasons: [...intent.reasons, "intent_restored_to_inventory_root_frame"]
};
extractedFilters = (0, semanticHintOverlay_1.applyAddressLlmSemanticHintsToExtraction)((0, addressFilterExtractor_1.extractAddressFilters)(userMessage, intent.intent), llmSemanticHints);
}
const followupMerged = mergeFollowupFilters(extractedFilters.extracted_filters, intent.intent, userMessage, effectiveFollowupContext);
const filters = {
extracted_filters: followupMerged.filters,
missing_required_filters: resolveMissingRequiredFilters(intent.intent, followupMerged.filters),
warnings: [...new Set([...extractedFilters.warnings, ...followupMerged.reasons])]
warnings: [...new Set([...extractedFilters.warnings, ...followupMerged.reasons])],
semantic_frame: extractedFilters.semantic_frame
};
const followupContextApplied = Boolean(followupContext) &&
const followupContextApplied = Boolean(effectiveFollowupContext) &&
(mode.reasons.includes("address_mode_from_followup_context") ||
intent.reasons.includes("intent_from_followup_context") ||
intent.reasons.includes("intent_restored_to_inventory_root_frame") ||
followupMerged.reasons.length > 0);
const baseReasons = [
...mode.reasons,
@@ -6,10 +6,11 @@ const addressQueryClassifier_1 = require("../addressQueryClassifier");
const addressQueryShapeClassifier_1 = require("../addressQueryShapeClassifier");
const addressIntentResolver_1 = require("../addressIntentResolver");
const addressFilterExtractor_1 = require("../addressFilterExtractor");
const ADDRESS_SEMANTIC_DATA_SIGNAL_PATTERN = /(?:\u0434\u043e\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0441\u0447(?:\u0435|\u0451)\u0442|\u0441\u0430\u043b\u044c\u0434\u043e|\u043e\u0431\u043e\u0440\u043e\u0442|\u043f\u043b\u0430\u0442(?:\u0435|\u0451)\u0436|\u043e\u043f\u0435\u0440\u0430\u0446|\u043f\u0435\u0440\u0438\u043e\u0434|\u0433\u043e\u0434|counterparty|contract|document|account|balance|turnover|operations?|doki|doky|dokument|dogovor|kontragent|schet|saldo|platezh|oplata)/iu;
const semanticHintOverlay_1 = require("./semanticHintOverlay");
const ADDRESS_SEMANTIC_DATA_SIGNAL_PATTERN = /(?:\u0434\u043e\u043a|\u0434\u043e\u0433\u043e\u0432\u043e\u0440|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442|\u0441\u0447(?:\u0435|\u0451)\u0442|\u0441\u0430\u043b\u044c\u0434\u043e|\u043e\u0431\u043e\u0440\u043e\u0442|\u043f\u043b\u0430\u0442(?:\u0435|\u0451)\u0436|\u043e\u043f\u0435\u0440\u0430\u0446|\u043f\u0435\u0440\u0438\u043e\u0434|\u0433\u043e\u0434|\u0441\u043a\u043b\u0430\u0434|\u0442\u043e\u0432\u0430\u0440|\u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440|counterparty|contract|document|account|balance|turnover|operations?|warehouse|stock|inventory|item|goods|doki|doky|dokument|dogovor|kontragent|schet|saldo|platezh|oplata)/iu;
const ADDRESS_SEMANTIC_ENTITY_SIGNAL_PATTERN = /(?:\u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442|\u043a\u043e\u043c\u043f\u0430\u043d|\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446|\u043a\u043e\u043d\u0442\u043e\u0440|customer|supplier|counterparty|company|vendor|client)/iu;
const ADDRESS_SEMANTIC_SCOPE_META_PATTERN = /(?:\u043a\u0430\u043a\u0430\u044f\s+\u0431\u0430\u0437\u0430|\u0431\u0430\u0437\u0430\s+\u043a\u0430\u043a\u043e\u0439\s+\u043a\u043e\u043d\u0442\u043e\u0440|\u043f\u043e\s+\u043a\u0430\u043a\u0438\u043c\s+\u043a\u043e\u043d\u0442\u043e\u0440|which\s+company\s+base|which\s+tenant|data\s+scope)/iu;
const ADDRESS_SEMANTIC_DEEP_INVESTIGATION_PATTERN = /(?:\u043f\u0440\u043e\u0432\u0435\u0440(?:\u044c|\u0438\u0442\u044c)|\u0440\u0430\u0437\u0431\u0435\u0440(?:\u0438|\u0430\u0442\u044c)|\u043f\u043e\u0447\u0435\u043c\u0443|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c|\u0440\u0430\u0437\u0440\u044b\u0432|\u0445\u0432\u043e\u0441\u0442|root\s*cause|trace\s*chain|state\s+transition)/iu;
const ADDRESS_SEMANTIC_DEEP_INVESTIGATION_PATTERN = /(?:\u043f\u043e\u0447\u0435\u043c\u0443|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c|\u0440\u0430\u0437\u0440\u044b\u0432|\u0445\u0432\u043e\u0441\u0442|root\s*cause|trace\s*chain|state\s+transition|\u043f\u0440\u043e\u0432\u0435\u0440(?:\u044c|\u0438\u0442\u044c).*(?:\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0437\u0430\u043a\u0440\u044b\u0442|\u0446\u0435\u043f\u043e\u0447|\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c|\u043e\u0448\u0438\u0431|\u0430\u043d\u043e\u043c\u0430\u043b|\u0440\u0438\u0441\u043a|\u0441\u0432\u0435\u0440\u043a)|\u0440\u0430\u0437\u0431\u0435\u0440(?:\u0438|\u0430\u0442\u044c).*(?:\u043f\u043e\u0447\u0435\u043c\u0443|\u0445\u0432\u043e\u0441\u0442|\u0440\u0430\u0437\u0440\u044b\u0432|\u0437\u0430\u043a\u0440\u044b\u0442|\u0446\u0435\u043f\u043e\u0447|\u043e\u0448\u0438\u0431|\u0430\u043d\u043e\u043c\u0430\u043b|\u0440\u0438\u0441\u043a))/iu;
function normalizeCompact(value) {
return String(value ?? "")
.toLowerCase()
@@ -127,8 +128,17 @@ function buildAddressLlmPredecomposeContractV1(input) {
const mode = (0, addressQueryClassifier_1.detectAddressQuestionMode)(canonicalMessage);
const shape = (0, addressQueryShapeClassifier_1.classifyAddressQueryShape)(canonicalMessage);
const intent = (0, addressIntentResolver_1.resolveAddressIntent)(canonicalMessage);
const extraction = (0, addressFilterExtractor_1.extractAddressFilters)(canonicalMessage, intent.intent);
const extraction = (0, semanticHintOverlay_1.applyAddressLlmSemanticHintsToExtraction)((0, addressFilterExtractor_1.extractAddressFilters)(canonicalMessage, intent.intent), input.semanticHints ?? null);
const filters = extraction.extracted_filters;
const semanticFrame = extraction.semantic_frame ?? {
scope_kind: "none",
anchor_kind: "none",
anchor_value: null,
date_scope_kind: "none",
date_basis_hint: null,
self_scope_detected: false,
selected_object_scope_detected: false
};
const periodScope = inferPeriodScope(filters, canonicalMessage);
return {
schema_version: "address_llm_predecompose_contract_v1",
@@ -153,8 +163,9 @@ function buildAddressLlmPredecomposeContractV1(input) {
period_from: toNonEmptyString(filters.period_from),
period_to: toNonEmptyString(filters.period_to),
as_of_date: toNonEmptyString(filters.as_of_date),
has_explicit_period: Boolean(toNonEmptyString(filters.as_of_date) || toNonEmptyString(filters.period_from) || toNonEmptyString(filters.period_to))
has_explicit_period: semanticFrame.date_scope_kind === "explicit"
},
semantics: semanticFrame,
aggregation_profile: inferAggregationProfile(intent.intent, shape.shape)
};
}
@@ -238,6 +249,7 @@ function buildAddressSemanticExtractionContractV1(input) {
as_of_date: predecomposeContract.period.as_of_date,
has_explicit_period: predecomposeContract.period.has_explicit_period
},
semantics: predecomposeContract.semantics,
guard_hints: {
source_data_signal_detected: sourceDataSignal,
canonical_data_signal_detected: canonicalDataSignal,
@@ -143,6 +143,7 @@ function resolvePrimaryAnchor(intent, filters) {
const contract = typeof filters.contract === "string" ? filters.contract.trim() : "";
const item = typeof filters.item === "string" ? filters.item.trim() : "";
const warehouse = typeof filters.warehouse === "string" ? filters.warehouse.trim() : "";
const organization = typeof filters.organization === "string" ? filters.organization.trim() : "";
const documentRef = typeof filters.document_ref === "string" ? filters.document_ref.trim() : "";
if (intent === "account_balance_snapshot" || intent === "documents_forming_balance") {
if (account) {
@@ -218,6 +219,15 @@ function resolvePrimaryAnchor(intent, filters) {
ambiguity_count: 0
};
}
if (organization) {
return {
anchor_type: "organization",
anchor_value_raw: organization,
anchor_value_resolved: organization,
resolver_confidence: "medium",
ambiguity_count: 0
};
}
if (documentRef) {
return {
anchor_type: "document_ref",
@@ -242,15 +252,24 @@ function refineAnchorFromRows(anchor, rows) {
if (anchor.anchor_type !== "counterparty" &&
anchor.anchor_type !== "contract" &&
anchor.anchor_type !== "item" &&
anchor.anchor_type !== "warehouse") {
anchor.anchor_type !== "warehouse" &&
anchor.anchor_type !== "organization") {
return anchor;
}
const needleRaw = String(anchor.anchor_value_raw ?? "").trim();
if (!needleRaw) {
return anchor;
}
const searchableRows = anchor.anchor_type === "item" || anchor.anchor_type === "warehouse"
? rows.flatMap((row) => [row.registrator, row.item ?? "", row.warehouse ?? "", row.account_dt ?? "", row.account_kt ?? "", ...row.analytics])
const searchableRows = anchor.anchor_type === "item" || anchor.anchor_type === "warehouse" || anchor.anchor_type === "organization"
? rows.flatMap((row) => [
row.registrator,
row.item ?? "",
row.warehouse ?? "",
row.organization ?? "",
row.account_dt ?? "",
row.account_kt ?? "",
...row.analytics
])
: rows.flatMap((row) => row.analytics);
const candidates = uniqueStrings(searchableRows
.map((value) => value.trim())
@@ -0,0 +1,138 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeAddressLlmSemanticHints = normalizeAddressLlmSemanticHints;
exports.applyAddressLlmSemanticHintsToExtraction = applyAddressLlmSemanticHintsToExtraction;
function toNonEmptyString(value) {
if (value === null || value === undefined) {
return null;
}
const normalized = String(value).trim();
return normalized.length > 0 ? normalized : null;
}
function normalizeToken(value) {
return String(value ?? "")
.trim()
.toLowerCase()
.replace(/\s+/g, "_");
}
function normalizeAddressLlmSemanticHints(value) {
if (!value || typeof value !== "object") {
return null;
}
const source = value;
const scopeToken = normalizeToken(source.scope_target_kind);
const dateToken = normalizeToken(source.date_scope_kind);
const scopeTargetKind = scopeToken === "self_scope" ||
scopeToken === "selected_object" ||
scopeToken === "organization" ||
scopeToken === "warehouse" ||
scopeToken === "counterparty" ||
scopeToken === "contract" ||
scopeToken === "item"
? scopeToken
: "none";
const dateScopeKind = dateToken === "explicit" || dateToken === "implicit_current" ? dateToken : "missing";
return {
scope_target_kind: scopeTargetKind,
scope_target_text: toNonEmptyString(source.scope_target_text),
date_scope_kind: dateScopeKind,
self_scope_detected: source.self_scope_detected === true || scopeTargetKind === "self_scope",
selected_object_scope_detected: source.selected_object_scope_detected === true || scopeTargetKind === "selected_object"
};
}
function defaultSemanticFrame(extraction) {
return (extraction.semantic_frame ?? {
scope_kind: "none",
anchor_kind: "none",
anchor_value: null,
date_scope_kind: "none",
date_basis_hint: null,
self_scope_detected: false,
selected_object_scope_detected: false
});
}
function pushWarning(warnings, value) {
if (!warnings.includes(value)) {
warnings.push(value);
}
}
function applyDateScopeHint(frame, dateScopeKind) {
if (dateScopeKind === "explicit") {
frame.date_scope_kind = "explicit";
return;
}
if (dateScopeKind === "implicit_current" && frame.date_scope_kind !== "explicit") {
frame.date_scope_kind = "implicit_current";
frame.date_basis_hint = "implicit_current_snapshot";
}
}
function applyAddressLlmSemanticHintsToExtraction(extraction, semanticHintsInput) {
const semanticHints = normalizeAddressLlmSemanticHints(semanticHintsInput);
if (!semanticHints) {
return extraction;
}
const extractedFilters = { ...(extraction.extracted_filters ?? {}) };
const warnings = [...(Array.isArray(extraction.warnings) ? extraction.warnings : [])];
const semanticFrame = { ...defaultSemanticFrame(extraction) };
const scopeTargetText = semanticHints.scope_target_text;
applyDateScopeHint(semanticFrame, semanticHints.date_scope_kind);
if (semanticHints.self_scope_detected) {
semanticFrame.scope_kind = "implicit_self_scope";
semanticFrame.anchor_kind = "self_scope";
semanticFrame.anchor_value = null;
semanticFrame.self_scope_detected = true;
}
if (semanticHints.selected_object_scope_detected) {
if (semanticFrame.scope_kind === "none") {
semanticFrame.scope_kind = "selected_object_scope";
semanticFrame.anchor_kind = "selected_object";
semanticFrame.anchor_value = null;
}
semanticFrame.selected_object_scope_detected = true;
}
if (semanticHints.scope_target_kind === "organization" && scopeTargetText) {
extractedFilters.organization = scopeTargetText;
pushWarning(warnings, "organization_from_llm_semantics");
if (toNonEmptyString(extractedFilters.warehouse)) {
delete extractedFilters.warehouse;
pushWarning(warnings, "warehouse_cleared_by_llm_organization_semantics");
}
semanticFrame.scope_kind = "explicit_anchor";
semanticFrame.anchor_kind = "organization";
semanticFrame.anchor_value = scopeTargetText;
}
if (semanticHints.scope_target_kind === "warehouse" && scopeTargetText) {
extractedFilters.warehouse = scopeTargetText;
pushWarning(warnings, "warehouse_from_llm_semantics");
semanticFrame.scope_kind = "explicit_anchor";
semanticFrame.anchor_kind = "warehouse";
semanticFrame.anchor_value = scopeTargetText;
}
if (semanticHints.scope_target_kind === "counterparty" && scopeTargetText) {
extractedFilters.counterparty = scopeTargetText;
pushWarning(warnings, "counterparty_from_llm_semantics");
semanticFrame.scope_kind = "explicit_anchor";
semanticFrame.anchor_kind = "counterparty";
semanticFrame.anchor_value = scopeTargetText;
}
if (semanticHints.scope_target_kind === "contract" && scopeTargetText) {
extractedFilters.contract = scopeTargetText;
pushWarning(warnings, "contract_from_llm_semantics");
semanticFrame.scope_kind = "explicit_anchor";
semanticFrame.anchor_kind = "contract";
semanticFrame.anchor_value = scopeTargetText;
}
if (semanticHints.scope_target_kind === "item" && scopeTargetText) {
extractedFilters.item = scopeTargetText;
pushWarning(warnings, "item_from_llm_semantics");
semanticFrame.scope_kind = "explicit_anchor";
semanticFrame.anchor_kind = "item";
semanticFrame.anchor_value = scopeTargetText;
}
return {
...extraction,
extracted_filters: extractedFilters,
warnings,
semantic_frame: semanticFrame
};
}