From cd8e98bd3f205247acee925575f6683d8c7d9af1 Mon Sep 17 00:00:00 2001 From: dctouch Date: Sun, 24 May 2026 12:40:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BA=D1=80=D0=B5=D0=BF=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20smoke=20=D0=B4=D0=BB=D1=8F=20open-contracts=20=D0=B8?= =?UTF-8?q?=20VAT=20acceptance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/dist/services/assistantService.js | 19 ++++++++++++++++++- .../backend/src/services/assistantService.ts | 19 ++++++++++++++++++- .../tests/addressQueryRuntimeM23.test.ts | 2 +- .../tests/addressVatConfirmedRoute.test.ts | 2 +- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/llm_normalizer/backend/dist/services/assistantService.js b/llm_normalizer/backend/dist/services/assistantService.js index dfbdb6c..1253d8d 100644 --- a/llm_normalizer/backend/dist/services/assistantService.js +++ b/llm_normalizer/backend/dist/services/assistantService.js @@ -3842,7 +3842,7 @@ function hasDeepAnalysisPreferenceSignal(text) { /(?:договор|контракт|документ|аванс|отгрузк)/iu.test(lower) && /(?:какие|какой|где|покажи|показать|список|проверь|проверить|уточни)/iu.test(lower) && !/(?:разложи|разложить|цепочк|механизм|почему|root\s*cause|trace\s*chain|корнев[а-я]*\s+причин)/iu.test(lower); - if (openContractsListQuestionSignal) { + if (openContractsListQuestionSignal || hasOpenContractsListQuestionSignal(lower)) { return false; } const broadOverviewRiskSignal = /(?:\u0432\s+\u0446\u0435\u043b\u043e\u043c|\u043e\u0431\u0449\p{L}*\s+\u043a\u0430\u0440\u0442\u0438\u043d|\u0442\u043e\u043f\s+\u0440\u0438\u0441\u043a|\u0447\u0442\u043e\s+\u043d\u0435\s+\u0442\u0430\u043a)/iu.test(lower); @@ -3875,6 +3875,9 @@ function hasDirectDeepAnalysisSignal(text) { if (!normalized) { return false; } + if (hasOpenContractsListQuestionSignal(normalized)) { + return false; + } const fixedAssetAmortizationCompletenessSignal = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|\u043e\u0441\u043d\u043e\u0432\u043d[\u0430-\u044f]*\s+\u0441\u0440\u0435\u0434\u0441\u0442\u0432|fixed\s*asset|depreciat|\b\u043e\u0441\b|\b0?1\s*[/\\]\s*0?2\b|\u0441\u0447[\u0435\u0451]\u0442[^\n]{0,16}\b0?1\b[^\n]{0,16}\b0?2\b)/iu.test(normalized) && /(?:\u043f\u043e\u043b\u043d[\u0430-\u044f]*|\u043f\u0440\u043e\u043f\u0443\u0449|\u043d\u0430\u0447\u0438\u0441\u043b|\u043f\u0440\u043e\u0432\u0435\u0440|\u043e\u0431\u044a\u0435\u043a\u0442|coverage|missing|expected|actual)/iu.test(normalized); const deferredExpenseDeepSignal = /(?:\b97(?:[.,]\d{1,2})?\b|\u0440\u0431\u043f|\u0440\u0430\u0441\u0445\u043e\u0434[\u044b\u043e\u0432\s]+\u0431\u0443\u0434\u0443\u0449[\u0438\u0445\u0435]\s+\u043f\u0435\u0440\u0438\u043e\u0434|deferred)/iu.test(normalized) && @@ -3919,6 +3922,9 @@ function hasOpenContractsAddressSignal(text) { if (!normalized) { return false; } + if (hasOpenContractsListQuestionSignal(normalized)) { + return true; + } const hasContractCue = /(?:договор|контракт|contract)/iu.test(normalized); if (!hasContractCue) { return false; @@ -3931,6 +3937,17 @@ function hasOpenContractsAddressSignal(text) { const hasTemporalCue = hasPeriodLiteral(normalized) || /\b\d{4}[-/.]\d{2}[-/.]\d{2}\b/.test(normalized); return hasRequestCue || hasTemporalCue; } +function hasOpenContractsListQuestionSignal(text) { + const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase()); + if (!normalized) { + return false; + } + const hasSettlementCue = /(?:договор|контракт|contract|взаиморасч[её]т|расч[её]т|оплат|плат[её]ж|документ|аванс|отгруз)/iu.test(normalized); + const hasOpenGapCue = /(?:незакрыт|не\s+закрыт|открыт|без[^\n]{0,40}(?:закрыт|документ|оплат)|нет[^\n]{0,40}(?:документ|оплат|закрыт)|оплат[а-я]*[^\n]{0,80}без[^\n]{0,40}закрыт|плат[её]ж[а-я]*[^\n]{0,80}без[^\n]{0,40}закрыт|требует[^\n]{0,40}ручн[а-я]*\s+проверк)/iu.test(normalized); + const hasListCue = /(?:какие|какой|где|покажи|показать|список|shortlist|найди|проверь|проверить|что\s+надо\s+проверить)/iu.test(normalized); + const hasDeepDiagnosticCue = /(?:разложи|разложить|цепочк|механизм|почему|root\s*cause|trace\s*chain|корнев[а-я]*\s+причин|что\s+меша[а-я]+\s+закрыт)/iu.test(normalized); + return hasSettlementCue && hasOpenGapCue && hasListCue && !hasDeepDiagnosticCue; +} function resolveAssistantOrchestrationDecision(input) { return assistantRoutePolicy.resolveAssistantOrchestrationDecision(input); } diff --git a/llm_normalizer/backend/src/services/assistantService.ts b/llm_normalizer/backend/src/services/assistantService.ts index 955c0fa..7586f46 100644 --- a/llm_normalizer/backend/src/services/assistantService.ts +++ b/llm_normalizer/backend/src/services/assistantService.ts @@ -3800,7 +3800,7 @@ function hasDeepAnalysisPreferenceSignal(text) { /(?:договор|контракт|документ|аванс|отгрузк)/iu.test(lower) && /(?:какие|какой|где|покажи|показать|список|проверь|проверить|уточни)/iu.test(lower) && !/(?:разложи|разложить|цепочк|механизм|почему|root\s*cause|trace\s*chain|корнев[а-я]*\s+причин)/iu.test(lower); - if (openContractsListQuestionSignal) { + if (openContractsListQuestionSignal || hasOpenContractsListQuestionSignal(lower)) { return false; } const broadOverviewRiskSignal = @@ -3834,6 +3834,9 @@ function hasDirectDeepAnalysisSignal(text) { if (!normalized) { return false; } + if (hasOpenContractsListQuestionSignal(normalized)) { + return false; + } const fixedAssetAmortizationCompletenessSignal = /(?:\u0430\u043c\u043e\u0440\u0442\u0438\u0437|\u043e\u0441\u043d\u043e\u0432\u043d[\u0430-\u044f]*\s+\u0441\u0440\u0435\u0434\u0441\u0442\u0432|fixed\s*asset|depreciat|\b\u043e\u0441\b|\b0?1\s*[/\\]\s*0?2\b|\u0441\u0447[\u0435\u0451]\u0442[^\n]{0,16}\b0?1\b[^\n]{0,16}\b0?2\b)/iu.test(normalized) && /(?:\u043f\u043e\u043b\u043d[\u0430-\u044f]*|\u043f\u0440\u043e\u043f\u0443\u0449|\u043d\u0430\u0447\u0438\u0441\u043b|\u043f\u0440\u043e\u0432\u0435\u0440|\u043e\u0431\u044a\u0435\u043a\u0442|coverage|missing|expected|actual)/iu.test(normalized); @@ -3881,6 +3884,9 @@ function hasOpenContractsAddressSignal(text) { if (!normalized) { return false; } + if (hasOpenContractsListQuestionSignal(normalized)) { + return true; + } const hasContractCue = /(?:договор|контракт|contract)/iu.test(normalized); if (!hasContractCue) { return false; @@ -3893,6 +3899,17 @@ function hasOpenContractsAddressSignal(text) { const hasTemporalCue = hasPeriodLiteral(normalized) || /\b\d{4}[-/.]\d{2}[-/.]\d{2}\b/.test(normalized); return hasRequestCue || hasTemporalCue; } +function hasOpenContractsListQuestionSignal(text) { + const normalized = compactWhitespace(repairAddressMojibake(String(text ?? "")).toLowerCase()); + if (!normalized) { + return false; + } + const hasSettlementCue = /(?:договор|контракт|contract|взаиморасч[её]т|расч[её]т|оплат|плат[её]ж|документ|аванс|отгруз)/iu.test(normalized); + const hasOpenGapCue = /(?:незакрыт|не\s+закрыт|открыт|без[^\n]{0,40}(?:закрыт|документ|оплат)|нет[^\n]{0,40}(?:документ|оплат|закрыт)|оплат[а-я]*[^\n]{0,80}без[^\n]{0,40}закрыт|плат[её]ж[а-я]*[^\n]{0,80}без[^\n]{0,40}закрыт|требует[^\n]{0,40}ручн[а-я]*\s+проверк)/iu.test(normalized); + const hasListCue = /(?:какие|какой|где|покажи|показать|список|shortlist|найди|проверь|проверить|что\s+надо\s+проверить)/iu.test(normalized); + const hasDeepDiagnosticCue = /(?:разложи|разложить|цепочк|механизм|почему|root\s*cause|trace\s*chain|корнев[а-я]*\s+причин|что\s+меша[а-я]+\s+закрыт)/iu.test(normalized); + return hasSettlementCue && hasOpenGapCue && hasListCue && !hasDeepDiagnosticCue; +} export function resolveAssistantOrchestrationDecision(input) { return assistantRoutePolicy.resolveAssistantOrchestrationDecision(input); } diff --git a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts index 1b9133b..7837904 100644 --- a/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts +++ b/llm_normalizer/backend/tests/addressQueryRuntimeM23.test.ts @@ -3528,7 +3528,7 @@ describe("address filter extraction for balance drilldown", () => { }); }); -describe("address query limited taxonomy and stage diagnostics", { timeout: 15000 }, () => { +describe("address query limited taxonomy and stage diagnostics", { timeout: 90000 }, () => { it("does not default standalone item provenance questions to today without explicit temporal cue", () => { const result = extractAddressFilters( "От какого поставщика куплен товар Столешница 600*3050*26 дуб ниагара", diff --git a/llm_normalizer/backend/tests/addressVatConfirmedRoute.test.ts b/llm_normalizer/backend/tests/addressVatConfirmedRoute.test.ts index 2558a73..287bb32 100644 --- a/llm_normalizer/backend/tests/addressVatConfirmedRoute.test.ts +++ b/llm_normalizer/backend/tests/addressVatConfirmedRoute.test.ts @@ -86,5 +86,5 @@ describe("vat payable confirmed as-of route", () => { expect(result?.debug.requested_result_mode).toBe("confirmed_balance"); expect(result?.debug.route_expectation_status).toBe("matched"); expect(result?.debug.limited_reason_category).not.toBe("unsupported"); - }, 30000); + }, 90000); });