From 8ae37bdccfaf83a937b42262005d98d3f98ffdea Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:32:50 +0530 Subject: [PATCH] refactor: improve Markdown fence handling in report generation and viewer --- .../app/agents/new_chat/tools/report.py | 18 +++++++++++++----- surfsense_web/components/markdown-viewer.tsx | 5 +++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/surfsense_backend/app/agents/new_chat/tools/report.py b/surfsense_backend/app/agents/new_chat/tools/report.py index 0e6ae2bbb..9f44aa918 100644 --- a/surfsense_backend/app/agents/new_chat/tools/report.py +++ b/surfsense_backend/app/agents/new_chat/tools/report.py @@ -43,6 +43,10 @@ logger = logging.getLogger(__name__) # Reusable formatting instructions appended to section-level and review prompts. _FORMATTING_RULES = """\ +- IMPORTANT: Output raw Markdown directly. Do NOT wrap the entire output in a \ +code fence (e.g. ```markdown, ````markdown, or any backtick fence). Individual \ +code examples and diagrams inside the report should still use fenced code blocks, \ +but the report itself must NOT be enclosed in one. - Maintain proper Markdown formatting throughout. - When including code examples, ALWAYS format them as proper fenced code blocks \ with the correct language identifier (e.g. ```java, ```python). Code inside code \ @@ -188,16 +192,20 @@ def _strip_wrapping_code_fences(text: str) -> str: Handles patterns like: ```markdown\\n...content...\\n``` + ````markdown\\n...content...\\n```` ```md\\n...content...\\n``` ```\\n...content...\\n``` ```json\\n...content...\\n``` + Supports 3 or more backticks (LLMs escalate when content has triple-backtick blocks). """ stripped = text.strip() - # Match opening fence with optional language tag - m = re.match(r"^```(?:markdown|md|json)?\s*\n", stripped) - if m and stripped.endswith("```"): - stripped = stripped[m.end() :] # remove opening fence - stripped = stripped[:-3].rstrip() # remove closing fence + # Match opening fence with 3+ backticks and optional language tag + m = re.match(r"^(`{3,})(?:markdown|md|json)?\s*\n", stripped) + if m: + fence = m.group(1) # e.g. "```" or "````" + if stripped.endswith(fence): + stripped = stripped[m.end() :] # remove opening fence + stripped = stripped[: -len(fence)].rstrip() # remove closing fence return stripped diff --git a/surfsense_web/components/markdown-viewer.tsx b/surfsense_web/components/markdown-viewer.tsx index 297c850e0..7e0fc17b1 100644 --- a/surfsense_web/components/markdown-viewer.tsx +++ b/surfsense_web/components/markdown-viewer.tsx @@ -24,8 +24,9 @@ interface MarkdownViewerProps { */ function stripOuterMarkdownFence(content: string): string { const trimmed = content.trim(); - const match = trimmed.match(/^```(?:markdown|md)?\s*\n([\s\S]+?)\n```\s*$/); - return match ? match[1] : content; + // Match 3+ backtick fences (LLMs escalate to 4+ when content has triple-backtick blocks) + const match = trimmed.match(/^(`{3,})(?:markdown|md)?\s*\n([\s\S]+?)\n\1\s*$/); + return match ? match[2] : content; } /**