diff --git a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java index ca3c10e..a46bf46 100644 --- a/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java +++ b/src/main/java/kz/konturai/parser/service/MarketingAnalysisService.java @@ -3813,8 +3813,10 @@ public class MarketingAnalysisService { .replace("[[CHART_CHANNEL_MATRIX]]", "") .replace("[[CHART_FUNNEL]]", "") .replace("[[CHART_SWOT]]", ""); - // Safety net: remove any unknown/variant placeholders too - cleaned = cleaned.replaceAll("(?i)\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_]+\\s*\\]\\]", ""); + // Safety net: remove any unknown/variant placeholders too. + // IMPORTANT: model sometimes outputs generic placeholders like [[CHART_...]]. + // We tolerate dots/hyphens/underscores to ensure they get removed. + cleaned = cleaned.replaceAll("(?i)\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_.-]+\\s*\\]\\]", ""); cleaned = cleaned.replaceAll("(\\r?\\n){3,}", "\n\n"); return cleaned; } @@ -3854,7 +3856,7 @@ public class MarketingAnalysisService { // spaces). Use a fast contains() check first, but fall back to regex to // tolerate extra spaces like: [[ CHART_SEASONALITY ]]. java.util.regex.Pattern presentPattern = java.util.regex.Pattern - .compile("\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_]+\\s*\\]\\]"); + .compile("\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_.-]+\\s*\\]\\]"); Set presentPlaceholders = new LinkedHashSet<>(); if (containsChartOrTablePlaceholder(result)) { java.util.regex.Matcher presentMatcher = presentPattern.matcher(result); @@ -3935,8 +3937,9 @@ public class MarketingAnalysisService { result = removePlaceholderWithSurroundingNewlines(result, placeholderVariant); } - // Final safety net: remove any leftover placeholders (known or unknown) - result = result.replaceAll("(?i)\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_]+\\s*\\]\\]", ""); + // Final safety net: remove any leftover placeholders (known or unknown), + // including generic tokens like [[CHART_...]]. + result = result.replaceAll("(?i)\\[\\[\\s*(CHART|TABLE)_[A-Za-z0-9_.-]+\\s*\\]\\]", ""); result = result.replaceAll("(\\r?\\n){3,}", "\n\n"); logger.info( diff --git a/src/test/java/kz/konturai/parser/service/MarketingAnalysisServiceChartPlacementTest.java b/src/test/java/kz/konturai/parser/service/MarketingAnalysisServiceChartPlacementTest.java index 2712a1e..f510cac 100644 --- a/src/test/java/kz/konturai/parser/service/MarketingAnalysisServiceChartPlacementTest.java +++ b/src/test/java/kz/konturai/parser/service/MarketingAnalysisServiceChartPlacementTest.java @@ -171,4 +171,23 @@ class MarketingAnalysisServiceChartPlacementTest { assertTrue(Pattern.compile("(?m)^```json:segmentsChannelMatrix$").matcher(out).find(), "Opening fence line must be exactly ```json: with no leading spaces"); } + + @Test + void replaceChartPlaceholders_shouldRemoveGenericChartDotsPlaceholder() throws Exception { + Method m = MarketingAnalysisService.class.getDeclaredMethod("replaceChartPlaceholders", String.class, + Map.class); + m.setAccessible(true); + + String report = "" + + "## I. Краткое резюме\n" + + "Текст перед.\n" + + "[[CHART_...]]\n" + + "Текст после.\n"; + + // Even if chartsData is empty, generic placeholders must be removed. + String out = (String) m.invoke(marketingAnalysisService, report, new HashMap()); + + assertFalse(out.contains("[[CHART_...]]"), + "Generic placeholder [[CHART_...]] must be removed from fullAnalysis"); + } }