diff --git a/resources/assets/js/utils/markdown.js b/resources/assets/js/utils/markdown.js index c8dea2090..34e0a2c76 100644 --- a/resources/assets/js/utils/markdown.js +++ b/resources/assets/js/utils/markdown.js @@ -268,6 +268,82 @@ const MarkdownPluginUtils = { export {MarkdownPluginUtils} +/** + * 合并连续的工具使用 + * 例如: + * > Tool: a + * > Tool: b + * > Tool: b + * 合并为: + * > Tool: a, b x 2 + * + * @param {string} text + * @returns {string} + */ +function mergeConsecutiveToolUse(text) { + const toolUsePattern = /^>\s*Tool:\s*([^<]+)<\/tool-use>\s*$/; + const lines = text.split('\n'); + const result = []; + let toolGroup = []; + + const formatToolGroup = (tools) => { + if (tools.length === 0) return ''; + if (tools.length === 1) return `> Tool: ${tools[0]}`; + + // 合并连续相同的工具 + const merged = []; + let currentTool = tools[0]; + let count = 1; + + for (let i = 1; i < tools.length; i++) { + if (tools[i] === currentTool) { + count++; + } else { + merged.push(count > 1 ? `${currentTool} x ${count}` : currentTool); + currentTool = tools[i]; + count = 1; + } + } + merged.push(count > 1 ? `${currentTool} x ${count}` : currentTool); + + return `> Tool: ${merged.join(', ')}`; + }; + + let pendingEmptyLines = []; // 暂存空行 + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const match = line.match(toolUsePattern); + + if (match) { + // 工具使用行,丢弃暂存的空行,继续收集 + pendingEmptyLines = []; + toolGroup.push(match[1].trim()); + } else if (line.trim() === '' && toolGroup.length > 0) { + // 空行且已有工具组,暂存空行 + pendingEmptyLines.push(line); + } else { + // 非工具使用的非空行,结束当前工具组 + if (toolGroup.length > 0) { + result.push(formatToolGroup(toolGroup)); + toolGroup = []; + } + // 输出暂存的空行 + result.push(...pendingEmptyLines); + pendingEmptyLines = []; + result.push(line); + } + } + + if (toolGroup.length > 0) { + result.push(formatToolGroup(toolGroup)); + } + // 输出末尾暂存的空行 + result.push(...pendingEmptyLines); + + return result.join('\n'); +} + export function MarkdownConver(text) { if (text === '...') { return '' @@ -292,6 +368,7 @@ export function MarkdownConver(text) { MarkdownPluginUtils.initReasoningPlugin(MarkdownUtils.mdi); } text = MarkdownPluginUtils.clearEmptyReasoning(text); + text = mergeConsecutiveToolUse(text); text = MarkdownUtils.mdi.render(text); return MarkdownUtils.formatMsg(text) }