diff --git a/llm_normalizer/backend/dist/services/answerComposer.js b/llm_normalizer/backend/dist/services/answerComposer.js index 970372c..ed7c681 100644 --- a/llm_normalizer/backend/dist/services/answerComposer.js +++ b/llm_normalizer/backend/dist/services/answerComposer.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.sanitizeAssistantReplyForUserFacing = sanitizeAssistantReplyForUserFacing; exports.composeAssistantAnswer = composeAssistantAnswer; function fallbackFromSummary(routeSummary) { if (!routeSummary || routeSummary.mode !== "deterministic_v2") { @@ -207,6 +208,10 @@ const HUMAN_SIGNAL_MAP = { amount_independent_risk: "Проблема не выглядит случайной суммовой погрешностью.", wrong_document_type: "Есть признак неверного типа закрывающего документа.", fixed_asset_card_mismatch: "Есть несоответствие между карточкой ОС, документом движения и начислением.", + contradictory_asset_state: "Состояние объекта ОС выглядит противоречивым по текущей опоре.", + disposed: "Есть признак выбытия объекта ОС в цепочке состояния.", + invalid_document_or_posting_transition: "Переход состояния ОС не подтвержден документами и проводками.", + asset_card_to_depreciation: "Переход от карточки ОС к начислению амортизации подтвержден не полностью.", supplier_tail_analysis: "Есть признаки незавершенного расчетного контура по поставщикам.", cross_entity_breakage: "Есть разрыв между связанными объектами в одной цепочке.", deferred_expense_to_writeoff: "Ожидаемая цепочка списания РБП выглядит незавершенной.", @@ -576,8 +581,13 @@ function stripSyntheticPlaceholders(value) { .trim(); } function sanitizeUserFacingReply(value) { - const withoutDebugBlocks = String(value ?? "") + const raw = String(value ?? ""); + const hardCutMatch = raw.match(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b/i); + const preCut = hardCutMatch ? raw.slice(0, hardCutMatch.index) : raw; + const withoutDebugBlocks = preCut .replace(/###\s*debug_payload_json[\s\S]*?(?:```[\s\S]*?```|$)/gi, "") + .replace(/###\s*technical_breakdown_json[\s\S]*?(?:```[\s\S]*?```|$)/gi, "") + .replace(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") .replace(/```json[\s\S]*?```/gi, ""); const normalized = scrubRawTechnicalRefs(withoutDebugBlocks).replace(/[ \t]+\n/g, "\n"); const cleanedLines = normalized @@ -1174,7 +1184,7 @@ function buildProblemCentricActions(input) { } } if (input.missingAnchors.period && input.mode !== "clarification_required") { - actions.push("Уточните период проверки (например, 2020-06), чтобы подтвердить незавершенное списание без лишнего шума."); + actions.push("Уточните период проверки (например, июль 2020), чтобы подтвердить незавершенное списание без лишнего шума."); } if (input.mode === "clarification_required") { if (input.missingAnchors.period) { @@ -1202,7 +1212,7 @@ function buildProblemCentricClarifications(input) { const questions = []; const unitTypes = new Set(input.units.map((item) => item.problem_unit_type)); if (input.missingAnchors.period) { - questions.push("Уточните период (например, 2020-06), РІ котором РЅСѓР¶РЅРѕ проверить проблемный кластер."); + questions.push("Уточните период (например, июль 2020), в котором нужно проверить проблемный кластер."); } if (input.missingAnchors.account) { questions.push("Уточните счет или СЃРІСЏР·РєСѓ счетов (например, 51/60), РіРґРµ РІС‹ ожидаете дефект."); @@ -1338,6 +1348,14 @@ function asRecordObject(value) { return value; } const EXPLICIT_PERIOD_ANCHOR_PATTERN = /(?:\b20\d{2}(?:[-./](?:0?[1-9]|1[0-2]))?(?:[-./](?:0?[1-9]|[12]\d|3[01]))?\b|\b(?:0?[1-9]|[12]\d|3[01])[./-](?:0?[1-9]|1[0-2])[./-](?:\d{2}|\d{4})\b|\b(?:январ[ьяе]|феврал[ьяе]|март[ае]?|апрел[ьяе]|ма[йея]|июн[ьяе]|июл[ьяе]|август[ае]?|сентябр[ьяе]|октябр[ьяе]|ноябр[ьяе]|декабр[ьяе]|january|february|march|april|may|june|july|august|september|october|november|december)\b)/i; +function hasPeriodAnchorInCompanyAnchors(anchors) { + if (!anchors) { + return false; + } + const dates = Array.isArray(anchors.dates) ? anchors.dates : []; + const periods = Array.isArray(anchors.periods) ? anchors.periods : []; + return dates.some((item) => String(item ?? "").trim().length > 0) || periods.some((item) => String(item ?? "").trim().length > 0); +} function hasPeriodAnchorInRetrieval(results) { for (const result of results) { const summary = asRecordObject(result.summary); @@ -1378,9 +1396,12 @@ function hasAccountAnchorInRetrieval(results) { } return false; } -function detectMissingAnchors(userMessage, retrievalResults = []) { +function detectMissingAnchors(userMessage, retrievalResults = [], options) { const lower = String(userMessage ?? "").toLowerCase(); - const hasPeriod = EXPLICIT_PERIOD_ANCHOR_PATTERN.test(lower) || hasPeriodAnchorInRetrieval(retrievalResults); + const hasPeriod = EXPLICIT_PERIOD_ANCHOR_PATTERN.test(lower) || + hasPeriodAnchorInRetrieval(retrievalResults) || + Boolean(options?.normalizationPeriodExplicit) || + hasPeriodAnchorInCompanyAnchors(options?.companyAnchors); const hasAccount = /(?:\bсчет\b|\baccount\b|\bschet\b|\b(?:0[1-9]|[1-9]\d)(?:\.\d{2})?\b|\b(?:60|62)\.\d{2}\s*\/\s*(?:60|62)\.\d{2}\b)/i.test(lower) || hasAccountAnchorInRetrieval(retrievalResults); const hasDocumentOrObject = /(?:документ|invoice|guid|object|obj|#\d+|\b№\s*[a-zа-я0-9-]+\b|\bid\b|\bref\b|dokument|doc)/i.test(lower); const hasCounterparty = /(?:контрагент|supplier|buyer|customer|kontragent|postavsh|pokupatel|договор|contract)/i.test(lower); @@ -1400,7 +1421,7 @@ function buildClarificationQuestions(input) { return questions; } if (input.missingAnchors.period) { - questions.push("Уточните период проверки (например, 2020-06)."); + questions.push("Уточните период проверки (например, июль 2020)."); } if (input.missingAnchors.account) { questions.push("Уточните счет или РіСЂСѓРїРїСѓ счетов (например, 19, 60, 62)."); @@ -1798,8 +1819,8 @@ function inferP0NarrativeDomain(units) { return "vat_document_register_book"; } if (hasCloseAccount || - units.some((unit) => ["period_close", "deferred_expense", "fixed_asset"].includes(String(unit.lifecycle_domain ?? ""))) || - units.some((unit) => unit.problem_unit_type === "period_risk_cluster" || unit.problem_unit_type === "lifecycle_anomaly_node")) { + units.some((unit) => ["period_close", "deferred_expense"].includes(String(unit.lifecycle_domain ?? ""))) || + units.some((unit) => unit.problem_unit_type === "period_risk_cluster")) { return "month_close_costs_20_44"; } return null; @@ -1842,8 +1863,7 @@ function p0NarrativeDomainFromHint(value) { } if (normalized.includes("month_close_costs_20_44") || normalized.includes("period_close") || - normalized.includes("deferred_expense") || - normalized.includes("fixed_asset")) { + normalized.includes("deferred_expense")) { return "month_close_costs_20_44"; } return null; @@ -2014,7 +2034,21 @@ function evaluateP0DomainEvidenceGrounding(results, focusDomain) { const topClass = classify(top); const hasAnyPrimary = substantive.some((item) => classify(item).inDomain); const hasForeignPrimary = topClass.foreignDomains.length > 0 && !topClass.inDomain; - const blocked = hasForeignPrimary && !hasAnyPrimary && !hasControlledCrossDomainHandoffInResult(top); + const topAccounts = collectResultAccounts(top); + const topDomains = collectResultDomains(top); + const topRelations = collectResultRelations(top); + const vatPrimarySignals = topAccounts.filter((item) => isVatAccountToken(item)).length + + topDomains.filter((item) => isVatDomainToken(item)).length + + topRelations.filter((item) => /invoice_to_vat|source_doc_present|invoice_linked|register_to_book|book_entry_generated|deduction_posted|vat_/i.test(item)).length; + const vatForeignSignals = topAccounts.filter((item) => isSettlementAccountToken(item) || isCloseCostsAccountToken(item)).length + + topDomains.filter((item) => isForeignToVatDomainToken(item)).length + + topRelations.filter((item) => /payment_to_settlement|statement_to_document|deferred_expense_to_writeoff|close_operation|allocation|period_close|fixed_asset/i.test(item)).length; + const vatContaminatedPrimary = focusDomain === "vat_document_register_book" && + topClass.inDomain && + topClass.foreignDomains.length > 0 && + vatForeignSignals > Math.max(1, vatPrimarySignals) && + !hasControlledCrossDomainHandoffInResult(top); + const blocked = (hasForeignPrimary && !hasAnyPrimary && !hasControlledCrossDomainHandoffInResult(top)) || vatContaminatedPrimary; return { has_primary: hasAnyPrimary, has_foreign_primary: hasForeignPrimary, @@ -2038,21 +2072,35 @@ function hasStrongNarrativeDomainSignalInText(userMessage, domain) { } if (domain === "month_close_costs_20_44") { return (accountTokens.some((item) => isCloseCostsAccountToken(item)) || - /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test(text)); + /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test(text)); } return false; } +function hasFixedAssetAmortizationSignalInText(userMessage) { + const text = String(userMessage ?? "").toLowerCase(); + const explicitFixedAssetAccountMention = /(?:сч(?:е|ё)т(?:а|у|ом|ов)?\s*(?:№|#|:)?\s*0[12](?:\.\d{1,2})?|\b0[12]\s*\/\s*0[12]\b)/iu.test(text); + return (explicitFixedAssetAccountMention || + /(основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/i.test(text)); +} +function hasExplicitMonthCloseSignalInText(userMessage) { + const text = String(userMessage ?? "").toLowerCase(); + return /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test(text); +} function inferP0FocusNarrativeDomain(userMessage, results, units, focusDomainHint) { const fromHint = p0NarrativeDomainFromHint(focusDomainHint); const fromMessage = inferNarrativeDomainFromText(userMessage); const strongFromMessage = Boolean(fromMessage && hasStrongNarrativeDomainSignalInText(userMessage, fromMessage)); const fromDomainGuard = inferP0NarrativeDomainFromDomainGuards(results); + const fixedAssetOnlySignal = hasFixedAssetAmortizationSignalInText(userMessage) && !hasExplicitMonthCloseSignalInText(userMessage); if (fromHint && fromMessage && fromHint !== fromMessage) { return strongFromMessage ? fromMessage : fromHint; } if (fromHint) { return fromHint; } + if (fromDomainGuard === "month_close_costs_20_44" && fixedAssetOnlySignal) { + return null; + } if (fromDomainGuard && fromMessage && fromDomainGuard !== fromMessage) { return strongFromMessage ? fromMessage : fromDomainGuard; } @@ -2333,6 +2381,7 @@ function buildProblemCentricAnswerStructure(input) { ], 10); const openUncertainties = uniqueStrings([ ...input.groundingCheck.missing_requirements, + ...(input.domainLockMiss ? ["primary_domain_evidence_not_confirmed"] : []), ...(input.missingAnchors.period ? ["missing_anchor:period"] : []), ...(input.mode === "clarification_required" && input.missingAnchors.account ? ["missing_anchor:account"] : []), ...(input.mode === "clarification_required" && input.missingAnchors.documentOrObject @@ -2415,6 +2464,8 @@ function limitationReasonToUserText(code) { function inferNarrativeDomainFromText(value) { const text = String(value ?? "").toLowerCase(); const accountTokens = extractAccountNumbersFromNarrativeText(text); + const fixedAssetSignal = hasFixedAssetAmortizationSignalInText(text); + const explicitMonthCloseSignal = hasExplicitMonthCloseSignalInText(text); let settlementScore = 0; let vatScore = 0; let monthCloseScore = 0; @@ -2436,9 +2487,12 @@ function inferNarrativeDomainFromText(value) { if (/(ндс|vat|сч[её]т(?:а|у|ом|е)?[-\s]?фактур(?:а|ы|е|у|ой)?|книг[аи]|регистр|вычет|налогов(?:ый|ого)?\s+эффект)/i.test(text)) { vatScore += 3; } - if (/(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test(text)) { + if (explicitMonthCloseSignal) { monthCloseScore += 3; } + if (fixedAssetSignal && !explicitMonthCloseSignal && settlementScore === 0 && vatScore === 0) { + return null; + } const maxScore = Math.max(settlementScore, vatScore, monthCloseScore); if (maxScore <= 0) { return null; @@ -2487,9 +2541,42 @@ function buildShortSectionLine(structure) { } return incomplete ? "Проблема подтверждается частично на текущей опоре." : "Проблема подтверждена на текущей опоре."; } +function humanizeCompositeDirectAnswer(value) { + const raw = String(value ?? "").trim(); + if (!raw) { + return null; + } + const tokenPattern = /\b[a-z][a-z0-9_:-]{2,}\b/gi; + const tokenMappings = uniqueStrings(Array.from(raw.matchAll(tokenPattern)) + .map((match) => humanizeTechnicalToken(String(match?.[0] ?? ""))) + .filter((item) => Boolean(item)) + .map((item) => ensureSentence(item)), 4); + const residualRaw = raw + .replace(tokenPattern, " ") + .replace(/[()]/g, " ") + .replace(/\s*[;:]\s*/g, " ") + .replace(/\s{2,}/g, " ") + .trim(); + const residualText = sanitizeUserText(residualRaw); + const lines = [...tokenMappings]; + if (residualText && !hasUserFacingLeakage(residualText)) { + lines.push(ensureSentence(residualText)); + } + const compact = dedupeNarrativeLines(lines, 3); + if (compact.length === 0) { + return null; + } + return compact.join(" "); +} function buildBrokenSectionLines(structure) { const direct = sanitizeUserText(structure.direct_answer); if (direct) { + if (/\b[a-z]+_[a-z0-9_:-]+\b/i.test(direct)) { + const compositeHumanized = humanizeCompositeDirectAnswer(direct); + if (compositeHumanized) { + return [compositeHumanized]; + } + } const mapped = mapDefectTokenToNarrative(direct) ?? humanizeTechnicalToken(direct); if (mapped) { return [ensureSentence(mapped)]; @@ -2501,17 +2588,37 @@ function buildBrokenSectionLines(structure) { } return ["Есть признаки нарушения в связанной цепочке документов и проводок."]; } -function buildWhySectionLines(structure) { +function buildWhySectionLines(structure, context) { const noteLines = dedupeNarrativeLines(structure.mechanism_block.mechanism_notes .map((item) => sanitizeSupportLine(item)) .filter((item) => Boolean(item)) .map((item) => mapDefectTokenToNarrative(item) ?? humanizeTechnicalToken(item) ?? item), 4); + const domain = context?.focusDomain ?? inferNarrativeDomainFromText(sanitizeUserText(structure.direct_answer) ?? ""); + const mechanismCorpus = `${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join(" ")}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetSignal = fixedAssetContextSignal || + ((context?.focusDomain ?? null) !== "settlements_60_62" && hasFixedAssetSignalInStructure(structure, context)); + const rbpSignal = hasRbpContextSignal(context) || hasRbpSignalInText(mechanismCorpus); const lines = [...noteLines]; if (structure.mechanism_block.status === "grounded") { lines.push("Признак проблемы повторяется в связанных документах и проводках."); } else if (structure.mechanism_block.status === "limited") { - lines.push("Часть ожидаемой цепочки подтверждена, но ключевой переход закрытия не подтвержден."); + if (domain === "vat_document_register_book") { + lines.push("Часть НДС-цепочки подтверждена, но один или несколько переходов документ -> счет-фактура -> регистр -> книга не подтверждены."); + } + else if (fixedAssetSignal) { + lines.push("По ОС часть переходов к начислению амортизации подтверждена не полностью, поэтому есть риск пропуска отдельных объектов."); + } + else if (rbpSignal) { + lines.push("По РБП часть списаний к концу периода подтверждена не полностью, поэтому остаток может сохраняться дольше ожидаемого."); + } + else if (domain === "month_close_costs_20_44") { + lines.push("Часть шагов закрытия периода подтверждена, но ключевой переход распределения/закрытия не подтвержден."); + } + else { + lines.push("Часть ожидаемой цепочки подтверждена, но ключевой переход не подтвержден."); + } } else { lines.push("Сигнал проблемы есть, но механизм подтвержден не полностью."); @@ -2554,7 +2661,7 @@ function buildCoverageSplitLines(structure, questionType = "unknown") { } return dedupeNarrativeLines(lines, 3); } -function buildEvidenceSectionLines(structure, questionType = "unknown") { +function buildEvidenceSectionLines(structure, questionType = "unknown", context) { const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0; const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0; const claimLinks = Array.isArray(structure.evidence_block.claim_evidence_links) @@ -2566,14 +2673,42 @@ function buildEvidenceSectionLines(structure, questionType = "unknown") { structure.evidence_block.coverage_note === "coverage_partial_or_limited"; const lines = []; const coverageSplitLines = buildCoverageSplitLines(structure, questionType); + const domain = context?.focusDomain ?? inferNarrativeDomainFromText(sanitizeUserText(structure.direct_answer) ?? ""); + const evidenceCorpus = `${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join(" ")}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetSignal = fixedAssetContextSignal || + ((context?.focusDomain ?? null) !== "settlements_60_62" && hasFixedAssetSignalInStructure(structure, context)); + const rbpSignal = hasRbpContextSignal(context) || hasRbpSignalInText(evidenceCorpus); if (questionType === "what_is_it_grounded_on") { - lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам."); + if (domain === "vat_document_register_book") { + lines.push("Основание собрано по НДС-цепочке: документ, счет-фактура, регистр НДС и запись книги."); + } + else if (fixedAssetSignal) { + lines.push("Основание собрано по ОС: карточка объекта, параметры амортизации, начисление и движения по 01/02."); + } + else if (rbpSignal) { + lines.push("Основание собрано по РБП: объект списания, документ списания и остаток на конец периода."); + } + else { + lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам."); + } } else if (questionType === "prove_or_guess") { lines.push("Основание разделено на подтвержденную часть и зону гипотез."); } else if (questionType === "which_chains_are_complete_vs_incomplete") { - lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные."); + if (domain === "vat_document_register_book") { + lines.push("Опора собрана по звеньям НДС-цепочки, чтобы разделить полные и неполные переходы."); + } + else if (rbpSignal) { + lines.push("Опора собрана по РБП-цепочке, чтобы разделить подтвержденное и неподтвержденное списание."); + } + else if (fixedAssetSignal) { + lines.push("Опора собрана по ОС-цепочке, чтобы разделить подтвержденные и неподтвержденные начисления амортизации."); + } + else { + lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные."); + } } if (evidenceCount > 0) { lines.push(`Вывод опирается на ${evidenceCount} подтвержденных наблюдений в текущем срезе.`); @@ -2584,11 +2719,25 @@ function buildEvidenceSectionLines(structure, questionType = "unknown") { if (claimLinks > 0) { lines.push("Есть связка между основным выводом и подтверждающими записями."); } - if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") { - lines.push("Опора частичная: часть требований покрыта не полностью."); + if (structure.evidence_block.coverage_note === "coverage_partial_or_limited" || reliabilityLimited) { + if (domain === "vat_document_register_book") { + lines.push("Опора частичная: по НДС-цепочке не подтверждены одно или несколько звеньев."); + } + else if (fixedAssetSignal) { + lines.push("Опора частичная: не по всем объектам ОС подтверждено попадание в начисление амортизации."); + } + else if (rbpSignal) { + lines.push("Опора частичная: не по всем объектам РБП подтверждено списание к концу периода."); + } + else if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") { + lines.push("Опора частичная: часть требований покрыта не полностью."); + } + else if (evidenceCount > 0) { + lines.push("Опора есть, но достаточна только для предварительного вывода."); + } } else if (evidenceCount > 0) { - lines.push(reliabilityLimited ? "Опора есть, но достаточна только для предварительного вывода." : "Опора достаточна для первичного вывода."); + lines.push("Опора достаточна для первичного вывода."); } if (lines.length === 0) { lines.push("Использована доступная выборка документов и проводок в текущем snapshot."); @@ -2616,6 +2765,123 @@ function buildDefaultChecksByDomain(domain) { } return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."]; } +function hasFixedAssetAnchorContext(context) { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used].join(" ").toLowerCase(); + return /(?:doc_type:amortization|account:0[12]|амортиз|основн|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|fixed\s*asset|depreciat)/i.test(corpus); +} +function hasFixedAssetContextSignal(context) { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used, context.userMessage ?? ""].join(" ").toLowerCase(); + return (hasFixedAssetAnchorContext(context) || + hasFixedAssetAmortizationSignalInText(corpus) || + /(?:\bос\b|основн(?:ые|ых)?\s+средств|амортиз|сч(?:е|ё)т\s*0[12])/i.test(corpus)); +} +function hasRbpAnchorContext(context) { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used].join(" ").toLowerCase(); + return /(?:\brbp(?:[_\s-]?writeoff)?\b|рбп|deferred[_\s-]?expense(?:[_\s-]?to[_\s-]?writeoff)?|doc_type:(?:deferred|rbp_writeoff)|счет\s*97|account:97)/i.test(corpus); +} +function hasRbpContextSignal(context) { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used, context.userMessage ?? ""].join(" "); + return hasRbpAnchorContext(context) || hasRbpSignalInText(corpus); +} +function hasRbpSignalInText(value) { + const text = String(value ?? "").toLowerCase(); + return /(?:\brbp(?:[_\s-]?writeoff)?\b|рбп|deferred[_\s-]?expense(?:[_\s-]?to[_\s-]?writeoff)?|счет\s*97|списани[ея]\s+рбп|остат(ок|ки)\s+рбп)/i.test(text); +} +function hasFixedAssetSignalInStructure(structure, context) { + const corpus = [ + structure.direct_answer, + ...structure.mechanism_block.mechanism_notes, + ...structure.evidence_block.mechanism_notes, + ...(structure.evidence_block.source_refs ?? []), + ...(structure.evidence_block.evidence_ids ?? []), + ...(context?.anchors.present ?? []), + ...(context?.anchors.used ?? []) + ] + .filter(Boolean) + .join(" "); + if (hasFixedAssetAnchorContext(context) || hasFixedAssetAmortizationSignalInText(corpus)) { + return true; + } + return /(?:asset_card_to_depreciation|fixed_asset|fixed_assets|амортиз|основн(?:ые|ых)?\s+средств|сч(?:е|ё)т\s*0[12]|\b0[12](?:\.\d{2})?\b)/i.test(corpus); +} +function buildFixedAssetChecksByQuestionType(questionType) { + if (questionType === "what_to_check_first") { + return [ + "Проверьте по каждому объекту ОС карточку и параметр амортизации (способ, срок, дата начала начисления).", + "Сверьте ввод в эксплуатацию и попадание объекта в набор начисления амортизации за нужный период.", + "Подтвердите начисление по объектам проводками и регистром амортизации." + ]; + } + if (questionType === "prove_or_guess") { + return [ + "Разделите доказанные и предположительные участки по цепочке ОС: принятие -> ввод -> начисление амортизации.", + "Проверьте, какие объекты отсутствуют в наборе начисления или имеют некорректные параметры амортизации." + ]; + } + if (questionType === "where_break_is") { + return [ + "Локализуйте разрыв в цепочке ОС: карточка объекта -> ввод в эксплуатацию -> начисление амортизации.", + "Сверьте, на каком шаге пропадает подтверждение по конкретным объектам." + ]; + } + if (questionType === "what_is_it_grounded_on") { + return [ + "Перечислите основание: карточка ОС, документ ввода в эксплуатацию, запись регистра амортизации, проводки по начислению." + ]; + } + return [ + "Проверьте ОС-контур: объект ОС -> ввод в эксплуатацию -> начисление амортизации по счетам 01/02.", + "Сверьте параметр амортизации и наличие начисления по каждому объекту ОС в периоде." + ]; +} +function buildRbpChecksByQuestionType(questionType) { + if (questionType === "what_to_check_first") { + return [ + "Проверьте список объектов РБП, которые должны были списаться к концу периода.", + "Сверьте документ списания РБП и движение по счету 97 по каждому объекту.", + "Проверьте остаток РБП после списания и причину, если часть суммы остается активной." + ]; + } + if (questionType === "prove_or_guess") { + return [ + "Разделите по РБП доказанное и гипотезу: где списание подтверждено, а где есть только косвенные признаки.", + "Проверьте, для каких объектов РБП нет подтверждения списания на конец периода." + ]; + } + if (questionType === "where_break_is") { + return [ + "Локализуйте разрыв в РБП-цепочке: объект РБП -> документ списания -> движение по счету 97.", + "Проверьте, на каком шаге исчезает подтверждение списания." + ]; + } + if (questionType === "what_is_it_grounded_on") { + return [ + "Перечислите основание по РБП: объект, документ списания, движение по счету 97, остаток на конец периода." + ]; + } + if (questionType === "which_chains_are_complete_vs_incomplete") { + return [ + "Разделите РБП-цепочки на: списание подтверждено, подтверждено частично, не подтверждено.", + "Проверьте, где к концу периода остается РБП без подтвержденного списания." + ]; + } + return [ + "Проверьте РБП-контур: объект РБП -> документ списания -> движение по счету 97.", + "Сверьте остаток РБП на конец периода и причину, если часть суммы не списана." + ]; +} function buildQuestionTypeDomainChecks(questionType, domain) { if (questionType === "what_to_check_first") { if (domain === "settlements_60_62") { @@ -2734,7 +3000,18 @@ function buildChecksSectionLines(structure, context) { const broken = sanitizeUserText(structure.direct_answer) ?? ""; const domain = context?.focusDomain ?? inferNarrativeDomainFromText(broken); const questionType = context?.questionType ?? "unknown"; - const domainFallback = buildQuestionTypeDomainChecks(questionType, domain); + const effectiveQuestionType = questionType === "unknown" ? "what_to_check_first" : questionType; + const fixedAssetMechanismSignal = hasFixedAssetAmortizationSignalInText(`${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join(" ")}`); + const domainAndEvidenceCorpus = `${broken} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join(" ")}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetCase = fixedAssetContextSignal || + (domain !== "settlements_60_62" && (hasFixedAssetSignalInStructure(structure, context) || fixedAssetMechanismSignal)); + const rbpCase = hasRbpContextSignal(context) || hasRbpSignalInText(domainAndEvidenceCorpus); + const domainFallback = fixedAssetCase + ? buildFixedAssetChecksByQuestionType(effectiveQuestionType) + : rbpCase + ? buildRbpChecksByQuestionType(effectiveQuestionType) + : buildQuestionTypeDomainChecks(questionType, domain); const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) => /missing_anchor:period/i.test(String(item ?? ""))); const lines = []; if (questionType === "what_to_check_first") { @@ -2764,18 +3041,21 @@ function buildChecksSectionLines(structure, context) { } } } + const filteredLines = fixedAssetCase || rbpCase + ? lines.filter((item) => !/проверьте связку документов и проводок по проблемному участку/i.test(item)) + : lines; if (hasMissingPeriod) { if (questionType === "what_to_check_first") { - lines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса."); + filteredLines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса."); } - else if (domain === "settlements_60_62" && lines.length > 0) { - lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); + else if (domain === "settlements_60_62" && filteredLines.length > 0) { + filteredLines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); } else { - lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); + filteredLines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); } } - return dedupeNarrativeLines(lines, questionType === "what_to_check_first" ? 3 : 5); + return dedupeNarrativeLines(filteredLines, questionType === "what_to_check_first" ? 3 : 5); } function humanizeLimitationToken(value) { const raw = String(value ?? "").trim(); @@ -2877,6 +3157,15 @@ function buildQuestionTypeShortLine(context) { return "\u0412\u044b\u0432\u043e\u0434 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d \u043d\u0430 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Ниже перечислены основания вывода по РБП: списание, остаток и подтверждение на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Ниже перечислены основания вывода по ОС/амортизации по данным учета."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Ниже перечислены основания вывода по НДС-цепочке по данным учета."; + } return "\u041d\u0438\u0436\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c \u0443\u0447\u0435\u0442\u0430."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { @@ -2895,8 +3184,14 @@ function buildQuestionTypeShortLine(context) { if (context.focusDomain === "month_close_costs_20_44") { return "Наиболее вероятная причина: цепочка распределения затрат и закрытия месяца подтверждена не полностью."; } + if (hasFixedAssetAnchorContext(context)) { + return "Наиболее вероятная причина: по ОС часть переходов от параметров амортизации к начислению подтверждена не полностью."; + } return "Наиболее вероятный механизм проблемы подтвержден частично и требует первичной проверки."; } + if (context.questionType === "unknown" && hasFixedAssetAnchorContext(context)) { + return "Риск неполного начисления амортизации подтвержден частично и требует проверки по объектам ОС."; + } return null; } function buildQuestionTypeBrokenLine(context) { @@ -2925,18 +3220,45 @@ function buildQuestionTypeWhyLine(context) { return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Фокус ответа по РБП: подтверждение списания и остатка на конец периода, а не общий close-narrative."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Фокус ответа по ОС: подтверждение попадания объектов в начисление амортизации."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Фокус ответа по НДС: подтверждение переходов между документом, счетом-фактурой, регистром и книгой."; + } return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043c\u0435\u0449\u0435\u043d \u0432 \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0430 \u043d\u0435 \u0432 \u043e\u0431\u0449\u0438\u0439 narrative."; } return null; } function buildQuestionTypeEvidenceLine(context) { if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Опора перечислена по РБП-объектам, документам списания и остаткам на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Опора перечислена по ОС-объектам, параметрам амортизации и движениям начисления."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Опора перечислена по НДС-звеньям: документ, счет-фактура, регистр и книга."; + } return "\u0412 \u044d\u0442\u043e\u043c \u043e\u0442\u0432\u0435\u0442\u0435 \u0432 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430."; } if (context.questionType === "prove_or_guess") { return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { + if (context.focusDomain === "vat_document_register_book") { + return "Опора собрана по НДС-звеньям, чтобы разделить полные и неполные переходы."; + } + if (hasRbpContextSignal(context)) { + return "Опора собрана по РБП-цепочке, чтобы разделить подтвержденное и неподтвержденное списание."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Опора собрана по ОС-цепочке, чтобы разделить подтвержденные и неподтвержденные начисления амортизации."; + } return "\u041e\u043f\u043e\u0440\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438."; } return null; @@ -2958,9 +3280,27 @@ function buildQuestionTypeCheckLine(context) { return "\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u043a\u0442\u044b \u043e\u0442 \u0433\u0438\u043f\u043e\u0442\u0435\u0437."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Сначала перечислите по РБП: объект, документ списания и остаток после списания на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Сначала перечислите по ОС: объект, параметры амортизации и подтверждение начисления за период."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Сначала перечислите по НДС: документ, счет-фактуру, запись регистра и запись книги."; + } return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u0435 \u043e\u043f\u043e\u0440\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u043a\u0438."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { + if (context.focusDomain === "vat_document_register_book") { + return "Сначала разложите НДС-цепочку по шагам: документ -> счет-фактура -> регистр -> книга."; + } + if (hasRbpContextSignal(context)) { + return "Сначала разложите РБП-цепочку на подтвержденное списание, частичное и неподтвержденное."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Сначала разложите ОС-цепочку на подтвержденное начисление, частичное и неподтвержденное."; + } return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435."; } return null; @@ -3001,15 +3341,101 @@ function applyQuestionTypeAndAnchorPolicy(input) { limitationLines: nextLimitations }; } +const RBP_WORDING_PATTERN = /(?:\bрбп\b|deferred[_\s-]?expense|сч(?:е|ё)т\s*97|объект\w*\s+рбп|списани[ея]\s+рбп|остат(?:ок|ки)\s+рбп|документ\s+списани[яе])/iu; +const FA_WORDING_PATTERN = /(?:\bос\b|основн(?:ые|ых)?\s+средств|амортиз|сч(?:е|ё)т\s*0[12]|01\/02|карточк\w*\s+ос|объект\w*\s+ос|ввод\w*\s+в\s+эксплуатац|fixed\s*asset|depreciat)/iu; +function hasRbpWordingPhrase(value) { + return RBP_WORDING_PATTERN.test(String(value ?? "")); +} +function hasFaWordingPhrase(value) { + return FA_WORDING_PATTERN.test(String(value ?? "")); +} +function resolveDomainWordingMode(structure, context) { + if (!context) { + return "neutral"; + } + const userMessage = String(context.userMessage ?? ""); + const explicitRbpFromMessage = hasRbpSignalInText(userMessage); + const explicitFaFromMessage = hasFixedAssetAmortizationSignalInText(userMessage); + if (explicitRbpFromMessage && !explicitFaFromMessage) { + return "rbp"; + } + if (explicitFaFromMessage && !explicitRbpFromMessage) { + return "fa_amortization"; + } + const anchorRbp = hasRbpAnchorContext(context); + const anchorFa = hasFixedAssetAnchorContext(context); + if (anchorRbp && !anchorFa) { + return "rbp"; + } + if (anchorFa && !anchorRbp) { + return "fa_amortization"; + } + const structureCorpus = [ + structure.direct_answer, + ...structure.mechanism_block.mechanism_notes, + ...structure.evidence_block.mechanism_notes, + ...(structure.evidence_block.source_refs ?? []), + ...(context.anchors.present ?? []), + ...(context.anchors.used ?? []) + ] + .filter(Boolean) + .join(" "); + const structureRbp = hasRbpSignalInText(structureCorpus); + const structureFa = hasFixedAssetAmortizationSignalInText(structureCorpus); + const rbpScore = [explicitRbpFromMessage, anchorRbp, structureRbp].filter(Boolean).length; + const faScore = [explicitFaFromMessage, anchorFa, structureFa].filter(Boolean).length; + if (rbpScore > faScore) { + return "rbp"; + } + if (faScore > rbpScore) { + return "fa_amortization"; + } + return "neutral"; +} +function enforceDomainWordingIsolation(payload, structure, context) { + const mode = resolveDomainWordingMode(structure, context); + if (mode === "neutral" || !context) { + return payload; + } + const effectiveQuestionType = context.questionType === "unknown" ? "what_to_check_first" : context.questionType; + const isForbidden = mode === "rbp" ? hasFaWordingPhrase : hasRbpWordingPhrase; + const filterLines = (lines) => lines.filter((line) => !isForbidden(line)); + const shortFallback = mode === "rbp" + ? "Признаки по РБП подтверждены частично и требуют проверки списания к концу периода." + : "Риск неполного начисления амортизации по объектам ОС подтвержден частично."; + const whyFallback = mode === "rbp" + ? ["По РБП часть списаний к концу периода подтверждена не полностью, поэтому остаток может сохраняться дольше ожидаемого."] + : ["По ОС часть переходов к начислению амортизации подтверждена не полностью, поэтому есть риск пропуска отдельных объектов."]; + const evidenceFallback = mode === "rbp" + ? ["Основание собрано по РБП: объект списания, документ списания и остаток на конец периода."] + : ["Основание собрано по ОС: карточка объекта, параметры амортизации, начисление и движения по 01/02."]; + const checkFallback = mode === "rbp" + ? buildRbpChecksByQuestionType(effectiveQuestionType).slice(0, 2) + : buildFixedAssetChecksByQuestionType(effectiveQuestionType).slice(0, 2); + const filteredShort = isForbidden(payload.shortLine) ? shortFallback : payload.shortLine; + const filteredBroken = dedupeNarrativeLines(filterLines(payload.brokenLines), 4); + const filteredWhy = dedupeNarrativeLines([...filterLines(payload.whyLines), ...(filterLines(payload.whyLines).length === 0 ? whyFallback : [])], 4); + const filteredEvidence = dedupeNarrativeLines([...filterLines(payload.evidenceLines), ...(filterLines(payload.evidenceLines).length === 0 ? evidenceFallback : [])], 7); + const filteredChecks = dedupeNarrativeLines([...filterLines(payload.checkLines), ...(filterLines(payload.checkLines).length === 0 ? checkFallback : [])], effectiveQuestionType === "what_to_check_first" ? 3 : 5); + const filteredLimitations = dedupeNarrativeLines(filterLines(payload.limitationLines), 6); + return { + shortLine: ensureSentence(filteredShort), + brokenLines: filteredBroken.length > 0 ? filteredBroken : payload.brokenLines, + whyLines: filteredWhy.length > 0 ? filteredWhy : whyFallback, + evidenceLines: filteredEvidence.length > 0 ? filteredEvidence : evidenceFallback, + checkLines: filteredChecks.length > 0 ? filteredChecks : checkFallback, + limitationLines: filteredLimitations.length > 0 ? filteredLimitations : payload.limitationLines + }; +} function renderPolicyReply(structure, context) { const questionType = context?.questionType ?? "unknown"; const shortLine = ensureSentence(buildShortSectionLine(structure)); const brokenLines = buildBrokenSectionLines(structure); - const whyLines = buildWhySectionLines(structure); - const evidenceLines = buildEvidenceSectionLines(structure, questionType); + const whyLines = buildWhySectionLines(structure, context); + const evidenceLines = buildEvidenceSectionLines(structure, questionType, context); const checkLines = buildChecksSectionLines(structure, context); const limitationLines = buildLimitationsSectionLines(structure); - const enriched = context + const enrichedBase = context ? applyQuestionTypeAndAnchorPolicy({ shortLine, brokenLines, @@ -3027,6 +3453,7 @@ function renderPolicyReply(structure, context) { checkLines, limitationLines }; + const enriched = enforceDomainWordingIsolation(enrichedBase, structure, context); return sanitizeUserFacingReply([ `Коротко: ${enriched.shortLine}`, `Что сломано:\n${formatList(enriched.brokenLines)}`, @@ -3117,7 +3544,10 @@ function composeAssistantAnswerV11(input) { reply_type: "clarification_required" } : decision; - const missingAnchors = detectMissingAnchors(input.userMessage, input.retrievalResults); + const missingAnchors = detectMissingAnchors(input.userMessage, input.retrievalResults, { + normalizationPeriodExplicit: Boolean(input.normalizationPeriodExplicit), + companyAnchors: input.companyAnchors ?? null + }); const hasProblemWeakSignal = policySignals.narrowing_strength !== "strong" || policySignals.minimum_evidence_failed || limitationReasonCodes.includes("missing_mechanism") || @@ -3158,7 +3588,8 @@ function composeAssistantAnswerV11(input) { assistant_reply: renderPolicyReply(problemCentricStructure, { questionType, focusDomain: focusNarrativeDomain, - anchors: anchorUsage + anchors: anchorUsage, + userMessage: input.userMessage }), fallback_type: guardedDecision.fallback_type, reply_type: guardedDecision.reply_type, @@ -3262,7 +3693,8 @@ function composeAssistantAnswerV11(input) { assistant_reply: renderPolicyReply(answerStructure, { questionType, focusDomain: focusNarrativeDomain, - anchors: anchorUsage + anchors: anchorUsage, + userMessage: input.userMessage }), fallback_type: guardedDecision.fallback_type, reply_type: guardedDecision.reply_type, @@ -3309,6 +3741,9 @@ function composeExplainableAnswer(input, scopeLabel) { .filter(Boolean) .join("\n\n")); } +function sanitizeAssistantReplyForUserFacing(value) { + return sanitizeUserFacingReply(value); +} function composeAssistantAnswer(input) { if (input.enableAnswerPolicyV11) { return composeAssistantAnswerV11(input); diff --git a/llm_normalizer/backend/dist/services/assistantDataLayer.js b/llm_normalizer/backend/dist/services/assistantDataLayer.js index 0374f71..96b8788 100644 --- a/llm_normalizer/backend/dist/services/assistantDataLayer.js +++ b/llm_normalizer/backend/dist/services/assistantDataLayer.js @@ -1242,11 +1242,17 @@ function cardResolutionScore(card, fragmentText, profile) { return 0; } const hasVatSoftAnchor = card.id === "vat_document_register_book" && hasStrongVatDomainSignal(fragmentText, profile); - const hasHardAnchor = accountMatches.length > 0 || markerHit || hasVatSoftAnchor; + const hasMonthCloseSignal = card.id === "month_close_costs_20_44" && hasStrongMonthCloseSignal(fragmentText, profile); + const fixedAssetOnlySignal = card.id === "month_close_costs_20_44" && hasFixedAssetSignal(fragmentText, profile) && !hasMonthCloseSignal && accountMatches.length === 0; + if (fixedAssetOnlySignal) { + return 0; + } + const markerWeight = card.id === "month_close_costs_20_44" ? hasMonthCloseSignal : markerHit; + const hasHardAnchor = accountMatches.length > 0 || markerWeight || hasVatSoftAnchor; if (!hasHardAnchor) { return 0; } - return accountMatches.length * 4 + domainMatches.length * 3 + (markerHit ? 2 : 0); + return accountMatches.length * 4 + domainMatches.length * 3 + (markerWeight ? 2 : 0); } function hasStrongVatDomainSignal(fragmentText, profile) { const text = String(fragmentText ?? ""); @@ -1256,6 +1262,19 @@ function hasStrongVatDomainSignal(fragmentText, profile) { profile.domain_scope.some((domain) => domain === "vat" || domain === "taxes") || profile.relation_patterns.some((pattern) => ["invoice_to_vat", "register_to_book", "book_entry_generated", "deduction_posted"].includes(pattern))); } +function hasStrongMonthCloseSignal(fragmentText, profile) { + const text = String(fragmentText ?? ""); + const hasMonthCloseLexicalAnchor = /(?:закрыти[ея]\s+месяц|закрыт[а-яё]*\s+период|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|month\s*close|period\s*close|close\s+operation)/iu.test(text); + return (hasMonthCloseLexicalAnchor || + profile.account_scope.some((account) => CLOSE_COST_ACCOUNTS.includes(account)) || + profile.domain_scope.some((domain) => domain === "period_close" || domain === "deferred_expense") || + profile.relation_patterns.some((pattern) => ["deferred_expense_to_writeoff", "close_operation", "allocation_rules_resolved", "residuals_zero_or_explained"].includes(pattern))); +} +function hasFixedAssetSignal(fragmentText, profile) { + const text = String(fragmentText ?? ""); + return (/(?:основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/iu.test(text) || + profile.account_scope.some((account) => account === "01" || account === "02")); +} function hasStrongSettlementAccountSignal(profile) { return profile.account_scope.some((account) => account === "51" || account === "60" || account === "62" || account === "76"); } @@ -1302,6 +1321,19 @@ function hasSettlementRecoverySignal(signals) { const hasSettlementDocument = signals.document_types.some((item) => ["bank_statement", "payment_order", "settlement_document", "supplier_receipt", "sales_document"].includes(item)); return hasSettlementAccount || hasSettlementDomain || hasSettlementRelation || hasSettlementDocument; } +function isVatAllowedAccountContext(account) { + const normalized = String(account ?? "").trim(); + return normalized === "19" || normalized === "68"; +} +function isVatAllowedDocumentContext(documentType) { + return /(?:invoice|vat_document|purchase_book|sales_book|tax_entry|supplier_receipt|sales_document|register)/i.test(String(documentType ?? "")); +} +function isVatAllowedRelationPattern(pattern) { + return /(?:invoice_to_vat|register_to_book|book_entry_generated|deduction_posted|document_to_posting|contract_to_documents|source_doc_present|invoice_linked)/i.test(String(pattern ?? "")); +} +function isVatAllowedGraphDomain(domain) { + return /(?:vat_flow)/i.test(String(domain ?? "")); +} function collectSourceRecords(data, sources) { const items = []; for (const source of sources) { @@ -2423,6 +2455,9 @@ class AssistantDataLayer { group.relations.set(relation, (group.relations.get(relation) ?? 0) + 1); } for (const account of evaluation.signals.account_context) { + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedAccountContext(account)) { + continue; + } if (semanticProfile.account_scope.length === 0 || semanticProfile.account_scope.includes(account)) { group.account_context.add(account); } @@ -2432,6 +2467,9 @@ class AssistantDataLayer { !["bank_statement", "payment_order", "settlement_document", "supplier_receipt", "sales_document", "manual_operation"].includes(item)) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedDocumentContext(item)) { + continue; + } group.document_context.add(item); } for (const item of evaluation.signals.relation_patterns) { @@ -2439,6 +2477,9 @@ class AssistantDataLayer { !["payment_to_settlement", "statement_to_document", "contract_to_documents", "document_to_posting"].includes(item)) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedRelationPattern(item)) { + continue; + } group.relation_pattern_hits.add(item); } for (const item of evaluation.signals.anomaly_patterns) { @@ -2457,6 +2498,9 @@ class AssistantDataLayer { !["bank_settlement", "customer_settlement"].includes(domain)) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedGraphDomain(domain)) { + continue; + } group.graph_domain_scope.add(domain); } for (const reason of evaluation.match_reasons.slice(0, 4)) { @@ -2471,16 +2515,24 @@ class AssistantDataLayer { const unknownLinks = Number(record.unknown_link_count ?? 0); const sampleAccountContext = domainCard?.id === "settlements_60_62" ? evaluation.signals.account_context.filter((item) => ["51", "60", "62", "76"].includes(item)) - : evaluation.signals.account_context; + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.account_context.filter((item) => isVatAllowedAccountContext(item)) + : evaluation.signals.account_context; const sampleDocumentContext = domainCard?.id === "settlements_60_62" ? evaluation.signals.document_types.filter((item) => ["bank_statement", "payment_order", "settlement_document", "supplier_receipt", "sales_document", "manual_operation"].includes(item)) - : evaluation.signals.document_types; + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.document_types.filter((item) => isVatAllowedDocumentContext(item)) + : evaluation.signals.document_types; const sampleRelationPatterns = domainCard?.id === "settlements_60_62" ? evaluation.signals.relation_patterns.filter((item) => ["payment_to_settlement", "statement_to_document", "contract_to_documents", "document_to_posting"].includes(item)) - : evaluation.signals.relation_patterns; + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.relation_patterns.filter((item) => isVatAllowedRelationPattern(item)) + : evaluation.signals.relation_patterns; const sampleGraphDomainScope = domainCard?.id === "settlements_60_62" ? evaluation.graph_domain_scope.filter((item) => ["bank_settlement", "customer_settlement"].includes(item)) - : evaluation.graph_domain_scope; + : domainCard?.id === "vat_document_register_book" + ? evaluation.graph_domain_scope.filter((item) => isVatAllowedGraphDomain(item)) + : evaluation.graph_domain_scope; group.samples.push({ source_entity: record.source_entity, source_id: record.source_id, diff --git a/llm_normalizer/backend/dist/services/assistantService.js b/llm_normalizer/backend/dist/services/assistantService.js index 293ce3a..d61fd5a 100644 --- a/llm_normalizer/backend/dist/services/assistantService.js +++ b/llm_normalizer/backend/dist/services/assistantService.js @@ -79,6 +79,30 @@ function extractFragments(normalized) { const source = normalized; return Array.isArray(source.fragments) ? source.fragments : []; } +function hasExplicitPeriodAnchorFromNormalized(normalized) { + const fragments = extractFragments(normalized); + const explicitPeriodPattern = /(?:\b20\d{2}(?:[-./](?:0?[1-9]|1[0-2]))?(?:[-./](?:0?[1-9]|[12]\d|3[01]))?\b|\b(?:0?[1-9]|[12]\d|3[01])[./-](?:0?[1-9]|1[0-2])[./-](?:\d{2}|\d{4})\b|\b(?:январ[ьяе]|феврал[ьяе]|март[ае]?|апрел[ьяе]|ма[йея]|июн[ьяе]|июл[ьяе]|август[ае]?|сентябр[ьяе]|октябр[ьяе]|ноябр[ьяе]|декабр[ьяе]|january|february|march|april|may|june|july|august|september|october|november|december)\b)/i; + for (const item of fragments) { + if (!item || typeof item !== "object") { + continue; + } + const fragment = item; + const timeScope = fragment.time_scope && typeof fragment.time_scope === "object" ? fragment.time_scope : null; + if (timeScope) { + const type = String(timeScope.type ?? "").trim().toLowerCase(); + const value = String(timeScope.value ?? "").trim(); + const confidence = String(timeScope.confidence ?? "").trim().toLowerCase(); + if ((type === "explicit" || type === "range") && value.length > 0 && confidence !== "low") { + return true; + } + } + const rawText = `${typeof fragment.raw_fragment_text === "string" ? fragment.raw_fragment_text : ""} ${typeof fragment.normalized_fragment_text === "string" ? fragment.normalized_fragment_text : ""}`; + if (explicitPeriodPattern.test(rawText)) { + return true; + } + } + return false; +} function extractExecutionState(normalized) { const fragments = extractFragments(normalized); return fragments.map((item) => { @@ -243,7 +267,7 @@ function extractAccountTokens(text) { return Array.from(explicitAccounts); } const spans = collectDateSpans(lower); - const hasAccountingLexeme = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b|оплат|расчет|аванс|долг|settlement|payment|счет|СЃС‡\.?)/iu.test(lower); + const hasAccountingLexeme = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b|оплат|расчет|аванс|долг|settlement|payment)/iu.test(lower); if (!hasAccountingLexeme) { return []; } @@ -432,9 +456,9 @@ function buildSkippedResult(item) { why_included: [], selection_reason: [mapNoRouteReason(item.no_route_reason)], risk_factors: [], - business_interpretation: ["Данный фрагмент РЅРµ был выполнен РёР·-Р·Р° no-route решения."], + business_interpretation: ["Данный фрагмент не был выполнен из-за no-route решения."], confidence: "low", - limitations: ["Фрагмент требует уточнения или отсутствует поддерживаемый маршрут."], + limitations: ["Фрагмент требует уточнения или отсутствует поддерживаемый маршрут."], errors: [] }); } @@ -681,28 +705,28 @@ function checkGrounding(userMessage, requirements, coverage, retrievalResults) { const reasons = []; if (!routeSubjectMatch) { status = "route_mismatch_blocked"; - reasons.push(`РќРµ подтверждены критичные предметные токены запроса: ${missingCriticalTokens.join(", ")}`); + reasons.push(`Не подтверждены критичные предметные токены запроса: ${missingCriticalTokens.join(", ")}`); } else if (accountOnlyMismatchRecoverable) { status = "partial"; - reasons.push(`Рчет-токены РЅРµ подтверждены напрямую (${missingCriticalTokens.join(", ")}), РЅРѕ есть релевантная РѕРїРѕСЂР° для ограниченного вывода.`); + reasons.push(`Счет-токены не подтверждены напрямую (${missingCriticalTokens.join(", ")}), но есть релевантная опора для ограниченного вывода.`); } else if (coverage.requirements_covered === 0) { status = "no_grounded_answer"; - reasons.push("РќРё РѕРґРЅРѕ требование РЅРµ получило подтвержденного покрытия."); + reasons.push("Ни одно требование не получило подтвержденного покрытия."); } else if (coverage.requirements_uncovered.length > 0 || coverage.requirements_partially_covered.length > 0 || coverage.clarification_needed_for.length > 0 || coverage.out_of_scope_requirements.length > 0) { status = "partial"; - reasons.push("Р’РѕРїСЂРѕСЃ покрыт частично: есть непокрытые или требующие уточнения требования."); + reasons.push("Вопрос покрыт частично: есть непокрытые или требующие уточнения требования."); } if (whyIncludedSummary.length === 0) { - reasons.push("Нет explainable-сигналов why_included РІ результатах выборки."); + reasons.push("Нет explainable-сигналов why_included в результатах выборки."); } if (missingSubjectTokens.length > 0 && missingCriticalTokens.length === 0) { - reasons.push(`Часть контекстных токенов РЅРµ подтверждена напрямую: ${missingSubjectTokens.join(", ")}`); + reasons.push(`Часть контекстных токенов не подтверждена напрямую: ${missingSubjectTokens.join(", ")}`); } const missingRequirements = [ ...coverage.requirements_uncovered, @@ -765,10 +789,10 @@ function buildAnswerStructureV11(input) { })), 8); const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults); const limitations = summarizeUnique([...input.retrievalResults.flatMap((item) => item.limitations), ...input.groundingCheck.reasons], 8); - const clarificationQuestions = input.coverageReport.clarification_needed_for.map((item) => `Уточните требование ${item}.`); + const clarificationQuestions = input.coverageReport.clarification_needed_for.map((item) => `Уточните требование ${item}.`); const recommendedActions = summarizeUnique([ - ...input.coverageReport.requirements_uncovered.map((item) => `Проверить непокрытое требование ${item}.`), - ...input.coverageReport.requirements_partially_covered.map((item) => `Доуточнить частично покрытое требование ${item}.`) + ...input.coverageReport.requirements_uncovered.map((item) => `Проверить непокрытое требование ${item}.`), + ...input.coverageReport.requirements_partially_covered.map((item) => `Доуточнить частично покрытое требование ${item}.`) ], 6); const mechanismStatus = mechanismNotes.length === 0 ? "unresolved" @@ -811,7 +835,8 @@ const FOLLOWUP_ROUTE_HINTS = new Set(["store_canonical", "store_feature_risk", " const FOLLOWUP_ACTIVE_DOMAIN_ROUTE_MAP = { settlements_60_62: "hybrid_store_plus_live", vat_document_register_book: "hybrid_store_plus_live", - month_close_costs_20_44: "hybrid_store_plus_live" + month_close_costs_20_44: "hybrid_store_plus_live", + fixed_asset_amortization: "hybrid_store_plus_live" }; const FOLLOWUP_BUSINESS_CONTEXT_MAX = 320; const FOLLOWUP_SUBJECT_MAX = 160; @@ -824,17 +849,17 @@ function hasAccountingSignal(text) { if (/(?:^|[\s,;:])\d{2}(?:\.\d{2})?(?=$|[\s,.;:])/i.test(lower)) { return true; } - return /(РїСЂРѕРІРѕРґРє|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|РЅРґСЃ|амортиз|СЂР±Рї|РѕСЃ|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|проводк|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|счёт|ндс|амортиз|рбп|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|закрыти|период|postavshchik|kontragent|schet|schetu|period|counterparty|supplier|invoice|posting|ledger|account|anomaly|risk)/i.test(lower); + return /(проводк|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|счёт|ндс|амортиз|рбп|ос|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|закрыти|период|postavshchik|kontragent|schet|schetu|period|counterparty|supplier|invoice|posting|ledger|account|anomaly|risk)/i.test(lower); } function hasFollowupMarker(text) { const compact = compactWhitespace(text.toLowerCase()); - return /^(Рё|Р° еще|Р° ещё|еще|ещё|добав|уточн|продолж|также|и|а если|а еще|а ещё|еще|ещё|добав|уточн|продолж|также|plus|also|dobav|utochn|prodolzh)/i.test(compact); + return /^(и|а еще|а ещё|еще|ещё|добав|уточн|продолж|также|а если|plus|also|dobav|utochn|prodolzh)/i.test(compact); } function hasReferentialPointer(text) { - return /(РїРѕ этому|РїРѕ тому|это Р¶Рµ|этой|этим|тому|по этому|по тому|это же|этой|этим|этому|из этого|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(text.toLowerCase()); + return /(по этому|по тому|это же|этой|этим|этому|из этого|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(text.toLowerCase()); } function hasSmallTalkSignal(text) { - return /(привет|как дела|спасибо|привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase()); + return /(привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase()); } function countTokens(text) { return compactWhitespace(text) @@ -878,12 +903,17 @@ function inferP0DomainFromMessage(text) { const hasVatAccount = accountTokens.some((token) => /^(?:19|68)(?:\.|$)/.test(token)); const hasSettlementAccount = accountTokens.some((token) => /^(?:51|60|62|76)(?:\.|$)/.test(token)); const hasMonthCloseAccount = accountTokens.some((token) => /^(?:97|2\d|3\d|4[0-4])(?:\.|$)/.test(token)); - const vatLexical = /(?:ндс|vat|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]\s+(?:покуп|продаж)|налогов)/i.test(lower); + const hasFixedAssetAccount = accountTokens.some((token) => /^(?:01|02|08)(?:\.|$)/.test(token)); + const vatLexical = /(?:ндс|vat|сч[её]т[\s-]?фактур|книг[аи]\s+(?:покуп|продаж)|налогов)/i.test(lower); const settlementLexical = /(?:долг|аванс|зач[её]т|взаимозач|расч[её]т|оплат|платеж|платёж|постав|покупател)/i.test(lower); - const monthCloseLexical = /(?:закрыти[ея]\s+месяц|закрытие счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых результат)/i.test(lower); + const monthCloseLexical = /(?:закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат)/i.test(lower); + const fixedAssetLexical = /(?:основн(?:ые|ых)?\s+сред|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/i.test(lower); if (hasVatAccount || vatLexical) { return "vat_document_register_book"; } + if (fixedAssetLexical || hasFixedAssetAccount) { + return "fixed_asset_amortization"; + } if (monthCloseLexical || hasMonthCloseAccount) { return "month_close_costs_20_44"; } @@ -1056,12 +1086,12 @@ function buildFollowupStateBinding(input) { const shouldAugmentQuestion = Boolean(subject) && (followupMarker || referentialPointer || !strongSignal); let normalizedQuestion = userMessage; if (shouldAugmentQuestion) { - const appendParts = [`Фокус текущего разбора: ${subject}`]; + const appendParts = [`Фокус текущего разбора: ${subject}`]; if (input.investigationState.focus.primary_accounts.length > 0 && !/\b\d{2}(?:\.\d{2})?\b/.test(userMessage)) { - appendParts.push(`Счета фокуса: ${input.investigationState.focus.primary_accounts.join(", ")}`); + appendParts.push(`Счета фокуса: ${input.investigationState.focus.primary_accounts.join(", ")}`); } if (periodHintFromState && !hasPeriodLiteral(userMessage)) { - appendParts.push(`Период фокуса: ${periodHintFromState}`); + appendParts.push(`Период фокуса: ${periodHintFromState}`); } const appendBlock = withCappedLength(compactWhitespace(appendParts.join("; ")), FOLLOWUP_QUESTION_APPEND_MAX); normalizedQuestion = `${userMessage}\n${appendBlock}`.trim(); @@ -1225,6 +1255,9 @@ class AssistantService { : null; const questionTypeClass = (0, questionTypeResolver_1.resolveQuestionType)(userMessage); const companyAnchors = (0, companyAnchorResolver_1.resolveCompanyAnchors)(userMessage); + const hasPeriodInCompanyAnchors = (Array.isArray(companyAnchors?.dates) && companyAnchors.dates.some((item) => String(item ?? "").trim().length > 0)) || + (Array.isArray(companyAnchors?.periods) && companyAnchors.periods.some((item) => String(item ?? "").trim().length > 0)); + const normalizationPeriodExplicit = hasExplicitPeriodAnchorFromNormalized(normalized.normalized) || hasPeriodInCompanyAnchors; const composition = (0, answerComposer_1.composeAssistantAnswer)({ userMessage, routeSummary: normalized.route_hint_summary, @@ -1235,15 +1268,21 @@ class AssistantService { focusDomainHint, questionTypeHint: questionTypeClass, companyAnchors, + normalizationPeriodExplicit, enableAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11, enableProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1, enableLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 }); + const safeAssistantReplyBase = (0, answerComposer_1.sanitizeAssistantReplyForUserFacing)(composition.assistant_reply); + const safeAssistantReply = String(safeAssistantReplyBase ?? "") + .replace(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") + .replace(/\b(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") + .trim(); const answerStructureV11 = config_1.FEATURE_ASSISTANT_CONTRACTS_V11 ? config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11 && composition.answer_structure_v11 ? composition.answer_structure_v11 : buildAnswerStructureV11({ - assistantReply: composition.assistant_reply, + assistantReply: safeAssistantReply, coverageReport: coverageEvaluation.coverage, groundingCheck, retrievalResults @@ -1304,7 +1343,7 @@ class AssistantService { message_id: `msg-${(0, nanoid_1.nanoid)(10)}`, session_id: sessionId, role: "assistant", - text: composition.assistant_reply, + text: safeAssistantReply, reply_type: composition.reply_type, created_at: new Date().toISOString(), trace_id: normalized.trace_id, @@ -1364,7 +1403,7 @@ class AssistantService { answer_structure_v11: answerStructureV11, investigation_state_snapshot: investigationStateSnapshot, fallback_type: composition.fallback_type, - assistant_reply: composition.assistant_reply, + assistant_reply: safeAssistantReply, reply_type: composition.reply_type, trace_id: normalized.trace_id } @@ -1372,7 +1411,7 @@ class AssistantService { return { ok: true, session_id: sessionId, - assistant_reply: composition.assistant_reply, + assistant_reply: safeAssistantReply, reply_type: composition.reply_type, conversation_item: assistantItem, debug, diff --git a/llm_normalizer/backend/dist/services/investigationState.js b/llm_normalizer/backend/dist/services/investigationState.js index 2253776..2855013 100644 --- a/llm_normalizer/backend/dist/services/investigationState.js +++ b/llm_normalizer/backend/dist/services/investigationState.js @@ -91,6 +91,10 @@ function isVatAccount(value) { const prefix = normalizeAccountPrefix(value); return prefix === "19" || prefix === "68"; } +function isFixedAssetAccount(value) { + const prefix = normalizeAccountPrefix(value); + return prefix === "01" || prefix === "02" || prefix === "08"; +} function isCloseCostsAccount(value) { const prefix = normalizeAccountPrefix(value); if (!prefix) { @@ -100,22 +104,34 @@ function isCloseCostsAccount(value) { return (account >= 20 && account <= 44) || prefix === "97"; } function inferFollowupActiveDomain(input) { - const corpus = `${input.userMessage} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase(); + const messageCorpus = String(input.userMessage ?? "").toLowerCase(); + const contextualCorpus = `${messageCorpus} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase(); + const hasFixedAssetLexicalSignal = /(?:амортиз|основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|объект[а-яё]*\s+ос|fixed\s*asset|depreciat)/i.test(messageCorpus); + const hasFixedAssetAccountSignal = input.focusAccounts.some((item) => isFixedAssetAccount(item)) && + /(?:сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|(?:01|02|08)(?:\.\d{2})?\s*\/\s*(?:01|02|08)(?:\.\d{2})?|\b0[128](?:\.\d{2})?\b)/i.test(messageCorpus); + if (hasFixedAssetLexicalSignal || hasFixedAssetAccountSignal) { + return "fixed_asset_amortization"; + } const hasSettlementSignal = input.focusAccounts.some((item) => isSettlementAccount(item)) || - /(60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расчет|расч[её]т|зачет|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(corpus); + /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(messageCorpus); if (hasSettlementSignal) { return "settlements_60_62"; } const hasVatSignal = input.focusAccounts.some((item) => isVatAccount(item)) || - /(ндс|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(corpus); + /(?:ндс|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(messageCorpus); if (hasVatSignal) { return "vat_document_register_book"; } const hasCloseSignal = input.focusAccounts.some((item) => isCloseCostsAccount(item)) || - /(закрыти|закрытие|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(corpus); + /(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(messageCorpus); if (hasCloseSignal) { return "month_close_costs_20_44"; } + if (/(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|аванс|долг|settlement|payment)/i.test(contextualCorpus) && + (input.previous.followup_context?.active_domain === "settlements_60_62" || + input.previous.focus.domain === "settlements_60_62")) { + return "settlements_60_62"; + } const routeDomain = deriveDomain(input.routeSummary); if (routeDomain && routeDomain !== "no_route") { return routeDomain; @@ -321,7 +337,7 @@ function updateInvestigationState(input) { const uncoveredRequirementIds = collectUncoveredRequirementIds(input.coverageReport); const activeDomain = inferFollowupActiveDomain({ userMessage: input.userMessage, - focusAccounts: mergedFocusAccounts, + focusAccounts: focusFromMessage, routeSummary: input.routeSummary, previous }); diff --git a/llm_normalizer/backend/src/services/answerComposer.ts b/llm_normalizer/backend/src/services/answerComposer.ts index 5e49b39..17387e6 100644 --- a/llm_normalizer/backend/src/services/answerComposer.ts +++ b/llm_normalizer/backend/src/services/answerComposer.ts @@ -24,6 +24,7 @@ interface ComposeAnswerInput { focusDomainHint?: string | null; questionTypeHint?: QuestionTypeClass | null; companyAnchors?: CompanyAnchorSet | null; + normalizationPeriodExplicit?: boolean; enableAnswerPolicyV11?: boolean; enableProblemCentricAnswerV1?: boolean; enableLifecycleAnswerV1?: boolean; @@ -61,6 +62,7 @@ interface AnswerRenderContext { questionType: QuestionTypeClass; focusDomain: P0NarrativeDomain; anchors: CompanyAnchorUsage; + userMessage?: string; } function withUniquePush(target: string[], value: string): void { @@ -259,6 +261,10 @@ const HUMAN_SIGNAL_MAP: Record = { amount_independent_risk: "Проблема не выглядит случайной суммовой погрешностью.", wrong_document_type: "Есть признак неверного типа закрывающего документа.", fixed_asset_card_mismatch: "Есть несоответствие между карточкой ОС, документом движения и начислением.", + contradictory_asset_state: "Состояние объекта ОС выглядит противоречивым по текущей опоре.", + disposed: "Есть признак выбытия объекта ОС в цепочке состояния.", + invalid_document_or_posting_transition: "Переход состояния ОС не подтвержден документами и проводками.", + asset_card_to_depreciation: "Переход от карточки ОС к начислению амортизации подтвержден не полностью.", supplier_tail_analysis: "Есть признаки незавершенного расчетного контура по поставщикам.", cross_entity_breakage: "Есть разрыв между связанными объектами в одной цепочке.", deferred_expense_to_writeoff: "Ожидаемая цепочка списания РБП выглядит незавершенной.", @@ -674,8 +680,13 @@ function stripSyntheticPlaceholders(value: string): string { } function sanitizeUserFacingReply(value: string): string { - const withoutDebugBlocks = String(value ?? "") + const raw = String(value ?? ""); + const hardCutMatch = raw.match(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b/i); + const preCut = hardCutMatch ? raw.slice(0, hardCutMatch.index) : raw; + const withoutDebugBlocks = preCut .replace(/###\s*debug_payload_json[\s\S]*?(?:```[\s\S]*?```|$)/gi, "") + .replace(/###\s*technical_breakdown_json[\s\S]*?(?:```[\s\S]*?```|$)/gi, "") + .replace(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") .replace(/```json[\s\S]*?```/gi, ""); const normalized = scrubRawTechnicalRefs(withoutDebugBlocks).replace(/[ \t]+\n/g, "\n"); const cleanedLines = normalized @@ -1384,7 +1395,7 @@ function buildProblemCentricActions(input: { } if (input.missingAnchors.period && input.mode !== "clarification_required") { - actions.push("Уточните период проверки (например, 2020-06), чтобы подтвердить незавершенное списание без лишнего шума."); + actions.push("Уточните период проверки (например, июль 2020), чтобы подтвердить незавершенное списание без лишнего шума."); } if (input.mode === "clarification_required") { @@ -1423,7 +1434,7 @@ function buildProblemCentricClarifications(input: { const unitTypes = new Set(input.units.map((item) => item.problem_unit_type)); if (input.missingAnchors.period) { - questions.push("Уточните период (например, 2020-06), РІ котором РЅСѓР¶РЅРѕ проверить проблемный кластер."); + questions.push("Уточните период (например, июль 2020), в котором нужно проверить проблемный кластер."); } if (input.missingAnchors.account) { questions.push("Уточните счет или СЃРІСЏР·РєСѓ счетов (например, 51/60), РіРґРµ РІС‹ ожидаете дефект."); @@ -1564,6 +1575,15 @@ function asRecordObject(value: unknown): Record | null { const EXPLICIT_PERIOD_ANCHOR_PATTERN = /(?:\b20\d{2}(?:[-./](?:0?[1-9]|1[0-2]))?(?:[-./](?:0?[1-9]|[12]\d|3[01]))?\b|\b(?:0?[1-9]|[12]\d|3[01])[./-](?:0?[1-9]|1[0-2])[./-](?:\d{2}|\d{4})\b|\b(?:январ[ьяе]|феврал[ьяе]|март[ае]?|апрел[ьяе]|ма[йея]|июн[ьяе]|июл[ьяе]|август[ае]?|сентябр[ьяе]|октябр[ьяе]|ноябр[ьяе]|декабр[ьяе]|january|february|march|april|may|june|july|august|september|october|november|december)\b)/i; +function hasPeriodAnchorInCompanyAnchors(anchors: CompanyAnchorSet | null | undefined): boolean { + if (!anchors) { + return false; + } + const dates = Array.isArray(anchors.dates) ? anchors.dates : []; + const periods = Array.isArray(anchors.periods) ? anchors.periods : []; + return dates.some((item) => String(item ?? "").trim().length > 0) || periods.some((item) => String(item ?? "").trim().length > 0); +} + function hasPeriodAnchorInRetrieval(results: UnifiedRetrievalResult[]): boolean { for (const result of results) { const summary = asRecordObject(result.summary); @@ -1606,9 +1626,20 @@ function hasAccountAnchorInRetrieval(results: UnifiedRetrievalResult[]): boolean return false; } -function detectMissingAnchors(userMessage: string, retrievalResults: UnifiedRetrievalResult[] = []): MissingAnchors { +function detectMissingAnchors( + userMessage: string, + retrievalResults: UnifiedRetrievalResult[] = [], + options?: { + normalizationPeriodExplicit?: boolean; + companyAnchors?: CompanyAnchorSet | null; + } +): MissingAnchors { const lower = String(userMessage ?? "").toLowerCase(); - const hasPeriod = EXPLICIT_PERIOD_ANCHOR_PATTERN.test(lower) || hasPeriodAnchorInRetrieval(retrievalResults); + const hasPeriod = + EXPLICIT_PERIOD_ANCHOR_PATTERN.test(lower) || + hasPeriodAnchorInRetrieval(retrievalResults) || + Boolean(options?.normalizationPeriodExplicit) || + hasPeriodAnchorInCompanyAnchors(options?.companyAnchors); const hasAccount = /(?:\bсчет\b|\baccount\b|\bschet\b|\b(?:0[1-9]|[1-9]\d)(?:\.\d{2})?\b|\b(?:60|62)\.\d{2}\s*\/\s*(?:60|62)\.\d{2}\b)/i.test( lower @@ -1640,7 +1671,7 @@ function buildClarificationQuestions(input: { } if (input.missingAnchors.period) { - questions.push("Уточните период проверки (например, 2020-06)."); + questions.push("Уточните период проверки (например, июль 2020)."); } if (input.missingAnchors.account) { questions.push("Уточните счет или РіСЂСѓРїРїСѓ счетов (например, 19, 60, 62)."); @@ -2106,8 +2137,8 @@ function inferP0NarrativeDomain(units: ProblemUnit[]): P0NarrativeDomain { } if ( hasCloseAccount || - units.some((unit) => ["period_close", "deferred_expense", "fixed_asset"].includes(String(unit.lifecycle_domain ?? ""))) || - units.some((unit) => unit.problem_unit_type === "period_risk_cluster" || unit.problem_unit_type === "lifecycle_anomaly_node") + units.some((unit) => ["period_close", "deferred_expense"].includes(String(unit.lifecycle_domain ?? ""))) || + units.some((unit) => unit.problem_unit_type === "period_risk_cluster") ) { return "month_close_costs_20_44"; } @@ -2158,8 +2189,7 @@ function p0NarrativeDomainFromHint(value: string | null | undefined): P0Narrativ if ( normalized.includes("month_close_costs_20_44") || normalized.includes("period_close") || - normalized.includes("deferred_expense") || - normalized.includes("fixed_asset") + normalized.includes("deferred_expense") ) { return "month_close_costs_20_44"; } @@ -2370,7 +2400,31 @@ function evaluateP0DomainEvidenceGrounding( const topClass = classify(top); const hasAnyPrimary = substantive.some((item) => classify(item).inDomain); const hasForeignPrimary = topClass.foreignDomains.length > 0 && !topClass.inDomain; - const blocked = hasForeignPrimary && !hasAnyPrimary && !hasControlledCrossDomainHandoffInResult(top); + const topAccounts = collectResultAccounts(top); + const topDomains = collectResultDomains(top); + const topRelations = collectResultRelations(top); + const vatPrimarySignals = + topAccounts.filter((item) => isVatAccountToken(item)).length + + topDomains.filter((item) => isVatDomainToken(item)).length + + topRelations.filter((item) => + /invoice_to_vat|source_doc_present|invoice_linked|register_to_book|book_entry_generated|deduction_posted|vat_/i.test(item) + ).length; + const vatForeignSignals = + topAccounts.filter((item) => isSettlementAccountToken(item) || isCloseCostsAccountToken(item)).length + + topDomains.filter((item) => isForeignToVatDomainToken(item)).length + + topRelations.filter((item) => + /payment_to_settlement|statement_to_document|deferred_expense_to_writeoff|close_operation|allocation|period_close|fixed_asset/i.test( + item + ) + ).length; + const vatContaminatedPrimary = + focusDomain === "vat_document_register_book" && + topClass.inDomain && + topClass.foreignDomains.length > 0 && + vatForeignSignals > Math.max(1, vatPrimarySignals) && + !hasControlledCrossDomainHandoffInResult(top); + const blocked = + (hasForeignPrimary && !hasAnyPrimary && !hasControlledCrossDomainHandoffInResult(top)) || vatContaminatedPrimary; return { has_primary: hasAnyPrimary, @@ -2403,7 +2457,7 @@ function hasStrongNarrativeDomainSignalInText(userMessage: string, domain: P0Nar if (domain === "month_close_costs_20_44") { return ( accountTokens.some((item) => isCloseCostsAccountToken(item)) || - /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test( + /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test( text ) ); @@ -2411,6 +2465,23 @@ function hasStrongNarrativeDomainSignalInText(userMessage: string, domain: P0Nar return false; } +function hasFixedAssetAmortizationSignalInText(userMessage: string): boolean { + const text = String(userMessage ?? "").toLowerCase(); + const explicitFixedAssetAccountMention = + /(?:сч(?:е|ё)т(?:а|у|ом|ов)?\s*(?:№|#|:)?\s*0[12](?:\.\d{1,2})?|\b0[12]\s*\/\s*0[12]\b)/iu.test(text); + return ( + explicitFixedAssetAccountMention || + /(основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/i.test(text) + ); +} + +function hasExplicitMonthCloseSignalInText(userMessage: string): boolean { + const text = String(userMessage ?? "").toLowerCase(); + return /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test( + text + ); +} + function inferP0FocusNarrativeDomain( userMessage: string, results: UnifiedRetrievalResult[], @@ -2421,12 +2492,16 @@ function inferP0FocusNarrativeDomain( const fromMessage = inferNarrativeDomainFromText(userMessage); const strongFromMessage = Boolean(fromMessage && hasStrongNarrativeDomainSignalInText(userMessage, fromMessage)); const fromDomainGuard = inferP0NarrativeDomainFromDomainGuards(results); + const fixedAssetOnlySignal = hasFixedAssetAmortizationSignalInText(userMessage) && !hasExplicitMonthCloseSignalInText(userMessage); if (fromHint && fromMessage && fromHint !== fromMessage) { return strongFromMessage ? fromMessage : fromHint; } if (fromHint) { return fromHint; } + if (fromDomainGuard === "month_close_costs_20_44" && fixedAssetOnlySignal) { + return null; + } if (fromDomainGuard && fromMessage && fromDomainGuard !== fromMessage) { return strongFromMessage ? fromMessage : fromDomainGuard; } @@ -2787,6 +2862,7 @@ function buildProblemCentricAnswerStructure(input: { const openUncertainties = uniqueStrings( [ ...input.groundingCheck.missing_requirements, + ...(input.domainLockMiss ? ["primary_domain_evidence_not_confirmed"] : []), ...(input.missingAnchors.period ? ["missing_anchor:period"] : []), ...(input.mode === "clarification_required" && input.missingAnchors.account ? ["missing_anchor:account"] : []), ...(input.mode === "clarification_required" && input.missingAnchors.documentOrObject @@ -2870,6 +2946,8 @@ function limitationReasonToUserText(code: EvidenceLimitationReasonCode): string function inferNarrativeDomainFromText(value: string): P0NarrativeDomain { const text = String(value ?? "").toLowerCase(); const accountTokens = extractAccountNumbersFromNarrativeText(text); + const fixedAssetSignal = hasFixedAssetAmortizationSignalInText(text); + const explicitMonthCloseSignal = hasExplicitMonthCloseSignalInText(text); let settlementScore = 0; let vatScore = 0; @@ -2898,14 +2976,14 @@ function inferNarrativeDomainFromText(value: string): P0NarrativeDomain { ) { vatScore += 3; } - if ( - /(закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых\s+результат|month\s*close|period\s*close|close\s+operation)/i.test( - text - ) - ) { + if (explicitMonthCloseSignal) { monthCloseScore += 3; } + if (fixedAssetSignal && !explicitMonthCloseSignal && settlementScore === 0 && vatScore === 0) { + return null; + } + const maxScore = Math.max(settlementScore, vatScore, monthCloseScore); if (maxScore <= 0) { return null; @@ -2960,9 +3038,50 @@ function buildShortSectionLine(structure: AnswerStructureV11): string { return incomplete ? "Проблема подтверждается частично на текущей опоре." : "Проблема подтверждена на текущей опоре."; } +function humanizeCompositeDirectAnswer(value: string): string | null { + const raw = String(value ?? "").trim(); + if (!raw) { + return null; + } + + const tokenPattern = /\b[a-z][a-z0-9_:-]{2,}\b/gi; + const tokenMappings = uniqueStrings( + Array.from(raw.matchAll(tokenPattern)) + .map((match) => humanizeTechnicalToken(String(match?.[0] ?? ""))) + .filter((item): item is string => Boolean(item)) + .map((item) => ensureSentence(item)), + 4 + ); + + const residualRaw = raw + .replace(tokenPattern, " ") + .replace(/[()]/g, " ") + .replace(/\s*[;:]\s*/g, " ") + .replace(/\s{2,}/g, " ") + .trim(); + const residualText = sanitizeUserText(residualRaw); + + const lines: string[] = [...tokenMappings]; + if (residualText && !hasUserFacingLeakage(residualText)) { + lines.push(ensureSentence(residualText)); + } + + const compact = dedupeNarrativeLines(lines, 3); + if (compact.length === 0) { + return null; + } + return compact.join(" "); +} + function buildBrokenSectionLines(structure: AnswerStructureV11): string[] { const direct = sanitizeUserText(structure.direct_answer); if (direct) { + if (/\b[a-z]+_[a-z0-9_:-]+\b/i.test(direct)) { + const compositeHumanized = humanizeCompositeDirectAnswer(direct); + if (compositeHumanized) { + return [compositeHumanized]; + } + } const mapped = mapDefectTokenToNarrative(direct) ?? humanizeTechnicalToken(direct); if (mapped) { return [ensureSentence(mapped)]; @@ -2975,7 +3094,7 @@ function buildBrokenSectionLines(structure: AnswerStructureV11): string[] { return ["Есть признаки нарушения в связанной цепочке документов и проводок."]; } -function buildWhySectionLines(structure: AnswerStructureV11): string[] { +function buildWhySectionLines(structure: AnswerStructureV11, context?: AnswerRenderContext): string[] { const noteLines = dedupeNarrativeLines( structure.mechanism_block.mechanism_notes .map((item) => sanitizeSupportLine(item)) @@ -2984,11 +3103,31 @@ function buildWhySectionLines(structure: AnswerStructureV11): string[] { 4 ); + const domain = context?.focusDomain ?? inferNarrativeDomainFromText(sanitizeUserText(structure.direct_answer) ?? ""); + const mechanismCorpus = `${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join( + " " + )}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetSignal = + fixedAssetContextSignal || + ((context?.focusDomain ?? null) !== "settlements_60_62" && hasFixedAssetSignalInStructure(structure, context)); + const rbpSignal = hasRbpContextSignal(context) || hasRbpSignalInText(mechanismCorpus); + const lines: string[] = [...noteLines]; if (structure.mechanism_block.status === "grounded") { lines.push("Признак проблемы повторяется в связанных документах и проводках."); } else if (structure.mechanism_block.status === "limited") { - lines.push("Часть ожидаемой цепочки подтверждена, но ключевой переход закрытия не подтвержден."); + if (domain === "vat_document_register_book") { + lines.push("Часть НДС-цепочки подтверждена, но один или несколько переходов документ -> счет-фактура -> регистр -> книга не подтверждены."); + } else if (fixedAssetSignal) { + lines.push("По ОС часть переходов к начислению амортизации подтверждена не полностью, поэтому есть риск пропуска отдельных объектов."); + } else if (rbpSignal) { + lines.push("По РБП часть списаний к концу периода подтверждена не полностью, поэтому остаток может сохраняться дольше ожидаемого."); + } else if (domain === "month_close_costs_20_44") { + lines.push("Часть шагов закрытия периода подтверждена, но ключевой переход распределения/закрытия не подтвержден."); + } else { + lines.push("Часть ожидаемой цепочки подтверждена, но ключевой переход не подтвержден."); + } } else { lines.push("Сигнал проблемы есть, но механизм подтвержден не полностью."); } @@ -3044,7 +3183,8 @@ function buildCoverageSplitLines( function buildEvidenceSectionLines( structure: AnswerStructureV11, - questionType: QuestionTypeClass = "unknown" + questionType: QuestionTypeClass = "unknown", + context?: AnswerRenderContext ): string[] { const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0; const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0; @@ -3058,13 +3198,38 @@ function buildEvidenceSectionLines( structure.evidence_block.coverage_note === "coverage_partial_or_limited"; const lines: string[] = []; const coverageSplitLines = buildCoverageSplitLines(structure, questionType); + const domain = context?.focusDomain ?? inferNarrativeDomainFromText(sanitizeUserText(structure.direct_answer) ?? ""); + const evidenceCorpus = `${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join( + " " + )}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetSignal = + fixedAssetContextSignal || + ((context?.focusDomain ?? null) !== "settlements_60_62" && hasFixedAssetSignalInStructure(structure, context)); + const rbpSignal = hasRbpContextSignal(context) || hasRbpSignalInText(evidenceCorpus); if (questionType === "what_is_it_grounded_on") { - lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам."); + if (domain === "vat_document_register_book") { + lines.push("Основание собрано по НДС-цепочке: документ, счет-фактура, регистр НДС и запись книги."); + } else if (fixedAssetSignal) { + lines.push("Основание собрано по ОС: карточка объекта, параметры амортизации, начисление и движения по 01/02."); + } else if (rbpSignal) { + lines.push("Основание собрано по РБП: объект списания, документ списания и остаток на конец периода."); + } else { + lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам."); + } } else if (questionType === "prove_or_guess") { lines.push("Основание разделено на подтвержденную часть и зону гипотез."); } else if (questionType === "which_chains_are_complete_vs_incomplete") { - lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные."); + if (domain === "vat_document_register_book") { + lines.push("Опора собрана по звеньям НДС-цепочки, чтобы разделить полные и неполные переходы."); + } else if (rbpSignal) { + lines.push("Опора собрана по РБП-цепочке, чтобы разделить подтвержденное и неподтвержденное списание."); + } else if (fixedAssetSignal) { + lines.push("Опора собрана по ОС-цепочке, чтобы разделить подтвержденные и неподтвержденные начисления амортизации."); + } else { + lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные."); + } } if (evidenceCount > 0) { @@ -3076,10 +3241,20 @@ function buildEvidenceSectionLines( if (claimLinks > 0) { lines.push("Есть связка между основным выводом и подтверждающими записями."); } - if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") { - lines.push("Опора частичная: часть требований покрыта не полностью."); + if (structure.evidence_block.coverage_note === "coverage_partial_or_limited" || reliabilityLimited) { + if (domain === "vat_document_register_book") { + lines.push("Опора частичная: по НДС-цепочке не подтверждены одно или несколько звеньев."); + } else if (fixedAssetSignal) { + lines.push("Опора частичная: не по всем объектам ОС подтверждено попадание в начисление амортизации."); + } else if (rbpSignal) { + lines.push("Опора частичная: не по всем объектам РБП подтверждено списание к концу периода."); + } else if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") { + lines.push("Опора частичная: часть требований покрыта не полностью."); + } else if (evidenceCount > 0) { + lines.push("Опора есть, но достаточна только для предварительного вывода."); + } } else if (evidenceCount > 0) { - lines.push(reliabilityLimited ? "Опора есть, но достаточна только для предварительного вывода." : "Опора достаточна для первичного вывода."); + lines.push("Опора достаточна для первичного вывода."); } if (lines.length === 0) { @@ -3110,6 +3285,143 @@ function buildDefaultChecksByDomain(domain: P0NarrativeDomain): string[] { return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."]; } +function hasFixedAssetAnchorContext(context?: AnswerRenderContext): boolean { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used].join(" ").toLowerCase(); + return /(?:doc_type:amortization|account:0[12]|амортиз|основн|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|fixed\s*asset|depreciat)/i.test( + corpus + ); +} + +function hasFixedAssetContextSignal(context?: AnswerRenderContext): boolean { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used, context.userMessage ?? ""].join(" ").toLowerCase(); + return ( + hasFixedAssetAnchorContext(context) || + hasFixedAssetAmortizationSignalInText(corpus) || + /(?:\bос\b|основн(?:ые|ых)?\s+средств|амортиз|сч(?:е|ё)т\s*0[12])/i.test(corpus) + ); +} + +function hasRbpAnchorContext(context?: AnswerRenderContext): boolean { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used].join(" ").toLowerCase(); + return /(?:\brbp(?:[_\s-]?writeoff)?\b|рбп|deferred[_\s-]?expense(?:[_\s-]?to[_\s-]?writeoff)?|doc_type:(?:deferred|rbp_writeoff)|счет\s*97|account:97)/i.test( + corpus + ); +} + +function hasRbpContextSignal(context?: AnswerRenderContext): boolean { + if (!context) { + return false; + } + const corpus = [...context.anchors.present, ...context.anchors.used, context.userMessage ?? ""].join(" "); + return hasRbpAnchorContext(context) || hasRbpSignalInText(corpus); +} + +function hasRbpSignalInText(value: string): boolean { + const text = String(value ?? "").toLowerCase(); + return /(?:\brbp(?:[_\s-]?writeoff)?\b|рбп|deferred[_\s-]?expense(?:[_\s-]?to[_\s-]?writeoff)?|счет\s*97|списани[ея]\s+рбп|остат(ок|ки)\s+рбп)/i.test( + text + ); +} + +function hasFixedAssetSignalInStructure(structure: AnswerStructureV11, context?: AnswerRenderContext): boolean { + const corpus = [ + structure.direct_answer, + ...structure.mechanism_block.mechanism_notes, + ...structure.evidence_block.mechanism_notes, + ...(structure.evidence_block.source_refs ?? []), + ...(structure.evidence_block.evidence_ids ?? []), + ...(context?.anchors.present ?? []), + ...(context?.anchors.used ?? []) + ] + .filter(Boolean) + .join(" "); + + if (hasFixedAssetAnchorContext(context) || hasFixedAssetAmortizationSignalInText(corpus)) { + return true; + } + + return /(?:asset_card_to_depreciation|fixed_asset|fixed_assets|амортиз|основн(?:ые|ых)?\s+средств|сч(?:е|ё)т\s*0[12]|\b0[12](?:\.\d{2})?\b)/i.test( + corpus + ); +} + +function buildFixedAssetChecksByQuestionType(questionType: QuestionTypeClass): string[] { + if (questionType === "what_to_check_first") { + return [ + "Проверьте по каждому объекту ОС карточку и параметр амортизации (способ, срок, дата начала начисления).", + "Сверьте ввод в эксплуатацию и попадание объекта в набор начисления амортизации за нужный период.", + "Подтвердите начисление по объектам проводками и регистром амортизации." + ]; + } + if (questionType === "prove_or_guess") { + return [ + "Разделите доказанные и предположительные участки по цепочке ОС: принятие -> ввод -> начисление амортизации.", + "Проверьте, какие объекты отсутствуют в наборе начисления или имеют некорректные параметры амортизации." + ]; + } + if (questionType === "where_break_is") { + return [ + "Локализуйте разрыв в цепочке ОС: карточка объекта -> ввод в эксплуатацию -> начисление амортизации.", + "Сверьте, на каком шаге пропадает подтверждение по конкретным объектам." + ]; + } + if (questionType === "what_is_it_grounded_on") { + return [ + "Перечислите основание: карточка ОС, документ ввода в эксплуатацию, запись регистра амортизации, проводки по начислению." + ]; + } + return [ + "Проверьте ОС-контур: объект ОС -> ввод в эксплуатацию -> начисление амортизации по счетам 01/02.", + "Сверьте параметр амортизации и наличие начисления по каждому объекту ОС в периоде." + ]; +} + +function buildRbpChecksByQuestionType(questionType: QuestionTypeClass): string[] { + if (questionType === "what_to_check_first") { + return [ + "Проверьте список объектов РБП, которые должны были списаться к концу периода.", + "Сверьте документ списания РБП и движение по счету 97 по каждому объекту.", + "Проверьте остаток РБП после списания и причину, если часть суммы остается активной." + ]; + } + if (questionType === "prove_or_guess") { + return [ + "Разделите по РБП доказанное и гипотезу: где списание подтверждено, а где есть только косвенные признаки.", + "Проверьте, для каких объектов РБП нет подтверждения списания на конец периода." + ]; + } + if (questionType === "where_break_is") { + return [ + "Локализуйте разрыв в РБП-цепочке: объект РБП -> документ списания -> движение по счету 97.", + "Проверьте, на каком шаге исчезает подтверждение списания." + ]; + } + if (questionType === "what_is_it_grounded_on") { + return [ + "Перечислите основание по РБП: объект, документ списания, движение по счету 97, остаток на конец периода." + ]; + } + if (questionType === "which_chains_are_complete_vs_incomplete") { + return [ + "Разделите РБП-цепочки на: списание подтверждено, подтверждено частично, не подтверждено.", + "Проверьте, где к концу периода остается РБП без подтвержденного списания." + ]; + } + return [ + "Проверьте РБП-контур: объект РБП -> документ списания -> движение по счету 97.", + "Сверьте остаток РБП на конец периода и причину, если часть суммы не списана." + ]; +} + function buildQuestionTypeDomainChecks(questionType: QuestionTypeClass, domain: P0NarrativeDomain): string[] { if (questionType === "what_to_check_first") { if (domain === "settlements_60_62") { @@ -3238,7 +3550,23 @@ function buildChecksSectionLines(structure: AnswerStructureV11, context?: Answer const broken = sanitizeUserText(structure.direct_answer) ?? ""; const domain = context?.focusDomain ?? inferNarrativeDomainFromText(broken); const questionType = context?.questionType ?? "unknown"; - const domainFallback = buildQuestionTypeDomainChecks(questionType, domain); + const effectiveQuestionType: QuestionTypeClass = questionType === "unknown" ? "what_to_check_first" : questionType; + const fixedAssetMechanismSignal = hasFixedAssetAmortizationSignalInText( + `${structure.direct_answer} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join(" ")}` + ); + const domainAndEvidenceCorpus = `${broken} ${structure.mechanism_block.mechanism_notes.join(" ")} ${structure.evidence_block.mechanism_notes.join( + " " + )}`; + const fixedAssetContextSignal = hasFixedAssetContextSignal(context); + const fixedAssetCase = + fixedAssetContextSignal || + (domain !== "settlements_60_62" && (hasFixedAssetSignalInStructure(structure, context) || fixedAssetMechanismSignal)); + const rbpCase = hasRbpContextSignal(context) || hasRbpSignalInText(domainAndEvidenceCorpus); + const domainFallback = fixedAssetCase + ? buildFixedAssetChecksByQuestionType(effectiveQuestionType) + : rbpCase + ? buildRbpChecksByQuestionType(effectiveQuestionType) + : buildQuestionTypeDomainChecks(questionType, domain); const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) => /missing_anchor:period/i.test(String(item ?? "")) ); @@ -3267,16 +3595,21 @@ function buildChecksSectionLines(structure: AnswerStructureV11, context?: Answer } } } + const filteredLines = + fixedAssetCase || rbpCase + ? lines.filter((item) => !/проверьте связку документов и проводок по проблемному участку/i.test(item)) + : lines; + if (hasMissingPeriod) { if (questionType === "what_to_check_first") { - lines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса."); - } else if (domain === "settlements_60_62" && lines.length > 0) { - lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); + filteredLines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса."); + } else if (domain === "settlements_60_62" && filteredLines.length > 0) { + filteredLines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); } else { - lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); + filteredLines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума."); } } - return dedupeNarrativeLines(lines, questionType === "what_to_check_first" ? 3 : 5); + return dedupeNarrativeLines(filteredLines, questionType === "what_to_check_first" ? 3 : 5); } function humanizeLimitationToken(value: string): string | null { @@ -3366,6 +3699,15 @@ function buildQuestionTypeShortLine(context: AnswerRenderContext): string | null return "\u0412\u044b\u0432\u043e\u0434 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d \u043d\u0430 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Ниже перечислены основания вывода по РБП: списание, остаток и подтверждение на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Ниже перечислены основания вывода по ОС/амортизации по данным учета."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Ниже перечислены основания вывода по НДС-цепочке по данным учета."; + } return "\u041d\u0438\u0436\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c \u0443\u0447\u0435\u0442\u0430."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { @@ -3384,8 +3726,14 @@ function buildQuestionTypeShortLine(context: AnswerRenderContext): string | null if (context.focusDomain === "month_close_costs_20_44") { return "Наиболее вероятная причина: цепочка распределения затрат и закрытия месяца подтверждена не полностью."; } + if (hasFixedAssetAnchorContext(context)) { + return "Наиболее вероятная причина: по ОС часть переходов от параметров амортизации к начислению подтверждена не полностью."; + } return "Наиболее вероятный механизм проблемы подтвержден частично и требует первичной проверки."; } + if (context.questionType === "unknown" && hasFixedAssetAnchorContext(context)) { + return "Риск неполного начисления амортизации подтвержден частично и требует проверки по объектам ОС."; + } return null; } @@ -3416,6 +3764,15 @@ function buildQuestionTypeWhyLine(context: AnswerRenderContext): string | null { return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Фокус ответа по РБП: подтверждение списания и остатка на конец периода, а не общий close-narrative."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Фокус ответа по ОС: подтверждение попадания объектов в начисление амортизации."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Фокус ответа по НДС: подтверждение переходов между документом, счетом-фактурой, регистром и книгой."; + } return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043c\u0435\u0449\u0435\u043d \u0432 \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0430 \u043d\u0435 \u0432 \u043e\u0431\u0449\u0438\u0439 narrative."; } return null; @@ -3423,12 +3780,30 @@ function buildQuestionTypeWhyLine(context: AnswerRenderContext): string | null { function buildQuestionTypeEvidenceLine(context: AnswerRenderContext): string | null { if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Опора перечислена по РБП-объектам, документам списания и остаткам на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Опора перечислена по ОС-объектам, параметрам амортизации и движениям начисления."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Опора перечислена по НДС-звеньям: документ, счет-фактура, регистр и книга."; + } return "\u0412 \u044d\u0442\u043e\u043c \u043e\u0442\u0432\u0435\u0442\u0435 \u0432 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430."; } if (context.questionType === "prove_or_guess") { return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { + if (context.focusDomain === "vat_document_register_book") { + return "Опора собрана по НДС-звеньям, чтобы разделить полные и неполные переходы."; + } + if (hasRbpContextSignal(context)) { + return "Опора собрана по РБП-цепочке, чтобы разделить подтвержденное и неподтвержденное списание."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Опора собрана по ОС-цепочке, чтобы разделить подтвержденные и неподтвержденные начисления амортизации."; + } return "\u041e\u043f\u043e\u0440\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438."; } return null; @@ -3452,9 +3827,27 @@ function buildQuestionTypeCheckLine(context: AnswerRenderContext): string | null return "\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u043a\u0442\u044b \u043e\u0442 \u0433\u0438\u043f\u043e\u0442\u0435\u0437."; } if (context.questionType === "what_is_it_grounded_on") { + if (hasRbpContextSignal(context)) { + return "Сначала перечислите по РБП: объект, документ списания и остаток после списания на конец периода."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Сначала перечислите по ОС: объект, параметры амортизации и подтверждение начисления за период."; + } + if (context.focusDomain === "vat_document_register_book") { + return "Сначала перечислите по НДС: документ, счет-фактуру, запись регистра и запись книги."; + } return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u0435 \u043e\u043f\u043e\u0440\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u043a\u0438."; } if (context.questionType === "which_chains_are_complete_vs_incomplete") { + if (context.focusDomain === "vat_document_register_book") { + return "Сначала разложите НДС-цепочку по шагам: документ -> счет-фактура -> регистр -> книга."; + } + if (hasRbpContextSignal(context)) { + return "Сначала разложите РБП-цепочку на подтвержденное списание, частичное и неподтвержденное."; + } + if (hasFixedAssetAnchorContext(context)) { + return "Сначала разложите ОС-цепочку на подтвержденное начисление, частичное и неподтвержденное."; + } return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435."; } return null; @@ -3539,15 +3932,152 @@ function applyQuestionTypeAndAnchorPolicy(input: { }; } +type DomainWordingMode = "neutral" | "rbp" | "fa_amortization"; + +const RBP_WORDING_PATTERN = + /(?:\bрбп\b|deferred[_\s-]?expense|сч(?:е|ё)т\s*97|объект\w*\s+рбп|списани[ея]\s+рбп|остат(?:ок|ки)\s+рбп|документ\s+списани[яе])/iu; +const FA_WORDING_PATTERN = + /(?:\bос\b|основн(?:ые|ых)?\s+средств|амортиз|сч(?:е|ё)т\s*0[12]|01\/02|карточк\w*\s+ос|объект\w*\s+ос|ввод\w*\s+в\s+эксплуатац|fixed\s*asset|depreciat)/iu; + +function hasRbpWordingPhrase(value: string): boolean { + return RBP_WORDING_PATTERN.test(String(value ?? "")); +} + +function hasFaWordingPhrase(value: string): boolean { + return FA_WORDING_PATTERN.test(String(value ?? "")); +} + +function resolveDomainWordingMode(structure: AnswerStructureV11, context?: AnswerRenderContext): DomainWordingMode { + if (!context) { + return "neutral"; + } + + const userMessage = String(context.userMessage ?? ""); + const explicitRbpFromMessage = hasRbpSignalInText(userMessage); + const explicitFaFromMessage = hasFixedAssetAmortizationSignalInText(userMessage); + + if (explicitRbpFromMessage && !explicitFaFromMessage) { + return "rbp"; + } + if (explicitFaFromMessage && !explicitRbpFromMessage) { + return "fa_amortization"; + } + + const anchorRbp = hasRbpAnchorContext(context); + const anchorFa = hasFixedAssetAnchorContext(context); + + if (anchorRbp && !anchorFa) { + return "rbp"; + } + if (anchorFa && !anchorRbp) { + return "fa_amortization"; + } + + const structureCorpus = [ + structure.direct_answer, + ...structure.mechanism_block.mechanism_notes, + ...structure.evidence_block.mechanism_notes, + ...(structure.evidence_block.source_refs ?? []), + ...(context.anchors.present ?? []), + ...(context.anchors.used ?? []) + ] + .filter(Boolean) + .join(" "); + const structureRbp = hasRbpSignalInText(structureCorpus); + const structureFa = hasFixedAssetAmortizationSignalInText(structureCorpus); + + const rbpScore = [explicitRbpFromMessage, anchorRbp, structureRbp].filter(Boolean).length; + const faScore = [explicitFaFromMessage, anchorFa, structureFa].filter(Boolean).length; + if (rbpScore > faScore) { + return "rbp"; + } + if (faScore > rbpScore) { + return "fa_amortization"; + } + + return "neutral"; +} + +function enforceDomainWordingIsolation( + payload: { + shortLine: string; + brokenLines: string[]; + whyLines: string[]; + evidenceLines: string[]; + checkLines: string[]; + limitationLines: string[]; + }, + structure: AnswerStructureV11, + context?: AnswerRenderContext +): { + shortLine: string; + brokenLines: string[]; + whyLines: string[]; + evidenceLines: string[]; + checkLines: string[]; + limitationLines: string[]; +} { + const mode = resolveDomainWordingMode(structure, context); + if (mode === "neutral" || !context) { + return payload; + } + + const effectiveQuestionType: QuestionTypeClass = context.questionType === "unknown" ? "what_to_check_first" : context.questionType; + const isForbidden = mode === "rbp" ? hasFaWordingPhrase : hasRbpWordingPhrase; + const filterLines = (lines: string[]): string[] => lines.filter((line) => !isForbidden(line)); + + const shortFallback = + mode === "rbp" + ? "Признаки по РБП подтверждены частично и требуют проверки списания к концу периода." + : "Риск неполного начисления амортизации по объектам ОС подтвержден частично."; + const whyFallback = + mode === "rbp" + ? ["По РБП часть списаний к концу периода подтверждена не полностью, поэтому остаток может сохраняться дольше ожидаемого."] + : ["По ОС часть переходов к начислению амортизации подтверждена не полностью, поэтому есть риск пропуска отдельных объектов."]; + const evidenceFallback = + mode === "rbp" + ? ["Основание собрано по РБП: объект списания, документ списания и остаток на конец периода."] + : ["Основание собрано по ОС: карточка объекта, параметры амортизации, начисление и движения по 01/02."]; + const checkFallback = + mode === "rbp" + ? buildRbpChecksByQuestionType(effectiveQuestionType).slice(0, 2) + : buildFixedAssetChecksByQuestionType(effectiveQuestionType).slice(0, 2); + + const filteredShort = isForbidden(payload.shortLine) ? shortFallback : payload.shortLine; + const filteredBroken = dedupeNarrativeLines(filterLines(payload.brokenLines), 4); + const filteredWhy = dedupeNarrativeLines( + [...filterLines(payload.whyLines), ...(filterLines(payload.whyLines).length === 0 ? whyFallback : [])], + 4 + ); + const filteredEvidence = dedupeNarrativeLines( + [...filterLines(payload.evidenceLines), ...(filterLines(payload.evidenceLines).length === 0 ? evidenceFallback : [])], + 7 + ); + const filteredChecks = dedupeNarrativeLines( + [...filterLines(payload.checkLines), ...(filterLines(payload.checkLines).length === 0 ? checkFallback : [])], + effectiveQuestionType === "what_to_check_first" ? 3 : 5 + ); + const filteredLimitations = dedupeNarrativeLines(filterLines(payload.limitationLines), 6); + + return { + shortLine: ensureSentence(filteredShort), + brokenLines: filteredBroken.length > 0 ? filteredBroken : payload.brokenLines, + whyLines: filteredWhy.length > 0 ? filteredWhy : whyFallback, + evidenceLines: filteredEvidence.length > 0 ? filteredEvidence : evidenceFallback, + checkLines: filteredChecks.length > 0 ? filteredChecks : checkFallback, + limitationLines: filteredLimitations.length > 0 ? filteredLimitations : payload.limitationLines + }; +} + function renderPolicyReply(structure: AnswerStructureV11, context?: AnswerRenderContext): string { const questionType = context?.questionType ?? "unknown"; const shortLine = ensureSentence(buildShortSectionLine(structure)); const brokenLines = buildBrokenSectionLines(structure); - const whyLines = buildWhySectionLines(structure); - const evidenceLines = buildEvidenceSectionLines(structure, questionType); + const whyLines = buildWhySectionLines(structure, context); + const evidenceLines = buildEvidenceSectionLines(structure, questionType, context); const checkLines = buildChecksSectionLines(structure, context); const limitationLines = buildLimitationsSectionLines(structure); - const enriched = context + const enrichedBase = context ? applyQuestionTypeAndAnchorPolicy({ shortLine, brokenLines, @@ -3565,6 +4095,7 @@ function renderPolicyReply(structure: AnswerStructureV11, context?: AnswerRender checkLines, limitationLines }; + const enriched = enforceDomainWordingIsolation(enrichedBase, structure, context); return sanitizeUserFacingReply( [ @@ -3684,7 +4215,10 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp } : decision; - const missingAnchors = detectMissingAnchors(input.userMessage, input.retrievalResults); + const missingAnchors = detectMissingAnchors(input.userMessage, input.retrievalResults, { + normalizationPeriodExplicit: Boolean(input.normalizationPeriodExplicit), + companyAnchors: input.companyAnchors ?? null + }); const hasProblemWeakSignal = policySignals.narrowing_strength !== "strong" || policySignals.minimum_evidence_failed || @@ -3732,7 +4266,8 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp assistant_reply: renderPolicyReply(problemCentricStructure, { questionType, focusDomain: focusNarrativeDomain, - anchors: anchorUsage + anchors: anchorUsage, + userMessage: input.userMessage }), fallback_type: guardedDecision.fallback_type, reply_type: guardedDecision.reply_type, @@ -3851,7 +4386,8 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp assistant_reply: renderPolicyReply(answerStructure, { questionType, focusDomain: focusNarrativeDomain, - anchors: anchorUsage + anchors: anchorUsage, + userMessage: input.userMessage }), fallback_type: guardedDecision.fallback_type, reply_type: guardedDecision.reply_type, @@ -3908,6 +4444,10 @@ function composeExplainableAnswer(input: ComposeAnswerInput, scopeLabel: "full" ); } +export function sanitizeAssistantReplyForUserFacing(value: string): string { + return sanitizeUserFacingReply(value); +} + export function composeAssistantAnswer(input: ComposeAnswerInput): ComposeAnswerOutput { if (input.enableAnswerPolicyV11) { return composeAssistantAnswerV11(input); diff --git a/llm_normalizer/backend/src/services/assistantDataLayer.ts b/llm_normalizer/backend/src/services/assistantDataLayer.ts index 15e891a..e6fc6fe 100644 --- a/llm_normalizer/backend/src/services/assistantDataLayer.ts +++ b/llm_normalizer/backend/src/services/assistantDataLayer.ts @@ -1636,12 +1636,19 @@ function cardResolutionScore(card: P0DomainCard, fragmentText: string, profile: } const hasVatSoftAnchor = card.id === "vat_document_register_book" && hasStrongVatDomainSignal(fragmentText, profile); - const hasHardAnchor = accountMatches.length > 0 || markerHit || hasVatSoftAnchor; + const hasMonthCloseSignal = card.id === "month_close_costs_20_44" && hasStrongMonthCloseSignal(fragmentText, profile); + const fixedAssetOnlySignal = + card.id === "month_close_costs_20_44" && hasFixedAssetSignal(fragmentText, profile) && !hasMonthCloseSignal && accountMatches.length === 0; + if (fixedAssetOnlySignal) { + return 0; + } + const markerWeight = card.id === "month_close_costs_20_44" ? hasMonthCloseSignal : markerHit; + const hasHardAnchor = accountMatches.length > 0 || markerWeight || hasVatSoftAnchor; if (!hasHardAnchor) { return 0; } - return accountMatches.length * 4 + domainMatches.length * 3 + (markerHit ? 2 : 0); + return accountMatches.length * 4 + domainMatches.length * 3 + (markerWeight ? 2 : 0); } function hasStrongVatDomainSignal(fragmentText: string, profile: SemanticRetrievalProfile): boolean { @@ -1660,6 +1667,30 @@ function hasStrongVatDomainSignal(fragmentText: string, profile: SemanticRetriev ); } +function hasStrongMonthCloseSignal(fragmentText: string, profile: SemanticRetrievalProfile): boolean { + const text = String(fragmentText ?? ""); + const hasMonthCloseLexicalAnchor = + /(?:закрыти[ея]\s+месяц|закрыт[а-яё]*\s+период|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|month\s*close|period\s*close|close\s+operation)/iu.test( + text + ); + return ( + hasMonthCloseLexicalAnchor || + profile.account_scope.some((account) => CLOSE_COST_ACCOUNTS.includes(account)) || + profile.domain_scope.some((domain) => domain === "period_close" || domain === "deferred_expense") || + profile.relation_patterns.some((pattern) => + ["deferred_expense_to_writeoff", "close_operation", "allocation_rules_resolved", "residuals_zero_or_explained"].includes(pattern) + ) + ); +} + +function hasFixedAssetSignal(fragmentText: string, profile: SemanticRetrievalProfile): boolean { + const text = String(fragmentText ?? ""); + return ( + /(?:основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/iu.test(text) || + profile.account_scope.some((account) => account === "01" || account === "02") + ); +} + function hasStrongSettlementAccountSignal(profile: SemanticRetrievalProfile): boolean { return profile.account_scope.some((account) => account === "51" || account === "60" || account === "62" || account === "76"); } @@ -1726,6 +1757,27 @@ function hasSettlementRecoverySignal(signals: RecordSemanticSignals): boolean { return hasSettlementAccount || hasSettlementDomain || hasSettlementRelation || hasSettlementDocument; } +function isVatAllowedAccountContext(account: string): boolean { + const normalized = String(account ?? "").trim(); + return normalized === "19" || normalized === "68"; +} + +function isVatAllowedDocumentContext(documentType: string): boolean { + return /(?:invoice|vat_document|purchase_book|sales_book|tax_entry|supplier_receipt|sales_document|register)/i.test( + String(documentType ?? "") + ); +} + +function isVatAllowedRelationPattern(pattern: string): boolean { + return /(?:invoice_to_vat|register_to_book|book_entry_generated|deduction_posted|document_to_posting|contract_to_documents|source_doc_present|invoice_linked)/i.test( + String(pattern ?? "") + ); +} + +function isVatAllowedGraphDomain(domain: string): boolean { + return /(?:vat_flow)/i.test(String(domain ?? "")); +} + function collectSourceRecords(data: DatasetBundle, sources: DatasetSourceName[]): SourceScopedRecord[] { const items: SourceScopedRecord[] = []; for (const source of sources) { @@ -2995,6 +3047,9 @@ export class AssistantDataLayer { group.relations.set(relation, (group.relations.get(relation) ?? 0) + 1); } for (const account of evaluation.signals.account_context) { + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedAccountContext(account)) { + continue; + } if (semanticProfile.account_scope.length === 0 || semanticProfile.account_scope.includes(account)) { group.account_context.add(account); } @@ -3006,6 +3061,9 @@ export class AssistantDataLayer { ) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedDocumentContext(item)) { + continue; + } group.document_context.add(item); } for (const item of evaluation.signals.relation_patterns) { @@ -3015,6 +3073,9 @@ export class AssistantDataLayer { ) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedRelationPattern(item)) { + continue; + } group.relation_pattern_hits.add(item); } for (const item of evaluation.signals.anomaly_patterns) { @@ -3035,6 +3096,9 @@ export class AssistantDataLayer { ) { continue; } + if (domainCard?.id === "vat_document_register_book" && !isVatAllowedGraphDomain(domain)) { + continue; + } group.graph_domain_scope.add(domain); } for (const reason of evaluation.match_reasons.slice(0, 4)) { @@ -3050,22 +3114,30 @@ export class AssistantDataLayer { const sampleAccountContext = domainCard?.id === "settlements_60_62" ? evaluation.signals.account_context.filter((item) => ["51", "60", "62", "76"].includes(item)) + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.account_context.filter((item) => isVatAllowedAccountContext(item)) : evaluation.signals.account_context; const sampleDocumentContext = domainCard?.id === "settlements_60_62" ? evaluation.signals.document_types.filter((item) => ["bank_statement", "payment_order", "settlement_document", "supplier_receipt", "sales_document", "manual_operation"].includes(item) ) + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.document_types.filter((item) => isVatAllowedDocumentContext(item)) : evaluation.signals.document_types; const sampleRelationPatterns = domainCard?.id === "settlements_60_62" ? evaluation.signals.relation_patterns.filter((item) => ["payment_to_settlement", "statement_to_document", "contract_to_documents", "document_to_posting"].includes(item) ) + : domainCard?.id === "vat_document_register_book" + ? evaluation.signals.relation_patterns.filter((item) => isVatAllowedRelationPattern(item)) : evaluation.signals.relation_patterns; const sampleGraphDomainScope = domainCard?.id === "settlements_60_62" ? evaluation.graph_domain_scope.filter((item) => ["bank_settlement", "customer_settlement"].includes(item)) + : domainCard?.id === "vat_document_register_book" + ? evaluation.graph_domain_scope.filter((item) => isVatAllowedGraphDomain(item)) : evaluation.graph_domain_scope; group.samples.push({ source_entity: record.source_entity, diff --git a/llm_normalizer/backend/src/services/assistantService.ts b/llm_normalizer/backend/src/services/assistantService.ts index c3c9a22..b0b786b 100644 --- a/llm_normalizer/backend/src/services/assistantService.ts +++ b/llm_normalizer/backend/src/services/assistantService.ts @@ -1,4 +1,4 @@ -// @ts-nocheck +// @ts-nocheck import * as nanoid_1 from "nanoid"; import * as stage1Contracts_1 from "../types/stage1Contracts"; import * as config_1 from "../config"; @@ -41,6 +41,30 @@ function extractFragments(normalized) { const source = normalized; return Array.isArray(source.fragments) ? source.fragments : []; } +function hasExplicitPeriodAnchorFromNormalized(normalized) { + const fragments = extractFragments(normalized); + const explicitPeriodPattern = /(?:\b20\d{2}(?:[-./](?:0?[1-9]|1[0-2]))?(?:[-./](?:0?[1-9]|[12]\d|3[01]))?\b|\b(?:0?[1-9]|[12]\d|3[01])[./-](?:0?[1-9]|1[0-2])[./-](?:\d{2}|\d{4})\b|\b(?:январ[ьяе]|феврал[ьяе]|март[ае]?|апрел[ьяе]|ма[йея]|июн[ьяе]|июл[ьяе]|август[ае]?|сентябр[ьяе]|октябр[ьяе]|ноябр[ьяе]|декабр[ьяе]|january|february|march|april|may|june|july|august|september|october|november|december)\b)/i; + for (const item of fragments) { + if (!item || typeof item !== "object") { + continue; + } + const fragment = item; + const timeScope = fragment.time_scope && typeof fragment.time_scope === "object" ? fragment.time_scope : null; + if (timeScope) { + const type = String(timeScope.type ?? "").trim().toLowerCase(); + const value = String(timeScope.value ?? "").trim(); + const confidence = String(timeScope.confidence ?? "").trim().toLowerCase(); + if ((type === "explicit" || type === "range") && value.length > 0 && confidence !== "low") { + return true; + } + } + const rawText = `${typeof fragment.raw_fragment_text === "string" ? fragment.raw_fragment_text : ""} ${typeof fragment.normalized_fragment_text === "string" ? fragment.normalized_fragment_text : ""}`; + if (explicitPeriodPattern.test(rawText)) { + return true; + } + } + return false; +} function extractExecutionState(normalized) { const fragments = extractFragments(normalized); return fragments.map((item) => { @@ -205,7 +229,7 @@ function extractAccountTokens(text) { return Array.from(explicitAccounts); } const spans = collectDateSpans(lower); - const hasAccountingLexeme = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b|оплат|расчет|аванс|долг|settlement|payment|счет|СЃС‡\.?)/iu.test(lower); + const hasAccountingLexeme = /(?:\bсчет(?:а|у|ом|ов)?\b|\bсч\.?\b|\baccount(?:s)?\b|\bschet(?:a|u|om|ov)?\b|оплат|расчет|аванс|долг|settlement|payment)/iu.test(lower); if (!hasAccountingLexeme) { return []; } @@ -394,9 +418,9 @@ function buildSkippedResult(item) { why_included: [], selection_reason: [mapNoRouteReason(item.no_route_reason)], risk_factors: [], - business_interpretation: ["Данный фрагмент РЅРµ был выполнен РёР·-Р·Р° no-route решения."], + business_interpretation: ["Данный фрагмент не был выполнен из-за no-route решения."], confidence: "low", - limitations: ["Фрагмент требует уточнения или отсутствует поддерживаемый маршрут."], + limitations: ["Фрагмент требует уточнения или отсутствует поддерживаемый маршрут."], errors: [] }); } @@ -643,28 +667,28 @@ function checkGrounding(userMessage, requirements, coverage, retrievalResults) { const reasons = []; if (!routeSubjectMatch) { status = "route_mismatch_blocked"; - reasons.push(`РќРµ подтверждены критичные предметные токены запроса: ${missingCriticalTokens.join(", ")}`); + reasons.push(`Не подтверждены критичные предметные токены запроса: ${missingCriticalTokens.join(", ")}`); } else if (accountOnlyMismatchRecoverable) { status = "partial"; - reasons.push(`Рчет-токены РЅРµ подтверждены напрямую (${missingCriticalTokens.join(", ")}), РЅРѕ есть релевантная РѕРїРѕСЂР° для ограниченного вывода.`); + reasons.push(`Счет-токены не подтверждены напрямую (${missingCriticalTokens.join(", ")}), но есть релевантная опора для ограниченного вывода.`); } else if (coverage.requirements_covered === 0) { status = "no_grounded_answer"; - reasons.push("РќРё РѕРґРЅРѕ требование РЅРµ получило подтвержденного покрытия."); + reasons.push("Ни одно требование не получило подтвержденного покрытия."); } else if (coverage.requirements_uncovered.length > 0 || coverage.requirements_partially_covered.length > 0 || coverage.clarification_needed_for.length > 0 || coverage.out_of_scope_requirements.length > 0) { status = "partial"; - reasons.push("Р’РѕРїСЂРѕСЃ покрыт частично: есть непокрытые или требующие уточнения требования."); + reasons.push("Вопрос покрыт частично: есть непокрытые или требующие уточнения требования."); } if (whyIncludedSummary.length === 0) { - reasons.push("Нет explainable-сигналов why_included РІ результатах выборки."); + reasons.push("Нет explainable-сигналов why_included в результатах выборки."); } if (missingSubjectTokens.length > 0 && missingCriticalTokens.length === 0) { - reasons.push(`Часть контекстных токенов РЅРµ подтверждена напрямую: ${missingSubjectTokens.join(", ")}`); + reasons.push(`Часть контекстных токенов не подтверждена напрямую: ${missingSubjectTokens.join(", ")}`); } const missingRequirements = [ ...coverage.requirements_uncovered, @@ -727,10 +751,10 @@ function buildAnswerStructureV11(input) { })), 8); const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults); const limitations = summarizeUnique([...input.retrievalResults.flatMap((item) => item.limitations), ...input.groundingCheck.reasons], 8); - const clarificationQuestions = input.coverageReport.clarification_needed_for.map((item) => `Уточните требование ${item}.`); + const clarificationQuestions = input.coverageReport.clarification_needed_for.map((item) => `Уточните требование ${item}.`); const recommendedActions = summarizeUnique([ - ...input.coverageReport.requirements_uncovered.map((item) => `Проверить непокрытое требование ${item}.`), - ...input.coverageReport.requirements_partially_covered.map((item) => `Доуточнить частично покрытое требование ${item}.`) + ...input.coverageReport.requirements_uncovered.map((item) => `Проверить непокрытое требование ${item}.`), + ...input.coverageReport.requirements_partially_covered.map((item) => `Доуточнить частично покрытое требование ${item}.`) ], 6); const mechanismStatus = mechanismNotes.length === 0 ? "unresolved" @@ -773,7 +797,8 @@ const FOLLOWUP_ROUTE_HINTS = new Set(["store_canonical", "store_feature_risk", " const FOLLOWUP_ACTIVE_DOMAIN_ROUTE_MAP = { settlements_60_62: "hybrid_store_plus_live", vat_document_register_book: "hybrid_store_plus_live", - month_close_costs_20_44: "hybrid_store_plus_live" + month_close_costs_20_44: "hybrid_store_plus_live", + fixed_asset_amortization: "hybrid_store_plus_live" }; const FOLLOWUP_BUSINESS_CONTEXT_MAX = 320; const FOLLOWUP_SUBJECT_MAX = 160; @@ -786,17 +811,17 @@ function hasAccountingSignal(text) { if (/(?:^|[\s,;:])\d{2}(?:\.\d{2})?(?=$|[\s,.;:])/i.test(lower)) { return true; } - return /(РїСЂРѕРІРѕРґРє|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|РЅРґСЃ|амортиз|СЂР±Рї|РѕСЃ|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|проводк|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|счёт|ндс|амортиз|рбп|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|закрыти|период|postavshchik|kontragent|schet|schetu|period|counterparty|supplier|invoice|posting|ledger|account|anomaly|risk)/i.test(lower); + return /(проводк|документ|реализац|поступлен|взаиморасчет|сальдо|остатк|счет|счёт|ндс|амортиз|рбп|ос|контрагент|поставщик|покупател|оплат|банк|выписк|склад|товар|материал|закрыти|период|postavshchik|kontragent|schet|schetu|period|counterparty|supplier|invoice|posting|ledger|account|anomaly|risk)/i.test(lower); } function hasFollowupMarker(text) { const compact = compactWhitespace(text.toLowerCase()); - return /^(Рё|Р° еще|Р° ещё|еще|ещё|добав|уточн|продолж|также|и|а если|а еще|а ещё|еще|ещё|добав|уточн|продолж|также|plus|also|dobav|utochn|prodolzh)/i.test(compact); + return /^(и|а еще|а ещё|еще|ещё|добав|уточн|продолж|также|а если|plus|also|dobav|utochn|prodolzh)/i.test(compact); } function hasReferentialPointer(text) { - return /(РїРѕ этому|РїРѕ тому|это Р¶Рµ|этой|этим|тому|по этому|по тому|это же|этой|этим|этому|из этого|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(text.toLowerCase()); + return /(по этому|по тому|это же|этой|этим|этому|из этого|в этом|тот же|same thing|that one|po etomu|po tomu)/i.test(text.toLowerCase()); } function hasSmallTalkSignal(text) { - return /(привет|как дела|спасибо|привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase()); + return /(привет|как дела|спасибо|благодарю|thanks|thank you|hello|hi)\b/i.test(text.toLowerCase()); } function countTokens(text) { return compactWhitespace(text) @@ -840,12 +865,17 @@ function inferP0DomainFromMessage(text) { const hasVatAccount = accountTokens.some((token) => /^(?:19|68)(?:\.|$)/.test(token)); const hasSettlementAccount = accountTokens.some((token) => /^(?:51|60|62|76)(?:\.|$)/.test(token)); const hasMonthCloseAccount = accountTokens.some((token) => /^(?:97|2\d|3\d|4[0-4])(?:\.|$)/.test(token)); - const vatLexical = /(?:ндс|vat|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]\s+(?:покуп|продаж)|налогов)/i.test(lower); + const hasFixedAssetAccount = accountTokens.some((token) => /^(?:01|02|08)(?:\.|$)/.test(token)); + const vatLexical = /(?:ндс|vat|сч[её]т[\s-]?фактур|книг[аи]\s+(?:покуп|продаж)|налогов)/i.test(lower); const settlementLexical = /(?:долг|аванс|зач[её]т|взаимозач|расч[её]т|оплат|платеж|платёж|постав|покупател)/i.test(lower); - const monthCloseLexical = /(?:закрыти[ея]\s+месяц|закрытие счетов|регламентн|косвенн|затрат|распределени|рбп|амортиз|финансовых результат)/i.test(lower); + const monthCloseLexical = /(?:закрыти[ея]\s+месяц|закрытие\s+счетов|регламентн|косвенн|затрат|распределени|рбп|финансовых\s+результат)/i.test(lower); + const fixedAssetLexical = /(?:основн(?:ые|ых)?\s+сред|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|амортиз|depreciat|fixed\s*asset)/i.test(lower); if (hasVatAccount || vatLexical) { return "vat_document_register_book"; } + if (fixedAssetLexical || hasFixedAssetAccount) { + return "fixed_asset_amortization"; + } if (monthCloseLexical || hasMonthCloseAccount) { return "month_close_costs_20_44"; } @@ -1018,12 +1048,12 @@ function buildFollowupStateBinding(input) { const shouldAugmentQuestion = Boolean(subject) && (followupMarker || referentialPointer || !strongSignal); let normalizedQuestion = userMessage; if (shouldAugmentQuestion) { - const appendParts = [`Фокус текущего разбора: ${subject}`]; + const appendParts = [`Фокус текущего разбора: ${subject}`]; if (input.investigationState.focus.primary_accounts.length > 0 && !/\b\d{2}(?:\.\d{2})?\b/.test(userMessage)) { - appendParts.push(`Счета фокуса: ${input.investigationState.focus.primary_accounts.join(", ")}`); + appendParts.push(`Счета фокуса: ${input.investigationState.focus.primary_accounts.join(", ")}`); } if (periodHintFromState && !hasPeriodLiteral(userMessage)) { - appendParts.push(`Период фокуса: ${periodHintFromState}`); + appendParts.push(`Период фокуса: ${periodHintFromState}`); } const appendBlock = withCappedLength(compactWhitespace(appendParts.join("; ")), FOLLOWUP_QUESTION_APPEND_MAX); normalizedQuestion = `${userMessage}\n${appendBlock}`.trim(); @@ -1187,6 +1217,9 @@ export class AssistantService { : null; const questionTypeClass = (0, questionTypeResolver_1.resolveQuestionType)(userMessage); const companyAnchors = (0, companyAnchorResolver_1.resolveCompanyAnchors)(userMessage); + const hasPeriodInCompanyAnchors = (Array.isArray(companyAnchors?.dates) && companyAnchors.dates.some((item) => String(item ?? "").trim().length > 0)) || + (Array.isArray(companyAnchors?.periods) && companyAnchors.periods.some((item) => String(item ?? "").trim().length > 0)); + const normalizationPeriodExplicit = hasExplicitPeriodAnchorFromNormalized(normalized.normalized) || hasPeriodInCompanyAnchors; const composition = (0, answerComposer_1.composeAssistantAnswer)({ userMessage, routeSummary: normalized.route_hint_summary, @@ -1197,15 +1230,21 @@ export class AssistantService { focusDomainHint, questionTypeHint: questionTypeClass, companyAnchors, + normalizationPeriodExplicit, enableAnswerPolicyV11: config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11, enableProblemCentricAnswerV1: config_1.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1, enableLifecycleAnswerV1: config_1.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 }); + const safeAssistantReplyBase = (0, answerComposer_1.sanitizeAssistantReplyForUserFacing)(composition.assistant_reply); + const safeAssistantReply = String(safeAssistantReplyBase ?? "") + .replace(/(?:^|\n)\s*#{0,6}\s*(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") + .replace(/\b(?:debug_payload_json|technical_breakdown_json)\b[\s\S]*$/gi, "") + .trim(); const answerStructureV11 = config_1.FEATURE_ASSISTANT_CONTRACTS_V11 ? config_1.FEATURE_ASSISTANT_ANSWER_POLICY_V11 && composition.answer_structure_v11 ? composition.answer_structure_v11 : buildAnswerStructureV11({ - assistantReply: composition.assistant_reply, + assistantReply: safeAssistantReply, coverageReport: coverageEvaluation.coverage, groundingCheck, retrievalResults @@ -1266,7 +1305,7 @@ export class AssistantService { message_id: `msg-${(0, nanoid_1.nanoid)(10)}`, session_id: sessionId, role: "assistant", - text: composition.assistant_reply, + text: safeAssistantReply, reply_type: composition.reply_type, created_at: new Date().toISOString(), trace_id: normalized.trace_id, @@ -1326,7 +1365,7 @@ export class AssistantService { answer_structure_v11: answerStructureV11, investigation_state_snapshot: investigationStateSnapshot, fallback_type: composition.fallback_type, - assistant_reply: composition.assistant_reply, + assistant_reply: safeAssistantReply, reply_type: composition.reply_type, trace_id: normalized.trace_id } @@ -1334,7 +1373,7 @@ export class AssistantService { return { ok: true, session_id: sessionId, - assistant_reply: composition.assistant_reply, + assistant_reply: safeAssistantReply, reply_type: composition.reply_type, conversation_item: assistantItem, debug, @@ -1343,3 +1382,4 @@ export class AssistantService { } } + diff --git a/llm_normalizer/backend/src/services/investigationState.ts b/llm_normalizer/backend/src/services/investigationState.ts index ac0b877..8a23429 100644 --- a/llm_normalizer/backend/src/services/investigationState.ts +++ b/llm_normalizer/backend/src/services/investigationState.ts @@ -1,4 +1,4 @@ -import type { +import type { AssistantRequirement, RequirementCoverageReport, UnifiedRetrievalResult @@ -146,6 +146,11 @@ function isVatAccount(value: string): boolean { return prefix === "19" || prefix === "68"; } +function isFixedAssetAccount(value: string): boolean { + const prefix = normalizeAccountPrefix(value); + return prefix === "01" || prefix === "02" || prefix === "08"; +} + function isCloseCostsAccount(value: string): boolean { const prefix = normalizeAccountPrefix(value); if (!prefix) { @@ -161,11 +166,26 @@ function inferFollowupActiveDomain(input: { routeSummary: RouteHintSummary | null; previous: InvestigationStateWithProblemUnits; }): string | null { - const corpus = `${input.userMessage} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase(); + const messageCorpus = String(input.userMessage ?? "").toLowerCase(); + const contextualCorpus = `${messageCorpus} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase(); + + const hasFixedAssetLexicalSignal = + /(?:амортиз|основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|объект[а-яё]*\s+ос|fixed\s*asset|depreciat)/i.test( + messageCorpus + ); + const hasFixedAssetAccountSignal = + input.focusAccounts.some((item) => isFixedAssetAccount(item)) && + /(?:сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|(?:01|02|08)(?:\.\d{2})?\s*\/\s*(?:01|02|08)(?:\.\d{2})?|\b0[128](?:\.\d{2})?\b)/i.test( + messageCorpus + ); + if (hasFixedAssetLexicalSignal || hasFixedAssetAccountSignal) { + return "fixed_asset_amortization"; + } + const hasSettlementSignal = input.focusAccounts.some((item) => isSettlementAccount(item)) || - /(60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расчет|расч[её]т|зачет|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test( - corpus + /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test( + messageCorpus ); if (hasSettlementSignal) { return "settlements_60_62"; @@ -173,18 +193,26 @@ function inferFollowupActiveDomain(input: { const hasVatSignal = input.focusAccounts.some((item) => isVatAccount(item)) || - /(ндс|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(corpus); + /(?:ндс|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(messageCorpus); if (hasVatSignal) { return "vat_document_register_book"; } const hasCloseSignal = input.focusAccounts.some((item) => isCloseCostsAccount(item)) || - /(закрыти|закрытие|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(corpus); + /(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(messageCorpus); if (hasCloseSignal) { return "month_close_costs_20_44"; } + if ( + /(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|аванс|долг|settlement|payment)/i.test(contextualCorpus) && + (input.previous.followup_context?.active_domain === "settlements_60_62" || + input.previous.focus.domain === "settlements_60_62") + ) { + return "settlements_60_62"; + } + const routeDomain = deriveDomain(input.routeSummary); if (routeDomain && routeDomain !== "no_route") { return routeDomain; @@ -450,7 +478,7 @@ export function updateInvestigationState(input: UpdateInvestigationStateInput): const uncoveredRequirementIds = collectUncoveredRequirementIds(input.coverageReport); const activeDomain = inferFollowupActiveDomain({ userMessage: input.userMessage, - focusAccounts: mergedFocusAccounts, + focusAccounts: focusFromMessage, routeSummary: input.routeSummary, previous }); @@ -498,3 +526,4 @@ export function updateInvestigationState(input: UpdateInvestigationStateInput): : {}) }; } + diff --git a/llm_normalizer/backend/tests/assistantEndpoint.test.ts b/llm_normalizer/backend/tests/assistantEndpoint.test.ts index 9112215..dda2f71 100644 --- a/llm_normalizer/backend/tests/assistantEndpoint.test.ts +++ b/llm_normalizer/backend/tests/assistantEndpoint.test.ts @@ -147,7 +147,7 @@ describe("assistant mode API", () => { }); expect(response.status).toBe(200); - expect(["partial_coverage", "route_mismatch_blocked", "factual_with_explanation"]).toContain( + expect(["partial_coverage", "clarification_required", "route_mismatch_blocked", "factual_with_explanation"]).toContain( String(response.body.reply_type) ); expect(["partial", "grounded", "route_mismatch_blocked"]).toContain( diff --git a/llm_normalizer/backend/tests/assistantFollowupStateBinding.test.ts b/llm_normalizer/backend/tests/assistantFollowupStateBinding.test.ts index 5c300ef..1ef4eb7 100644 --- a/llm_normalizer/backend/tests/assistantFollowupStateBinding.test.ts +++ b/llm_normalizer/backend/tests/assistantFollowupStateBinding.test.ts @@ -194,6 +194,43 @@ describe.sequential("assistant follow-up state binding", () => { expect(second.body.debug?.investigation_state_snapshot?.turn_index).toBe(2); }); + it("rebinds follow-up domain away from settlements on fixed-asset amortization query", async () => { + const app = await createAppWithFlags({ + state: "1", + binding: "1", + problemUnits: "1", + continuity: "1", + answerPolicy: "1", + problemCentric: "1" + }); + const sessionId = `asst-wave16-fa-domain-${Date.now()}`; + + const first = await request(app).post("/api/assistant/message").send({ + session_id: sessionId, + useMock: true, + promptVersion: "normalizer_v2_0_2", + user_message: "Почему деньги ушли, а долг по 60.01/62.02 остался?" + }); + expect(first.status).toBe(200); + expect(first.body.debug?.investigation_state_snapshot?.followup_context?.active_domain).toBe("settlements_60_62"); + + const second = await request(app).post("/api/assistant/message").send({ + session_id: sessionId, + useMock: true, + promptVersion: "normalizer_v2_0_2", + user_message: + "Полно ли начислена амортизация по объектам ОС за июль? Проверь по 01/02, нет ли пропущенных объектов." + }); + + expect(second.status).toBe(200); + const activeDomain = String(second.body.debug?.investigation_state_snapshot?.followup_context?.active_domain ?? ""); + expect(activeDomain).not.toBe("settlements_60_62"); + expect(activeDomain).toMatch(/fixed_asset_amortization|month_close_costs_20_44|no_route|hybrid_store_plus_live|fixed_asset/i); + + const settlementActions = second.body.debug?.investigation_state_snapshot?.followup_context?.settlement_next_actions; + expect(Array.isArray(settlementActions) ? settlementActions.length : 0).toBe(0); + }); + it("keeps UTF-8 follow-up period refinement in-scope with soft continuity hints", async () => { const app = await createAppWithFlags({ state: "1", diff --git a/llm_normalizer/backend/tests/assistantWave16LiveCorrectivePassRegression.test.ts b/llm_normalizer/backend/tests/assistantWave16LiveCorrectivePassRegression.test.ts new file mode 100644 index 0000000..a009012 --- /dev/null +++ b/llm_normalizer/backend/tests/assistantWave16LiveCorrectivePassRegression.test.ts @@ -0,0 +1,546 @@ +import { describe, expect, it } from "vitest"; +import { composeAssistantAnswer } from "../src/services/answerComposer"; +import { resolveCompanyAnchors } from "../src/services/companyAnchorResolver"; +import type { AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../src/types/assistant"; +import type { ProblemUnit } from "../src/types/stage2ProblemUnits"; + +function buildRouteSummary() { + return { + mode: "deterministic_v2" as const, + message_in_scope: true, + scope_confidence: "high" as const, + planner: { + total_fragments: 1, + in_scope_fragments: 1, + out_of_scope_fragments: 0, + discarded_fragments: 0, + contains_multiple_tasks: false + }, + decisions: [], + fallback: { + type: "none" as const, + message: null + } + }; +} + +function buildCoverage(input?: Partial): RequirementCoverageReport { + return { + requirements_total: 1, + requirements_covered: 1, + requirements_uncovered: [], + requirements_partially_covered: [], + clarification_needed_for: [], + out_of_scope_requirements: [], + ...input + }; +} + +function buildGrounding(input?: Partial): AnswerGroundingCheck { + return { + status: "partial", + route_subject_match: true, + missing_requirements: [], + reasons: [], + why_included_summary: ["wave16-live"], + selection_reason_summary: ["wave16-live"], + ...input + }; +} + +function buildProblemUnit(input?: Partial): ProblemUnit { + return { + schema_version: "problem_unit_v0_1", + problem_unit_id: input?.problem_unit_id ?? "pu-live-1", + problem_unit_type: input?.problem_unit_type ?? "cross_branch_inconsistency_cluster", + title: input?.title ?? "Live corrective test unit", + mechanism_summary: input?.mechanism_summary ?? "Mechanism candidate: invoice_to_vat.", + business_defect_class: input?.business_defect_class ?? "invoice_to_vat", + severity: input?.severity ?? { + score: 0.61, + grade: "medium" + }, + confidence: input?.confidence ?? { + score: 0.58, + grade: "medium" + }, + lifecycle_domain: input?.lifecycle_domain ?? "vat_flow", + affected_entities: input?.affected_entities ?? ["Document:DOC-1"], + affected_documents: input?.affected_documents ?? ["Document:DOC-1"], + affected_postings: input?.affected_postings ?? ["Posting:POST-1"], + affected_accounts: input?.affected_accounts ?? ["19"], + affected_counterparties: input?.affected_counterparties ?? ["Counterparty:CP-1"], + affected_contracts: input?.affected_contracts ?? ["Contract:CTR-1"], + failed_expected_edge: input?.failed_expected_edge ?? "invoice_to_vat", + period_impact: input?.period_impact ?? { + is_period_sensitive: true, + impact_class: "close_risk" + }, + evidence_pack: input?.evidence_pack ?? ["ev-1"], + entity_backlinks: input?.entity_backlinks ?? [{ entity: "Document", id: "DOC-1" }], + snapshot_limitations: input?.snapshot_limitations ?? [] + }; +} + +function buildRetrieval(input?: Partial): UnifiedRetrievalResult { + return { + fragment_id: "F1", + requirement_ids: ["R1"], + route: "hybrid_store_plus_live", + status: "ok", + result_type: "chain", + items: [ + { + source_entity: "Document", + source_id: "DOC-1", + display_name: "Документ", + account_context: ["19"], + document_context: ["invoice", "vat_document"], + relation_pattern_hits: ["invoice_to_vat", "document_to_posting"], + graph_domain_scope: ["vat_flow"], + period: "2020-07" + } + ], + summary: { + semantic_profile: { + account_scope: ["19"], + domain_scope: ["vat", "taxes"], + relation_patterns: ["invoice_to_vat", "document_to_posting"], + period_scope: { + from: "2020-07-01", + to: "2020-07-31", + granularity: "month" + } + }, + domain_purity_guard: { + domain_card_id: "vat_document_register_book" + }, + broad_query_detected: false, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "strong" + }, + evidence: [ + { + evidence_id: "ev-1", + claim_ref: "requirement:R1", + source_type: "retrieval_item", + source_ref: { + schema_version: "evidence_source_ref_v1", + namespace: "snapshot_2020_07", + entity: "document", + id: "DOC-1", + period: "2020-07", + canonical_ref: "evidence_source_ref_v1|snapshot_2020_07|document|doc-1|2020-07" + }, + pointer: { + fragment_id: "F1", + route: "hybrid_store_plus_live", + source: { + namespace: "snapshot_2020_07", + entity: "document", + id: "DOC-1", + period: "2020-07" + }, + locator: { + field_path: "risk_score", + item_index: 0 + } + }, + evidence_kind: "mechanism_link", + mechanism_note: "invoice_to_vat", + confidence: "medium", + limitation: null, + payload: { + value: 1 + } + } + ], + candidate_evidence: [], + problem_units: [buildProblemUnit()], + problem_unit_summary: { + schema_version: "problem_unit_summary_v0_1", + units_total: 1, + duplicate_collapses: 0, + unit_types: ["cross_branch_inconsistency_cluster"], + type_distribution: { + cross_branch_inconsistency_cluster: 1 + }, + severity_distribution: { + low: 0, + medium: 1, + high: 0 + }, + confidence_distribution: { + low: 0, + medium: 1, + high: 0 + }, + primary_unit_type: "cross_branch_inconsistency_cluster" + }, + why_included: ["wave16-live"], + selection_reason: ["wave16-live"], + risk_factors: ["cross_branch_inconsistency"], + business_interpretation: ["wave16-live"], + confidence: "medium", + limitations: [], + errors: [], + ...input + }; +} + +describe("wave16 live corrective pass regressions", () => { + it("removes leaked debug payload scaffolding from user-facing reply", () => { + const output = composeAssistantAnswer({ + userMessage: "Проверь НДС цепочку в июле.", + routeSummary: buildRouteSummary(), + retrievalResults: [ + buildRetrieval({ + selection_reason: [ + "### debug_payload_json\n```json\n{\"trace_id\":\"abc\",\"route_summary\":{\"mode\":\"x\"}}\n```" + ] + }) + ], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка НДС", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage(), + groundingCheck: buildGrounding({ status: "grounded" }), + focusDomainHint: "vat_document_register_book", + questionTypeHint: "why_breaks", + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.assistant_reply).not.toMatch(/debug_payload_json|technical_breakdown_json|trace_id|route_summary/i); + }); + + it("does not claim missing period when normalization already extracted explicit period", () => { + const output = composeAssistantAnswer({ + userMessage: "Рошибка РєРѕРґРёСЂРѕРІРєРё", // emulates noisy text from live channel + routeSummary: buildRouteSummary(), + retrievalResults: [ + buildRetrieval({ + summary: { + semantic_profile: { + account_scope: ["19"], + domain_scope: ["vat", "taxes"], + relation_patterns: ["invoice_to_vat"] + }, + domain_purity_guard: { + domain_card_id: "vat_document_register_book" + }, + broad_query_detected: true, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "weak" + } + }) + ], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка периода", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage({ + requirements_covered: 0, + requirements_partially_covered: ["R1"], + clarification_needed_for: ["R1"] + }), + groundingCheck: buildGrounding({ + status: "partial", + reasons: ["Mechanism is unresolved for part of the evidence."] + }), + focusDomainHint: "vat_document_register_book", + questionTypeHint: "which_chains_are_complete_vs_incomplete", + normalizationPeriodExplicit: true, + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.assistant_reply).not.toMatch(/период в запросе не указан/i); + expect(output.assistant_reply).not.toMatch(/уточните период проверки/i); + }); + + it("blocks VAT primary synthesis when top evidence is cross-domain polluted", () => { + const polluted = buildRetrieval({ + items: [ + { + source_entity: "Document", + source_id: "DOC-1", + display_name: "Документ", + account_context: ["25", "20", "19"], + document_context: ["invoice", "vat_document", "deferred_expense_document"], + relation_pattern_hits: ["invoice_to_vat", "deferred_expense_to_writeoff"], + graph_domain_scope: ["vat_flow", "deferred_expense", "period_close", "bank_settlement", "fixed_asset"], + period: "2020-07" + } + ], + summary: { + semantic_profile: { + account_scope: ["19"], + domain_scope: ["vat", "taxes"], + relation_patterns: ["invoice_to_vat", "deferred_expense_to_writeoff"], + period_scope: { + from: "2020-07-01", + to: "2020-07-31", + granularity: "month" + } + }, + domain_purity_guard: { + domain_card_id: "vat_document_register_book" + }, + broad_query_detected: false, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "strong" + } + }); + + const output = composeAssistantAnswer({ + userMessage: "Проверь НДС-цепочку: документ -> счет-фактура -> регистр -> книга.", + routeSummary: buildRouteSummary(), + retrievalResults: [polluted], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка НДС", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage(), + groundingCheck: buildGrounding({ status: "grounded" }), + focusDomainHint: "vat_document_register_book", + questionTypeHint: "why_breaks", + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.reply_type).toBe("clarification_required"); + expect(output.answer_structure_v11?.uncertainty_block.open_uncertainties).toContain("primary_domain_evidence_not_confirmed"); + }); + + it("uses VAT-specific partial-coverage wording instead of generic chain template", () => { + const output = composeAssistantAnswer({ + userMessage: + "13 июля поступление, 15 июля реализация. НДС-цепочка по этим движениям полная или есть выпадение?", + routeSummary: buildRouteSummary(), + retrievalResults: [ + buildRetrieval({ + summary: { + semantic_profile: { + account_scope: ["19", "68"], + domain_scope: ["vat", "taxes"], + relation_patterns: ["invoice_to_vat", "document_to_posting"], + period_scope: { + from: "2020-07-01", + to: "2020-07-31", + granularity: "month" + } + }, + domain_purity_guard: { + domain_card_id: "vat_document_register_book" + }, + broad_query_detected: false, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "strong" + } + }) + ], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка полноты НДС-цепочки", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage({ requirements_covered: 0, requirements_partially_covered: ["R1"] }), + groundingCheck: buildGrounding({ status: "partial" }), + focusDomainHint: "vat_document_register_book", + questionTypeHint: "which_chains_are_complete_vs_incomplete", + normalizationPeriodExplicit: true, + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.assistant_reply).toMatch(/НДС-цепочк|НДС-звеньям|документ -> счет-фактура -> регистр -> книга/i); + expect(output.assistant_reply).not.toMatch(/ключевой переход закрытия/i); + }); + + it("renders RBP answer in RBP language with RBP-first checks", () => { + const output = composeAssistantAnswer({ + userMessage: + "31 июля прошло Списание РБП за июль. Есть ли признаки, что часть РБП к концу июля живет дольше ожидаемого?", + routeSummary: buildRouteSummary(), + retrievalResults: [ + buildRetrieval({ + items: [ + { + source_entity: "Document", + source_id: "DOC-RBP-1", + display_name: "Списание РБП", + account_context: ["97"], + document_context: ["deferred_expense_document"], + relation_pattern_hits: ["deferred_expense_to_writeoff", "document_to_posting", "asset_card_to_depreciation"], + graph_domain_scope: ["deferred_expense", "period_close", "fixed_asset"], + period: "2020-07" + } + ], + summary: { + semantic_profile: { + account_scope: ["97", "01"], + domain_scope: ["deferred_expense", "period_close"], + relation_patterns: ["deferred_expense_to_writeoff", "document_to_posting", "asset_card_to_depreciation"], + period_scope: { + from: "2020-07-01", + to: "2020-07-31", + granularity: "month" + } + }, + domain_purity_guard: { + domain_card_id: "month_close_costs_20_44" + }, + broad_query_detected: false, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "strong" + } + }) + ], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка списания РБП", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage({ requirements_covered: 0, requirements_partially_covered: ["R1"] }), + groundingCheck: buildGrounding({ status: "partial" }), + questionTypeHint: "what_is_it_grounded_on", + companyAnchors: resolveCompanyAnchors( + "31 июля прошло Списание РБП за июль. Есть ли признаки, что часть РБП к концу июля живет дольше ожидаемого?" + ), + normalizationPeriodExplicit: true, + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.assistant_reply).toMatch(/РБП|списани[ея]\s+РБП|счет\s*97/i); + expect(output.assistant_reply).toMatch(/документ списания|остаток/i); + expect(output.assistant_reply).not.toMatch(/амортиз|объект\w*\s+ОС|01\/02|сч[её]т\s*0[12]/i); + expect(output.assistant_reply).not.toMatch(/отдельн\w*\s+проверк\w*\s+расчетн\w*\s+связк/i); + }); + + it("does not collapse fixed-asset amortization question into month-close primary narrative", () => { + const unit = buildProblemUnit({ + problem_unit_id: "pu-fa-1", + problem_unit_type: "lifecycle_anomaly_node", + lifecycle_domain: "fixed_asset", + affected_accounts: ["01", "02"], + mechanism_summary: "Mechanism candidate: asset_card_to_depreciation.", + business_defect_class: "asset_card_to_depreciation", + failed_expected_edge: "asset_card_to_depreciation" + }); + + const output = composeAssistantAnswer({ + userMessage: "Полно ли начислена амортизация по всем объектам ОС за июль?", + routeSummary: buildRouteSummary(), + retrievalResults: [ + buildRetrieval({ + problem_units: [unit], + problem_unit_summary: { + schema_version: "problem_unit_summary_v0_1", + units_total: 1, + duplicate_collapses: 0, + unit_types: ["lifecycle_anomaly_node"], + type_distribution: { + lifecycle_anomaly_node: 1 + }, + severity_distribution: { + low: 0, + medium: 1, + high: 0 + }, + confidence_distribution: { + low: 0, + medium: 1, + high: 0 + }, + primary_unit_type: "lifecycle_anomaly_node" + }, + summary: { + semantic_profile: { + account_scope: ["01", "02"], + domain_scope: ["fixed_assets"], + relation_patterns: ["asset_card_to_depreciation", "deferred_expense_to_writeoff"], + period_scope: { + from: "2020-07-01", + to: "2020-07-31", + granularity: "month" + } + }, + domain_purity_guard: { + domain_card_id: "month_close_costs_20_44" + }, + broad_query_detected: false, + broad_result_flag: false, + minimum_evidence_failed: false, + narrowing_strength: "strong" + } + }) + ], + requirements: [ + { + requirement_id: "R1", + source_fragment_id: "F1", + requirement_text: "Проверка амортизации", + subject_tokens: [], + status: "covered", + route: "hybrid_store_plus_live" + } + ], + coverageReport: buildCoverage({ requirements_covered: 0, requirements_partially_covered: ["R1"] }), + groundingCheck: buildGrounding({ status: "partial" }), + questionTypeHint: "why_breaks", + companyAnchors: resolveCompanyAnchors("Полно ли начислена амортизация по всем объектам ОС за июль?"), + normalizationPeriodExplicit: true, + enableAnswerPolicyV11: true, + enableProblemCentricAnswerV1: true, + enableLifecycleAnswerV1: true + }); + + expect(output.assistant_reply).not.toMatch(/цепочка распределения затрат и закрытия месяца/i); + expect(output.assistant_reply).toMatch(/карточк[аеи] ОС|амортизац/i); + expect(output.assistant_reply).not.toMatch(/contradictory_asset_state|invalid_document_or_posting_transition|\bdisposed\b/i); + expect(output.assistant_reply).not.toMatch(/Проверьте связку документов и проводок по проблемному участку/i); + expect(output.assistant_reply).toMatch(/объект\w*\s+ОС|параметр\w*\s+амортиз|01\/02|счет\w*\s*0[12]/i); + expect(output.assistant_reply).not.toMatch(/РБП|сч[её]т\s*97|документ\s+списани[яе]|остат(ок|ки)\s+РБП|списани[ея]\s+РБП/i); + }); +}); diff --git a/llm_normalizer/data/eval_cases/eval-0E46_gT0ds.report.json b/llm_normalizer/data/eval_cases/eval-0E46_gT0ds.report.json new file mode 100644 index 0000000..bb46887 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-0E46_gT0ds.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-0E46_gT0ds", + "timestamp": "2026-03-28T14:52:43.266Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "j4C5JaGsdTX31w", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "EiQ_YNd0Z7R8dD", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-0yLbBfl8QU.report.json b/llm_normalizer/data/eval_cases/eval-0yLbBfl8QU.report.json new file mode 100644 index 0000000..d5c7e63 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-0yLbBfl8QU.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-0yLbBfl8QU", + "timestamp": "2026-03-28T14:16:11.152Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "zeDz4lWmOU-sXS", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "Igp7ZjWw22GURw", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-1yuAQTgSrA.report.json b/llm_normalizer/data/eval_cases/eval-1yuAQTgSrA.report.json new file mode 100644 index 0000000..de6a4c2 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-1yuAQTgSrA.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-1yuAQTgSrA", + "timestamp": "2026-03-28T14:52:43.542Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "dFofCt0krmlNxt", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "yuuGwyY-66-Acz", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-3lDLPY889T.report.json b/llm_normalizer/data/eval_cases/eval-3lDLPY889T.report.json new file mode 100644 index 0000000..c88d53c --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-3lDLPY889T.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-3lDLPY889T", + "timestamp": "2026-03-28T14:17:31.550Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "QsfgBqSscHBfPL", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "lrWC9XpjJMN6AA", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-48cua6GzNX.report.json b/llm_normalizer/data/eval_cases/eval-48cua6GzNX.report.json new file mode 100644 index 0000000..5fe6524 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-48cua6GzNX.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-48cua6GzNX", + "timestamp": "2026-03-28T17:57:44.019Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "60LijB0t8hI6Rv", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "tpPYPTRrOcZlBe", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-LbmAxVpEYt.report.json b/llm_normalizer/data/eval_cases/eval-LbmAxVpEYt.report.json new file mode 100644 index 0000000..14c6e62 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-LbmAxVpEYt.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-LbmAxVpEYt", + "timestamp": "2026-03-28T14:17:29.773Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "THNkvQLVaISlq3", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "0kU7UfIyaxB3TA", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "ZCKIUbbI9qTMwd", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-LpNMUq6e64.report.json b/llm_normalizer/data/eval_cases/eval-LpNMUq6e64.report.json new file mode 100644 index 0000000..0239936 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-LpNMUq6e64.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-LpNMUq6e64", + "timestamp": "2026-03-28T14:52:41.774Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "jjxZOJBxrjPdsX", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "bYY62dikNsLECZ", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "baezDULblEFYkL", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-Mk-Ep58vC0.report.json b/llm_normalizer/data/eval_cases/eval-Mk-Ep58vC0.report.json new file mode 100644 index 0000000..9519af6 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-Mk-Ep58vC0.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-Mk-Ep58vC0", + "timestamp": "2026-03-28T17:57:43.813Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "p_blMcrrCin_H_", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "esnlDt05yjx_03", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-N-nY96elpX.report.json b/llm_normalizer/data/eval_cases/eval-N-nY96elpX.report.json new file mode 100644 index 0000000..db01124 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-N-nY96elpX.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-N-nY96elpX", + "timestamp": "2026-03-28T14:23:36.202Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "7mDZkne0GBSe62", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "wAVEOezciqQrtm", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "t5shV55ZPr8wir", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-S-kDKKK4xO.report.json b/llm_normalizer/data/eval_cases/eval-S-kDKKK4xO.report.json new file mode 100644 index 0000000..5ca6e79 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-S-kDKKK4xO.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-S-kDKKK4xO", + "timestamp": "2026-03-28T14:17:31.390Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "kl3P7qQGw4BRHD", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "yn8Ku_R5880RIB", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-UNNoKia3JQ.report.json b/llm_normalizer/data/eval_cases/eval-UNNoKia3JQ.report.json new file mode 100644 index 0000000..7a5577c --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-UNNoKia3JQ.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-UNNoKia3JQ", + "timestamp": "2026-03-28T17:57:42.190Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "MY7YLMfaYP3kVR", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "gpwnhjSvR3NClQ", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "WYxkGFarO1WeXH", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-X71VuszUM2.report.json b/llm_normalizer/data/eval_cases/eval-X71VuszUM2.report.json new file mode 100644 index 0000000..1d9031f --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-X71VuszUM2.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-X71VuszUM2", + "timestamp": "2026-03-28T14:47:45.410Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "QacgRS_Ur2ayEH", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "qtDYC8oP9w1v9B", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-ZBwXbFcCs9.report.json b/llm_normalizer/data/eval_cases/eval-ZBwXbFcCs9.report.json new file mode 100644 index 0000000..4fad4ae --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-ZBwXbFcCs9.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-ZBwXbFcCs9", + "timestamp": "2026-03-28T14:23:37.997Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "NMt3FqmnkEylaF", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "TNvDsELGZj_UDF", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-_XbcacYQdg.report.json b/llm_normalizer/data/eval_cases/eval-_XbcacYQdg.report.json new file mode 100644 index 0000000..135da11 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-_XbcacYQdg.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-_XbcacYQdg", + "timestamp": "2026-03-28T14:16:09.590Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "84nqlo6CKGkRZm", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "Z-6QeOaELWJ40H", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "fq5Nw3FI86Ldc1", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-bNqkZdQS9g.report.json b/llm_normalizer/data/eval_cases/eval-bNqkZdQS9g.report.json new file mode 100644 index 0000000..0975b29 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-bNqkZdQS9g.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-bNqkZdQS9g", + "timestamp": "2026-03-28T14:23:37.732Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "AnVPMdNwg-rf3T", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по счету 97", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "utvJRunZgTZrKu", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-c2aEXxAGeh.report.json b/llm_normalizer/data/eval_cases/eval-c2aEXxAGeh.report.json new file mode 100644 index 0000000..80a29aa --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-c2aEXxAGeh.report.json @@ -0,0 +1,137 @@ +{ + "run_id": "eval-c2aEXxAGeh", + "timestamp": "2026-03-28T14:47:43.841Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 3 + }, + "cases_total": 3, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 33.33, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 33.33, + "routed_fragment_rate": 66.67, + "no_route_fragment_rate": 33.33, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 66.67, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 3, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 1, + "no_route": 1, + "batch_refresh_then_store": 1 + }, + "fallback_distribution": { + "none": 1, + "out_of_scope": 1, + "clarification": 1 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь хвосты по поставщикам и разложи цепочку", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "r6cJKlGb3sAo1h", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Как вообще по ФСБУ", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 1, + "unclear_fragments": 0, + "fallback_type": "out_of_scope", + "predicted_route_status": "no_route", + "expected_route_status": null, + "predicted_no_route_reason": "out_of_scope", + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "RwtT5cfOm8EQzm", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-003", + "raw_question": "Покажи топ рисков за июнь 2020", + "validation_passed": true, + "message_in_scope": false, + "scope_confidence": "low", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 0, + "out_of_scope_fragments": 0, + "unclear_fragments": 1, + "fallback_type": "clarification", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 0, + "trace_id": "aTubcVPJxRPKKy", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-giqMXxi5Cr.report.json b/llm_normalizer/data/eval_cases/eval-giqMXxi5Cr.report.json new file mode 100644 index 0000000..0c3f3c0 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-giqMXxi5Cr.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-giqMXxi5Cr", + "timestamp": "2026-03-28T14:16:11.355Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "lqgRK3iNNLJBPj", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "8F-6dbOWrrZ8DS", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/data/eval_cases/eval-rbxdQ0Yd-g.report.json b/llm_normalizer/data/eval_cases/eval-rbxdQ0Yd-g.report.json new file mode 100644 index 0000000..8c8cce3 --- /dev/null +++ b/llm_normalizer/data/eval_cases/eval-rbxdQ0Yd-g.report.json @@ -0,0 +1,111 @@ +{ + "run_id": "eval-rbxdQ0Yd-g", + "timestamp": "2026-03-28T14:47:45.752Z", + "mode": "single-pass-strict", + "use_mock": true, + "prompt_version": "normalizer_v2_0_2", + "schema_version": "v2_0_2", + "dataset": { + "source": "inline_raw_questions", + "file": null, + "raw_questions_count": 2 + }, + "cases_total": 2, + "metrics": { + "schema_validation_pass_rate": 100, + "scope_detection_accuracy": null, + "scope_in_scope_rate": 100, + "multi_intent_detected_rate": 0, + "clarification_required_rate": 0, + "avg_fragments_per_message": 1, + "out_of_scope_fragment_rate": 0, + "routed_fragment_rate": 100, + "no_route_fragment_rate": 0, + "route_resolution_accuracy": null, + "no_route_precision": null, + "false_no_route_rate": null, + "execution_state_consistency_rate": 100, + "executable_with_soft_assumptions_rate": 100, + "soft_assumption_used_fragment_rate": 100, + "clarification_precision": null, + "clarification_recall": null, + "false_clarification_rate": null + }, + "budget": { + "requests_total": 0, + "retries_used": 0 + }, + "clarification_eval": { + "labeled_cases": 0, + "true_positive": 0, + "false_positive": 0, + "false_negative": 0 + }, + "route_eval": { + "labeled_cases": 0, + "correct_cases": 0, + "expected_routed_cases": 0, + "no_route_true_positive": 0, + "no_route_false_positive": 0 + }, + "scope_eval": { + "labeled_cases": 0, + "correct_cases": 0 + }, + "execution_state_eval": { + "checks_total": 2, + "checks_passed": 2 + }, + "route_distribution": { + "hybrid_store_plus_live": 2 + }, + "fallback_distribution": { + "none": 2 + }, + "results": [ + { + "case_id": "BQ-001", + "raw_question": "Проверь счет 60 за июнь 2020", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "KRtUQA4OmuvZXo", + "request_count_for_case": 0 + }, + { + "case_id": "BQ-002", + "raw_question": "Покажи риски по НДС и по закрытию", + "validation_passed": true, + "message_in_scope": true, + "scope_confidence": "high", + "contains_multiple_tasks": false, + "fragments_total": 1, + "in_scope_fragments": 1, + "out_of_scope_fragments": 0, + "unclear_fragments": 0, + "fallback_type": "none", + "predicted_route_status": "routed", + "expected_route_status": null, + "predicted_no_route_reason": null, + "expected_no_route_reason": null, + "predicted_clarification_required": false, + "expected_clarification_required": null, + "executable_with_soft_assumptions_fragments": 1, + "trace_id": "6HH1OsFaNsR6hO", + "request_count_for_case": 0 + } + ] +} \ No newline at end of file diff --git a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_14_Domain_Regression_Rollback_Domain_Locked_Anchor_Usage.zip b/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_14_Domain_Regression_Rollback_Domain_Locked_Anchor_Usage.zip deleted file mode 100644 index 30422e1..0000000 Binary files a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_14_Domain_Regression_Rollback_Domain_Locked_Anchor_Usage.zip and /dev/null differ diff --git a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_15_Question_Type_Contract_First_Check_Relevance.zip b/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_15_Question_Type_Contract_First_Check_Relevance.zip deleted file mode 100644 index 3aab8fc..0000000 Binary files a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_15_Question_Type_Contract_First_Check_Relevance.zip and /dev/null differ diff --git a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_16_Residual_FAIL_Cleanup_Generic_Answer_Squeeze.zip b/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_16_Residual_FAIL_Cleanup_Generic_Answer_Squeeze.zip deleted file mode 100644 index 733a41d..0000000 Binary files a/llm_normalizer/docs/runs/2026-03-28_Stage_04_Wave_16_Residual_FAIL_Cleanup_Generic_Answer_Squeeze.zip and /dev/null differ diff --git a/llm_normalizer/frontend/dist/assets/index-HMlzOgoV.js b/llm_normalizer/frontend/dist/assets/index-PA_66ng-.js similarity index 70% rename from llm_normalizer/frontend/dist/assets/index-HMlzOgoV.js rename to llm_normalizer/frontend/dist/assets/index-PA_66ng-.js index c5c9a6b..0787805 100644 --- a/llm_normalizer/frontend/dist/assets/index-HMlzOgoV.js +++ b/llm_normalizer/frontend/dist/assets/index-PA_66ng-.js @@ -1,11 +1,11 @@ -(function(){const k=document.createElement("link").relList;if(k&&k.supports&&k.supports("modulepreload"))return;for(const E of document.querySelectorAll('link[rel="modulepreload"]'))L(E);new MutationObserver(E=>{for(const U of E)if(U.type==="childList")for(const H of U.addedNodes)H.tagName==="LINK"&&H.rel==="modulepreload"&&L(H)}).observe(document,{childList:!0,subtree:!0});function d(E){const U={};return E.integrity&&(U.integrity=E.integrity),E.referrerPolicy&&(U.referrerPolicy=E.referrerPolicy),E.crossOrigin==="use-credentials"?U.credentials="include":E.crossOrigin==="anonymous"?U.credentials="omit":U.credentials="same-origin",U}function L(E){if(E.ep)return;E.ep=!0;const U=d(E);fetch(E.href,U)}})();function Ka(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}var Qi={exports:{}},Wr={},Wi={exports:{}},G={};var La;function Bf(){if(La)return G;La=1;var s=Symbol.for("react.element"),k=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),L=Symbol.for("react.strict_mode"),E=Symbol.for("react.profiler"),U=Symbol.for("react.provider"),H=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),A=Symbol.for("react.suspense"),ae=Symbol.for("react.memo"),ue=Symbol.for("react.lazy"),D=Symbol.iterator;function $(p){return p===null||typeof p!="object"?null:(p=D&&p[D]||p["@@iterator"],typeof p=="function"?p:null)}var Se={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ke=Object.assign,ee={};function q(p,y,W){this.props=p,this.context=y,this.refs=ee,this.updater=W||Se}q.prototype.isReactComponent={},q.prototype.setState=function(p,y){if(typeof p!="object"&&typeof p!="function"&&p!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,p,y,"setState")},q.prototype.forceUpdate=function(p){this.updater.enqueueForceUpdate(this,p,"forceUpdate")};function $e(){}$e.prototype=q.prototype;function ze(p,y,W){this.props=p,this.context=y,this.refs=ee,this.updater=W||Se}var Be=ze.prototype=new $e;Be.constructor=ze,ke(Be,q.prototype),Be.isPureReactComponent=!0;var Y=Array.isArray,Re=Object.prototype.hasOwnProperty,_e={current:null},Le={key:!0,ref:!0,__self:!0,__source:!0};function Ke(p,y,W){var X,K={},Z=null,le=null;if(y!=null)for(X in y.ref!==void 0&&(le=y.ref),y.key!==void 0&&(Z=""+y.key),y)Re.call(y,X)&&!Le.hasOwnProperty(X)&&(K[X]=y[X]);var te=arguments.length-2;if(te===1)K.children=W;else if(1>>1,y=C[p];if(0>>1;pE(K,P))ZE(le,K)?(C[p]=le,C[Z]=P,p=Z):(C[p]=K,C[X]=P,p=X);else if(ZE(le,P))C[p]=le,C[Z]=P,p=Z;else break e}}return I}function E(C,I){var P=C.sortIndex-I.sortIndex;return P!==0?P:C.id-I.id}if(typeof performance=="object"&&typeof performance.now=="function"){var U=performance;s.unstable_now=function(){return U.now()}}else{var H=Date,b=H.now();s.unstable_now=function(){return H.now()-b}}var A=[],ae=[],ue=1,D=null,$=3,Se=!1,ke=!1,ee=!1,q=typeof setTimeout=="function"?setTimeout:null,$e=typeof clearTimeout=="function"?clearTimeout:null,ze=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Be(C){for(var I=d(ae);I!==null;){if(I.callback===null)L(ae);else if(I.startTime<=C)L(ae),I.sortIndex=I.expirationTime,k(A,I);else break;I=d(ae)}}function Y(C){if(ee=!1,Be(C),!ke)if(d(A)!==null)ke=!0,ve(Re);else{var I=d(ae);I!==null&&ce(Y,I.startTime-C)}}function Re(C,I){ke=!1,ee&&(ee=!1,$e(Ke),Ke=-1),Se=!0;var P=$;try{for(Be(I),D=d(A);D!==null&&(!(D.expirationTime>I)||C&&!jt());){var p=D.callback;if(typeof p=="function"){D.callback=null,$=D.priorityLevel;var y=p(D.expirationTime<=I);I=s.unstable_now(),typeof y=="function"?D.callback=y:D===d(A)&&L(A),Be(I)}else L(A);D=d(A)}if(D!==null)var W=!0;else{var X=d(ae);X!==null&&ce(Y,X.startTime-I),W=!1}return W}finally{D=null,$=P,Se=!1}}var _e=!1,Le=null,Ke=-1,mt=5,ot=-1;function jt(){return!(s.unstable_now()-otC||125p?(C.sortIndex=P,k(ae,C),d(A)===null&&C===d(ae)&&(ee?($e(Ke),Ke=-1):ee=!0,ce(Y,P-p))):(C.sortIndex=y,k(A,C),ke||Se||(ke=!0,ve(Re))),C},s.unstable_shouldYield=jt,s.unstable_wrapCallback=function(C){var I=$;return function(){var P=$;$=I;try{return C.apply(this,arguments)}finally{$=P}}}})(Xi)),Xi}var Fa;function Yf(){return Fa||(Fa=1,Yi.exports=Kf()),Yi.exports}var Ua;function Xf(){if(Ua)return Ze;Ua=1;var s=Ji(),k=Yf();function d(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),A=Object.prototype.hasOwnProperty,ae=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ue={},D={};function $(e){return A.call(D,e)?!0:A.call(ue,e)?!1:ae.test(e)?D[e]=!0:(ue[e]=!0,!1)}function Se(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function ke(e,t,n,r){if(t===null||typeof t>"u"||Se(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ee(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var q={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){q[e]=new ee(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];q[t]=new ee(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){q[e]=new ee(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){q[e]=new ee(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){q[e]=new ee(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){q[e]=new ee(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){q[e]=new ee(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){q[e]=new ee(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){q[e]=new ee(e,5,!1,e.toLowerCase(),null,!1,!1)});var $e=/[\-:]([a-z])/g;function ze(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){q[e]=new ee(e,1,!1,e.toLowerCase(),null,!1,!1)}),q.xlinkHref=new ee("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){q[e]=new ee(e,1,!1,e.toLowerCase(),null,!0,!0)});function Be(e,t,n,r){var l=q.hasOwnProperty(t)?q[t]:null;(l!==null?l.type!==0:r||!(2{for(const R of C)if(R.type==="childList")for(const H of R.addedNodes)H.tagName==="LINK"&&H.rel==="modulepreload"&&P(H)}).observe(document,{childList:!0,subtree:!0});function m(C){const R={};return C.integrity&&(R.integrity=C.integrity),C.referrerPolicy&&(R.referrerPolicy=C.referrerPolicy),C.crossOrigin==="use-credentials"?R.credentials="include":C.crossOrigin==="anonymous"?R.credentials="omit":R.credentials="same-origin",R}function P(C){if(C.ep)return;C.ep=!0;const R=m(C);fetch(C.href,R)}})();function Ka(s){return s&&s.__esModule&&Object.prototype.hasOwnProperty.call(s,"default")?s.default:s}var Qi={exports:{}},Wr={},Wi={exports:{}},G={};var La;function Bf(){if(La)return G;La=1;var s=Symbol.for("react.element"),k=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),P=Symbol.for("react.strict_mode"),C=Symbol.for("react.profiler"),R=Symbol.for("react.provider"),H=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),A=Symbol.for("react.suspense"),ae=Symbol.for("react.memo"),ue=Symbol.for("react.lazy"),I=Symbol.iterator;function $(d){return d===null||typeof d!="object"?null:(d=I&&d[I]||d["@@iterator"],typeof d=="function"?d:null)}var Se={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ke=Object.assign,ee={};function q(d,y,W){this.props=d,this.context=y,this.refs=ee,this.updater=W||Se}q.prototype.isReactComponent={},q.prototype.setState=function(d,y){if(typeof d!="object"&&typeof d!="function"&&d!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,d,y,"setState")},q.prototype.forceUpdate=function(d){this.updater.enqueueForceUpdate(this,d,"forceUpdate")};function $e(){}$e.prototype=q.prototype;function ze(d,y,W){this.props=d,this.context=y,this.refs=ee,this.updater=W||Se}var Be=ze.prototype=new $e;Be.constructor=ze,ke(Be,q.prototype),Be.isPureReactComponent=!0;var Y=Array.isArray,Re=Object.prototype.hasOwnProperty,_e={current:null},Le={key:!0,ref:!0,__self:!0,__source:!0};function Ke(d,y,W){var X,K={},Z=null,le=null;if(y!=null)for(X in y.ref!==void 0&&(le=y.ref),y.key!==void 0&&(Z=""+y.key),y)Re.call(y,X)&&!Le.hasOwnProperty(X)&&(K[X]=y[X]);var te=arguments.length-2;if(te===1)K.children=W;else if(1>>1,y=E[d];if(0>>1;dC(K,j))ZC(le,K)?(E[d]=le,E[Z]=j,d=Z):(E[d]=K,E[X]=j,d=X);else if(ZC(le,j))E[d]=le,E[Z]=j,d=Z;else break e}}return M}function C(E,M){var j=E.sortIndex-M.sortIndex;return j!==0?j:E.id-M.id}if(typeof performance=="object"&&typeof performance.now=="function"){var R=performance;s.unstable_now=function(){return R.now()}}else{var H=Date,b=H.now();s.unstable_now=function(){return H.now()-b}}var A=[],ae=[],ue=1,I=null,$=3,Se=!1,ke=!1,ee=!1,q=typeof setTimeout=="function"?setTimeout:null,$e=typeof clearTimeout=="function"?clearTimeout:null,ze=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Be(E){for(var M=m(ae);M!==null;){if(M.callback===null)P(ae);else if(M.startTime<=E)P(ae),M.sortIndex=M.expirationTime,k(A,M);else break;M=m(ae)}}function Y(E){if(ee=!1,Be(E),!ke)if(m(A)!==null)ke=!0,ve(Re);else{var M=m(ae);M!==null&&ce(Y,M.startTime-E)}}function Re(E,M){ke=!1,ee&&(ee=!1,$e(Ke),Ke=-1),Se=!0;var j=$;try{for(Be(M),I=m(A);I!==null&&(!(I.expirationTime>M)||E&&!jt());){var d=I.callback;if(typeof d=="function"){I.callback=null,$=I.priorityLevel;var y=d(I.expirationTime<=M);M=s.unstable_now(),typeof y=="function"?I.callback=y:I===m(A)&&P(A),Be(M)}else P(A);I=m(A)}if(I!==null)var W=!0;else{var X=m(ae);X!==null&&ce(Y,X.startTime-M),W=!1}return W}finally{I=null,$=j,Se=!1}}var _e=!1,Le=null,Ke=-1,mt=5,ot=-1;function jt(){return!(s.unstable_now()-otE||125d?(E.sortIndex=j,k(ae,E),m(A)===null&&E===m(ae)&&(ee?($e(Ke),Ke=-1):ee=!0,ce(Y,j-d))):(E.sortIndex=y,k(A,E),ke||Se||(ke=!0,ve(Re))),E},s.unstable_shouldYield=jt,s.unstable_wrapCallback=function(E){var M=$;return function(){var j=$;$=M;try{return E.apply(this,arguments)}finally{$=j}}}})(Xi)),Xi}var Fa;function Yf(){return Fa||(Fa=1,Yi.exports=Kf()),Yi.exports}var Ua;function Xf(){if(Ua)return Ze;Ua=1;var s=Ji(),k=Yf();function m(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),A=Object.prototype.hasOwnProperty,ae=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ue={},I={};function $(e){return A.call(I,e)?!0:A.call(ue,e)?!1:ae.test(e)?I[e]=!0:(ue[e]=!0,!1)}function Se(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function ke(e,t,n,r){if(t===null||typeof t>"u"||Se(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ee(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var q={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){q[e]=new ee(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];q[t]=new ee(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){q[e]=new ee(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){q[e]=new ee(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){q[e]=new ee(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){q[e]=new ee(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){q[e]=new ee(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){q[e]=new ee(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){q[e]=new ee(e,5,!1,e.toLowerCase(),null,!1,!1)});var $e=/[\-:]([a-z])/g;function ze(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace($e,ze);q[t]=new ee(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){q[e]=new ee(e,1,!1,e.toLowerCase(),null,!1,!1)}),q.xlinkHref=new ee("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){q[e]=new ee(e,1,!1,e.toLowerCase(),null,!0,!0)});function Be(e,t,n,r){var l=q.hasOwnProperty(t)?q[t]:null;(l!==null?l.type!==0:r||!(2u||l[i]!==o[u]){var a=` -`+l[i].replace(" at new "," at ");return e.displayName&&a.includes("")&&(a=a.replace("",e.displayName)),a}while(1<=i&&0<=u);break}}}finally{W=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?y(e):""}function K(e){switch(e.tag){case 5:return y(e.type);case 16:return y("Lazy");case 13:return y("Suspense");case 19:return y("SuspenseList");case 0:case 2:case 15:return e=X(e.type,!1),e;case 11:return e=X(e.type.render,!1),e;case 1:return e=X(e.type,!0),e;default:return""}}function Z(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Le:return"Fragment";case _e:return"Portal";case mt:return"Profiler";case Ke:return"StrictMode";case Me:return"Suspense";case et:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case jt:return(e.displayName||"Context")+".Consumer";case ot:return(e._context.displayName||"Context")+".Provider";case Ie:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case it:return t=e.displayName||null,t!==null?t:Z(e.type)||"Memo";case ve:t=e._payload,e=e._init;try{return Z(e(t))}catch{}}return null}function le(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Z(t);case 8:return t===Ke?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function te(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ie(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function He(e){var t=ie(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vt(e){e._valueTracker||(e._valueTracker=He(e))}function Zn(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ie(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $t(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Pn(e,t){var n=t.checked;return P({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function bn(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=te(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Kr(e,t){t=t.checked,t!=null&&Be(e,"checked",t,!1)}function Tt(e,t){Kr(e,t);var n=te(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?fn(e,t.type,n):t.hasOwnProperty("defaultValue")&&fn(e,t.type,te(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Yr(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function fn(e,t,n){(t!=="number"||$t(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var zt=Array.isArray;function Q(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=Tn.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Bt(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ht={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Xr=["Webkit","ms","Moz","O"];Object.keys(Ht).forEach(function(e){Xr.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ht[t]=Ht[e]})});function Gr(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ht.hasOwnProperty(e)&&Ht[e]?(""+t).trim():t+"px"}function Jr(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Gr(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var oo=P({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function lr(e,t){if(t){if(oo[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(d(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(d(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(d(61))}if(t.style!=null&&typeof t.style!="object")throw Error(d(62))}}function or(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ir=null;function ur(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var sr=null,w=null,O=null;function V(e){if(e=zr(e)){if(typeof sr!="function")throw Error(d(280));var t=e.stateNode;t&&(t=wl(t),sr(e.stateNode,e.type,t))}}function Ee(e){w?O?O.push(e):O=[e]:w=e}function qi(){if(w){var e=w,t=O;if(O=w=null,V(e),t)for(e=0;e>>=0,e===0?32:31-(rc(e)/lc|0)|0}var tl=64,nl=4194304;function dr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function rl(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=dr(u):(o&=i,o!==0&&(r=dr(o)))}else i=n&~l,i!==0?r=dr(i):o!==0&&(r=dr(o));if(r===0)return 0;if(t!==0&&t!==r&&(t&l)===0&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function pr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-ht(t),e[t]=n}function sc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=xr),Cu=" ",Nu=!1;function Pu(e,t){switch(e){case"keyup":return Mc.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ju(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ln=!1;function Uc(e,t){switch(e){case"compositionend":return ju(t);case"keypress":return t.which!==32?null:(Nu=!0,Cu);case"textInput":return e=t.data,e===Cu&&Nu?null:e;default:return null}}function Ac(e,t){if(Ln)return e==="compositionend"||!Eo&&Pu(e,t)?(e=wu(),sl=go=Xt=null,Ln=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Iu(n)}}function Fu(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Fu(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Uu(){for(var e=window,t=$t();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$t(e.document)}return t}function Po(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Xc(e){var t=Uu(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Fu(n.ownerDocument.documentElement,n)){if(r!==null&&Po(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=Mu(n,o);var i=Mu(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,On=null,jo=null,Cr=null,To=!1;function Au(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;To||On==null||On!==$t(r)||(r=On,"selectionStart"in r&&Po(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Cr&&Er(Cr,r)||(Cr=r,r=vl(jo,"onSelect"),0Un||(e.current=$o[Un],$o[Un]=null,Un--)}function se(e,t){Un++,$o[Un]=e.current,e.current=t}var Zt={},Fe=qt(Zt),Ye=qt(!1),vn=Zt;function An(e,t){var n=e.type.contextTypes;if(!n)return Zt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function Xe(e){return e=e.childContextTypes,e!=null}function Sl(){de(Ye),de(Fe)}function es(e,t,n){if(Fe.current!==Zt)throw Error(d(168));se(Fe,t),se(Ye,n)}function ts(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(d(108,le(e)||"Unknown",l));return P({},n,r)}function xl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Zt,vn=Fe.current,se(Fe,e),se(Ye,Ye.current),!0}function ns(e,t,n){var r=e.stateNode;if(!r)throw Error(d(169));n?(e=ts(e,t,vn),r.__reactInternalMemoizedMergedChildContext=e,de(Ye),de(Fe),se(Fe,e)):de(Ye),se(Ye,n)}var Lt=null,kl=!1,Bo=!1;function rs(e){Lt===null?Lt=[e]:Lt.push(e)}function uf(e){kl=!0,rs(e)}function bt(){if(!Bo&&Lt!==null){Bo=!0;var e=0,t=oe;try{var n=Lt;for(oe=1;e>=i,l-=i,Ot=1<<32-ht(t)+l|n<B?(Te=F,F=null):Te=F.sibling;var re=g(m,F,h[B],_);if(re===null){F===null&&(F=Te);break}e&&F&&re.alternate===null&&t(m,F),f=o(re,f,B),M===null?R=re:M.sibling=re,M=re,F=Te}if(B===h.length)return n(m,F),pe&&gn(m,B),R;if(F===null){for(;BB?(Te=F,F=null):Te=F.sibling;var an=g(m,F,re.value,_);if(an===null){F===null&&(F=Te);break}e&&F&&an.alternate===null&&t(m,F),f=o(an,f,B),M===null?R=an:M.sibling=an,M=an,F=Te}if(re.done)return n(m,F),pe&&gn(m,B),R;if(F===null){for(;!re.done;B++,re=h.next())re=x(m,re.value,_),re!==null&&(f=o(re,f,B),M===null?R=re:M.sibling=re,M=re);return pe&&gn(m,B),R}for(F=r(m,F);!re.done;B++,re=h.next())re=N(F,m,B,re.value,_),re!==null&&(e&&re.alternate!==null&&F.delete(re.key===null?B:re.key),f=o(re,f,B),M===null?R=re:M.sibling=re,M=re);return e&&F.forEach(function($f){return t(m,$f)}),pe&&gn(m,B),R}function we(m,f,h,_){if(typeof h=="object"&&h!==null&&h.type===Le&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case Re:e:{for(var R=h.key,M=f;M!==null;){if(M.key===R){if(R=h.type,R===Le){if(M.tag===7){n(m,M.sibling),f=l(M,h.props.children),f.return=m,m=f;break e}}else if(M.elementType===R||typeof R=="object"&&R!==null&&R.$$typeof===ve&&as(R)===M.type){n(m,M.sibling),f=l(M,h.props),f.ref=Rr(m,M,h),f.return=m,m=f;break e}n(m,M);break}else t(m,M);M=M.sibling}h.type===Le?(f=Nn(h.props.children,m.mode,_,h.key),f.return=m,m=f):(_=Jl(h.type,h.key,h.props,null,m.mode,_),_.ref=Rr(m,f,h),_.return=m,m=_)}return i(m);case _e:e:{for(M=h.key;f!==null;){if(f.key===M)if(f.tag===4&&f.stateNode.containerInfo===h.containerInfo&&f.stateNode.implementation===h.implementation){n(m,f.sibling),f=l(f,h.children||[]),f.return=m,m=f;break e}else{n(m,f);break}else t(m,f);f=f.sibling}f=Ai(h,m.mode,_),f.return=m,m=f}return i(m);case ve:return M=h._init,we(m,f,M(h._payload),_)}if(zt(h))return T(m,f,h,_);if(I(h))return z(m,f,h,_);Nl(m,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,f!==null&&f.tag===6?(n(m,f.sibling),f=l(f,h),f.return=m,m=f):(n(m,f),f=Ui(h,m.mode,_),f.return=m,m=f),i(m)):n(m,f)}return we}var Hn=cs(!0),fs=cs(!1),Pl=qt(null),jl=null,Qn=null,Xo=null;function Go(){Xo=Qn=jl=null}function Jo(e){var t=Pl.current;de(Pl),e._currentValue=t}function qo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Wn(e,t){jl=e,Xo=Qn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Ge=!0),e.firstContext=null)}function at(e){var t=e._currentValue;if(Xo!==e)if(e={context:e,memoizedValue:t,next:null},Qn===null){if(jl===null)throw Error(d(308));Qn=e,jl.dependencies={lanes:0,firstContext:e}}else Qn=Qn.next=e;return t}var wn=null;function Zo(e){wn===null?wn=[e]:wn.push(e)}function ds(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Zo(t)):(n.next=l.next,l.next=n),t.interleaved=n,It(e,r)}function It(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var en=!1;function bo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function ps(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Mt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function tn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(ne&2)!==0){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,It(e,n)}return l=r.interleaved,l===null?(t.next=t,Zo(r)):(t.next=l.next,l.next=t),r.interleaved=t,It(e,n)}function Tl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,po(e,n)}}function ms(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function zl(e,t,n,r){var l=e.updateQueue;en=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var a=u,v=a.next;a.next=null,i===null?o=v:i.next=v,i=a;var S=e.alternate;S!==null&&(S=S.updateQueue,u=S.lastBaseUpdate,u!==i&&(u===null?S.firstBaseUpdate=v:u.next=v,S.lastBaseUpdate=a))}if(o!==null){var x=l.baseState;i=0,S=v=a=null,u=o;do{var g=u.lane,N=u.eventTime;if((r&g)===g){S!==null&&(S=S.next={eventTime:N,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var T=e,z=u;switch(g=t,N=n,z.tag){case 1:if(T=z.payload,typeof T=="function"){x=T.call(N,x,g);break e}x=T;break e;case 3:T.flags=T.flags&-65537|128;case 0:if(T=z.payload,g=typeof T=="function"?T.call(N,x,g):T,g==null)break e;x=P({},x,g);break e;case 2:en=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,g=l.effects,g===null?l.effects=[u]:g.push(u))}else N={eventTime:N,lane:g,tag:u.tag,payload:u.payload,callback:u.callback,next:null},S===null?(v=S=N,a=x):S=S.next=N,i|=g;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;g=u,u=g.next,g.next=null,l.lastBaseUpdate=g,l.shared.pending=null}}while(!0);if(S===null&&(a=x),l.baseState=a,l.firstBaseUpdate=v,l.lastBaseUpdate=S,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);kn|=i,e.lanes=i,e.memoizedState=x}}function hs(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=li.transition;li.transition={};try{e(!1),t()}finally{oe=n,li.transition=r}}function Ds(){return ct().memoizedState}function ff(e,t,n){var r=on(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Is(e))Ms(t,n);else if(n=ds(e,t,n,r),n!==null){var l=We();xt(n,e,r,l),Fs(n,t,r)}}function df(e,t,n){var r=on(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Is(e))Ms(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,vt(u,i)){var a=t.interleaved;a===null?(l.next=l,Zo(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}n=ds(e,t,l,r),n!==null&&(l=We(),xt(n,e,r,l),Fs(n,t,r))}}function Is(e){var t=e.alternate;return e===he||t!==null&&t===he}function Ms(e,t){Ir=Ol=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Fs(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,po(e,n)}}var Ml={readContext:at,useCallback:Ue,useContext:Ue,useEffect:Ue,useImperativeHandle:Ue,useInsertionEffect:Ue,useLayoutEffect:Ue,useMemo:Ue,useReducer:Ue,useRef:Ue,useState:Ue,useDebugValue:Ue,useDeferredValue:Ue,useTransition:Ue,useMutableSource:Ue,useSyncExternalStore:Ue,useId:Ue,unstable_isNewReconciler:!1},pf={readContext:at,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:at,useEffect:Ns,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Dl(4194308,4,Ts.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Dl(4194308,4,e,t)},useInsertionEffect:function(e,t){return Dl(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ff.bind(null,he,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:Es,useDebugValue:fi,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=Es(!1),t=e[0];return e=cf.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=he,l=Nt();if(pe){if(n===void 0)throw Error(d(407));n=n()}else{if(n=t(),je===null)throw Error(d(349));(xn&30)!==0||ws(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,Ns(xs.bind(null,r,o,e),[e]),r.flags|=2048,Ur(9,Ss.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Nt(),t=je.identifierPrefix;if(pe){var n=Dt,r=Ot;n=(r&~(1<<32-ht(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Mr++,0")&&(a=a.replace("",e.displayName)),a}while(1<=i&&0<=u);break}}}finally{W=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?y(e):""}function K(e){switch(e.tag){case 5:return y(e.type);case 16:return y("Lazy");case 13:return y("Suspense");case 19:return y("SuspenseList");case 0:case 2:case 15:return e=X(e.type,!1),e;case 11:return e=X(e.type.render,!1),e;case 1:return e=X(e.type,!0),e;default:return""}}function Z(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Le:return"Fragment";case _e:return"Portal";case mt:return"Profiler";case Ke:return"StrictMode";case Me:return"Suspense";case et:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case jt:return(e.displayName||"Context")+".Consumer";case ot:return(e._context.displayName||"Context")+".Provider";case Ie:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case it:return t=e.displayName||null,t!==null?t:Z(e.type)||"Memo";case ve:t=e._payload,e=e._init;try{return Z(e(t))}catch{}}return null}function le(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Z(t);case 8:return t===Ke?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function te(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ie(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function He(e){var t=ie(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Vt(e){e._valueTracker||(e._valueTracker=He(e))}function Zn(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ie(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $t(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Pn(e,t){var n=t.checked;return j({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function bn(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=te(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Kr(e,t){t=t.checked,t!=null&&Be(e,"checked",t,!1)}function Tt(e,t){Kr(e,t);var n=te(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?fn(e,t.type,n):t.hasOwnProperty("defaultValue")&&fn(e,t.type,te(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Yr(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function fn(e,t,n){(t!=="number"||$t(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var zt=Array.isArray;function Q(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=Tn.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Bt(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ht={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Xr=["Webkit","ms","Moz","O"];Object.keys(Ht).forEach(function(e){Xr.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ht[t]=Ht[e]})});function Gr(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ht.hasOwnProperty(e)&&Ht[e]?(""+t).trim():t+"px"}function Jr(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Gr(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var oo=j({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function lr(e,t){if(t){if(oo[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(m(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(m(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(m(61))}if(t.style!=null&&typeof t.style!="object")throw Error(m(62))}}function or(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ir=null;function ur(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var sr=null,w=null,D=null;function V(e){if(e=zr(e)){if(typeof sr!="function")throw Error(m(280));var t=e.stateNode;t&&(t=wl(t),sr(e.stateNode,e.type,t))}}function Ee(e){w?D?D.push(e):D=[e]:w=e}function qi(){if(w){var e=w,t=D;if(D=w=null,V(e),t)for(e=0;e>>=0,e===0?32:31-(rc(e)/lc|0)|0}var tl=64,nl=4194304;function dr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function rl(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=dr(u):(o&=i,o!==0&&(r=dr(o)))}else i=n&~l,i!==0?r=dr(i):o!==0&&(r=dr(o));if(r===0)return 0;if(t!==0&&t!==r&&(t&l)===0&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function pr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-ht(t),e[t]=n}function sc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=xr),Cu=" ",Nu=!1;function Pu(e,t){switch(e){case"keyup":return Mc.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ju(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Ln=!1;function Uc(e,t){switch(e){case"compositionend":return ju(t);case"keypress":return t.which!==32?null:(Nu=!0,Cu);case"textInput":return e=t.data,e===Cu&&Nu?null:e;default:return null}}function Ac(e,t){if(Ln)return e==="compositionend"||!Eo&&Pu(e,t)?(e=wu(),sl=go=Xt=null,Ln=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Iu(n)}}function Fu(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Fu(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Uu(){for(var e=window,t=$t();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$t(e.document)}return t}function Po(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Xc(e){var t=Uu(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Fu(n.ownerDocument.documentElement,n)){if(r!==null&&Po(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=Mu(n,o);var i=Mu(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,On=null,jo=null,Cr=null,To=!1;function Au(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;To||On==null||On!==$t(r)||(r=On,"selectionStart"in r&&Po(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Cr&&Er(Cr,r)||(Cr=r,r=vl(jo,"onSelect"),0Un||(e.current=$o[Un],$o[Un]=null,Un--)}function se(e,t){Un++,$o[Un]=e.current,e.current=t}var Zt={},Fe=qt(Zt),Ye=qt(!1),vn=Zt;function An(e,t){var n=e.type.contextTypes;if(!n)return Zt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function Xe(e){return e=e.childContextTypes,e!=null}function Sl(){de(Ye),de(Fe)}function es(e,t,n){if(Fe.current!==Zt)throw Error(m(168));se(Fe,t),se(Ye,n)}function ts(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(m(108,le(e)||"Unknown",l));return j({},n,r)}function xl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Zt,vn=Fe.current,se(Fe,e),se(Ye,Ye.current),!0}function ns(e,t,n){var r=e.stateNode;if(!r)throw Error(m(169));n?(e=ts(e,t,vn),r.__reactInternalMemoizedMergedChildContext=e,de(Ye),de(Fe),se(Fe,e)):de(Ye),se(Ye,n)}var Lt=null,kl=!1,Bo=!1;function rs(e){Lt===null?Lt=[e]:Lt.push(e)}function uf(e){kl=!0,rs(e)}function bt(){if(!Bo&&Lt!==null){Bo=!0;var e=0,t=oe;try{var n=Lt;for(oe=1;e>=i,l-=i,Ot=1<<32-ht(t)+l|n<B?(Te=U,U=null):Te=U.sibling;var re=g(p,U,h[B],_);if(re===null){U===null&&(U=Te);break}e&&U&&re.alternate===null&&t(p,U),f=o(re,f,B),F===null?O=re:F.sibling=re,F=re,U=Te}if(B===h.length)return n(p,U),pe&&gn(p,B),O;if(U===null){for(;BB?(Te=U,U=null):Te=U.sibling;var an=g(p,U,re.value,_);if(an===null){U===null&&(U=Te);break}e&&U&&an.alternate===null&&t(p,U),f=o(an,f,B),F===null?O=an:F.sibling=an,F=an,U=Te}if(re.done)return n(p,U),pe&&gn(p,B),O;if(U===null){for(;!re.done;B++,re=h.next())re=x(p,re.value,_),re!==null&&(f=o(re,f,B),F===null?O=re:F.sibling=re,F=re);return pe&&gn(p,B),O}for(U=r(p,U);!re.done;B++,re=h.next())re=N(U,p,B,re.value,_),re!==null&&(e&&re.alternate!==null&&U.delete(re.key===null?B:re.key),f=o(re,f,B),F===null?O=re:F.sibling=re,F=re);return e&&U.forEach(function($f){return t(p,$f)}),pe&&gn(p,B),O}function we(p,f,h,_){if(typeof h=="object"&&h!==null&&h.type===Le&&h.key===null&&(h=h.props.children),typeof h=="object"&&h!==null){switch(h.$$typeof){case Re:e:{for(var O=h.key,F=f;F!==null;){if(F.key===O){if(O=h.type,O===Le){if(F.tag===7){n(p,F.sibling),f=l(F,h.props.children),f.return=p,p=f;break e}}else if(F.elementType===O||typeof O=="object"&&O!==null&&O.$$typeof===ve&&as(O)===F.type){n(p,F.sibling),f=l(F,h.props),f.ref=Rr(p,F,h),f.return=p,p=f;break e}n(p,F);break}else t(p,F);F=F.sibling}h.type===Le?(f=Nn(h.props.children,p.mode,_,h.key),f.return=p,p=f):(_=Jl(h.type,h.key,h.props,null,p.mode,_),_.ref=Rr(p,f,h),_.return=p,p=_)}return i(p);case _e:e:{for(F=h.key;f!==null;){if(f.key===F)if(f.tag===4&&f.stateNode.containerInfo===h.containerInfo&&f.stateNode.implementation===h.implementation){n(p,f.sibling),f=l(f,h.children||[]),f.return=p,p=f;break e}else{n(p,f);break}else t(p,f);f=f.sibling}f=Ai(h,p.mode,_),f.return=p,p=f}return i(p);case ve:return F=h._init,we(p,f,F(h._payload),_)}if(zt(h))return z(p,f,h,_);if(M(h))return L(p,f,h,_);Nl(p,h)}return typeof h=="string"&&h!==""||typeof h=="number"?(h=""+h,f!==null&&f.tag===6?(n(p,f.sibling),f=l(f,h),f.return=p,p=f):(n(p,f),f=Ui(h,p.mode,_),f.return=p,p=f),i(p)):n(p,f)}return we}var Hn=cs(!0),fs=cs(!1),Pl=qt(null),jl=null,Qn=null,Xo=null;function Go(){Xo=Qn=jl=null}function Jo(e){var t=Pl.current;de(Pl),e._currentValue=t}function qo(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Wn(e,t){jl=e,Xo=Qn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Ge=!0),e.firstContext=null)}function at(e){var t=e._currentValue;if(Xo!==e)if(e={context:e,memoizedValue:t,next:null},Qn===null){if(jl===null)throw Error(m(308));Qn=e,jl.dependencies={lanes:0,firstContext:e}}else Qn=Qn.next=e;return t}var wn=null;function Zo(e){wn===null?wn=[e]:wn.push(e)}function ds(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Zo(t)):(n.next=l.next,l.next=n),t.interleaved=n,It(e,r)}function It(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var en=!1;function bo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function ps(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Mt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function tn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(ne&2)!==0){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,It(e,n)}return l=r.interleaved,l===null?(t.next=t,Zo(r)):(t.next=l.next,l.next=t),r.interleaved=t,It(e,n)}function Tl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,po(e,n)}}function ms(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function zl(e,t,n,r){var l=e.updateQueue;en=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var a=u,v=a.next;a.next=null,i===null?o=v:i.next=v,i=a;var S=e.alternate;S!==null&&(S=S.updateQueue,u=S.lastBaseUpdate,u!==i&&(u===null?S.firstBaseUpdate=v:u.next=v,S.lastBaseUpdate=a))}if(o!==null){var x=l.baseState;i=0,S=v=a=null,u=o;do{var g=u.lane,N=u.eventTime;if((r&g)===g){S!==null&&(S=S.next={eventTime:N,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var z=e,L=u;switch(g=t,N=n,L.tag){case 1:if(z=L.payload,typeof z=="function"){x=z.call(N,x,g);break e}x=z;break e;case 3:z.flags=z.flags&-65537|128;case 0:if(z=L.payload,g=typeof z=="function"?z.call(N,x,g):z,g==null)break e;x=j({},x,g);break e;case 2:en=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,g=l.effects,g===null?l.effects=[u]:g.push(u))}else N={eventTime:N,lane:g,tag:u.tag,payload:u.payload,callback:u.callback,next:null},S===null?(v=S=N,a=x):S=S.next=N,i|=g;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;g=u,u=g.next,g.next=null,l.lastBaseUpdate=g,l.shared.pending=null}}while(!0);if(S===null&&(a=x),l.baseState=a,l.firstBaseUpdate=v,l.lastBaseUpdate=S,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);kn|=i,e.lanes=i,e.memoizedState=x}}function hs(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=li.transition;li.transition={};try{e(!1),t()}finally{oe=n,li.transition=r}}function Ds(){return ct().memoizedState}function ff(e,t,n){var r=on(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Is(e))Ms(t,n);else if(n=ds(e,t,n,r),n!==null){var l=We();xt(n,e,r,l),Fs(n,t,r)}}function df(e,t,n){var r=on(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Is(e))Ms(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,vt(u,i)){var a=t.interleaved;a===null?(l.next=l,Zo(t)):(l.next=a.next,a.next=l),t.interleaved=l;return}}catch{}n=ds(e,t,l,r),n!==null&&(l=We(),xt(n,e,r,l),Fs(n,t,r))}}function Is(e){var t=e.alternate;return e===he||t!==null&&t===he}function Ms(e,t){Ir=Ol=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Fs(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,po(e,n)}}var Ml={readContext:at,useCallback:Ue,useContext:Ue,useEffect:Ue,useImperativeHandle:Ue,useInsertionEffect:Ue,useLayoutEffect:Ue,useMemo:Ue,useReducer:Ue,useRef:Ue,useState:Ue,useDebugValue:Ue,useDeferredValue:Ue,useTransition:Ue,useMutableSource:Ue,useSyncExternalStore:Ue,useId:Ue,unstable_isNewReconciler:!1},pf={readContext:at,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:at,useEffect:Ns,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Dl(4194308,4,Ts.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Dl(4194308,4,e,t)},useInsertionEffect:function(e,t){return Dl(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ff.bind(null,he,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:Es,useDebugValue:fi,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=Es(!1),t=e[0];return e=cf.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=he,l=Nt();if(pe){if(n===void 0)throw Error(m(407));n=n()}else{if(n=t(),je===null)throw Error(m(349));(xn&30)!==0||ws(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,Ns(xs.bind(null,r,o,e),[e]),r.flags|=2048,Ur(9,Ss.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Nt(),t=je.identifierPrefix;if(pe){var n=Dt,r=Ot;n=(r&~(1<<32-ht(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Mr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[Et]=t,e[Tr]=r,ra(e,t,!1,!1),t.stateNode=e;e:{switch(i=or(n,r),n){case"dialog":fe("cancel",e),fe("close",e),l=r;break;case"iframe":case"object":case"embed":fe("load",e),l=r;break;case"video":case"audio":for(l=0;lJn&&(t.flags|=128,r=!0,Ar(o,!1),t.lanes=4194304)}else{if(!r)if(e=Rl(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Ar(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!pe)return Ae(t),null}else 2*ge()-o.renderingStartTime>Jn&&n!==1073741824&&(t.flags|=128,r=!0,Ar(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=ge(),t.sibling=null,n=me.current,se(me,r?n&1|2:n&1),t):(Ae(t),null);case 22:case 23:return Ii(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(lt&1073741824)!==0&&(Ae(t),t.subtreeFlags&6&&(t.flags|=8192)):Ae(t),null;case 24:return null;case 25:return null}throw Error(d(156,t.tag))}function xf(e,t){switch(Qo(t),t.tag){case 1:return Xe(t.type)&&Sl(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Kn(),de(Ye),de(Fe),ri(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return ti(t),null;case 13:if(de(me),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(d(340));Bn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return de(me),null;case 4:return Kn(),null;case 10:return Jo(t.type._context),null;case 22:case 23:return Ii(),null;case 24:return null;default:return null}}var Vl=!1,Ve=!1,kf=typeof WeakSet=="function"?WeakSet:Set,j=null;function Xn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ye(e,t,r)}else n.current=null}function _i(e,t,n){try{n()}catch(r){ye(e,t,r)}}var ia=!1;function _f(e,t){if(Io=il,e=Uu(),Po(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,a=-1,v=0,S=0,x=e,g=null;t:for(;;){for(var N;x!==n||l!==0&&x.nodeType!==3||(u=i+l),x!==o||r!==0&&x.nodeType!==3||(a=i+r),x.nodeType===3&&(i+=x.nodeValue.length),(N=x.firstChild)!==null;)g=x,x=N;for(;;){if(x===e)break t;if(g===n&&++v===l&&(u=i),g===o&&++S===r&&(a=i),(N=x.nextSibling)!==null)break;x=g,g=x.parentNode}x=N}n=u===-1||a===-1?null:{start:u,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(Mo={focusedElem:e,selectionRange:n},il=!1,j=t;j!==null;)if(t=j,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,j=e;else for(;j!==null;){t=j;try{var T=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(T!==null){var z=T.memoizedProps,we=T.memoizedState,m=t.stateNode,f=m.getSnapshotBeforeUpdate(t.elementType===t.type?z:gt(t.type,z),we);m.__reactInternalSnapshotBeforeUpdate=f}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(d(163))}}catch(_){ye(t,t.return,_)}if(e=t.sibling,e!==null){e.return=t.return,j=e;break}j=t.return}return T=ia,ia=!1,T}function Vr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&_i(t,n,o)}l=l.next}while(l!==r)}}function $l(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ei(e){var t=e.ref;if(t!==null){var n=e.stateNode;e.tag,e=n,typeof t=="function"?t(e):t.current=e}}function ua(e){var t=e.alternate;t!==null&&(e.alternate=null,ua(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Et],delete t[Tr],delete t[Vo],delete t[lf],delete t[of])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function sa(e){return e.tag===5||e.tag===3||e.tag===4}function aa(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||sa(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ci(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=gl));else if(r!==4&&(e=e.child,e!==null))for(Ci(e,t,n),e=e.sibling;e!==null;)Ci(e,t,n),e=e.sibling}function Ni(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ni(e,t,n),e=e.sibling;e!==null;)Ni(e,t,n),e=e.sibling}var Oe=null,wt=!1;function nn(e,t,n){for(n=n.child;n!==null;)ca(e,t,n),n=n.sibling}function ca(e,t,n){if(_t&&typeof _t.onCommitFiberUnmount=="function")try{_t.onCommitFiberUnmount(el,n)}catch{}switch(n.tag){case 5:Ve||Xn(n,t);case 6:var r=Oe,l=wt;Oe=null,nn(e,t,n),Oe=r,wt=l,Oe!==null&&(wt?(e=Oe,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Oe.removeChild(n.stateNode));break;case 18:Oe!==null&&(wt?(e=Oe,n=n.stateNode,e.nodeType===8?Ao(e.parentNode,n):e.nodeType===1&&Ao(e,n),gr(e)):Ao(Oe,n.stateNode));break;case 4:r=Oe,l=wt,Oe=n.stateNode.containerInfo,wt=!0,nn(e,t,n),Oe=r,wt=l;break;case 0:case 11:case 14:case 15:if(!Ve&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&((o&2)!==0||(o&4)!==0)&&_i(n,t,i),l=l.next}while(l!==r)}nn(e,t,n);break;case 1:if(!Ve&&(Xn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){ye(n,t,u)}nn(e,t,n);break;case 21:nn(e,t,n);break;case 22:n.mode&1?(Ve=(r=Ve)||n.memoizedState!==null,nn(e,t,n),Ve=r):nn(e,t,n);break;default:nn(e,t,n)}}function fa(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new kf),t.forEach(function(r){var l=Lf.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function St(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=ge()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Cf(r/1960))-r,10e?16:e,ln===null)var r=!1;else{if(e=ln,ln=null,Kl=0,(ne&6)!==0)throw Error(d(331));var l=ne;for(ne|=4,j=e.current;j!==null;){var o=j,i=o.child;if((j.flags&16)!==0){var u=o.deletions;if(u!==null){for(var a=0;age()-Ti?En(e,0):ji|=n),qe(e,t)}function Ea(e,t){t===0&&((e.mode&1)===0?t=1:(t=nl,nl<<=1,(nl&130023424)===0&&(nl=4194304)));var n=We();e=It(e,t),e!==null&&(pr(e,t,n),qe(e,n))}function Rf(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ea(e,n)}function Lf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(d(314))}r!==null&&r.delete(t),Ea(e,n)}var Ca;Ca=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Ye.current)Ge=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Ge=!1,wf(e,t,n);Ge=(e.flags&131072)!==0}else Ge=!1,pe&&(t.flags&1048576)!==0&&ls(t,El,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Al(e,t),e=t.pendingProps;var l=An(t,Fe.current);Wn(t,n),l=ii(null,t,r,e,l,n);var o=ui();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Xe(r)?(o=!0,xl(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,bo(t),l.updater=Fl,t.stateNode=l,l._reactInternals=t,pi(t,r,e,n),t=yi(null,t,r,!0,o,n)):(t.tag=0,pe&&o&&Ho(t),Qe(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Al(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Df(r),e=gt(r,e),l){case 0:t=vi(null,t,r,e,n);break e;case 1:t=qs(null,t,r,e,n);break e;case 11:t=Ks(null,t,r,e,n);break e;case 14:t=Ys(null,t,r,gt(r.type,e),n);break e}throw Error(d(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),vi(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),qs(e,t,r,l,n);case 3:e:{if(Zs(t),e===null)throw Error(d(387));r=t.pendingProps,o=t.memoizedState,l=o.element,ps(e,t),zl(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=Yn(Error(d(423)),t),t=bs(e,t,r,n,l);break e}else if(r!==l){l=Yn(Error(d(424)),t),t=bs(e,t,r,n,l);break e}else for(rt=Jt(t.stateNode.containerInfo.firstChild),nt=t,pe=!0,yt=null,n=fs(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Bn(),r===l){t=Ft(e,t,n);break e}Qe(e,t,r,n)}t=t.child}return t;case 5:return vs(t),e===null&&Ko(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Fo(r,l)?i=null:o!==null&&Fo(r,o)&&(t.flags|=32),Js(e,t),Qe(e,t,i,n),t.child;case 6:return e===null&&Ko(t),null;case 13:return ea(e,t,n);case 4:return ei(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Hn(t,null,r,n):Qe(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),Ks(e,t,r,l,n);case 7:return Qe(e,t,t.pendingProps,n),t.child;case 8:return Qe(e,t,t.pendingProps.children,n),t.child;case 12:return Qe(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,se(Pl,r._currentValue),r._currentValue=i,o!==null)if(vt(o.value,i)){if(o.children===l.children&&!Ye.current){t=Ft(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var a=u.firstContext;a!==null;){if(a.context===r){if(o.tag===1){a=Mt(-1,n&-n),a.tag=2;var v=o.updateQueue;if(v!==null){v=v.shared;var S=v.pending;S===null?a.next=a:(a.next=S.next,S.next=a),v.pending=a}}o.lanes|=n,a=o.alternate,a!==null&&(a.lanes|=n),qo(o.return,n,t),u.lanes|=n;break}a=a.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(d(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),qo(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}Qe(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Wn(t,n),l=at(l),r=r(l),t.flags|=1,Qe(e,t,r,n),t.child;case 14:return r=t.type,l=gt(r,t.pendingProps),l=gt(r.type,l),Ys(e,t,r,l,n);case 15:return Xs(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),Al(e,t),t.tag=1,Xe(r)?(e=!0,xl(t)):e=!1,Wn(t,n),As(t,r,l),pi(t,r,l,n),yi(null,t,r,!0,e,n);case 19:return na(e,t,n);case 22:return Gs(e,t,n)}throw Error(d(156,t.tag))};function Na(e,t){return ou(e,t)}function Of(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function dt(e,t,n,r){return new Of(e,t,n,r)}function Fi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Df(e){if(typeof e=="function")return Fi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Ie)return 11;if(e===it)return 14}return 2}function sn(e,t){var n=e.alternate;return n===null?(n=dt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Jl(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")Fi(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Le:return Nn(n.children,l,o,t);case Ke:i=8,l|=8;break;case mt:return e=dt(12,n,t,l|2),e.elementType=mt,e.lanes=o,e;case Me:return e=dt(13,n,t,l),e.elementType=Me,e.lanes=o,e;case et:return e=dt(19,n,t,l),e.elementType=et,e.lanes=o,e;case ce:return ql(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case ot:i=10;break e;case jt:i=9;break e;case Ie:i=11;break e;case it:i=14;break e;case ve:i=16,r=null;break e}throw Error(d(130,e==null?e:typeof e,""))}return t=dt(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function Nn(e,t,n,r){return e=dt(7,e,r,t),e.lanes=n,e}function ql(e,t,n,r){return e=dt(22,e,r,t),e.elementType=ce,e.lanes=n,e.stateNode={isHidden:!1},e}function Ui(e,t,n){return e=dt(6,e,null,t),e.lanes=n,e}function Ai(e,t,n){return t=dt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function If(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=fo(0),this.expirationTimes=fo(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=fo(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Vi(e,t,n,r,l,o,i,u,a){return e=new If(e,t,n,u,a),t===1?(t=1,o===!0&&(t|=8)):t=0,o=dt(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},bo(o),e}function Mf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(s)}catch(k){console.error(k)}}return s(),Ki.exports=Xf(),Ki.exports}var Va;function Jf(){if(Va)return lo;Va=1;var s=Gf();return lo.createRoot=s.createRoot,lo.hydrateRoot=s.hydrateRoot,lo}var qf=Jf();const Zf=Ka(qf),bf="/api";async function be(s,k){const d=await fetch(`${bf}${s}`,{...k,headers:{"Content-Type":"application/json",...k?.headers??{}}}),L=await d.json();if(!d.ok){const E=L.error?.message??"Ошибка запроса";throw new Error(E)}return L}const pt={async testConnection(s){return be("/openai/test-connection",{method:"POST",body:JSON.stringify({apiKey:s.apiKey,model:s.model,baseUrl:s.baseUrl})})},async normalize(s){return be("/normalize",{method:"POST",body:JSON.stringify({apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion,systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples,userQuestion:s.query.userQuestion,context:{period_hint:s.query.periodHint??"",business_context:s.query.businessContext??"",expected_route:s.query.expectedRoute??""},saveAsTestCase:!!s.saveAsTestCase,useMock:!!s.useMock})})},async loadHistory(){return be("/history")},async loadTrace(s){return be(`/history/${s}`)},async loadPresets(){return be("/presets")},async savePreset(s){return be("/presets/save",{method:"POST",body:JSON.stringify(s)})},async runEval(s){return be("/eval/run",{method:"POST",body:JSON.stringify({normalizeConfig:{apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion,systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples},caseIds:s.caseIds,useMock:!!s.useMock,mode:s.mode??"standard",caseSetFile:s.caseSetFile,rawQuestions:s.rawQuestions})})},async startRun(){return be("/accounting-agent/v1/runs/start",{method:"POST",body:JSON.stringify({initiator:"ndc_operator",source:"gui"})})},async finishRun(s){return be("/accounting-agent/v1/runs/finish",{method:"POST",body:JSON.stringify({runId:s,status:"DONE",source:"gui",reason:"Остановлено оператором из GUI"})})},async listRuns(){return be("/accounting-agent/v1/runs")},async listResults(){return be("/accounting-agent/v1/results")},async runTrace(s){return be(`/accounting-agent/v1/trace/run/${s}`)},async sendAssistantMessage(s){return be("/assistant/message",{method:"POST",body:JSON.stringify({session_id:s.sessionId??"",mode:"assistant",message:s.userMessage,user_message:s.userMessage,apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion??"normalizer_v2_0_2",systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples,context:{period_hint:s.context?.periodHint??"",business_context:s.context?.businessContext??""},useMock:!!s.useMock})})},async loadAssistantSession(s){return be(`/assistant/session/${s}`)}};function kt({value:s}){return c.jsx("pre",{className:"json-view",children:JSON.stringify(s??{},null,2)})}function cn({title:s,subtitle:k,actions:d,children:L}){return c.jsxs("section",{className:"panel-frame",children:[c.jsxs("header",{className:"panel-header",children:[c.jsxs("div",{children:[c.jsx("h2",{children:s}),k?c.jsx("p",{children:k}):null]}),d?c.jsx("div",{className:"panel-actions",children:d}):null]}),c.jsx("div",{className:"panel-body",children:L})]})}function ed(s){return s==="assistant"?"Ассистент":"Вы"}function td(s){const k=new Date(s);return Number.isNaN(k.getTime())?s:k.toLocaleTimeString("ru-RU")}function nd(s){try{return JSON.stringify(s,null,2)}catch{return String(s)}}function rd(s,k){const d=[];d.push("# Assistant conversation export"),d.push(`session_id: ${s||"n/a"}`),d.push(`exported_at: ${new Date().toISOString()}`),d.push("");for(let L=0;L{ee.current&&(ee.current.scrollTop=ee.current.scrollHeight)},[k,Se]),J.useEffect(()=>()=>{q.current!==null&&window.clearTimeout(q.current)},[]);async function Be(){if(k.length===0)return;const Y=rd(s,k),Re=await ld(Y);ze(Re?"success":"error"),q.current!==null&&window.clearTimeout(q.current),q.current=window.setTimeout(()=>{ze("idle")},2200)}return c.jsxs(cn,{title:"Режим ассистента",subtitle:"Диалоговый слой поверх normalizer, маршрутизации и factual retrieval.",actions:c.jsxs("div",{className:"assistant-panel-actions",children:[c.jsx("button",{type:"button",className:"assistant-copy-btn",onClick:()=>{Be()},disabled:k.length===0,children:"Скопировать чат"}),$e==="success"?c.jsx("span",{className:"assistant-copy-feedback success",children:"Скопировано"}):null,$e==="error"?c.jsx("span",{className:"assistant-copy-feedback error",children:"Ошибка копирования"}):null,c.jsx("span",{className:"status-chip",children:s?`session: ${s}`:"новая сессия"})]}),children:[c.jsxs("div",{ref:ee,className:"assistant-chat-list",children:[k.length===0?c.jsx("div",{className:"assistant-empty muted",children:"Диалог пуст. Отправьте первый вопрос, чтобы запустить контур ассистента."}):null,k.map(Y=>c.jsxs("article",{className:`assistant-msg ${Y.role}`,children:[c.jsxs("header",{className:"assistant-msg-head",children:[c.jsx("strong",{children:ed(Y.role)}),c.jsx("span",{children:td(Y.created_at)})]}),c.jsx("div",{className:"assistant-msg-body",children:Y.text}),Y.role==="assistant"&&Y.debug?c.jsxs("details",{className:"assistant-debug",children:[c.jsx("summary",{children:"Показать технический разбор"}),c.jsx(kt,{value:Y.debug})]}):null]},Y.message_id))]}),c.jsxs("div",{className:"assistant-compose",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["Подсказка по периоду",c.jsx("input",{value:E,onChange:Y=>U(Y.target.value)})]}),c.jsxs("label",{children:["Бизнес-контекст",c.jsx("input",{value:H,onChange:Y=>b(Y.target.value)})]})]}),c.jsxs("label",{className:"full-width",children:["Сообщение",c.jsx("textarea",{value:d,onChange:Y=>L(Y.target.value),rows:4,placeholder:"Введите вопрос к данным компании..."})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("label",{className:"checkbox-row",children:[c.jsx("input",{type:"checkbox",checked:A,onChange:Y=>ae(Y.target.checked)}),"Mock-режим"]}),c.jsx("button",{type:"button",onClick:()=>ue(),disabled:$||!d.trim(),children:$?"Выполняю...":"Отправить"}),c.jsx("button",{type:"button",onClick:()=>D(),disabled:$&&k.length===0,children:"Сбросить сессию"})]}),Se?c.jsx("p",{className:"diff-summary",children:Se}):null,ke?c.jsx("p",{className:"error-text",children:ke}):null]})]})}function $a({value:s,onChange:k,onTestConnection:d,onSaveLocalConfig:L,lastStatus:E,busy:U}){return c.jsxs(cn,{title:"Подключение OpenAI",subtitle:"Ключ живет только в памяти сессии (не пишется в localStorage).",actions:c.jsx("span",{className:"status-chip",children:E||"Статус: не проверено"}),children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["OpenAI API Key",c.jsx("input",{type:"password",value:s.apiKey,onChange:H=>k({...s,apiKey:H.target.value}),placeholder:"sk-..."})]}),c.jsxs("label",{children:["Model ID",c.jsx("input",{value:s.model,onChange:H=>k({...s,model:H.target.value})})]}),c.jsxs("label",{children:["Base URL",c.jsx("input",{value:s.baseUrl,onChange:H=>k({...s,baseUrl:H.target.value})})]}),c.jsxs("label",{children:["Temperature",c.jsx("input",{type:"number",step:"0.1",value:s.temperature,onChange:H=>k({...s,temperature:Number(H.target.value)})})]}),c.jsxs("label",{children:["Max output tokens",c.jsx("input",{type:"number",value:s.maxOutputTokens,onChange:H=>k({...s,maxOutputTokens:Number(H.target.value)})})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsx("button",{type:"button",onClick:()=>L(),children:"Сохранить локальную конфигурацию"}),c.jsx("button",{type:"button",onClick:()=>d(),disabled:U,children:U?"Проверяем...":"Проверить подключение"})]})]})}function id({items:s,onRefresh:k,onOpenTrace:d}){return c.jsx(cn,{title:"История нормализаций",subtitle:"Короткий вопрос, confidence, route hint и статус валидации.",actions:c.jsx("button",{type:"button",onClick:()=>k(),children:"Обновить"}),children:c.jsxs("div",{className:"history-list",children:[s.length===0?c.jsx("p",{className:"muted",children:"История пока пустая."}):null,s.map(L=>c.jsxs("button",{type:"button",className:"history-item",onClick:()=>d(L.trace_id),children:[c.jsxs("div",{className:"history-row",children:[c.jsx("strong",{children:L.route_hint??"route: n/a"}),c.jsx("span",{children:L.validation_passed?"schema: ok":"schema: fail"})]}),c.jsx("p",{children:L.question_short}),c.jsxs("div",{className:"history-row",children:[c.jsx("span",{children:L.model}),c.jsx("span",{children:new Date(L.timestamp).toLocaleString("ru-RU")})]})]},L.trace_id))]})})}function At(s){return s==null||s===""?"—":String(s)}function ud({result:s}){return c.jsx(cn,{title:"Runtime метрики",subtitle:"trace_id, токены, latency и статус валидации.",children:c.jsxs("div",{className:"metrics-grid",children:[c.jsxs("div",{children:[c.jsx("span",{children:"trace_id"}),c.jsx("strong",{children:At(s?.trace_id)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"request_started_at"}),c.jsx("strong",{children:At(s?new Date(Date.now()-s.latency_ms).toISOString():null)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"request_finished_at"}),c.jsx("strong",{children:At(s?new Date().toISOString():null)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"latency_ms"}),c.jsx("strong",{children:At(s?.latency_ms)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"input_tokens"}),c.jsx("strong",{children:At(s?.usage?.input_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"output_tokens"}),c.jsx("strong",{children:At(s?.usage?.output_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"total_tokens"}),c.jsx("strong",{children:At(s?.usage?.total_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"validation_status"}),c.jsx("strong",{children:s?.validation?.passed?"passed":"failed"})]}),c.jsxs("div",{children:[c.jsx("span",{children:"prompt_version"}),c.jsx("strong",{children:At(s?.prompt_version)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"schema_version"}),c.jsx("strong",{children:At(s?.schema_version)})]})]})})}const sd={normalized:"Normalized JSON",fragments:"Fragment View",scope:"Scope View",flags:"Flags View",route:"Route Simulation",raw:"Raw model output",validation:"Validation",logs:"Logs"};function ad(s){return s&&typeof s=="object"?s:null}function cd({tab:s,onTabChange:k,result:d,appLogs:L}){const E=["normalized","fragments","scope","flags","route","raw","validation","logs"],U=ad(d?.normalized),H=String(U?.schema_version??""),b=H==="normalized_query_v2"||H==="normalized_query_v2_0_1"||H==="normalized_query_v2_0_2",A=b?{fragments:U?.fragments??[],discarded_fragments:U?.discarded_fragments??[]}:{note:"Fragment View доступен для normalized_query_v2."},ae=b?{message_in_scope:U?.message_in_scope??null,scope_confidence:U?.scope_confidence??null,contains_multiple_tasks:U?.contains_multiple_tasks??null,global_notes:U?.global_notes??null}:{note:"Scope View доступен для normalized_query_v2."},ue=b?Array.isArray(U?.fragments)?(U?.fragments).map(D=>({fragment_id:D.fragment_id??null,domain_relevance:D.domain_relevance??null,candidate_labels:D.candidate_labels??[],execution_readiness:D.execution_readiness??null,clarification_reason:D.clarification_reason??null,soft_assumption_used:D.soft_assumption_used??[],route_status:D.route_status??null,no_route_reason:D.no_route_reason??null,flags:D.flags??{}})):[]:{note:"Flags View доступен для normalized_query_v2."};return c.jsxs(cn,{title:"Выходные данные",subtitle:"Structured output и диагностические вкладки.",children:[c.jsx("div",{className:"tab-row",children:E.map(D=>c.jsx("button",{type:"button",className:s===D?"tab active":"tab",onClick:()=>k(D),children:sd[D]},D))}),s==="normalized"?c.jsx(kt,{value:d?.normalized??{note:"Нет данных."}}):null,s==="fragments"?c.jsx(kt,{value:A}):null,s==="scope"?c.jsx(kt,{value:ae}):null,s==="flags"?c.jsx(kt,{value:ue}):null,s==="route"?c.jsx(kt,{value:d?.route_hint_summary??{note:"Нет данных."}}):null,s==="raw"?c.jsx(kt,{value:d?.raw_model_output??{note:"Нет данных."}}):null,s==="validation"?c.jsx(kt,{value:d?.validation??{note:"Нет данных."}}):null,s==="logs"?c.jsx(kt,{value:L}):null]})}function Ba({value:s,onChange:k,presets:d,selectedPresetId:L,onSelectPreset:E,onLoadPreset:U,onSavePreset:H,onResetDefaults:b,onDiffPrevious:A,presetName:ae,onPresetNameChange:ue,diffSummary:D}){return c.jsxs(cn,{title:"Prompt Manager",subtitle:"Системный, developer и domain уровни управляются отдельно.",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["Системный prompt",c.jsx("textarea",{value:s.systemPrompt,onChange:$=>k({...s,systemPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Developer / Instruction prompt",c.jsx("textarea",{value:s.developerPrompt,onChange:$=>k({...s,developerPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Domain prompt",c.jsx("textarea",{value:s.domainPrompt,onChange:$=>k({...s,domainPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Schema notes",c.jsx("textarea",{value:s.schemaNotes,onChange:$=>k({...s,schemaNotes:$.target.value}),rows:6})]}),c.jsxs("label",{className:"full-width",children:["Few-shot examples",c.jsx("textarea",{value:s.fewShotExamples,onChange:$=>k({...s,fewShotExamples:$.target.value}),rows:8})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("select",{value:L,onChange:$=>E($.target.value),children:[c.jsx("option",{value:"",children:"Выберите preset..."}),d.map($=>c.jsx("option",{value:$.id,children:$.name},$.id))]}),c.jsx("button",{type:"button",onClick:()=>U(),children:"Загрузить preset"}),c.jsx("input",{value:ae,onChange:$=>ue($.target.value),placeholder:"Имя для сохранения"}),c.jsx("button",{type:"button",onClick:()=>H(),children:"Сохранить preset"}),c.jsx("button",{type:"button",onClick:()=>A(),children:"Diff с предыдущим"}),c.jsx("button",{type:"button",onClick:()=>b(),children:"Сбросить к default"})]}),D?c.jsx("p",{className:"diff-summary",children:D}):null]})}function fd({value:s,onChange:k,onApplyBatchFormat:d,onNormalize:L,busy:E,useMock:U,onUseMockChange:H,errorMessage:b}){return c.jsxs(cn,{title:"Запрос пользователя",subtitle:"NDC semantic front-end: нормализуем, но не отвечаем за бухгалтерскую суть.",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{className:"full-width",children:["Raw user question",c.jsx("textarea",{value:s.userQuestion,onChange:A=>k({...s,userQuestion:A.target.value}),rows:6,placeholder:"Например: По каким покупателям у нас на конец июня висят отгрузки без оплаты..."})]}),c.jsxs("label",{className:"full-width",children:["Batch queries (`;` separator)",c.jsx("textarea",{value:s.batchQuestionsRaw,onChange:A=>k({...s,batchQuestionsRaw:A.target.value}),onBlur:()=>d(),rows:8,placeholder:"Вопрос 1; Вопрос 2; Вопрос 3"})]}),c.jsxs("label",{children:["Optional period context",c.jsx("input",{value:s.periodHint,onChange:A=>k({...s,periodHint:A.target.value})})]}),c.jsxs("label",{children:["Optional business context",c.jsx("input",{value:s.businessContext,onChange:A=>k({...s,businessContext:A.target.value})})]}),c.jsxs("label",{children:["Optional expected route (eval)",c.jsx("input",{value:s.expectedRoute,onChange:A=>k({...s,expectedRoute:A.target.value})})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("label",{className:"checkbox-row",children:[c.jsx("input",{type:"checkbox",checked:U,onChange:A=>H(A.target.checked)}),"Mock-режим (без вызова OpenAI)"]}),c.jsx("button",{type:"button",onClick:()=>d(),disabled:E||!s.batchQuestionsRaw.trim(),children:"Применить `;` в переносы"}),c.jsx("button",{type:"button",onClick:()=>L(!1),disabled:E||!s.userQuestion.trim(),children:E?"Нормализуем...":"Normalize"}),c.jsx("button",{type:"button",onClick:()=>L(!0),disabled:E||!s.userQuestion.trim(),children:E?"Сохраняем...":"Normalize + Save as test case"})]}),b?c.jsx("p",{className:"error-text",children:b}):null]})}function dd({runs:s,selectedRunId:k,onSelectRun:d,onStartRun:L,onFinishRun:E,onRefreshRuns:U,onRunEval:H,onCopyEvalReport:b,evalBusy:A,traceItems:ae,evalReport:ue}){return c.jsxs(cn,{title:"NDC Run Monitor",subtitle:"Важно: кнопка Запустить run создает только run-сущность. Кнопка eval запускает batch-проверку normalizer v2.0.2.",children:[c.jsxs("div",{className:"button-row",children:[c.jsx("button",{type:"button",onClick:()=>L(),children:"Запустить run"}),c.jsx("button",{type:"button",onClick:()=>E(),disabled:!k,children:"Завершить выбранный run"}),c.jsx("button",{type:"button",onClick:()=>U(),children:"Обновить runs"}),c.jsx("button",{type:"button",onClick:()=>H(),disabled:A,children:A?"Идет eval v2.0.2...":"Запустить eval v2.0.2"})]}),c.jsxs("div",{className:"runtime-grid",children:[c.jsxs("div",{className:"runtime-runs",children:[s.map(D=>c.jsxs("button",{type:"button",className:k===D.runId?"history-item selected":"history-item",onClick:()=>d(D.runId),children:[c.jsxs("div",{className:"history-row",children:[c.jsx("strong",{children:D.status}),c.jsx("span",{children:D.runId})]}),c.jsxs("div",{className:"history-row",children:[c.jsx("span",{children:D.sessionId}),c.jsx("span",{children:new Date(D.updatedAt).toLocaleString("ru-RU")})]})]},D.runId)),s.length===0?c.jsx("p",{className:"muted",children:"Нет активных запусков."}):null]}),c.jsxs("div",{children:[c.jsx("h3",{children:"Trace выбранного run"}),c.jsx(kt,{value:ae}),c.jsxs("div",{className:"eval-report-wrap",children:[c.jsx("h3",{style:{marginTop:12},children:"Отчет eval"}),c.jsx(kt,{value:ue??{note:"Eval пока не запускался"}}),c.jsx("button",{type:"button",className:"copy-cube-button",title:"Скопировать отчет eval",onClick:()=>b(),children:"⧉"})]})]})]})]})}const pd={apiKey:"",model:"gpt-4o-mini",baseUrl:"https://api.openai.com/v1",temperature:0,maxOutputTokens:700},Ha={systemPrompt:"Ты semantic-normalizer для бухгалтерского ассистента NDC. Возвращай только JSON по схеме normalized_query_v2_0_2.",developerPrompt:"Сначала делай decomposition сообщения на task fragments, затем определяй domain scope и route-critical flags. Для каждого fragment заполняй execution_readiness + route_status + no_route_reason. Если fragment routable, не оставляй его в no_route.",domainPrompt:"Контур: данные текущего предприятия в 1С/NDC. In-scope: документы, проводки, взаиморасчеты, остатки, периодное закрытие, аномалии и контрольные проверки. Out-of-scope: общая теория, законы и оффтоп.",schemaNotes:"schema_version: normalized_query_v2_0_2. Строгий JSON без дополнительных полей.",fewShotExamples:"Q: Проверь по поставщикам хвосты и разложи цепочку документов/оплат. => fragment in_scope, flags: multi_entity + chain_explanation. Q: Как вообще по ФСБУ? => out_of_scope/generic_accounting."},md={userQuestion:"",batchQuestionsRaw:"",periodHint:"",businessContext:"",expectedRoute:""},Qa="ndc_normalizer_session_config_v1",Gi=["Разбираю запрос","Ищу данные","Собираю ответ"],hd="assistant",Wa="normalizer_v2_0_2";function vd(s){return`[${new Date().toLocaleTimeString("ru-RU")}] ${s}`}function yd(s,k){if(!k)return"Previous preset is not selected.";const L=["systemPrompt","developerPrompt","domainPrompt","schemaNotes","fewShotExamples"].filter(E=>s[E]!==k[E]).map(E=>`${E}: ${Math.abs(s[E].length-k[E].length)} chars delta`);return L.length===0?"No changes against previous preset.":`Changed fields: ${L.length}. ${L.join(" | ")}`}function gd(){const[s,k]=J.useState(pd),[d,L]=J.useState(Ha),[E,U]=J.useState(md),[H,b]=J.useState(null),[A,ae]=J.useState([]),[ue,D]=J.useState([]),[$,Se]=J.useState("normalized"),[ke,ee]=J.useState(!1),[q,$e]=J.useState(""),[ze,Be]=J.useState([]),[Y,Re]=J.useState(""),[_e,Le]=J.useState("NDC custom preset"),[Ke,mt]=J.useState(null),[ot,jt]=J.useState(""),[Ie,Me]=J.useState(!1),[et,it]=J.useState([]),[ve,ce]=J.useState(""),[C,I]=J.useState([]),[P,p]=J.useState(!1),[y,W]=J.useState(null),[X,K]=J.useState(""),[Z,le]=J.useState(hd),[te,ie]=J.useState(""),[He,Vt]=J.useState([]),[Zn,$t]=J.useState(""),[Pn,bn]=J.useState(!1),[Kr,Tt]=J.useState(""),[Yr,fn]=J.useState(""),zt=J.useRef(!1),Q=w=>{D(O=>[vd(w),...O].slice(0,300))};function er(){let w=0;Tt(Gi[0]);const O=window.setInterval(()=>{w=Math.min(w+1,Gi.length-1),Tt(Gi[w])},650);return()=>window.clearInterval(O)}J.useEffect(()=>{const w=localStorage.getItem(Qa);if(w)try{const O=JSON.parse(w);k(V=>({...V,model:O.model??V.model,baseUrl:O.baseUrl??V.baseUrl,temperature:O.temperature??V.temperature,maxOutputTokens:O.maxOutputTokens??V.maxOutputTokens}))}catch{}dn(),tr(),pn()},[]);async function dn(){try{const w=await pt.loadHistory();ae(w.items??[])}catch(w){Q(`History load error: ${w instanceof Error?w.message:String(w)}`)}}async function tr(){try{const O=(await pt.loadPresets()).presets??[];if(Be(O),zt.current)return;const V=O.find(Ee=>Ee.prompt_version===Wa)??O.find(Ee=>Ee.id==="default-normalizer-v2_0_2");if(!V){zt.current=!0,Q(`Preset autoload skipped: ${Wa} not found.`);return}Re(V.id),mt(d),L({systemPrompt:V.systemPrompt,developerPrompt:V.developerPrompt,domainPrompt:V.domainPrompt,schemaNotes:V.schemaNotes??"",fewShotExamples:V.fewShotExamples??""}),zt.current=!0,Q(`Preset autoloaded: ${V.name} (${V.prompt_version}).`)}catch(w){Q(`Presets load error: ${w instanceof Error?w.message:String(w)}`)}}async function pn(){try{const w=await pt.listRuns();it(w.items??[])}catch(w){Q(`Runs load error: ${w instanceof Error?w.message:String(w)}`)}}function nr(){localStorage.setItem(Qa,JSON.stringify({model:s.model,baseUrl:s.baseUrl,temperature:s.temperature,maxOutputTokens:s.maxOutputTokens})),Q("Local config saved (without API key).")}async function jn(){ee(!0),K("");try{const w=await pt.testConnection(s);$e(`OK - ${w.model}`),Q(`OpenAI connection ok: ${w.model}`)}catch(w){const O=w instanceof Error?w.message:String(w);$e("Connection error"),K(`Test connection: ${O}`),Q(`Test connection error: ${O}`)}finally{ee(!1)}}async function Tn(w){ee(!0),K("");try{const O=await pt.normalize({connection:s,prompts:d,promptVersion:"normalizer_v2_0_2",query:{userQuestion:E.userQuestion,periodHint:E.periodHint,businessContext:E.businessContext,expectedRoute:E.expectedRoute},saveAsTestCase:w,useMock:Ie});b(O),Se("normalized"),Q(`Normalize done: trace=${O.trace_id}, validation=${O.validation.passed?"passed":"failed"}`),dn()}catch(O){const V=O instanceof Error?O.message:String(O);K(`Normalize: ${V}`),Q(`Normalize error: ${V}`)}finally{ee(!1)}}function rr(){const w=ze.find(O=>O.id===Y);if(!w){Q("Preset is not selected.");return}mt(d),L({systemPrompt:w.systemPrompt,developerPrompt:w.developerPrompt,domainPrompt:w.domainPrompt,schemaNotes:w.schemaNotes??"",fewShotExamples:w.fewShotExamples??""}),Q(`Preset loaded: ${w.name}`)}async function Bt(){try{await pt.savePreset({name:_e||"NDC preset",prompt_version:"normalizer_v2_0_2",systemPrompt:d.systemPrompt,developerPrompt:d.developerPrompt,domainPrompt:d.domainPrompt,schemaNotes:d.schemaNotes,fewShotExamples:d.fewShotExamples}),Q("Preset saved."),await tr()}catch(w){Q(`Preset save error: ${w instanceof Error?w.message:String(w)}`)}}function Ht(){L(Ha),Q("Prompt panel reset to defaults.")}function Xr(){const w=yd(d,Ke);jt(w),Q(w)}function Gr(){const w=E.batchQuestionsRaw.split(";").map(O=>O.trim()).filter(Boolean).join(` +`+o.stack}return{value:e,source:t,stack:l,digest:null}}function mi(e,t,n){return{value:e,source:null,stack:n??null,digest:t??null}}function hi(e,t){try{console.error(t.value)}catch(n){setTimeout(function(){throw n})}}var vf=typeof WeakMap=="function"?WeakMap:Map;function $s(e,t,n){n=Mt(-1,n),n.tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Ql||(Ql=!0,zi=r),hi(e,t)},n}function Bs(e,t,n){n=Mt(-1,n),n.tag=3;var r=e.type.getDerivedStateFromError;if(typeof r=="function"){var l=t.value;n.payload=function(){return r(l)},n.callback=function(){hi(e,t)}}var o=e.stateNode;return o!==null&&typeof o.componentDidCatch=="function"&&(n.callback=function(){hi(e,t),typeof r!="function"&&(rn===null?rn=new Set([this]):rn.add(this));var i=t.stack;this.componentDidCatch(t.value,{componentStack:i!==null?i:""})}),n}function Hs(e,t,n){var r=e.pingCache;if(r===null){r=e.pingCache=new vf;var l=new Set;r.set(t,l)}else l=r.get(t),l===void 0&&(l=new Set,r.set(t,l));l.has(n)||(l.add(n),e=zf.bind(null,e,t,n),t.then(e,e))}function Qs(e){do{var t;if((t=e.tag===13)&&(t=e.memoizedState,t=t!==null?t.dehydrated!==null:!0),t)return e;e=e.return}while(e!==null);return null}function Ws(e,t,n,r,l){return(e.mode&1)===0?(e===t?e.flags|=65536:(e.flags|=128,n.flags|=131072,n.flags&=-52805,n.tag===1&&(n.alternate===null?n.tag=17:(t=Mt(-1,1),t.tag=2,tn(n,t,1))),n.lanes|=1),e):(e.flags|=65536,e.lanes=l,e)}var yf=Y.ReactCurrentOwner,Ge=!1;function Qe(e,t,n,r){t.child=e===null?fs(t,null,n,r):Hn(t,e.child,n,r)}function Ks(e,t,n,r,l){n=n.render;var o=t.ref;return Wn(t,l),r=ii(e,t,n,r,o,l),n=ui(),e!==null&&!Ge?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~l,Ft(e,t,l)):(pe&&n&&Ho(t),t.flags|=1,Qe(e,t,r,l),t.child)}function Ys(e,t,n,r,l){if(e===null){var o=n.type;return typeof o=="function"&&!Fi(o)&&o.defaultProps===void 0&&n.compare===null&&n.defaultProps===void 0?(t.tag=15,t.type=o,Xs(e,t,o,r,l)):(e=Jl(n.type,null,r,t,t.mode,l),e.ref=t.ref,e.return=t,t.child=e)}if(o=e.child,(e.lanes&l)===0){var i=o.memoizedProps;if(n=n.compare,n=n!==null?n:Er,n(i,r)&&e.ref===t.ref)return Ft(e,t,l)}return t.flags|=1,e=sn(o,r),e.ref=t.ref,e.return=t,t.child=e}function Xs(e,t,n,r,l){if(e!==null){var o=e.memoizedProps;if(Er(o,r)&&e.ref===t.ref)if(Ge=!1,t.pendingProps=r=o,(e.lanes&l)!==0)(e.flags&131072)!==0&&(Ge=!0);else return t.lanes=e.lanes,Ft(e,t,l)}return vi(e,t,n,r,l)}function Gs(e,t,n){var r=t.pendingProps,l=r.children,o=e!==null?e.memoizedState:null;if(r.mode==="hidden")if((t.mode&1)===0)t.memoizedState={baseLanes:0,cachePool:null,transitions:null},se(Gn,lt),lt|=n;else{if((n&1073741824)===0)return e=o!==null?o.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,se(Gn,lt),lt|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=o!==null?o.baseLanes:n,se(Gn,lt),lt|=r}else o!==null?(r=o.baseLanes|n,t.memoizedState=null):r=n,se(Gn,lt),lt|=r;return Qe(e,t,l,n),t.child}function Js(e,t){var n=t.ref;(e===null&&n!==null||e!==null&&e.ref!==n)&&(t.flags|=512,t.flags|=2097152)}function vi(e,t,n,r,l){var o=Xe(n)?vn:Fe.current;return o=An(t,o),Wn(t,l),n=ii(e,t,n,r,o,l),r=ui(),e!==null&&!Ge?(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~l,Ft(e,t,l)):(pe&&r&&Ho(t),t.flags|=1,Qe(e,t,n,l),t.child)}function qs(e,t,n,r,l){if(Xe(n)){var o=!0;xl(t)}else o=!1;if(Wn(t,l),t.stateNode===null)Al(e,t),As(t,n,r),pi(t,n,r,l),r=!0;else if(e===null){var i=t.stateNode,u=t.memoizedProps;i.props=u;var a=i.context,v=n.contextType;typeof v=="object"&&v!==null?v=at(v):(v=Xe(n)?vn:Fe.current,v=An(t,v));var S=n.getDerivedStateFromProps,x=typeof S=="function"||typeof i.getSnapshotBeforeUpdate=="function";x||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==r||a!==v)&&Vs(t,i,r,v),en=!1;var g=t.memoizedState;i.state=g,zl(t,r,i,l),a=t.memoizedState,u!==r||g!==a||Ye.current||en?(typeof S=="function"&&(di(t,n,S,r),a=t.memoizedState),(u=en||Us(t,n,u,r,g,a,v))?(x||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=r,t.memoizedState=a),i.props=r,i.state=a,i.context=v,r=u):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),r=!1)}else{i=t.stateNode,ps(e,t),u=t.memoizedProps,v=t.type===t.elementType?u:gt(t.type,u),i.props=v,x=t.pendingProps,g=i.context,a=n.contextType,typeof a=="object"&&a!==null?a=at(a):(a=Xe(n)?vn:Fe.current,a=An(t,a));var N=n.getDerivedStateFromProps;(S=typeof N=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==x||g!==a)&&Vs(t,i,r,a),en=!1,g=t.memoizedState,i.state=g,zl(t,r,i,l);var z=t.memoizedState;u!==x||g!==z||Ye.current||en?(typeof N=="function"&&(di(t,n,N,r),z=t.memoizedState),(v=en||Us(t,n,v,r,g,z,a)||!1)?(S||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(r,z,a),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(r,z,a)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&g===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&g===e.memoizedState||(t.flags|=1024),t.memoizedProps=r,t.memoizedState=z),i.props=r,i.state=z,i.context=a,r=v):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&g===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&g===e.memoizedState||(t.flags|=1024),r=!1)}return yi(e,t,n,r,o,l)}function yi(e,t,n,r,l,o){Js(e,t);var i=(t.flags&128)!==0;if(!r&&!i)return l&&ns(t,n,!1),Ft(e,t,o);r=t.stateNode,yf.current=t;var u=i&&typeof n.getDerivedStateFromError!="function"?null:r.render();return t.flags|=1,e!==null&&i?(t.child=Hn(t,e.child,null,o),t.child=Hn(t,null,u,o)):Qe(e,t,u,o),t.memoizedState=r.state,l&&ns(t,n,!0),t.child}function Zs(e){var t=e.stateNode;t.pendingContext?es(e,t.pendingContext,t.pendingContext!==t.context):t.context&&es(e,t.context,!1),ei(e,t.containerInfo)}function bs(e,t,n,r,l){return Bn(),Yo(l),t.flags|=256,Qe(e,t,n,r),t.child}var gi={dehydrated:null,treeContext:null,retryLane:0};function wi(e){return{baseLanes:e,cachePool:null,transitions:null}}function ea(e,t,n){var r=t.pendingProps,l=me.current,o=!1,i=(t.flags&128)!==0,u;if((u=i)||(u=e!==null&&e.memoizedState===null?!1:(l&2)!==0),u?(o=!0,t.flags&=-129):(e===null||e.memoizedState!==null)&&(l|=1),se(me,l&1),e===null)return Ko(t),e=t.memoizedState,e!==null&&(e=e.dehydrated,e!==null)?((t.mode&1)===0?t.lanes=1:e.data==="$!"?t.lanes=8:t.lanes=1073741824,null):(i=r.children,e=r.fallback,o?(r=t.mode,o=t.child,i={mode:"hidden",children:i},(r&1)===0&&o!==null?(o.childLanes=0,o.pendingProps=i):o=ql(i,r,0,null),e=Nn(e,r,n,null),o.return=t,e.return=t,o.sibling=e,t.child=o,t.child.memoizedState=wi(n),t.memoizedState=gi,e):Si(t,i));if(l=e.memoizedState,l!==null&&(u=l.dehydrated,u!==null))return gf(e,t,i,r,u,l,n);if(o){o=r.fallback,i=t.mode,l=e.child,u=l.sibling;var a={mode:"hidden",children:r.children};return(i&1)===0&&t.child!==l?(r=t.child,r.childLanes=0,r.pendingProps=a,t.deletions=null):(r=sn(l,a),r.subtreeFlags=l.subtreeFlags&14680064),u!==null?o=sn(u,o):(o=Nn(o,i,n,null),o.flags|=2),o.return=t,r.return=t,r.sibling=o,t.child=r,r=o,o=t.child,i=e.child.memoizedState,i=i===null?wi(n):{baseLanes:i.baseLanes|n,cachePool:null,transitions:i.transitions},o.memoizedState=i,o.childLanes=e.childLanes&~n,t.memoizedState=gi,r}return o=e.child,e=o.sibling,r=sn(o,{mode:"visible",children:r.children}),(t.mode&1)===0&&(r.lanes=n),r.return=t,r.sibling=null,e!==null&&(n=t.deletions,n===null?(t.deletions=[e],t.flags|=16):n.push(e)),t.child=r,t.memoizedState=null,r}function Si(e,t){return t=ql({mode:"visible",children:t},e.mode,0,null),t.return=e,e.child=t}function Ul(e,t,n,r){return r!==null&&Yo(r),Hn(t,e.child,null,n),e=Si(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function gf(e,t,n,r,l,o,i){if(n)return t.flags&256?(t.flags&=-257,r=mi(Error(m(422))),Ul(e,t,i,r)):t.memoizedState!==null?(t.child=e.child,t.flags|=128,null):(o=r.fallback,l=t.mode,r=ql({mode:"visible",children:r.children},l,0,null),o=Nn(o,l,i,null),o.flags|=2,r.return=t,o.return=t,r.sibling=o,t.child=r,(t.mode&1)!==0&&Hn(t,e.child,null,i),t.child.memoizedState=wi(i),t.memoizedState=gi,o);if((t.mode&1)===0)return Ul(e,t,i,null);if(l.data==="$!"){if(r=l.nextSibling&&l.nextSibling.dataset,r)var u=r.dgst;return r=u,o=Error(m(419)),r=mi(o,r,void 0),Ul(e,t,i,r)}if(u=(i&e.childLanes)!==0,Ge||u){if(r=je,r!==null){switch(i&-i){case 4:l=2;break;case 16:l=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:l=32;break;case 536870912:l=268435456;break;default:l=0}l=(l&(r.suspendedLanes|i))!==0?0:l,l!==0&&l!==o.retryLane&&(o.retryLane=l,It(e,l),xt(r,e,l,-1))}return Mi(),r=mi(Error(m(421))),Ul(e,t,i,r)}return l.data==="$?"?(t.flags|=128,t.child=e.child,t=Rf.bind(null,e),l._reactRetry=t,null):(e=o.treeContext,rt=Jt(l.nextSibling),nt=t,pe=!0,yt=null,e!==null&&(ut[st++]=Ot,ut[st++]=Dt,ut[st++]=yn,Ot=e.id,Dt=e.overflow,yn=t),t=Si(t,r.children),t.flags|=4096,t)}function ta(e,t,n){e.lanes|=t;var r=e.alternate;r!==null&&(r.lanes|=t),qo(e.return,t,n)}function xi(e,t,n,r,l){var o=e.memoizedState;o===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:l}:(o.isBackwards=t,o.rendering=null,o.renderingStartTime=0,o.last=r,o.tail=n,o.tailMode=l)}function na(e,t,n){var r=t.pendingProps,l=r.revealOrder,o=r.tail;if(Qe(e,t,r.children,n),r=me.current,(r&2)!==0)r=r&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&ta(e,n,t);else if(e.tag===19)ta(e,n,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(se(me,r),(t.mode&1)===0)t.memoizedState=null;else switch(l){case"forwards":for(n=t.child,l=null;n!==null;)e=n.alternate,e!==null&&Rl(e)===null&&(l=n),n=n.sibling;n=l,n===null?(l=t.child,t.child=null):(l=n.sibling,n.sibling=null),xi(t,!1,l,n,o);break;case"backwards":for(n=null,l=t.child,t.child=null;l!==null;){if(e=l.alternate,e!==null&&Rl(e)===null){t.child=l;break}e=l.sibling,l.sibling=n,n=l,l=e}xi(t,!0,n,null,o);break;case"together":xi(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function Al(e,t){(t.mode&1)===0&&e!==null&&(e.alternate=null,t.alternate=null,t.flags|=2)}function Ft(e,t,n){if(e!==null&&(t.dependencies=e.dependencies),kn|=t.lanes,(n&t.childLanes)===0)return null;if(e!==null&&t.child!==e.child)throw Error(m(153));if(t.child!==null){for(e=t.child,n=sn(e,e.pendingProps),t.child=n,n.return=t;e.sibling!==null;)e=e.sibling,n=n.sibling=sn(e,e.pendingProps),n.return=t;n.sibling=null}return t.child}function wf(e,t,n){switch(t.tag){case 3:Zs(t),Bn();break;case 5:vs(t);break;case 1:Xe(t.type)&&xl(t);break;case 4:ei(t,t.stateNode.containerInfo);break;case 10:var r=t.type._context,l=t.memoizedProps.value;se(Pl,r._currentValue),r._currentValue=l;break;case 13:if(r=t.memoizedState,r!==null)return r.dehydrated!==null?(se(me,me.current&1),t.flags|=128,null):(n&t.child.childLanes)!==0?ea(e,t,n):(se(me,me.current&1),e=Ft(e,t,n),e!==null?e.sibling:null);se(me,me.current&1);break;case 19:if(r=(n&t.childLanes)!==0,(e.flags&128)!==0){if(r)return na(e,t,n);t.flags|=128}if(l=t.memoizedState,l!==null&&(l.rendering=null,l.tail=null,l.lastEffect=null),se(me,me.current),r)break;return null;case 22:case 23:return t.lanes=0,Gs(e,t,n)}return Ft(e,t,n)}var ra,ki,la,oa;ra=function(e,t){for(var n=t.child;n!==null;){if(n.tag===5||n.tag===6)e.appendChild(n.stateNode);else if(n.tag!==4&&n.child!==null){n.child.return=n,n=n.child;continue}if(n===t)break;for(;n.sibling===null;){if(n.return===null||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},ki=function(){},la=function(e,t,n,r){var l=e.memoizedProps;if(l!==r){e=t.stateNode,Sn(Ct.current);var o=null;switch(n){case"input":l=Pn(e,l),r=Pn(e,r),o=[];break;case"select":l=j({},l,{value:void 0}),r=j({},r,{value:void 0}),o=[];break;case"textarea":l=er(e,l),r=er(e,r),o=[];break;default:typeof l.onClick!="function"&&typeof r.onClick=="function"&&(e.onclick=gl)}lr(n,r);var i;n=null;for(v in l)if(!r.hasOwnProperty(v)&&l.hasOwnProperty(v)&&l[v]!=null)if(v==="style"){var u=l[v];for(i in u)u.hasOwnProperty(i)&&(n||(n={}),n[i]="")}else v!=="dangerouslySetInnerHTML"&&v!=="children"&&v!=="suppressContentEditableWarning"&&v!=="suppressHydrationWarning"&&v!=="autoFocus"&&(C.hasOwnProperty(v)?o||(o=[]):(o=o||[]).push(v,null));for(v in r){var a=r[v];if(u=l?.[v],r.hasOwnProperty(v)&&a!==u&&(a!=null||u!=null))if(v==="style")if(u){for(i in u)!u.hasOwnProperty(i)||a&&a.hasOwnProperty(i)||(n||(n={}),n[i]="");for(i in a)a.hasOwnProperty(i)&&u[i]!==a[i]&&(n||(n={}),n[i]=a[i])}else n||(o||(o=[]),o.push(v,n)),n=a;else v==="dangerouslySetInnerHTML"?(a=a?a.__html:void 0,u=u?u.__html:void 0,a!=null&&u!==a&&(o=o||[]).push(v,a)):v==="children"?typeof a!="string"&&typeof a!="number"||(o=o||[]).push(v,""+a):v!=="suppressContentEditableWarning"&&v!=="suppressHydrationWarning"&&(C.hasOwnProperty(v)?(a!=null&&v==="onScroll"&&fe("scroll",e),o||u===a||(o=[])):(o=o||[]).push(v,a))}n&&(o=o||[]).push("style",n);var v=o;(t.updateQueue=v)&&(t.flags|=4)}},oa=function(e,t,n,r){n!==r&&(t.flags|=4)};function Ar(e,t){if(!pe)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;t!==null;)t.alternate!==null&&(n=t),t=t.sibling;n===null?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;n!==null;)n.alternate!==null&&(r=n),n=n.sibling;r===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Ae(e){var t=e.alternate!==null&&e.alternate.child===e.child,n=0,r=0;if(t)for(var l=e.child;l!==null;)n|=l.lanes|l.childLanes,r|=l.subtreeFlags&14680064,r|=l.flags&14680064,l.return=e,l=l.sibling;else for(l=e.child;l!==null;)n|=l.lanes|l.childLanes,r|=l.subtreeFlags,r|=l.flags,l.return=e,l=l.sibling;return e.subtreeFlags|=r,e.childLanes=n,t}function Sf(e,t,n){var r=t.pendingProps;switch(Qo(t),t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ae(t),null;case 1:return Xe(t.type)&&Sl(),Ae(t),null;case 3:return r=t.stateNode,Kn(),de(Ye),de(Fe),ri(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),(e===null||e.child===null)&&(Cl(t)?t.flags|=4:e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,yt!==null&&(Oi(yt),yt=null))),ki(e,t),Ae(t),null;case 5:ti(t);var l=Sn(Dr.current);if(n=t.type,e!==null&&t.stateNode!=null)la(e,t,n,r,l),e.ref!==t.ref&&(t.flags|=512,t.flags|=2097152);else{if(!r){if(t.stateNode===null)throw Error(m(166));return Ae(t),null}if(e=Sn(Ct.current),Cl(t)){r=t.stateNode,n=t.type;var o=t.memoizedProps;switch(r[Et]=t,r[Tr]=o,e=(t.mode&1)!==0,n){case"dialog":fe("cancel",r),fe("close",r);break;case"iframe":case"object":case"embed":fe("load",r);break;case"video":case"audio":for(l=0;l<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[Et]=t,e[Tr]=r,ra(e,t,!1,!1),t.stateNode=e;e:{switch(i=or(n,r),n){case"dialog":fe("cancel",e),fe("close",e),l=r;break;case"iframe":case"object":case"embed":fe("load",e),l=r;break;case"video":case"audio":for(l=0;lJn&&(t.flags|=128,r=!0,Ar(o,!1),t.lanes=4194304)}else{if(!r)if(e=Rl(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Ar(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!pe)return Ae(t),null}else 2*ge()-o.renderingStartTime>Jn&&n!==1073741824&&(t.flags|=128,r=!0,Ar(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=ge(),t.sibling=null,n=me.current,se(me,r?n&1|2:n&1),t):(Ae(t),null);case 22:case 23:return Ii(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(lt&1073741824)!==0&&(Ae(t),t.subtreeFlags&6&&(t.flags|=8192)):Ae(t),null;case 24:return null;case 25:return null}throw Error(m(156,t.tag))}function xf(e,t){switch(Qo(t),t.tag){case 1:return Xe(t.type)&&Sl(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Kn(),de(Ye),de(Fe),ri(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return ti(t),null;case 13:if(de(me),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(m(340));Bn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return de(me),null;case 4:return Kn(),null;case 10:return Jo(t.type._context),null;case 22:case 23:return Ii(),null;case 24:return null;default:return null}}var Vl=!1,Ve=!1,kf=typeof WeakSet=="function"?WeakSet:Set,T=null;function Xn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ye(e,t,r)}else n.current=null}function _i(e,t,n){try{n()}catch(r){ye(e,t,r)}}var ia=!1;function _f(e,t){if(Io=il,e=Uu(),Po(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,a=-1,v=0,S=0,x=e,g=null;t:for(;;){for(var N;x!==n||l!==0&&x.nodeType!==3||(u=i+l),x!==o||r!==0&&x.nodeType!==3||(a=i+r),x.nodeType===3&&(i+=x.nodeValue.length),(N=x.firstChild)!==null;)g=x,x=N;for(;;){if(x===e)break t;if(g===n&&++v===l&&(u=i),g===o&&++S===r&&(a=i),(N=x.nextSibling)!==null)break;x=g,g=x.parentNode}x=N}n=u===-1||a===-1?null:{start:u,end:a}}else n=null}n=n||{start:0,end:0}}else n=null;for(Mo={focusedElem:e,selectionRange:n},il=!1,T=t;T!==null;)if(t=T,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,T=e;else for(;T!==null;){t=T;try{var z=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(z!==null){var L=z.memoizedProps,we=z.memoizedState,p=t.stateNode,f=p.getSnapshotBeforeUpdate(t.elementType===t.type?L:gt(t.type,L),we);p.__reactInternalSnapshotBeforeUpdate=f}break;case 3:var h=t.stateNode.containerInfo;h.nodeType===1?h.textContent="":h.nodeType===9&&h.documentElement&&h.removeChild(h.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(m(163))}}catch(_){ye(t,t.return,_)}if(e=t.sibling,e!==null){e.return=t.return,T=e;break}T=t.return}return z=ia,ia=!1,z}function Vr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&_i(t,n,o)}l=l.next}while(l!==r)}}function $l(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ei(e){var t=e.ref;if(t!==null){var n=e.stateNode;e.tag,e=n,typeof t=="function"?t(e):t.current=e}}function ua(e){var t=e.alternate;t!==null&&(e.alternate=null,ua(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Et],delete t[Tr],delete t[Vo],delete t[lf],delete t[of])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function sa(e){return e.tag===5||e.tag===3||e.tag===4}function aa(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||sa(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ci(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=gl));else if(r!==4&&(e=e.child,e!==null))for(Ci(e,t,n),e=e.sibling;e!==null;)Ci(e,t,n),e=e.sibling}function Ni(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ni(e,t,n),e=e.sibling;e!==null;)Ni(e,t,n),e=e.sibling}var Oe=null,wt=!1;function nn(e,t,n){for(n=n.child;n!==null;)ca(e,t,n),n=n.sibling}function ca(e,t,n){if(_t&&typeof _t.onCommitFiberUnmount=="function")try{_t.onCommitFiberUnmount(el,n)}catch{}switch(n.tag){case 5:Ve||Xn(n,t);case 6:var r=Oe,l=wt;Oe=null,nn(e,t,n),Oe=r,wt=l,Oe!==null&&(wt?(e=Oe,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Oe.removeChild(n.stateNode));break;case 18:Oe!==null&&(wt?(e=Oe,n=n.stateNode,e.nodeType===8?Ao(e.parentNode,n):e.nodeType===1&&Ao(e,n),gr(e)):Ao(Oe,n.stateNode));break;case 4:r=Oe,l=wt,Oe=n.stateNode.containerInfo,wt=!0,nn(e,t,n),Oe=r,wt=l;break;case 0:case 11:case 14:case 15:if(!Ve&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&((o&2)!==0||(o&4)!==0)&&_i(n,t,i),l=l.next}while(l!==r)}nn(e,t,n);break;case 1:if(!Ve&&(Xn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){ye(n,t,u)}nn(e,t,n);break;case 21:nn(e,t,n);break;case 22:n.mode&1?(Ve=(r=Ve)||n.memoizedState!==null,nn(e,t,n),Ve=r):nn(e,t,n);break;default:nn(e,t,n)}}function fa(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new kf),t.forEach(function(r){var l=Lf.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function St(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=ge()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Cf(r/1960))-r,10e?16:e,ln===null)var r=!1;else{if(e=ln,ln=null,Kl=0,(ne&6)!==0)throw Error(m(331));var l=ne;for(ne|=4,T=e.current;T!==null;){var o=T,i=o.child;if((T.flags&16)!==0){var u=o.deletions;if(u!==null){for(var a=0;age()-Ti?En(e,0):ji|=n),qe(e,t)}function Ea(e,t){t===0&&((e.mode&1)===0?t=1:(t=nl,nl<<=1,(nl&130023424)===0&&(nl=4194304)));var n=We();e=It(e,t),e!==null&&(pr(e,t,n),qe(e,n))}function Rf(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Ea(e,n)}function Lf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(m(314))}r!==null&&r.delete(t),Ea(e,n)}var Ca;Ca=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Ye.current)Ge=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Ge=!1,wf(e,t,n);Ge=(e.flags&131072)!==0}else Ge=!1,pe&&(t.flags&1048576)!==0&&ls(t,El,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Al(e,t),e=t.pendingProps;var l=An(t,Fe.current);Wn(t,n),l=ii(null,t,r,e,l,n);var o=ui();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Xe(r)?(o=!0,xl(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,bo(t),l.updater=Fl,t.stateNode=l,l._reactInternals=t,pi(t,r,e,n),t=yi(null,t,r,!0,o,n)):(t.tag=0,pe&&o&&Ho(t),Qe(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Al(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=Df(r),e=gt(r,e),l){case 0:t=vi(null,t,r,e,n);break e;case 1:t=qs(null,t,r,e,n);break e;case 11:t=Ks(null,t,r,e,n);break e;case 14:t=Ys(null,t,r,gt(r.type,e),n);break e}throw Error(m(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),vi(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),qs(e,t,r,l,n);case 3:e:{if(Zs(t),e===null)throw Error(m(387));r=t.pendingProps,o=t.memoizedState,l=o.element,ps(e,t),zl(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=Yn(Error(m(423)),t),t=bs(e,t,r,n,l);break e}else if(r!==l){l=Yn(Error(m(424)),t),t=bs(e,t,r,n,l);break e}else for(rt=Jt(t.stateNode.containerInfo.firstChild),nt=t,pe=!0,yt=null,n=fs(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Bn(),r===l){t=Ft(e,t,n);break e}Qe(e,t,r,n)}t=t.child}return t;case 5:return vs(t),e===null&&Ko(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Fo(r,l)?i=null:o!==null&&Fo(r,o)&&(t.flags|=32),Js(e,t),Qe(e,t,i,n),t.child;case 6:return e===null&&Ko(t),null;case 13:return ea(e,t,n);case 4:return ei(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Hn(t,null,r,n):Qe(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),Ks(e,t,r,l,n);case 7:return Qe(e,t,t.pendingProps,n),t.child;case 8:return Qe(e,t,t.pendingProps.children,n),t.child;case 12:return Qe(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,se(Pl,r._currentValue),r._currentValue=i,o!==null)if(vt(o.value,i)){if(o.children===l.children&&!Ye.current){t=Ft(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var a=u.firstContext;a!==null;){if(a.context===r){if(o.tag===1){a=Mt(-1,n&-n),a.tag=2;var v=o.updateQueue;if(v!==null){v=v.shared;var S=v.pending;S===null?a.next=a:(a.next=S.next,S.next=a),v.pending=a}}o.lanes|=n,a=o.alternate,a!==null&&(a.lanes|=n),qo(o.return,n,t),u.lanes|=n;break}a=a.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(m(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),qo(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}Qe(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Wn(t,n),l=at(l),r=r(l),t.flags|=1,Qe(e,t,r,n),t.child;case 14:return r=t.type,l=gt(r,t.pendingProps),l=gt(r.type,l),Ys(e,t,r,l,n);case 15:return Xs(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:gt(r,l),Al(e,t),t.tag=1,Xe(r)?(e=!0,xl(t)):e=!1,Wn(t,n),As(t,r,l),pi(t,r,l,n),yi(null,t,r,!0,e,n);case 19:return na(e,t,n);case 22:return Gs(e,t,n)}throw Error(m(156,t.tag))};function Na(e,t){return ou(e,t)}function Of(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function dt(e,t,n,r){return new Of(e,t,n,r)}function Fi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Df(e){if(typeof e=="function")return Fi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Ie)return 11;if(e===it)return 14}return 2}function sn(e,t){var n=e.alternate;return n===null?(n=dt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Jl(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")Fi(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Le:return Nn(n.children,l,o,t);case Ke:i=8,l|=8;break;case mt:return e=dt(12,n,t,l|2),e.elementType=mt,e.lanes=o,e;case Me:return e=dt(13,n,t,l),e.elementType=Me,e.lanes=o,e;case et:return e=dt(19,n,t,l),e.elementType=et,e.lanes=o,e;case ce:return ql(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case ot:i=10;break e;case jt:i=9;break e;case Ie:i=11;break e;case it:i=14;break e;case ve:i=16,r=null;break e}throw Error(m(130,e==null?e:typeof e,""))}return t=dt(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function Nn(e,t,n,r){return e=dt(7,e,r,t),e.lanes=n,e}function ql(e,t,n,r){return e=dt(22,e,r,t),e.elementType=ce,e.lanes=n,e.stateNode={isHidden:!1},e}function Ui(e,t,n){return e=dt(6,e,null,t),e.lanes=n,e}function Ai(e,t,n){return t=dt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function If(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=fo(0),this.expirationTimes=fo(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=fo(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Vi(e,t,n,r,l,o,i,u,a){return e=new If(e,t,n,u,a),t===1?(t=1,o===!0&&(t|=8)):t=0,o=dt(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},bo(o),e}function Mf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(s)}catch(k){console.error(k)}}return s(),Ki.exports=Xf(),Ki.exports}var Va;function Jf(){if(Va)return lo;Va=1;var s=Gf();return lo.createRoot=s.createRoot,lo.hydrateRoot=s.hydrateRoot,lo}var qf=Jf();const Zf=Ka(qf),bf="/api";async function be(s,k){const m=await fetch(`${bf}${s}`,{...k,headers:{"Content-Type":"application/json",...k?.headers??{}}}),P=await m.json();if(!m.ok){const C=P.error?.message??"Ошибка запроса";throw new Error(C)}return P}const pt={async testConnection(s){return be("/openai/test-connection",{method:"POST",body:JSON.stringify({apiKey:s.apiKey,model:s.model,baseUrl:s.baseUrl})})},async normalize(s){return be("/normalize",{method:"POST",body:JSON.stringify({apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion,systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples,userQuestion:s.query.userQuestion,context:{period_hint:s.query.periodHint??"",business_context:s.query.businessContext??"",expected_route:s.query.expectedRoute??""},saveAsTestCase:!!s.saveAsTestCase,useMock:!!s.useMock})})},async loadHistory(){return be("/history")},async loadTrace(s){return be(`/history/${s}`)},async loadPresets(){return be("/presets")},async savePreset(s){return be("/presets/save",{method:"POST",body:JSON.stringify(s)})},async runEval(s){return be("/eval/run",{method:"POST",body:JSON.stringify({normalizeConfig:{apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion,systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples},caseIds:s.caseIds,useMock:!!s.useMock,mode:s.mode??"standard",caseSetFile:s.caseSetFile,rawQuestions:s.rawQuestions})})},async startRun(){return be("/accounting-agent/v1/runs/start",{method:"POST",body:JSON.stringify({initiator:"ndc_operator",source:"gui"})})},async finishRun(s){return be("/accounting-agent/v1/runs/finish",{method:"POST",body:JSON.stringify({runId:s,status:"DONE",source:"gui",reason:"Остановлено оператором из GUI"})})},async listRuns(){return be("/accounting-agent/v1/runs")},async listResults(){return be("/accounting-agent/v1/results")},async runTrace(s){return be(`/accounting-agent/v1/trace/run/${s}`)},async sendAssistantMessage(s){return be("/assistant/message",{method:"POST",body:JSON.stringify({session_id:s.sessionId??"",mode:"assistant",message:s.userMessage,user_message:s.userMessage,apiKey:s.connection.apiKey,model:s.connection.model,baseUrl:s.connection.baseUrl,temperature:s.connection.temperature,maxOutputTokens:s.connection.maxOutputTokens,promptVersion:s.promptVersion??"normalizer_v2_0_2",systemPrompt:s.prompts.systemPrompt,developerPrompt:s.prompts.developerPrompt,domainPrompt:s.prompts.domainPrompt,fewShotExamples:s.prompts.fewShotExamples,context:{period_hint:s.context?.periodHint??"",business_context:s.context?.businessContext??""},useMock:!!s.useMock})})},async loadAssistantSession(s){return be(`/assistant/session/${s}`)}};function kt({value:s}){return c.jsx("pre",{className:"json-view",children:JSON.stringify(s??{},null,2)})}function cn({title:s,subtitle:k,actions:m,children:P}){return c.jsxs("section",{className:"panel-frame",children:[c.jsxs("header",{className:"panel-header",children:[c.jsxs("div",{children:[c.jsx("h2",{children:s}),k?c.jsx("p",{children:k}):null]}),m?c.jsx("div",{className:"panel-actions",children:m}):null]}),c.jsx("div",{className:"panel-body",children:P})]})}function ed(s){return s==="assistant"?"Ассистент":"Вы"}function td(s){const k=new Date(s);return Number.isNaN(k.getTime())?s:k.toLocaleTimeString("ru-RU")}function nd(s){try{return JSON.stringify(s,null,2)}catch{return String(s)}}function rd(s,k,m=!1){const P=[];P.push("# Assistant conversation export"),P.push(`session_id: ${s||"n/a"}`),P.push(`exported_at: ${new Date().toISOString()}`),P.push("");for(let C=0;C{ee.current&&(ee.current.scrollTop=ee.current.scrollHeight)},[k,Se]),J.useEffect(()=>()=>{q.current!==null&&window.clearTimeout(q.current)},[]);async function Be(){if(k.length===0)return;const Y=rd(s,k,!0),Re=await ld(Y);ze(Re?"success":"error"),q.current!==null&&window.clearTimeout(q.current),q.current=window.setTimeout(()=>{ze("idle")},2200)}return c.jsxs(cn,{title:"Режим ассистента",subtitle:"Диалоговый слой поверх normalizer, маршрутизации и factual retrieval.",actions:c.jsxs("div",{className:"assistant-panel-actions",children:[c.jsx("button",{type:"button",className:"assistant-copy-btn",onClick:()=>{Be()},disabled:k.length===0,children:"Скопировать чат"}),$e==="success"?c.jsx("span",{className:"assistant-copy-feedback success",children:"Скопировано"}):null,$e==="error"?c.jsx("span",{className:"assistant-copy-feedback error",children:"Ошибка копирования"}):null,c.jsx("span",{className:"status-chip",children:s?`session: ${s}`:"новая сессия"})]}),children:[c.jsxs("div",{ref:ee,className:"assistant-chat-list",children:[k.length===0?c.jsx("div",{className:"assistant-empty muted",children:"Диалог пуст. Отправьте первый вопрос, чтобы запустить контур ассистента."}):null,k.map(Y=>c.jsxs("article",{className:`assistant-msg ${Y.role}`,children:[c.jsxs("header",{className:"assistant-msg-head",children:[c.jsx("strong",{children:ed(Y.role)}),c.jsx("span",{children:td(Y.created_at)})]}),c.jsx("div",{className:"assistant-msg-body",children:Y.text}),Y.role==="assistant"&&Y.debug?c.jsxs("details",{className:"assistant-debug",children:[c.jsx("summary",{children:"Показать технический разбор"}),c.jsx(kt,{value:Y.debug})]}):null]},Y.message_id))]}),c.jsxs("div",{className:"assistant-compose",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["Подсказка по периоду",c.jsx("input",{value:C,onChange:Y=>R(Y.target.value)})]}),c.jsxs("label",{children:["Бизнес-контекст",c.jsx("input",{value:H,onChange:Y=>b(Y.target.value)})]})]}),c.jsxs("label",{className:"full-width",children:["Сообщение",c.jsx("textarea",{value:m,onChange:Y=>P(Y.target.value),rows:4,placeholder:"Введите вопрос к данным компании..."})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("label",{className:"checkbox-row",children:[c.jsx("input",{type:"checkbox",checked:A,onChange:Y=>ae(Y.target.checked)}),"Mock-режим"]}),c.jsx("button",{type:"button",onClick:()=>ue(),disabled:$||!m.trim(),children:$?"Выполняю...":"Отправить"}),c.jsx("button",{type:"button",onClick:()=>I(),disabled:$&&k.length===0,children:"Сбросить сессию"})]}),Se?c.jsx("p",{className:"diff-summary",children:Se}):null,ke?c.jsx("p",{className:"error-text",children:ke}):null]})]})}function $a({value:s,onChange:k,onTestConnection:m,onSaveLocalConfig:P,lastStatus:C,busy:R}){return c.jsxs(cn,{title:"Подключение OpenAI",subtitle:"Ключ живет только в памяти сессии (не пишется в localStorage).",actions:c.jsx("span",{className:"status-chip",children:C||"Статус: не проверено"}),children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["OpenAI API Key",c.jsx("input",{type:"password",value:s.apiKey,onChange:H=>k({...s,apiKey:H.target.value}),placeholder:"sk-..."})]}),c.jsxs("label",{children:["Model ID",c.jsx("input",{value:s.model,onChange:H=>k({...s,model:H.target.value})})]}),c.jsxs("label",{children:["Base URL",c.jsx("input",{value:s.baseUrl,onChange:H=>k({...s,baseUrl:H.target.value})})]}),c.jsxs("label",{children:["Temperature",c.jsx("input",{type:"number",step:"0.1",value:s.temperature,onChange:H=>k({...s,temperature:Number(H.target.value)})})]}),c.jsxs("label",{children:["Max output tokens",c.jsx("input",{type:"number",value:s.maxOutputTokens,onChange:H=>k({...s,maxOutputTokens:Number(H.target.value)})})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsx("button",{type:"button",onClick:()=>P(),children:"Сохранить локальную конфигурацию"}),c.jsx("button",{type:"button",onClick:()=>m(),disabled:R,children:R?"Проверяем...":"Проверить подключение"})]})]})}function id({items:s,onRefresh:k,onOpenTrace:m}){return c.jsx(cn,{title:"История нормализаций",subtitle:"Короткий вопрос, confidence, route hint и статус валидации.",actions:c.jsx("button",{type:"button",onClick:()=>k(),children:"Обновить"}),children:c.jsxs("div",{className:"history-list",children:[s.length===0?c.jsx("p",{className:"muted",children:"История пока пустая."}):null,s.map(P=>c.jsxs("button",{type:"button",className:"history-item",onClick:()=>m(P.trace_id),children:[c.jsxs("div",{className:"history-row",children:[c.jsx("strong",{children:P.route_hint??"route: n/a"}),c.jsx("span",{children:P.validation_passed?"schema: ok":"schema: fail"})]}),c.jsx("p",{children:P.question_short}),c.jsxs("div",{className:"history-row",children:[c.jsx("span",{children:P.model}),c.jsx("span",{children:new Date(P.timestamp).toLocaleString("ru-RU")})]})]},P.trace_id))]})})}function At(s){return s==null||s===""?"—":String(s)}function ud({result:s}){return c.jsx(cn,{title:"Runtime метрики",subtitle:"trace_id, токены, latency и статус валидации.",children:c.jsxs("div",{className:"metrics-grid",children:[c.jsxs("div",{children:[c.jsx("span",{children:"trace_id"}),c.jsx("strong",{children:At(s?.trace_id)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"request_started_at"}),c.jsx("strong",{children:At(s?new Date(Date.now()-s.latency_ms).toISOString():null)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"request_finished_at"}),c.jsx("strong",{children:At(s?new Date().toISOString():null)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"latency_ms"}),c.jsx("strong",{children:At(s?.latency_ms)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"input_tokens"}),c.jsx("strong",{children:At(s?.usage?.input_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"output_tokens"}),c.jsx("strong",{children:At(s?.usage?.output_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"total_tokens"}),c.jsx("strong",{children:At(s?.usage?.total_tokens)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"validation_status"}),c.jsx("strong",{children:s?.validation?.passed?"passed":"failed"})]}),c.jsxs("div",{children:[c.jsx("span",{children:"prompt_version"}),c.jsx("strong",{children:At(s?.prompt_version)})]}),c.jsxs("div",{children:[c.jsx("span",{children:"schema_version"}),c.jsx("strong",{children:At(s?.schema_version)})]})]})})}const sd={normalized:"Normalized JSON",fragments:"Fragment View",scope:"Scope View",flags:"Flags View",route:"Route Simulation",raw:"Raw model output",validation:"Validation",logs:"Logs"};function ad(s){return s&&typeof s=="object"?s:null}function cd({tab:s,onTabChange:k,result:m,appLogs:P}){const C=["normalized","fragments","scope","flags","route","raw","validation","logs"],R=ad(m?.normalized),H=String(R?.schema_version??""),b=H==="normalized_query_v2"||H==="normalized_query_v2_0_1"||H==="normalized_query_v2_0_2",A=b?{fragments:R?.fragments??[],discarded_fragments:R?.discarded_fragments??[]}:{note:"Fragment View доступен для normalized_query_v2."},ae=b?{message_in_scope:R?.message_in_scope??null,scope_confidence:R?.scope_confidence??null,contains_multiple_tasks:R?.contains_multiple_tasks??null,global_notes:R?.global_notes??null}:{note:"Scope View доступен для normalized_query_v2."},ue=b?Array.isArray(R?.fragments)?(R?.fragments).map(I=>({fragment_id:I.fragment_id??null,domain_relevance:I.domain_relevance??null,candidate_labels:I.candidate_labels??[],execution_readiness:I.execution_readiness??null,clarification_reason:I.clarification_reason??null,soft_assumption_used:I.soft_assumption_used??[],route_status:I.route_status??null,no_route_reason:I.no_route_reason??null,flags:I.flags??{}})):[]:{note:"Flags View доступен для normalized_query_v2."};return c.jsxs(cn,{title:"Выходные данные",subtitle:"Structured output и диагностические вкладки.",children:[c.jsx("div",{className:"tab-row",children:C.map(I=>c.jsx("button",{type:"button",className:s===I?"tab active":"tab",onClick:()=>k(I),children:sd[I]},I))}),s==="normalized"?c.jsx(kt,{value:m?.normalized??{note:"Нет данных."}}):null,s==="fragments"?c.jsx(kt,{value:A}):null,s==="scope"?c.jsx(kt,{value:ae}):null,s==="flags"?c.jsx(kt,{value:ue}):null,s==="route"?c.jsx(kt,{value:m?.route_hint_summary??{note:"Нет данных."}}):null,s==="raw"?c.jsx(kt,{value:m?.raw_model_output??{note:"Нет данных."}}):null,s==="validation"?c.jsx(kt,{value:m?.validation??{note:"Нет данных."}}):null,s==="logs"?c.jsx(kt,{value:P}):null]})}function Ba({value:s,onChange:k,presets:m,selectedPresetId:P,onSelectPreset:C,onLoadPreset:R,onSavePreset:H,onResetDefaults:b,onDiffPrevious:A,presetName:ae,onPresetNameChange:ue,diffSummary:I}){return c.jsxs(cn,{title:"Prompt Manager",subtitle:"Системный, developer и domain уровни управляются отдельно.",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{children:["Системный prompt",c.jsx("textarea",{value:s.systemPrompt,onChange:$=>k({...s,systemPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Developer / Instruction prompt",c.jsx("textarea",{value:s.developerPrompt,onChange:$=>k({...s,developerPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Domain prompt",c.jsx("textarea",{value:s.domainPrompt,onChange:$=>k({...s,domainPrompt:$.target.value}),rows:6})]}),c.jsxs("label",{children:["Schema notes",c.jsx("textarea",{value:s.schemaNotes,onChange:$=>k({...s,schemaNotes:$.target.value}),rows:6})]}),c.jsxs("label",{className:"full-width",children:["Few-shot examples",c.jsx("textarea",{value:s.fewShotExamples,onChange:$=>k({...s,fewShotExamples:$.target.value}),rows:8})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("select",{value:P,onChange:$=>C($.target.value),children:[c.jsx("option",{value:"",children:"Выберите preset..."}),m.map($=>c.jsx("option",{value:$.id,children:$.name},$.id))]}),c.jsx("button",{type:"button",onClick:()=>R(),children:"Загрузить preset"}),c.jsx("input",{value:ae,onChange:$=>ue($.target.value),placeholder:"Имя для сохранения"}),c.jsx("button",{type:"button",onClick:()=>H(),children:"Сохранить preset"}),c.jsx("button",{type:"button",onClick:()=>A(),children:"Diff с предыдущим"}),c.jsx("button",{type:"button",onClick:()=>b(),children:"Сбросить к default"})]}),I?c.jsx("p",{className:"diff-summary",children:I}):null]})}function fd({value:s,onChange:k,onApplyBatchFormat:m,onNormalize:P,busy:C,useMock:R,onUseMockChange:H,errorMessage:b}){return c.jsxs(cn,{title:"Запрос пользователя",subtitle:"NDC semantic front-end: нормализуем, но не отвечаем за бухгалтерскую суть.",children:[c.jsxs("div",{className:"grid-two",children:[c.jsxs("label",{className:"full-width",children:["Raw user question",c.jsx("textarea",{value:s.userQuestion,onChange:A=>k({...s,userQuestion:A.target.value}),rows:6,placeholder:"Например: По каким покупателям у нас на конец июня висят отгрузки без оплаты..."})]}),c.jsxs("label",{className:"full-width",children:["Batch queries (`;` separator)",c.jsx("textarea",{value:s.batchQuestionsRaw,onChange:A=>k({...s,batchQuestionsRaw:A.target.value}),onBlur:()=>m(),rows:8,placeholder:"Вопрос 1; Вопрос 2; Вопрос 3"})]}),c.jsxs("label",{children:["Optional period context",c.jsx("input",{value:s.periodHint,onChange:A=>k({...s,periodHint:A.target.value})})]}),c.jsxs("label",{children:["Optional business context",c.jsx("input",{value:s.businessContext,onChange:A=>k({...s,businessContext:A.target.value})})]}),c.jsxs("label",{children:["Optional expected route (eval)",c.jsx("input",{value:s.expectedRoute,onChange:A=>k({...s,expectedRoute:A.target.value})})]})]}),c.jsxs("div",{className:"button-row",children:[c.jsxs("label",{className:"checkbox-row",children:[c.jsx("input",{type:"checkbox",checked:R,onChange:A=>H(A.target.checked)}),"Mock-режим (без вызова OpenAI)"]}),c.jsx("button",{type:"button",onClick:()=>m(),disabled:C||!s.batchQuestionsRaw.trim(),children:"Применить `;` в переносы"}),c.jsx("button",{type:"button",onClick:()=>P(!1),disabled:C||!s.userQuestion.trim(),children:C?"Нормализуем...":"Normalize"}),c.jsx("button",{type:"button",onClick:()=>P(!0),disabled:C||!s.userQuestion.trim(),children:C?"Сохраняем...":"Normalize + Save as test case"})]}),b?c.jsx("p",{className:"error-text",children:b}):null]})}function dd({runs:s,selectedRunId:k,onSelectRun:m,onStartRun:P,onFinishRun:C,onRefreshRuns:R,onRunEval:H,onCopyEvalReport:b,evalBusy:A,traceItems:ae,evalReport:ue}){return c.jsxs(cn,{title:"NDC Run Monitor",subtitle:"Важно: кнопка Запустить run создает только run-сущность. Кнопка eval запускает batch-проверку normalizer v2.0.2.",children:[c.jsxs("div",{className:"button-row",children:[c.jsx("button",{type:"button",onClick:()=>P(),children:"Запустить run"}),c.jsx("button",{type:"button",onClick:()=>C(),disabled:!k,children:"Завершить выбранный run"}),c.jsx("button",{type:"button",onClick:()=>R(),children:"Обновить runs"}),c.jsx("button",{type:"button",onClick:()=>H(),disabled:A,children:A?"Идет eval v2.0.2...":"Запустить eval v2.0.2"})]}),c.jsxs("div",{className:"runtime-grid",children:[c.jsxs("div",{className:"runtime-runs",children:[s.map(I=>c.jsxs("button",{type:"button",className:k===I.runId?"history-item selected":"history-item",onClick:()=>m(I.runId),children:[c.jsxs("div",{className:"history-row",children:[c.jsx("strong",{children:I.status}),c.jsx("span",{children:I.runId})]}),c.jsxs("div",{className:"history-row",children:[c.jsx("span",{children:I.sessionId}),c.jsx("span",{children:new Date(I.updatedAt).toLocaleString("ru-RU")})]})]},I.runId)),s.length===0?c.jsx("p",{className:"muted",children:"Нет активных запусков."}):null]}),c.jsxs("div",{children:[c.jsx("h3",{children:"Trace выбранного run"}),c.jsx(kt,{value:ae}),c.jsxs("div",{className:"eval-report-wrap",children:[c.jsx("h3",{style:{marginTop:12},children:"Отчет eval"}),c.jsx(kt,{value:ue??{note:"Eval пока не запускался"}}),c.jsx("button",{type:"button",className:"copy-cube-button",title:"Скопировать отчет eval",onClick:()=>b(),children:"⧉"})]})]})]})]})}const pd={apiKey:"",model:"gpt-4o-mini",baseUrl:"https://api.openai.com/v1",temperature:0,maxOutputTokens:700},Ha={systemPrompt:"Ты semantic-normalizer для бухгалтерского ассистента NDC. Возвращай только JSON по схеме normalized_query_v2_0_2.",developerPrompt:"Сначала делай decomposition сообщения на task fragments, затем определяй domain scope и route-critical flags. Для каждого fragment заполняй execution_readiness + route_status + no_route_reason. Если fragment routable, не оставляй его в no_route.",domainPrompt:"Контур: данные текущего предприятия в 1С/NDC. In-scope: документы, проводки, взаиморасчеты, остатки, периодное закрытие, аномалии и контрольные проверки. Out-of-scope: общая теория, законы и оффтоп.",schemaNotes:"schema_version: normalized_query_v2_0_2. Строгий JSON без дополнительных полей.",fewShotExamples:"Q: Проверь по поставщикам хвосты и разложи цепочку документов/оплат. => fragment in_scope, flags: multi_entity + chain_explanation. Q: Как вообще по ФСБУ? => out_of_scope/generic_accounting."},md={userQuestion:"",batchQuestionsRaw:"",periodHint:"",businessContext:"",expectedRoute:""},Qa="ndc_normalizer_session_config_v1",Gi=["Разбираю запрос","Ищу данные","Собираю ответ"],hd="assistant",Wa="normalizer_v2_0_2";function vd(s){return`[${new Date().toLocaleTimeString("ru-RU")}] ${s}`}function yd(s,k){if(!k)return"Previous preset is not selected.";const P=["systemPrompt","developerPrompt","domainPrompt","schemaNotes","fewShotExamples"].filter(C=>s[C]!==k[C]).map(C=>`${C}: ${Math.abs(s[C].length-k[C].length)} chars delta`);return P.length===0?"No changes against previous preset.":`Changed fields: ${P.length}. ${P.join(" | ")}`}function gd(){const[s,k]=J.useState(pd),[m,P]=J.useState(Ha),[C,R]=J.useState(md),[H,b]=J.useState(null),[A,ae]=J.useState([]),[ue,I]=J.useState([]),[$,Se]=J.useState("normalized"),[ke,ee]=J.useState(!1),[q,$e]=J.useState(""),[ze,Be]=J.useState([]),[Y,Re]=J.useState(""),[_e,Le]=J.useState("NDC custom preset"),[Ke,mt]=J.useState(null),[ot,jt]=J.useState(""),[Ie,Me]=J.useState(!1),[et,it]=J.useState([]),[ve,ce]=J.useState(""),[E,M]=J.useState([]),[j,d]=J.useState(!1),[y,W]=J.useState(null),[X,K]=J.useState(""),[Z,le]=J.useState(hd),[te,ie]=J.useState(""),[He,Vt]=J.useState([]),[Zn,$t]=J.useState(""),[Pn,bn]=J.useState(!1),[Kr,Tt]=J.useState(""),[Yr,fn]=J.useState(""),zt=J.useRef(!1),Q=w=>{I(D=>[vd(w),...D].slice(0,300))};function er(){let w=0;Tt(Gi[0]);const D=window.setInterval(()=>{w=Math.min(w+1,Gi.length-1),Tt(Gi[w])},650);return()=>window.clearInterval(D)}J.useEffect(()=>{const w=localStorage.getItem(Qa);if(w)try{const D=JSON.parse(w);k(V=>({...V,model:D.model??V.model,baseUrl:D.baseUrl??V.baseUrl,temperature:D.temperature??V.temperature,maxOutputTokens:D.maxOutputTokens??V.maxOutputTokens}))}catch{}dn(),tr(),pn()},[]);async function dn(){try{const w=await pt.loadHistory();ae(w.items??[])}catch(w){Q(`History load error: ${w instanceof Error?w.message:String(w)}`)}}async function tr(){try{const D=(await pt.loadPresets()).presets??[];if(Be(D),zt.current)return;const V=D.find(Ee=>Ee.prompt_version===Wa)??D.find(Ee=>Ee.id==="default-normalizer-v2_0_2");if(!V){zt.current=!0,Q(`Preset autoload skipped: ${Wa} not found.`);return}Re(V.id),mt(m),P({systemPrompt:V.systemPrompt,developerPrompt:V.developerPrompt,domainPrompt:V.domainPrompt,schemaNotes:V.schemaNotes??"",fewShotExamples:V.fewShotExamples??""}),zt.current=!0,Q(`Preset autoloaded: ${V.name} (${V.prompt_version}).`)}catch(w){Q(`Presets load error: ${w instanceof Error?w.message:String(w)}`)}}async function pn(){try{const w=await pt.listRuns();it(w.items??[])}catch(w){Q(`Runs load error: ${w instanceof Error?w.message:String(w)}`)}}function nr(){localStorage.setItem(Qa,JSON.stringify({model:s.model,baseUrl:s.baseUrl,temperature:s.temperature,maxOutputTokens:s.maxOutputTokens})),Q("Local config saved (without API key).")}async function jn(){ee(!0),K("");try{const w=await pt.testConnection(s);$e(`OK - ${w.model}`),Q(`OpenAI connection ok: ${w.model}`)}catch(w){const D=w instanceof Error?w.message:String(w);$e("Connection error"),K(`Test connection: ${D}`),Q(`Test connection error: ${D}`)}finally{ee(!1)}}async function Tn(w){ee(!0),K("");try{const D=await pt.normalize({connection:s,prompts:m,promptVersion:"normalizer_v2_0_2",query:{userQuestion:C.userQuestion,periodHint:C.periodHint,businessContext:C.businessContext,expectedRoute:C.expectedRoute},saveAsTestCase:w,useMock:Ie});b(D),Se("normalized"),Q(`Normalize done: trace=${D.trace_id}, validation=${D.validation.passed?"passed":"failed"}`),dn()}catch(D){const V=D instanceof Error?D.message:String(D);K(`Normalize: ${V}`),Q(`Normalize error: ${V}`)}finally{ee(!1)}}function rr(){const w=ze.find(D=>D.id===Y);if(!w){Q("Preset is not selected.");return}mt(m),P({systemPrompt:w.systemPrompt,developerPrompt:w.developerPrompt,domainPrompt:w.domainPrompt,schemaNotes:w.schemaNotes??"",fewShotExamples:w.fewShotExamples??""}),Q(`Preset loaded: ${w.name}`)}async function Bt(){try{await pt.savePreset({name:_e||"NDC preset",prompt_version:"normalizer_v2_0_2",systemPrompt:m.systemPrompt,developerPrompt:m.developerPrompt,domainPrompt:m.domainPrompt,schemaNotes:m.schemaNotes,fewShotExamples:m.fewShotExamples}),Q("Preset saved."),await tr()}catch(w){Q(`Preset save error: ${w instanceof Error?w.message:String(w)}`)}}function Ht(){P(Ha),Q("Prompt panel reset to defaults.")}function Xr(){const w=yd(m,Ke);jt(w),Q(w)}function Gr(){const w=C.batchQuestionsRaw.split(";").map(D=>D.trim()).filter(Boolean).join(` -`);w&&(U(O=>({...O,batchQuestionsRaw:w})),Q("Batch field formatted: `;` converted to blank-line separators."))}async function Jr(w){try{const V=(await pt.loadTrace(w)).trace,Ee=V.parsed_normalized_json??null;b({trace_id:String(V.trace_id??w),ok:!!V.validation_result?.passed,normalized:Ee,route_hint_summary:V.route_hint_summary??(Ee?{route_hint:Ee.route_hint??null,confidence:Ee.confidence?.route_hint??null}:null),raw_model_output:V.raw_model_response??{},validation:V.validation_result??{passed:!1,errors:["validation not found"]},usage:V.usage??{input_tokens:0,output_tokens:0,total_tokens:0},latency_ms:Number(V.latency_ms??0),prompt_version:String(V.prompt_version??"unknown"),schema_version:String(V.schema_version??"unknown")}),Se("raw"),K(""),Q(`Trace opened: ${w}`)}catch(O){const V=O instanceof Error?O.message:String(O);K(`Trace: ${V}`),Q(`Trace open error ${w}: ${V}`)}}async function oo(){try{const w=await pt.startRun();ce(w.run.runId),Q(`Run started: ${w.run.runId}`),Q("Tip: start run does not execute normalize by itself. Use 'Run eval v2.0.2' button."),await pn()}catch(w){Q(`Run start error: ${w instanceof Error?w.message:String(w)}`)}}async function lr(){if(ve)try{await pt.finishRun(ve),Q(`Run finished: ${ve}`),await pn()}catch(w){Q(`Run finish error: ${w instanceof Error?w.message:String(w)}`)}}async function or(){p(!0),K("");try{Q("Starting eval in v2 contour.");const w=E.batchQuestionsRaw.trim()||E.userQuestion.trim();if(!w)throw new Error("Fill batch field or Raw user question first.");const O=await pt.runEval({connection:s,prompts:d,promptVersion:"normalizer_v2_0_2",mode:"single-pass-strict",rawQuestions:w,useMock:Ie});W(O.report),Q("Eval v2.0.2 run finished.");const V=O.report;if(V.run_id&&Q(`Eval run id: ${V.run_id}`),V.metrics){const Ee=V.metrics;Q(`Eval metrics v2.0.2: schema=${Ee.schema_validation_pass_rate??"n/a"}%, route_accuracy=${Ee.route_resolution_accuracy??"n/a"}%, no_route_precision=${Ee.no_route_precision??"n/a"}%, state_consistency=${Ee.execution_state_consistency_rate??"n/a"}%`)}await dn()}catch(w){const O=w instanceof Error?w.message:String(w);O.includes("Legacy eval runner supports normalized_query_v1 only")?(W({status:"plan_only",prompt_version:"normalizer_v2",reason:"backend eval runner is still legacy-v1 only",plan_file:"reports/v2_pilot_eval_plan.md",next_steps:["run cheap mock sanity for schema/fragment/scope","run small real batch (10-15 messages, temperature=0)","run challenge-30 replay with v2 metrics"]}),Q("Backend is legacy-only for eval right now. Showing v2 pilot plan.")):(K(`Eval: ${O}`),Q(`Eval run error: ${O}`))}finally{p(!1)}}async function ir(){try{const w=JSON.stringify(y??{},null,2);await navigator.clipboard.writeText(w),Q("Eval report copied to clipboard.")}catch(w){Q(`Eval report copy error: ${w instanceof Error?w.message:String(w)}`)}}function ur(){ie(""),Vt([]),$t(""),Tt(""),fn(""),Q("Assistant session reset.")}async function sr(){const w=Zn.trim();if(!w)return;bn(!0),fn(""),$t(""),Vt(V=>[...V,{message_id:`local-${Date.now()}`,session_id:te||"pending",role:"user",text:w,reply_type:null,created_at:new Date().toISOString(),trace_id:null,debug:null}]);const O=er();try{const V=await pt.sendAssistantMessage({connection:s,prompts:d,userMessage:w,sessionId:te||void 0,promptVersion:"normalizer_v2_0_2",context:{periodHint:E.periodHint,businessContext:E.businessContext},useMock:Ie});ie(V.session_id),Vt(V.conversation),Tt("Ответ готов"),Q(`Assistant reply received: trace=${V.debug.trace_id}`)}catch(V){const Ee=V instanceof Error?V.message:String(V);fn(Ee),Tt("Ошибка ассистента"),Q(`Assistant error: ${Ee}`)}finally{O(),bn(!1)}}return J.useEffect(()=>{if(!ve){I([]);return}pt.runTrace(ve).then(w=>I(w.items)).catch(w=>Q(`Run trace error: ${w instanceof Error?w.message:String(w)}`))},[ve]),c.jsxs("main",{className:"app-root",children:[c.jsxs("div",{className:"hero",children:[c.jsx("h1",{children:"NDC AI First Layer"}),c.jsx("p",{children:"Два режима в одном интерфейсе: диагностика декомпозиции и диалоговый ассистент на общем backend-контуре."})]}),c.jsxs("div",{className:"mode-switch-row",children:[c.jsx("button",{type:"button",className:Z==="assistant"?"tab active":"tab",onClick:()=>le("assistant"),children:"Ассистент"}),c.jsx("button",{type:"button",className:Z==="decomposition"?"tab active":"tab",onClick:()=>le("decomposition"),children:"Декомпозиция"})]}),Z==="assistant"?c.jsxs("div",{className:"layout-grid",children:[c.jsx($a,{value:s,onChange:k,onSaveLocalConfig:nr,onTestConnection:jn,lastStatus:q,busy:ke||Pn}),c.jsx(Ba,{value:d,onChange:L,presets:ze,selectedPresetId:Y,onSelectPreset:Re,onLoadPreset:rr,onSavePreset:Bt,onResetDefaults:Ht,onDiffPrevious:Xr,presetName:_e,onPresetNameChange:Le,diffSummary:ot}),c.jsx(od,{sessionId:te,conversation:He,inputValue:Zn,onInputChange:$t,periodHint:E.periodHint,onPeriodHintChange:w=>U(O=>({...O,periodHint:w})),businessContext:E.businessContext,onBusinessContextChange:w=>U(O=>({...O,businessContext:w})),useMock:Ie,onUseMockChange:Me,onSend:sr,onClear:ur,busy:Pn,statusText:Kr,errorMessage:Yr})]}):c.jsxs("div",{className:"layout-grid",children:[c.jsx($a,{value:s,onChange:k,onSaveLocalConfig:nr,onTestConnection:jn,lastStatus:q,busy:ke}),c.jsx(Ba,{value:d,onChange:L,presets:ze,selectedPresetId:Y,onSelectPreset:Re,onLoadPreset:rr,onSavePreset:Bt,onResetDefaults:Ht,onDiffPrevious:Xr,presetName:_e,onPresetNameChange:Le,diffSummary:ot}),c.jsx(fd,{value:E,onChange:U,onApplyBatchFormat:Gr,onNormalize:Tn,busy:ke,useMock:Ie,onUseMockChange:Me,errorMessage:X}),c.jsx(cd,{tab:$,onTabChange:Se,result:H,appLogs:ue}),c.jsx(ud,{result:H}),c.jsx(id,{items:A,onRefresh:dn,onOpenTrace:Jr}),c.jsx(dd,{runs:et,selectedRunId:ve,onSelectRun:ce,onStartRun:oo,onFinishRun:lr,onRefreshRuns:pn,onRunEval:or,onCopyEvalReport:ir,evalBusy:P,traceItems:C,evalReport:y})]})]})}Zf.createRoot(document.getElementById("root")).render(c.jsx(Wf.StrictMode,{children:c.jsx(gd,{})})); +`);w&&(R(D=>({...D,batchQuestionsRaw:w})),Q("Batch field formatted: `;` converted to blank-line separators."))}async function Jr(w){try{const V=(await pt.loadTrace(w)).trace,Ee=V.parsed_normalized_json??null;b({trace_id:String(V.trace_id??w),ok:!!V.validation_result?.passed,normalized:Ee,route_hint_summary:V.route_hint_summary??(Ee?{route_hint:Ee.route_hint??null,confidence:Ee.confidence?.route_hint??null}:null),raw_model_output:V.raw_model_response??{},validation:V.validation_result??{passed:!1,errors:["validation not found"]},usage:V.usage??{input_tokens:0,output_tokens:0,total_tokens:0},latency_ms:Number(V.latency_ms??0),prompt_version:String(V.prompt_version??"unknown"),schema_version:String(V.schema_version??"unknown")}),Se("raw"),K(""),Q(`Trace opened: ${w}`)}catch(D){const V=D instanceof Error?D.message:String(D);K(`Trace: ${V}`),Q(`Trace open error ${w}: ${V}`)}}async function oo(){try{const w=await pt.startRun();ce(w.run.runId),Q(`Run started: ${w.run.runId}`),Q("Tip: start run does not execute normalize by itself. Use 'Run eval v2.0.2' button."),await pn()}catch(w){Q(`Run start error: ${w instanceof Error?w.message:String(w)}`)}}async function lr(){if(ve)try{await pt.finishRun(ve),Q(`Run finished: ${ve}`),await pn()}catch(w){Q(`Run finish error: ${w instanceof Error?w.message:String(w)}`)}}async function or(){d(!0),K("");try{Q("Starting eval in v2 contour.");const w=C.batchQuestionsRaw.trim()||C.userQuestion.trim();if(!w)throw new Error("Fill batch field or Raw user question first.");const D=await pt.runEval({connection:s,prompts:m,promptVersion:"normalizer_v2_0_2",mode:"single-pass-strict",rawQuestions:w,useMock:Ie});W(D.report),Q("Eval v2.0.2 run finished.");const V=D.report;if(V.run_id&&Q(`Eval run id: ${V.run_id}`),V.metrics){const Ee=V.metrics;Q(`Eval metrics v2.0.2: schema=${Ee.schema_validation_pass_rate??"n/a"}%, route_accuracy=${Ee.route_resolution_accuracy??"n/a"}%, no_route_precision=${Ee.no_route_precision??"n/a"}%, state_consistency=${Ee.execution_state_consistency_rate??"n/a"}%`)}await dn()}catch(w){const D=w instanceof Error?w.message:String(w);D.includes("Legacy eval runner supports normalized_query_v1 only")?(W({status:"plan_only",prompt_version:"normalizer_v2",reason:"backend eval runner is still legacy-v1 only",plan_file:"reports/v2_pilot_eval_plan.md",next_steps:["run cheap mock sanity for schema/fragment/scope","run small real batch (10-15 messages, temperature=0)","run challenge-30 replay with v2 metrics"]}),Q("Backend is legacy-only for eval right now. Showing v2 pilot plan.")):(K(`Eval: ${D}`),Q(`Eval run error: ${D}`))}finally{d(!1)}}async function ir(){try{const w=JSON.stringify(y??{},null,2);await navigator.clipboard.writeText(w),Q("Eval report copied to clipboard.")}catch(w){Q(`Eval report copy error: ${w instanceof Error?w.message:String(w)}`)}}function ur(){ie(""),Vt([]),$t(""),Tt(""),fn(""),Q("Assistant session reset.")}async function sr(){const w=Zn.trim();if(!w)return;bn(!0),fn(""),$t(""),Vt(V=>[...V,{message_id:`local-${Date.now()}`,session_id:te||"pending",role:"user",text:w,reply_type:null,created_at:new Date().toISOString(),trace_id:null,debug:null}]);const D=er();try{const V=await pt.sendAssistantMessage({connection:s,prompts:m,userMessage:w,sessionId:te||void 0,promptVersion:"normalizer_v2_0_2",context:{periodHint:C.periodHint,businessContext:C.businessContext},useMock:Ie});ie(V.session_id),Vt(V.conversation),Tt("Ответ готов"),Q(`Assistant reply received: trace=${V.debug.trace_id}`)}catch(V){const Ee=V instanceof Error?V.message:String(V);fn(Ee),Tt("Ошибка ассистента"),Q(`Assistant error: ${Ee}`)}finally{D(),bn(!1)}}return J.useEffect(()=>{if(!ve){M([]);return}pt.runTrace(ve).then(w=>M(w.items)).catch(w=>Q(`Run trace error: ${w instanceof Error?w.message:String(w)}`))},[ve]),c.jsxs("main",{className:"app-root",children:[c.jsxs("div",{className:"hero",children:[c.jsx("h1",{children:"NDC AI First Layer"}),c.jsx("p",{children:"Два режима в одном интерфейсе: диагностика декомпозиции и диалоговый ассистент на общем backend-контуре."})]}),c.jsxs("div",{className:"mode-switch-row",children:[c.jsx("button",{type:"button",className:Z==="assistant"?"tab active":"tab",onClick:()=>le("assistant"),children:"Ассистент"}),c.jsx("button",{type:"button",className:Z==="decomposition"?"tab active":"tab",onClick:()=>le("decomposition"),children:"Декомпозиция"})]}),Z==="assistant"?c.jsxs("div",{className:"layout-grid",children:[c.jsx($a,{value:s,onChange:k,onSaveLocalConfig:nr,onTestConnection:jn,lastStatus:q,busy:ke||Pn}),c.jsx(Ba,{value:m,onChange:P,presets:ze,selectedPresetId:Y,onSelectPreset:Re,onLoadPreset:rr,onSavePreset:Bt,onResetDefaults:Ht,onDiffPrevious:Xr,presetName:_e,onPresetNameChange:Le,diffSummary:ot}),c.jsx(od,{sessionId:te,conversation:He,inputValue:Zn,onInputChange:$t,periodHint:C.periodHint,onPeriodHintChange:w=>R(D=>({...D,periodHint:w})),businessContext:C.businessContext,onBusinessContextChange:w=>R(D=>({...D,businessContext:w})),useMock:Ie,onUseMockChange:Me,onSend:sr,onClear:ur,busy:Pn,statusText:Kr,errorMessage:Yr})]}):c.jsxs("div",{className:"layout-grid",children:[c.jsx($a,{value:s,onChange:k,onSaveLocalConfig:nr,onTestConnection:jn,lastStatus:q,busy:ke}),c.jsx(Ba,{value:m,onChange:P,presets:ze,selectedPresetId:Y,onSelectPreset:Re,onLoadPreset:rr,onSavePreset:Bt,onResetDefaults:Ht,onDiffPrevious:Xr,presetName:_e,onPresetNameChange:Le,diffSummary:ot}),c.jsx(fd,{value:C,onChange:R,onApplyBatchFormat:Gr,onNormalize:Tn,busy:ke,useMock:Ie,onUseMockChange:Me,errorMessage:X}),c.jsx(cd,{tab:$,onTabChange:Se,result:H,appLogs:ue}),c.jsx(ud,{result:H}),c.jsx(id,{items:A,onRefresh:dn,onOpenTrace:Jr}),c.jsx(dd,{runs:et,selectedRunId:ve,onSelectRun:ce,onStartRun:oo,onFinishRun:lr,onRefreshRuns:pn,onRunEval:or,onCopyEvalReport:ir,evalBusy:j,traceItems:E,evalReport:y})]})]})}Zf.createRoot(document.getElementById("root")).render(c.jsx(Wf.StrictMode,{children:c.jsx(gd,{})})); diff --git a/llm_normalizer/frontend/dist/index.html b/llm_normalizer/frontend/dist/index.html index fb9d7e9..9ceee52 100644 --- a/llm_normalizer/frontend/dist/index.html +++ b/llm_normalizer/frontend/dist/index.html @@ -4,7 +4,7 @@ NDC AI Normalizer Playground - + diff --git a/llm_normalizer/frontend/src/components/AssistantPanel.tsx b/llm_normalizer/frontend/src/components/AssistantPanel.tsx index 1b90bba..0c32092 100644 --- a/llm_normalizer/frontend/src/components/AssistantPanel.tsx +++ b/llm_normalizer/frontend/src/components/AssistantPanel.tsx @@ -41,7 +41,11 @@ function stringifyDebug(value: unknown): string { } } -function buildConversationExport(sessionId: string, conversation: AssistantConversationItem[]): string { +function buildConversationExport( + sessionId: string, + conversation: AssistantConversationItem[], + includeDebugPayload = false +): string { const lines: string[] = []; lines.push("# Assistant conversation export"); lines.push(`session_id: ${sessionId || "n/a"}`); @@ -61,7 +65,7 @@ function buildConversationExport(sessionId: string, conversation: AssistantConve lines.push(item.text || "(empty)"); lines.push(""); - if (item.role === "assistant" && item.debug) { + if (includeDebugPayload && item.role === "assistant" && item.debug) { lines.push("### debug_payload_json"); lines.push("```json"); lines.push(stringifyDebug(item.debug)); @@ -143,7 +147,8 @@ export function AssistantPanel({ if (conversation.length === 0) { return; } - const exportText = buildConversationExport(sessionId, conversation); + // Copy full run context for diagnostics (including debug payload blocks). + const exportText = buildConversationExport(sessionId, conversation, true); const copied = await copyTextToClipboard(exportText); setCopyState(copied ? "success" : "error");