fix: Handle special cases of LLM output not following markdown syntax (20250525, formated) This commit introduces a utility function to address rendering issues in LLM output, particularly for Chinese characters and parentheses. The function ensures minimal modification of the original text while fixing markdown parsing problems. Changes include: - Added in for handling specific cases. - Updated in to incorporate the new utility. The fix ensures proper rendering of bold/italic text containing Chinese parentheses, improving readability for non-English content.

This commit is contained in:
YuQX 2025-05-25 19:37:43 +08:00
parent 49fe137553
commit 8ef7938c96
2 changed files with 90 additions and 83 deletions

View File

@ -20,7 +20,7 @@ import markedExtension from '$lib/utils/marked/extension';
import markedKatexExtension from '$lib/utils/marked/katex-extension'; import markedKatexExtension from '$lib/utils/marked/katex-extension';
import hljs from 'highlight.js'; import hljs from 'highlight.js';
import { specialCases } from '$lib/utils/processResponseContent/special-cases' import { specialCases } from '$lib/utils/processResponseContent/special-cases';
////////////////////////// //////////////////////////
// Helper functions // Helper functions

View File

@ -11,71 +11,78 @@
*/ */
export const specialCases = (src: string): string => { export const specialCases = (src: string): string => {
const lines = src.split('\n'); // Process from line to line. const lines = src.split('\n'); // Process from line to line.
const processedLines = lines.map(line => { const processedLines = lines.map((line) => {
// 1. 中文 (Chinese, CN)
if (/[\u4e00-\u9fa5]/.test(line)) {
// Only execute if there are Chinese characters.
// 1. 中文 (Chinese, CN) // 1.1. Problems caused by Chinese parentheses
if (/[\u4e00-\u9fa5]/.test(line)) { // Only execute if there are Chinese characters. /* Discription:
* When `*` has Chinese parentheses on the inside, markdown parser ignore bold or italic style.
* - e.g. `**中文名English**中文内容` will be parsed directly,
* instead of `<strong>中文名English</strong>中文内容`.
* Solution:
* Adding a `space` before and after the bold/italic part can solve the problem.
* - e.g. `**中文名English**中文内容` -> ` **中文名English** 中文内容`
* Note:
* Similar problem was found with English parentheses and other full delimiters,
* but they are not handled here because they are less likely to appear in LLM output.
* Change the behavior in future if needed.
*/
// 1.1. Problems caused by Chinese parentheses if (line.includes('*')) {
/* Discription: // Only execute if `*` is found in line.
* When `*` has Chinese parentheses on the inside, markdown parser ignore bold or italic style.
* - e.g. `**中文名English**中文内容` will be parsed directly,
* instead of `<strong>中文名English</strong>中文内容`.
* Solution:
* Adding a `space` before and after the bold/italic part can solve the problem.
* - e.g. `**中文名English**中文内容` -> ` **中文名English** 中文内容`
* Note:
* Similar problem was found with English parentheses and other full delimiters,
* but they are not handled here because they are less likely to appear in LLM output.
* Change the behavior in future if needed.
*/
if (line.includes('*')) { // Only execute if `*` is found in line. // 1.1.1. Handle **bold** with Chinese parentheses
// 1.1.1. Handle **bold** with Chinese parentheses line = processCN_01(line, '**', '', '');
line = processCN_01(line, '**', '', ''); // 1.1.2. Handle *italic* with Chinese parentheses
// 1.1.2. Handle *italic* with Chinese parentheses line = processCN_01(line, '*', '', '');
line = processCN_01(line, '*', '', ''); }
} }
return line;
} });
return line; const result = processedLines.join('\n');
}); return result;
const result = processedLines.join('\n'); };
return result;
}
////////////////////////// //////////////////////////
// Helper functions // Helper functions
////////////////////////// //////////////////////////
function isChineseChar(char: string): boolean { function isChineseChar(char: string): boolean {
return /\p{Script=Han}/u.test(char); return /\p{Script=Han}/u.test(char);
} }
function escapeRegExp(string: string): string { function escapeRegExp(string: string): string {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
} }
////////////////////////// //////////////////////////
// Main functions // Main functions
////////////////////////// //////////////////////////
// Handle case `1.1.1` and `1.1.2` // Handle case `1.1.1` and `1.1.2`
function processCN_01(line: string, symbol: string, leftSymbol: string, rightSymbol: string): string { function processCN_01(
const escapedSymbol = escapeRegExp(symbol); line: string,
const regex = new RegExp(`(.*?)(?<!${escapedSymbol})(${escapedSymbol})([^${escapedSymbol}]+)(${escapedSymbol})(?!${escapedSymbol})(.*?)`, 'g'); symbol: string,
return line.replace(regex, (match, l, left, content, right, r) => { leftSymbol: string,
const result = ( rightSymbol: string
(content.startsWith(leftSymbol) || content.endsWith(rightSymbol)) && ): string {
(!l || (l && l.length > 0 && isChineseChar(l[l.length - 1]))) && const escapedSymbol = escapeRegExp(symbol);
(!r || (r && r.length > 0 && isChineseChar(r[0]))) const regex = new RegExp(
) `(.*?)(?<!${escapedSymbol})(${escapedSymbol})([^${escapedSymbol}]+)(${escapedSymbol})(?!${escapedSymbol})(.*?)`,
if (result) { 'g'
return ` ${left}${content}${right} `; );
} else { return line.replace(regex, (match, l, left, content, right, r) => {
return match; const result =
} (content.startsWith(leftSymbol) || content.endsWith(rightSymbol)) &&
}); (!l || (l && l.length > 0 && isChineseChar(l[l.length - 1]))) &&
(!r || (r && r.length > 0 && isChineseChar(r[0])));
if (result) {
return ` ${left}${content}${right} `;
} else {
return match;
}
});
} }