mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
perf: tool rendering in conversation page (#7937)
* fix(dashboard): route conversation history tool messages through ToolCallCard When viewing conversation history, large tool outputs (e.g. a single git log --stat producing tens of KB) caused the browser renderer to freeze. Root cause: formattedMessages mapped every role (including tool / system / _checkpoint) into user/bot bubbles, and bot plain strings went through markstream-vue's MarkdownRender. Single 88KB tool messages plus 88-of-them adding up to ~349KB of synchronous markdown parsing was enough to block the main thread for 5+ seconds. This patch: - Indexes tool-role messages by tool_call_id - Filters formattedMessages to user/assistant only — tool, system and _checkpoint roles no longer render as standalone bubbles - Converts assistant.tool_calls (OpenAI shape, with tc.name/tc.arguments fallbacks) into the existing tool_call MessagePart, attaching the paired result so MessageList's ToolCallCard renders it (default collapsed, no longer feeds large strings into the markdown renderer) - Drops empty placeholder plain parts when an assistant message only carries tool_calls - Sets ts/finished_ts to 0 as a sentinel: ToolCallCard.toolCallDuration returns "" when startTime <= 0, suppressing a misleading "0ms" duration that would otherwise appear because conversation history has no real timing data Behavior change: tool results are now embedded in their assistant's ToolCallCard.result instead of appearing as separate bot bubbles. This matches the main chat UI's behavior. Fixes #7929 Refs #7372 #7456 * style(dashboard): use single scrollbar in conversation history preview ToolCallCard's result/args panes have their own max-height + overflow, which produced a nested scrollbar when nested inside the history preview's already-scrollable .conversation-messages-container. Override those constraints inside the preview only — the outer 500px-bounded container already provides scroll bounds, so a single scrollbar feels cleaner. The main chat UI is unaffected. --------- Co-authored-by: wanger <wanger@example.com>
This commit is contained in:
@@ -539,28 +539,54 @@ export default {
|
||||
|
||||
// 将对话历史转换为 MessageList 组件期望的格式
|
||||
formattedMessages() {
|
||||
return this.conversationHistory.map(msg => {
|
||||
console.log('处理消息:', msg.role, msg.content);
|
||||
|
||||
// 将消息内容转换为 MessagePart[] 格式
|
||||
const messageParts = this.convertContentToMessageParts(msg.content);
|
||||
|
||||
if (msg.role === 'user') {
|
||||
return {
|
||||
content: {
|
||||
type: 'user',
|
||||
message: messageParts
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: {
|
||||
type: 'bot',
|
||||
message: messageParts
|
||||
}
|
||||
};
|
||||
// 按 tool_call_id 索引 tool 角色消息的执行结果
|
||||
const toolResultsById = {};
|
||||
for (const msg of this.conversationHistory) {
|
||||
if (msg.role === 'tool' && msg.tool_call_id) {
|
||||
toolResultsById[msg.tool_call_id] = msg.content;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.conversationHistory
|
||||
// tool / system 等非聊天角色不直接渲染为气泡,避免大文本走 markdown 路径卡死页面
|
||||
.filter(msg => msg.role === 'user' || msg.role === 'assistant')
|
||||
.map(msg => {
|
||||
console.log('处理消息:', msg.role, msg.content);
|
||||
|
||||
const messageParts = this.convertContentToMessageParts(msg.content)
|
||||
// 丢弃 convertContentToMessageParts 兜底插入的空 plain,避免 assistant 仅有工具调用时渲染空气泡
|
||||
.filter(part => part.type !== 'plain' || (part.text && part.text.trim()));
|
||||
|
||||
// 把 OpenAI 风格的 assistant.tool_calls 转成 MessageList 已支持的 tool_call part
|
||||
if (msg.role === 'assistant' && Array.isArray(msg.tool_calls) && msg.tool_calls.length) {
|
||||
const toolCalls = msg.tool_calls.map(tc => {
|
||||
const fn = tc.function || {};
|
||||
return {
|
||||
id: tc.id,
|
||||
name: fn.name || tc.name,
|
||||
args: fn.arguments ?? tc.arguments,
|
||||
result: toolResultsById[tc.id],
|
||||
// 历史回放无真实耗时数据:
|
||||
// ts: 0 → ToolCallCard.toolCallDuration 在 startTime<=0 时早退,跳过时长显示
|
||||
// finished_ts: 1 → MessageList.toolCallStatusText 视为已完成(避免误显示"运行中")
|
||||
ts: 0,
|
||||
finished_ts: 1,
|
||||
};
|
||||
});
|
||||
messageParts.push({ type: 'tool_call', tool_calls: toolCalls });
|
||||
}
|
||||
|
||||
const finalParts = messageParts.length
|
||||
? messageParts
|
||||
: [{ type: 'plain', text: '' }];
|
||||
|
||||
return {
|
||||
content: {
|
||||
type: msg.role === 'user' ? 'user' : 'bot',
|
||||
message: finalParts,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1173,6 +1199,18 @@ export default {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
/* 让 ToolCallCard 内部的 args/result 自然展开,由外层容器统一滚动,避免双滚动条 */
|
||||
.conversation-messages-container .detail-json,
|
||||
.conversation-messages-container .detail-result {
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* 历史回放无真实状态数据,隐藏 IPython 工具的"已完成"标签,与其它工具卡片保持一致 */
|
||||
.conversation-messages-container .tool-call-inline-status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 暗色模式下的聊天消息容器 */
|
||||
.v-theme--dark .conversation-messages-container {
|
||||
background-color: #1e1e1e;
|
||||
|
||||
Reference in New Issue
Block a user