From 33d8320ce8c26948aeefe4bbc3c4c247cc939179 Mon Sep 17 00:00:00 2001 From: Vivek Dubey Date: Wed, 6 May 2026 16:44:34 +0530 Subject: [PATCH] fix: normalize double-escaped LaTeX backslashes and HTML entities (#14564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14562 ## Problem LLMs like DeepSeek V4 Flash and Qwen3-MAX return \\( and \\[ (double backslash) in LaTeX output. The preprocessLaTeX() function only handled single backslash delimiters, so equations showed as raw text. HTML entities like < and > were also not decoded. ## Solution Added normalization step before existing delimiter conversion: - \\( → \( and \\[ → \[ - < → < and > → > and & → & --------- Co-authored-by: Vivek --- web/src/utils/chat.ts | 13 ++++++++++++- web/src/utils/tests/chat.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 web/src/utils/tests/chat.test.ts diff --git a/web/src/utils/chat.ts b/web/src/utils/chat.ts index cc0024950..879289d3b 100644 --- a/web/src/utils/chat.ts +++ b/web/src/utils/chat.ts @@ -56,14 +56,25 @@ const BLOCK_MATH_RE = /\\\[([\s\S]*?)(? { - const blockProcessedContent = content.replace( + const normalizedContent = content + .replace(/\\\\\[/g, '\\[') + .replace(/\\\\\(/g, '\\(') + .replace(/\\\\\]/g, '\\]') + .replace(/\\\\\)/g, '\\)') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&'); + + const blockProcessedContent = normalizedContent.replace( BLOCK_MATH_RE, (_, equation) => `$$${equation}$$`, ); + const inlineProcessedContent = blockProcessedContent.replace( INLINE_MATH_RE, (_, equation) => `$${equation}$`, ); + return inlineProcessedContent; }; diff --git a/web/src/utils/tests/chat.test.ts b/web/src/utils/tests/chat.test.ts new file mode 100644 index 000000000..82b478071 --- /dev/null +++ b/web/src/utils/tests/chat.test.ts @@ -0,0 +1,26 @@ +import { preprocessLaTeX } from '../chat'; + +test('handles double-escaped inline LaTeX', () => { + const result = preprocessLaTeX('\\\\(\\\\Delta = b^2\\\\)'); + expect(result).toBe('$\\Delta = b^2$'); +}); + +test('handles double-escaped block LaTeX', () => { + const result = preprocessLaTeX('\\\\[E = mc^2\\\\]'); + expect(result).toBe('$$E = mc^2$$'); +}); + +test('decodes HTML entities', () => { + const result = preprocessLaTeX('a < b & c > d'); + expect(result).toBe('a < b & c > d'); +}); + +test('handles mixed double-escaped delimiters with HTML entities', () => { + const result = preprocessLaTeX('\\\\(x < y\\\\)'); + expect(result).toBe('$x < y$'); +}); + +test('passes through already correct single-escaped delimiters unchanged', () => { + const result = preprocessLaTeX('\\(x = 1\\)'); + expect(result).toBe('$x = 1$'); +}); \ No newline at end of file