update pay function

This commit is contained in:
2025-11-30 17:41:55 +08:00
parent 9d9d3430b7
commit 6763151c57

View File

@@ -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);
if (textAfter) { const incompleteMatch = remainingText.match(incompleteEchartsRegex);
parts.push({ type: 'text', content: textAfter });
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) {
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') {