update pay function
This commit is contained in:
@@ -39,6 +39,7 @@ const cleanBrokenJson = (text) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析 Markdown 内容,提取 ECharts 代码块
|
* 解析 Markdown 内容,提取 ECharts 代码块
|
||||||
|
* 支持处理不完整的代码块(LLM 输出被截断的情况)
|
||||||
* @param {string} markdown - Markdown 文本
|
* @param {string} markdown - Markdown 文本
|
||||||
* @returns {Array} - 包含文本和图表的数组
|
* @returns {Array} - 包含文本和图表的数组
|
||||||
*/
|
*/
|
||||||
@@ -49,11 +50,17 @@ const parseMarkdownWithCharts = (markdown) => {
|
|||||||
const cleanedMarkdown = cleanBrokenJson(markdown);
|
const cleanedMarkdown = cleanBrokenJson(markdown);
|
||||||
|
|
||||||
const parts = [];
|
const parts = [];
|
||||||
const echartsRegex = /```echarts\s*\n([\s\S]*?)```/g;
|
|
||||||
|
// 方案1: 匹配完整的 echarts 代码块
|
||||||
|
const completeEchartsRegex = /```echarts\s*\n([\s\S]*?)```/g;
|
||||||
|
// 方案2: 匹配不完整的 echarts 代码块(没有结束的 ```)
|
||||||
|
const incompleteEchartsRegex = /```echarts\s*\n([\s\S]*?)$/;
|
||||||
|
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
while ((match = echartsRegex.exec(cleanedMarkdown)) !== null) {
|
// 首先尝试匹配完整的代码块
|
||||||
|
while ((match = completeEchartsRegex.exec(cleanedMarkdown)) !== null) {
|
||||||
// 添加代码块前的文本
|
// 添加代码块前的文本
|
||||||
if (match.index > lastIndex) {
|
if (match.index > lastIndex) {
|
||||||
const textBefore = cleanedMarkdown.substring(lastIndex, match.index).trim();
|
const textBefore = cleanedMarkdown.substring(lastIndex, match.index).trim();
|
||||||
@@ -69,15 +76,36 @@ const parseMarkdownWithCharts = (markdown) => {
|
|||||||
lastIndex = match.index + match[0].length;
|
lastIndex = match.index + match[0].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加剩余文本
|
// 检查剩余内容是否包含不完整的 echarts 代码块
|
||||||
if (lastIndex < cleanedMarkdown.length) {
|
if (lastIndex < cleanedMarkdown.length) {
|
||||||
const textAfter = cleanedMarkdown.substring(lastIndex).trim();
|
const remainingText = cleanedMarkdown.substring(lastIndex);
|
||||||
|
const incompleteMatch = remainingText.match(incompleteEchartsRegex);
|
||||||
|
|
||||||
|
if (incompleteMatch) {
|
||||||
|
// 提取不完整代码块之前的文本
|
||||||
|
const textBeforeIncomplete = remainingText.substring(0, incompleteMatch.index).trim();
|
||||||
|
if (textBeforeIncomplete) {
|
||||||
|
parts.push({ type: 'text', content: textBeforeIncomplete });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取不完整的 echarts 内容
|
||||||
|
const incompleteChartConfig = incompleteMatch[1].trim();
|
||||||
|
if (incompleteChartConfig) {
|
||||||
|
logger.warn('检测到不完整的 echarts 代码块(缺少结束符)', {
|
||||||
|
contentPreview: incompleteChartConfig.substring(0, 100),
|
||||||
|
});
|
||||||
|
parts.push({ type: 'chart', content: incompleteChartConfig });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通剩余文本
|
||||||
|
const textAfter = remainingText.trim();
|
||||||
if (textAfter) {
|
if (textAfter) {
|
||||||
parts.push({ type: 'text', content: textAfter });
|
parts.push({ type: 'text', content: textAfter });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果没有找到图表,返回整个 markdown 作为文本
|
// 如果没有找到任何图表,返回整个 markdown 作为文本
|
||||||
if (parts.length === 0) {
|
if (parts.length === 0) {
|
||||||
parts.push({ type: 'text', content: cleanedMarkdown });
|
parts.push({ type: 'text', content: cleanedMarkdown });
|
||||||
}
|
}
|
||||||
@@ -240,11 +268,12 @@ export const MarkdownWithCharts = ({ content, variant = 'auto' }) => {
|
|||||||
// 移除可能的前后空白和不可见字符
|
// 移除可能的前后空白和不可见字符
|
||||||
cleanContent = cleanContent.replace(/^\s+|\s+$/g, '');
|
cleanContent = cleanContent.replace(/^\s+|\s+$/g, '');
|
||||||
|
|
||||||
// 检查 JSON 是否完整并尝试修复
|
// ========== 增强的 JSON 修复逻辑 ==========
|
||||||
// 使用栈来跟踪括号,确保正确的闭合顺序
|
// 使用栈来跟踪括号和字符串状态
|
||||||
const stack = [];
|
const stack = [];
|
||||||
let inString = false;
|
let inString = false;
|
||||||
let escape = false;
|
let escape = false;
|
||||||
|
let stringStartPos = -1;
|
||||||
|
|
||||||
for (let i = 0; i < cleanContent.length; i++) {
|
for (let i = 0; i < cleanContent.length; i++) {
|
||||||
const char = cleanContent[i];
|
const char = cleanContent[i];
|
||||||
@@ -260,7 +289,13 @@ export const MarkdownWithCharts = ({ content, variant = 'auto' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (char === '"') {
|
if (char === '"') {
|
||||||
inString = !inString;
|
if (inString) {
|
||||||
|
inString = false;
|
||||||
|
stringStartPos = -1;
|
||||||
|
} else {
|
||||||
|
inString = true;
|
||||||
|
stringStartPos = i;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +314,22 @@ export const MarkdownWithCharts = ({ content, variant = 'auto' }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果栈不为空,说明有未闭合的括号,需要补全
|
// 修复1: 如果字符串未闭合,需要关闭字符串
|
||||||
|
if (inString) {
|
||||||
|
logger.warn('检测到未闭合的字符串,尝试修复', {
|
||||||
|
position: stringStartPos,
|
||||||
|
});
|
||||||
|
// 找到最后一个有意义的位置(非空白)
|
||||||
|
let lastMeaningful = cleanContent.length - 1;
|
||||||
|
while (lastMeaningful > stringStartPos && /\s/.test(cleanContent[lastMeaningful])) {
|
||||||
|
lastMeaningful--;
|
||||||
|
}
|
||||||
|
// 截取到最后一个有意义的字符,然后闭合字符串
|
||||||
|
cleanContent = cleanContent.substring(0, lastMeaningful + 1) + '"';
|
||||||
|
logger.info('字符串已修复(添加闭合引号)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复2: 如果栈不为空,说明有未闭合的括号,需要补全
|
||||||
if (stack.length > 0) {
|
if (stack.length > 0) {
|
||||||
logger.warn('检测到不完整的 ECharts JSON,尝试修复', {
|
logger.warn('检测到不完整的 ECharts JSON,尝试修复', {
|
||||||
unclosed: stack.join(''),
|
unclosed: stack.join(''),
|
||||||
@@ -291,11 +341,67 @@ export const MarkdownWithCharts = ({ content, variant = 'auto' }) => {
|
|||||||
cleanContent += open === '{' ? '}' : ']';
|
cleanContent += open === '{' ? '}' : ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('ECharts JSON 已修复');
|
logger.info('ECharts JSON 括号已修复');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复3: 尝试清理可能的尾部垃圾字符(如截断导致的无效字符)
|
||||||
|
// 找到最后一个有效的 JSON 结束字符
|
||||||
|
const lastValidEnd = Math.max(
|
||||||
|
cleanContent.lastIndexOf('}'),
|
||||||
|
cleanContent.lastIndexOf(']'),
|
||||||
|
cleanContent.lastIndexOf('"')
|
||||||
|
);
|
||||||
|
if (lastValidEnd > 0 && lastValidEnd < cleanContent.length - 1) {
|
||||||
|
const tail = cleanContent.substring(lastValidEnd + 1).trim();
|
||||||
|
// 如果尾部不是有效的 JSON 字符,则截断
|
||||||
|
if (tail && !/^[,\}\]\s]*$/.test(tail)) {
|
||||||
|
logger.warn('检测到尾部垃圾字符,截断处理', { tail: tail.substring(0, 50) });
|
||||||
|
cleanContent = cleanContent.substring(0, lastValidEnd + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试解析 JSON
|
// 尝试解析 JSON
|
||||||
const chartOption = JSON.parse(cleanContent);
|
let chartOption;
|
||||||
|
try {
|
||||||
|
chartOption = JSON.parse(cleanContent);
|
||||||
|
} catch (parseError) {
|
||||||
|
// 如果解析失败,尝试更激进的修复
|
||||||
|
logger.warn('首次 JSON 解析失败,尝试更激进的修复', { error: parseError.message });
|
||||||
|
|
||||||
|
// 尝试找到 JSON 的有效开始和结束
|
||||||
|
const jsonStart = cleanContent.indexOf('{');
|
||||||
|
if (jsonStart >= 0) {
|
||||||
|
let fixedContent = cleanContent.substring(jsonStart);
|
||||||
|
// 重新计算并补全括号
|
||||||
|
const fixStack = [];
|
||||||
|
let fixInString = false;
|
||||||
|
let fixEscape = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < fixedContent.length; i++) {
|
||||||
|
const char = fixedContent[i];
|
||||||
|
if (fixEscape) { fixEscape = false; continue; }
|
||||||
|
if (char === '\\' && fixInString) { fixEscape = true; continue; }
|
||||||
|
if (char === '"') { fixInString = !fixInString; continue; }
|
||||||
|
if (fixInString) continue;
|
||||||
|
if (char === '{' || char === '[') fixStack.push(char);
|
||||||
|
else if (char === '}' && fixStack.length > 0 && fixStack[fixStack.length - 1] === '{') fixStack.pop();
|
||||||
|
else if (char === ']' && fixStack.length > 0 && fixStack[fixStack.length - 1] === '[') fixStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果仍在字符串中,关闭字符串
|
||||||
|
if (fixInString) fixedContent += '"';
|
||||||
|
// 补全括号
|
||||||
|
while (fixStack.length > 0) {
|
||||||
|
const open = fixStack.pop();
|
||||||
|
fixedContent += open === '{' ? '}' : ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
chartOption = JSON.parse(fixedContent);
|
||||||
|
logger.info('激进修复成功');
|
||||||
|
} else {
|
||||||
|
throw parseError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 验证是否是有效的 ECharts 配置
|
// 验证是否是有效的 ECharts 配置
|
||||||
if (!chartOption || typeof chartOption !== 'object') {
|
if (!chartOption || typeof chartOption !== 'object') {
|
||||||
|
|||||||
Reference in New Issue
Block a user