"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveTemporalGuard = resolveTemporalGuard; exports.applyTemporalHintToExecutionPlan = applyTemporalHintToExecutionPlan; exports.resolveDomainPolarityGuard = resolveDomainPolarityGuard; exports.applyPolarityHintToExecutionPlan = applyPolarityHintToExecutionPlan; exports.applyDomainPolarityGuardToRetrievalResults = applyDomainPolarityGuardToRetrievalResults; exports.applyEvidenceAdmissibilityGate = applyEvidenceAdmissibilityGate; exports.evaluateGroundedAnswerEligibility = evaluateGroundedAnswerEligibility; exports.applyEligibilityToGroundingCheck = applyEligibilityToGroundingCheck; const JULY_YEAR = "2020"; const JULY_MONTH = "07"; const JULY_WINDOW = { from: "2020-07-01", to: "2020-07-31", granularity: "month" }; const RUS_MONTH_TO_NUMBER = { января: "01", январь: "01", февраля: "02", февраль: "02", марта: "03", март: "03", апреля: "04", апрель: "04", мая: "05", май: "05", июня: "06", июнь: "06", июля: "07", июль: "07", августа: "08", август: "08", сентября: "09", сентябрь: "09", октября: "10", октябрь: "10", ноября: "11", ноябрь: "11", декабря: "12", декабрь: "12" }; function uniqueStrings(values) { return Array.from(new Set(values.map((item) => String(item ?? "").trim()).filter(Boolean))); } function toObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value; } function toObjectArray(value) { if (!Array.isArray(value)) { return []; } return value.filter((item) => Boolean(item) && typeof item === "object"); } function accountPrefix(value) { const token = String(value ?? "").trim(); const match = token.match(/^(\d{2})/); return match ? match[1] : null; } function extractAccountsFromText(text) { const lower = String(text ?? "").toLowerCase(); const accounts = new Set(); const contextualPattern = /(?:\b(?:сч(?:е|ё)т(?:а|у|ом|ов)?|account|schet)\b\s*(?:№|#|:)?\s*)(\d{2}(?:\.\d{2})?)/giu; let contextualMatch = null; while ((contextualMatch = contextualPattern.exec(lower)) !== null) { const token = String(contextualMatch[1] ?? "").trim(); if (token) { accounts.add(token); } } const pairPattern = /\b(\d{2}\.\d{2})\s*\/\s*(\d{2}\.\d{2})\b/g; let pairMatch = null; while ((pairMatch = pairPattern.exec(lower)) !== null) { const left = String(pairMatch[1] ?? "").trim(); const right = String(pairMatch[2] ?? "").trim(); if (left) accounts.add(left); if (right) accounts.add(right); } return Array.from(accounts); } function extractAccountsFromUnknown(value) { if (Array.isArray(value)) { return uniqueStrings(value.flatMap((item) => extractAccountsFromUnknown(item))); } if (value && typeof value === "object") { return uniqueStrings(Object.values(value).flatMap((item) => extractAccountsFromUnknown(item))); } if (typeof value !== "string" && typeof value !== "number") { return []; } const text = String(value); const matches = text.match(/\b\d{2}(?:\.\d{2})?\b/g) ?? []; return uniqueStrings(matches); } function normalizeTwoDigits(value) { return String(value).padStart(2, "0"); } function parseYear(raw) { const token = String(raw ?? "").trim(); if (token.length === 2) { return `20${token}`; } return token; } function normalizeDateIso(input) { const year = String(input.year ?? "").trim(); const month = normalizeTwoDigits(input.month ?? ""); if (!/^\d{4}$/.test(year) || !/^\d{2}$/.test(month)) { return null; } if (input.day === undefined || input.day === null || String(input.day).trim().length === 0) { return `${year}-${month}`; } const day = normalizeTwoDigits(input.day ?? ""); if (!/^\d{2}$/.test(day)) { return null; } return `${year}-${month}-${day}`; } function parseDateLike(raw) { const value = String(raw ?? "").trim().toLowerCase(); if (!value) { return null; } const isoDay = value.match(/\b(20\d{2})[-/.](0[1-9]|1[0-2])[-/.](0[1-9]|[12]\d|3[01])\b/); if (isoDay) { return normalizeDateIso({ year: isoDay[1], month: isoDay[2], day: isoDay[3] }); } const isoMonth = value.match(/\b(20\d{2})[-/.](0[1-9]|1[0-2])\b/); if (isoMonth) { return normalizeDateIso({ year: isoMonth[1], month: isoMonth[2] }); } const dayMonthYear = value.match(/\b(0?[1-9]|[12]\d|3[01])[./-](0?[1-9]|1[0-2])[./-](\d{2}|\d{4})\b/); if (dayMonthYear) { return normalizeDateIso({ year: parseYear(dayMonthYear[3]), month: dayMonthYear[2], day: dayMonthYear[1] }); } const rusMonthYear = value.match(/\b(январь|февраль|март|апрель|май|июнь|июль|август|сентябрь|октябрь|ноябрь|декабрь)\s+(20\d{2})\b/i); if (rusMonthYear) { const month = RUS_MONTH_TO_NUMBER[String(rusMonthYear[1] ?? "").toLowerCase()]; if (!month) return null; return normalizeDateIso({ year: rusMonthYear[2], month }); } return null; } function monthStart(iso) { const month = String(iso ?? "").slice(0, 7); if (!/^\d{4}-\d{2}$/.test(month)) { return null; } return `${month}-01`; } function normalizeEvidenceDate(value) { const parsed = parseDateLike(value); if (!parsed) { return null; } if (/^\d{4}-\d{2}-\d{2}$/.test(parsed)) { return parsed; } if (/^\d{4}-\d{2}$/.test(parsed)) { return monthStart(parsed); } return null; } function isPeriodWithinWindow(periodIso, window) { const normalized = normalizeEvidenceDate(periodIso); if (!normalized) { return false; } return normalized >= window.from && normalized <= window.to; } function extractNormalizedFragments(normalized) { if (!normalized || typeof normalized !== "object") { return []; } const source = normalized; return toObjectArray(source.fragments); } function normalizedAnchorFromFragments(normalized) { const fragments = extractNormalizedFragments(normalized); for (const fragment of fragments) { const timeScope = toObject(fragment.time_scope); const type = String(timeScope?.type ?? "").trim().toLowerCase(); const value = String(timeScope?.value ?? "").trim(); if (!value) { continue; } const parsed = parseDateLike(value); if (parsed) { return { value: parsed, source: `normalized_time_scope:${type || "unknown"}` }; } if (/(?:июл|july)/i.test(value)) { return { value: `${JULY_YEAR}-${JULY_MONTH}`, source: `normalized_time_scope:${type || "unknown"}` }; } } return { value: null, source: "normalized_time_scope:missing" }; } function collectRawTemporalAnchorText(userMessage, companyAnchors) { return [userMessage, ...(companyAnchors?.periods ?? []), ...(companyAnchors?.dates ?? [])] .map((item) => String(item ?? "").trim()) .filter(Boolean) .join(" "); } function resolveJulyAnchor(rawText) { const raw = String(rawText ?? ""); const lower = raw.toLowerCase(); const explicitYear = lower.match(/\b(20\d{2})\b/)?.[1] ?? null; const dayByNamedJuly = lower.match(/(?:^|\D)(0?[1-9]|[12]\d|3[01])\s+(?:июл(?:я|ь)?|july)(?:\D|$)/i); const dayByNumeric = lower.match(/\b(0?[1-9]|[12]\d|3[01])[./-](0?7)(?:[./-](\d{2}|\d{4}))?\b/); const monthByNamed = /(июл|july)/i.test(lower); const monthByNumeric = /\b20\d{2}[-/.]0?7\b/.test(lower); if (!dayByNamedJuly && !dayByNumeric && !monthByNamed && !monthByNumeric) { return { raw: null, resolved: null, source: "no_july_anchor", window: null, applyGuard: false }; } const dayValue = dayByNamedJuly?.[1] ?? dayByNumeric?.[1] ?? null; const explicitDayYear = dayByNumeric?.[3] ? parseYear(dayByNumeric[3]) : null; const anchorYear = explicitDayYear ?? explicitYear ?? JULY_YEAR; const applyGuard = anchorYear === JULY_YEAR; if (!applyGuard) { return { raw: dayByNamedJuly?.[0] ?? dayByNumeric?.[0] ?? (monthByNamed ? "июль" : "07"), resolved: normalizeDateIso({ year: anchorYear, month: JULY_MONTH, ...(dayValue ? { day: dayValue } : {}) }), source: "explicit_non_snapshot_year", window: null, applyGuard: false }; } if (dayValue) { const dayIso = normalizeDateIso({ year: JULY_YEAR, month: JULY_MONTH, day: dayValue }); if (dayIso) { return { raw: dayByNamedJuly?.[0] ?? dayByNumeric?.[0] ?? null, resolved: dayIso, source: "company_snapshot_july_day_lock", window: { from: dayIso, to: dayIso, granularity: "day" }, applyGuard: true }; } } return { raw: monthByNamed ? "июль" : "2020-07", resolved: `${JULY_YEAR}-${JULY_MONTH}`, source: "company_snapshot_july_month_lock", window: JULY_WINDOW, applyGuard: true }; } function resolveTemporalGuard(input) { const rawAnchorText = collectRawTemporalAnchorText(input.userMessage, input.companyAnchors); const julyAnchor = resolveJulyAnchor(rawAnchorText); const normalizedAnchor = normalizedAnchorFromFragments(input.normalized); const reasonCodes = []; if (!julyAnchor.applyGuard) { return { raw_time_anchor: julyAnchor.raw, resolved_time_anchor: normalizedAnchor.value, temporal_resolution_source: normalizedAnchor.source, temporal_guard_applied: false, temporal_guard_outcome: "passed", primary_period_window: null, reason_codes: [] }; } let outcome = "passed"; if (normalizedAnchor.value && julyAnchor.window && !isPeriodWithinWindow(normalizedAnchor.value, julyAnchor.window)) { outcome = "failed_out_of_snapshot_window"; reasonCodes.push("normalized_anchor_out_of_snapshot_window"); } else if (!normalizedAnchor.value && !julyAnchor.resolved) { outcome = "ambiguous_limited"; reasonCodes.push("missing_time_anchor_under_snapshot_lock"); } return { raw_time_anchor: julyAnchor.raw, resolved_time_anchor: julyAnchor.resolved ?? normalizedAnchor.value, temporal_resolution_source: julyAnchor.source, temporal_guard_applied: true, temporal_guard_outcome: outcome, primary_period_window: julyAnchor.window, reason_codes: reasonCodes }; } function applyTemporalHintToExecutionPlan(executionPlan, temporal) { if (!temporal.temporal_guard_applied) { return executionPlan; } const hint = temporal.primary_period_window?.granularity === "day" && temporal.resolved_time_anchor ? `в рамках company snapshot даты ${temporal.resolved_time_anchor}` : `в рамках company snapshot июля 2020 (${JULY_WINDOW.from}..${JULY_WINDOW.to})`; return executionPlan.map((item) => { if (!item.should_execute) { return item; } const text = String(item.fragment_text ?? "").trim(); if (/2020-07|июл|july/i.test(text)) { return item; } return { ...item, fragment_text: `${text}; ${hint}`.trim() }; }); } function resolveDomainPolarityGuard(input) { const lower = String(input.userMessage ?? "").toLowerCase(); const accounts = uniqueStrings([...(input.companyAnchors?.accounts ?? []), ...extractAccountsFromText(lower)]); const prefixes = new Set(accounts.map((item) => accountPrefix(item)).filter((item) => Boolean(item))); const settlementSignal = input.focusDomainHint === "settlements_60_62" || prefixes.has("60") || prefixes.has("62") || prefixes.has("51") || prefixes.has("76") || /(?:расч[её]т|оплат|аванс|долг|settlement|payment|tail|хвост|незакры|зач[её]т)/i.test(lower); if (!settlementSignal) { return { applied: false, polarity: "not_applicable", outcome: "not_applicable", supplier_score: 0, customer_score: 0, account_scope: accounts, rejected_problem_units: 0, rejected_evidence: 0, critical_contradiction: false, reason_codes: [] }; } const supplierScore = (/(?:поставщ|supplier|vendor|кредитор|обязательств|payable)/i.test(lower) ? 2 : 0) + (prefixes.has("60") ? 2 : 0) + (/(?:счет\s*60|по\s*60)/i.test(lower) ? 1 : 0); const customerScore = (/(?:покупат|customer|buyer|дебитор|receivable)/i.test(lower) ? 2 : 0) + (prefixes.has("62") ? 2 : 0) + (/(?:счет\s*62|по\s*62)/i.test(lower) ? 1 : 0); let polarity = "mixed_or_unresolved"; if (supplierScore > 0 && customerScore === 0) { polarity = "supplier_payable"; } else if (customerScore > 0 && supplierScore === 0) { polarity = "customer_receivable"; } const unresolved = polarity === "mixed_or_unresolved"; return { applied: true, polarity, outcome: unresolved ? "limited_unresolved_polarity" : "passed", supplier_score: supplierScore, customer_score: customerScore, account_scope: accounts, rejected_problem_units: 0, rejected_evidence: 0, critical_contradiction: unresolved, reason_codes: unresolved ? ["unresolved_supplier_customer_polarity"] : [] }; } function applyPolarityHintToExecutionPlan(executionPlan, polarity) { if (!polarity.applied || polarity.polarity === "mixed_or_unresolved" || polarity.polarity === "not_applicable") { return executionPlan; } const hint = polarity.polarity === "supplier_payable" ? "контекст: расчеты с поставщиком, обязательство, счет 60" : "контекст: расчеты с покупателем, дебиторская задолженность, счет 62"; return executionPlan.map((item) => { if (!item.should_execute) { return item; } const text = String(item.fragment_text ?? "").trim(); if (polarity.polarity === "supplier_payable" && /(поставщ|supplier|счет\s*60|по\s*60)/i.test(text)) { return item; } if (polarity.polarity === "customer_receivable" && /(покупат|customer|счет\s*62|по\s*62)/i.test(text)) { return item; } return { ...item, fragment_text: `${text}; ${hint}`.trim() }; }); } function containsReceivableSignal(value) { return /(?:customer_settlement|stale_receivable|receivable_closed|receivable|дебитор)/i.test(value); } function containsPayableSignal(value) { return /(?:bank_settlement|payable|обязательств|supplier|поставщ|счет\s*60|\b60(?:\.\d{2})?\b)/i.test(value); } function problemUnitCorpus(unit) { return [ unit.lifecycle_domain ?? "", unit.problem_unit_type, unit.business_defect_class ?? "", unit.failed_expected_edge ?? "", unit.invalid_transition ?? "", unit.mechanism_summary ?? "", unit.business_lifecycle_interpretation ?? "", ...(unit.affected_accounts ?? []) ] .join(" ") .toLowerCase(); } function isProblemUnitCompatible(unit, polarity) { if (polarity === "supplier_payable") { return !containsReceivableSignal(problemUnitCorpus(unit)); } if (polarity === "customer_receivable") { return !containsPayableSignal(problemUnitCorpus(unit)); } return true; } function evidenceCorpus(evidence) { return JSON.stringify({ limitation: evidence.limitation, payload: evidence.payload, mechanism: evidence.mechanism_note, source_ref: evidence.source_ref, pointer: evidence.pointer }).toLowerCase(); } function evidenceAccounts(evidence) { const payload = toObject(evidence.payload); const direct = uniqueStrings([ ...extractAccountsFromUnknown(payload?.account_context), ...extractAccountsFromUnknown(payload?.account_debit), ...extractAccountsFromUnknown(payload?.account_credit) ]); if (direct.length > 0) { return direct; } return uniqueStrings([ ...extractAccountsFromUnknown(evidence.payload), ...extractAccountsFromUnknown(evidence.pointer ?? null) ]); } function isEvidenceCompatibleWithPolarity(evidence, polarity) { const corpus = evidenceCorpus(evidence); const accounts = evidenceAccounts(evidence).map((item) => accountPrefix(item)).filter((item) => Boolean(item)); if (polarity === "supplier_payable") { if (containsReceivableSignal(corpus)) { return false; } if (accounts.length > 0 && accounts.every((item) => item === "62")) { return false; } } if (polarity === "customer_receivable") { if (containsPayableSignal(corpus)) { return false; } if (accounts.length > 0 && accounts.every((item) => item === "60")) { return false; } } return true; } function applyDomainPolarityGuardToRetrievalResults(input) { if (!input.guard.applied || input.guard.polarity === "not_applicable" || input.guard.polarity === "mixed_or_unresolved") { return { retrievalResults: input.retrievalResults, audit: input.guard }; } let rejectedProblemUnits = 0; let rejectedEvidence = 0; let criticalContradiction = false; const adjusted = input.retrievalResults.map((result) => { const originalUnits = Array.isArray(result.problem_units) ? result.problem_units : []; const filteredUnits = originalUnits.filter((unit) => isProblemUnitCompatible(unit, input.guard.polarity)); rejectedProblemUnits += Math.max(0, originalUnits.length - filteredUnits.length); const originalEvidence = Array.isArray(result.evidence) ? result.evidence : []; const filteredEvidence = originalEvidence.filter((item) => isEvidenceCompatibleWithPolarity(item, input.guard.polarity)); rejectedEvidence += Math.max(0, originalEvidence.length - filteredEvidence.length); if (originalUnits.length > 0 && filteredUnits.length === 0 && originalEvidence.length > 0 && filteredEvidence.length === 0) { criticalContradiction = true; } return { ...result, evidence: filteredEvidence, ...(Array.isArray(result.problem_units) ? { problem_units: filteredUnits } : {}) }; }); const reasonCodes = []; if (rejectedProblemUnits > 0) { reasonCodes.push("polarity_problem_unit_filter_applied"); } if (rejectedEvidence > 0) { reasonCodes.push("polarity_evidence_filter_applied"); } if (criticalContradiction) { reasonCodes.push("critical_domain_polarity_contradiction"); } return { retrievalResults: adjusted, audit: { ...input.guard, rejected_problem_units: rejectedProblemUnits, rejected_evidence: rejectedEvidence, critical_contradiction: criticalContradiction, outcome: criticalContradiction ? "blocked_conflict" : "passed", reason_codes: uniqueStrings([...(input.guard.reason_codes ?? []), ...reasonCodes]) } }; } function initRejectBreakdown() { return { wrong_period: 0, wrong_domain: 0, wrong_account_scope: 0, weak_source_mapping: 0, zero_live_match: 0, future_dated_or_out_of_window: 0 }; } function isVatPrefix(prefix) { return prefix === "19" || prefix === "68"; } function isSettlementPrefix(prefix) { return prefix === "51" || prefix === "60" || prefix === "62" || prefix === "76"; } function isMonthClosePrefix(prefix) { const numeric = Number(prefix); if (prefix === "97") { return true; } if (!Number.isFinite(numeric)) { return false; } return numeric >= 20 && numeric <= 44; } function expectedAccountPrefixes(input) { const explicit = uniqueStrings([...(input.companyAnchors?.accounts ?? []), ...extractAccountsFromText(input.userMessage)]) .map((item) => accountPrefix(item)) .filter((item) => Boolean(item)); if (explicit.length > 0) { return uniqueStrings(explicit); } if (input.focusDomainHint === "vat_document_register_book") { return ["19", "68"]; } if (input.focusDomainHint === "month_close_costs_20_44") { return ["20", "25", "26", "44", "97", "01", "02", "08"]; } if (input.focusDomainHint === "settlements_60_62") { if (input.polarity === "supplier_payable") { return ["60", "51", "76"]; } if (input.polarity === "customer_receivable") { return ["62", "51"]; } return ["60", "62", "51", "76"]; } return []; } function isLiveEvidence(evidence) { const payload = toObject(evidence.payload); if (String(payload?.source_layer ?? "").trim().toLowerCase() === "mcp_live_probe") { return true; } const sourceEntity = String(evidence.pointer?.source?.entity ?? "").toLowerCase(); return sourceEntity.includes("mcplivemovement"); } function extractEvidencePeriod(evidence) { const payload = toObject(evidence.payload); return (String(evidence.source_ref?.period ?? "").trim() || String(evidence.pointer?.source?.period ?? "").trim() || String(payload?.period ?? "").trim() || null); } function isExpectedAccountScopeMatch(accounts, expectedPrefixes) { if (accounts.length === 0 || expectedPrefixes.length === 0) { return true; } const prefixes = accounts.map((item) => accountPrefix(item)).filter((item) => Boolean(item)); if (prefixes.length === 0) { return true; } return prefixes.some((prefix) => expectedPrefixes.includes(prefix)); } function hasWrongDomainByAccounts(accounts, focusDomainHint) { if (accounts.length === 0 || !focusDomainHint) { return false; } const prefixes = accounts.map((item) => accountPrefix(item)).filter((item) => Boolean(item)); if (prefixes.length === 0) { return false; } if (focusDomainHint === "settlements_60_62") { return prefixes.every((prefix) => isVatPrefix(prefix)); } if (focusDomainHint === "vat_document_register_book") { return prefixes.every((prefix) => isSettlementPrefix(prefix) || isMonthClosePrefix(prefix)); } if (focusDomainHint === "month_close_costs_20_44") { return prefixes.every((prefix) => isSettlementPrefix(prefix) || isVatPrefix(prefix)); } return false; } function extractLiveMatchedRows(result) { const summary = toObject(result.summary); const live = toObject(summary?.live_mcp); const value = Number(live?.matched_rows); return Number.isFinite(value) ? value : null; } function liveAccountScopeWasApplied(result) { const summary = toObject(result.summary); const live = toObject(summary?.live_mcp); const accountScope = live?.account_scope; return Array.isArray(accountScope) && accountScope.length > 0; } function evidenceAdmissibilityReasons(input) { const reasons = new Set(); if (input.evidence.limitation?.reason_code === "weak_source_mapping") { reasons.add("weak_source_mapping"); } if (input.zeroLiveMatch && isLiveEvidence(input.evidence)) { reasons.add("zero_live_match"); } const period = extractEvidencePeriod(input.evidence); if (period && input.temporal.primary_period_window) { const normalized = normalizeEvidenceDate(period); if (normalized && normalized > input.temporal.primary_period_window.to) { reasons.add("future_dated_or_out_of_window"); } else if (normalized && !isPeriodWithinWindow(normalized, input.temporal.primary_period_window)) { reasons.add("wrong_period"); } } const accounts = evidenceAccounts(input.evidence); if (!isExpectedAccountScopeMatch(accounts, input.expectedPrefixes)) { reasons.add("wrong_account_scope"); } if (hasWrongDomainByAccounts(accounts, input.focusDomainHint)) { reasons.add("wrong_domain"); } return Array.from(reasons); } function isLiveItem(item) { return String(item.source_layer ?? "").trim().toLowerCase() === "mcp_live_probe"; } function itemPeriod(item) { const value = String(item.period ?? item.Period ?? "").trim(); return value || null; } function itemAccounts(item) { const direct = uniqueStrings([ ...extractAccountsFromUnknown(item.account_context), ...extractAccountsFromUnknown(item.account_debit), ...extractAccountsFromUnknown(item.account_credit) ]); if (direct.length > 0) { return direct; } return uniqueStrings([...extractAccountsFromUnknown(item)]); } function itemRejectReasons(input) { const reasons = new Set(); if (input.zeroLiveMatch && isLiveItem(input.item)) { reasons.add("zero_live_match"); } const period = itemPeriod(input.item); if (period && input.temporal.primary_period_window) { const normalized = normalizeEvidenceDate(period); if (normalized && normalized > input.temporal.primary_period_window.to) { reasons.add("future_dated_or_out_of_window"); } else if (normalized && !isPeriodWithinWindow(normalized, input.temporal.primary_period_window)) { reasons.add("wrong_period"); } } const accounts = itemAccounts(input.item); if (!isExpectedAccountScopeMatch(accounts, input.expectedPrefixes)) { reasons.add("wrong_account_scope"); } if (hasWrongDomainByAccounts(accounts, input.focusDomainHint)) { reasons.add("wrong_domain"); } return Array.from(reasons); } function addRejectReason(target, reason) { target[reason] += 1; } function applyEvidenceAdmissibilityGate(input) { const rejectBreakdown = initRejectBreakdown(); const categoryBreakdown = { hard_evidence: 0, supporting_signal: 0, inadmissible_noise: 0 }; let candidateEvidenceTotal = 0; let admissibleEvidenceCount = 0; let rejectedEvidenceCount = 0; let rejectedItemCount = 0; const expectedPrefixes = expectedAccountPrefixes({ focusDomainHint: input.focusDomainHint, polarity: input.polarity, companyAnchors: input.companyAnchors, userMessage: input.userMessage }); const adjusted = input.retrievalResults.map((result) => { const matchedRows = extractLiveMatchedRows(result); const zeroLiveMatch = matchedRows === 0 && liveAccountScopeWasApplied(result); const evidence = Array.isArray(result.evidence) ? result.evidence : []; candidateEvidenceTotal += evidence.length; const admissibleEvidence = []; for (const item of evidence) { const reasons = evidenceAdmissibilityReasons({ evidence: item, temporal: input.temporal, focusDomainHint: input.focusDomainHint, expectedPrefixes, zeroLiveMatch }); if (reasons.length > 0) { rejectedEvidenceCount += 1; categoryBreakdown.inadmissible_noise += 1; for (const reason of reasons) { addRejectReason(rejectBreakdown, reason); } continue; } const limitationCode = String(item.limitation?.reason_code ?? "").trim(); if (!limitationCode && item.confidence !== "low") { categoryBreakdown.hard_evidence += 1; } else { categoryBreakdown.supporting_signal += 1; } admissibleEvidenceCount += 1; admissibleEvidence.push(item); } const items = Array.isArray(result.items) ? result.items : []; const admissibleItems = []; for (const item of items) { const reasons = itemRejectReasons({ item, temporal: input.temporal, focusDomainHint: input.focusDomainHint, expectedPrefixes, zeroLiveMatch }); if (reasons.length > 0) { rejectedItemCount += 1; for (const reason of reasons) { addRejectReason(rejectBreakdown, reason); } continue; } admissibleItems.push(item); } const summary = { ...(toObject(result.summary) ?? {}), evidence_admissibility_gate: { candidate_evidence: evidence.length, admissible_evidence: admissibleEvidence.length, rejected_evidence: Math.max(0, evidence.length - admissibleEvidence.length), rejected_items: Math.max(0, items.length - admissibleItems.length) } }; const limitations = [...(result.limitations ?? [])]; if (zeroLiveMatch) { limitations.push("Live probe matched_rows=0; live evidence excluded from grounded answer."); } if (admissibleEvidence.length === 0 && evidence.length > 0) { limitations.push("Admissibility gate removed non-admissible evidence for current scope."); } const normalizedStatus = result.status === "ok" && admissibleEvidence.length === 0 && admissibleItems.length === 0 ? "partial" : result.status; return { ...result, status: normalizedStatus, items: admissibleItems, evidence: admissibleEvidence, summary, limitations: uniqueStrings(limitations) }; }); const reasonCodes = []; if (rejectedEvidenceCount > 0) { reasonCodes.push("inadmissible_evidence_filtered"); } if (admissibleEvidenceCount === 0) { reasonCodes.push("no_admissible_evidence_for_grounded_answer"); } if (rejectedItemCount > 0) { reasonCodes.push("inadmissible_items_filtered"); } return { retrievalResults: adjusted, audit: { candidate_evidence_total: candidateEvidenceTotal, admissible_evidence_count: admissibleEvidenceCount, rejected_evidence_count: rejectedEvidenceCount, rejected_item_count: rejectedItemCount, reject_breakdown: rejectBreakdown, category_breakdown: categoryBreakdown, reason_codes: uniqueStrings(reasonCodes) } }; } function evaluateGroundedAnswerEligibility(input) { const temporalPassed = input.temporal.temporal_guard_outcome === "passed"; const polarityPassed = !input.polarity.applied || input.polarity.outcome === "passed" || input.polarity.outcome === "not_applicable"; const admissibleEvidenceCount = input.evidence.admissible_evidence_count; const criticalContradiction = Boolean(input.polarity.critical_contradiction); const eligible = temporalPassed && polarityPassed && admissibleEvidenceCount > 0 && !criticalContradiction; const reasonCodes = []; if (!temporalPassed) { reasonCodes.push(`temporal_guard_${input.temporal.temporal_guard_outcome}`); } if (!polarityPassed) { reasonCodes.push(`polarity_guard_${input.polarity.outcome}`); } if (admissibleEvidenceCount <= 0) { reasonCodes.push("admissible_evidence_count_zero"); } if (criticalContradiction) { reasonCodes.push("critical_domain_or_account_contradiction"); } return { eligible, temporal_passed: temporalPassed, polarity_passed: polarityPassed, admissible_evidence_count: admissibleEvidenceCount, critical_contradiction: criticalContradiction, outcome: eligible ? "grounded_allowed" : "limited_or_insufficient_evidence", reason_codes: uniqueStrings(reasonCodes) }; } function applyEligibilityToGroundingCheck(groundingCheck, eligibility) { if (eligibility.eligible) { return groundingCheck; } const status = eligibility.admissible_evidence_count <= 0 || !eligibility.temporal_passed ? "no_grounded_answer" : "partial"; const reasonMap = { admissible_evidence_count_zero: "Недостаточно допустимого evidence для обоснованного ответа.", critical_domain_or_account_contradiction: "Есть критическое противоречие по domain/account scope.", temporal_guard_failed_out_of_snapshot_window: "Temporal anchor вышел за окно company snapshot (июль 2020).", temporal_guard_ambiguous_limited: "Temporal anchor не разрешен надежно в пределах company snapshot.", polarity_guard_limited_unresolved_polarity: "Не удалось надежно определить supplier/customer polarity.", polarity_guard_blocked_conflict: "Обнаружен конфликт supplier/customer polarity в retrieval-контуре." }; const reasons = [ ...(Array.isArray(groundingCheck.reasons) ? groundingCheck.reasons : []), ...eligibility.reason_codes.map((code) => reasonMap[code] ?? code) ]; return { ...groundingCheck, status, reasons: uniqueStrings(reasons) }; }