Skip to content

perf: tool rendering in conversation page#7937

Merged
Soulter merged 2 commits intoAstrBotDevs:masterfrom
10knamesmore:fix/7929-conversation-tool-rendering
May 3, 2026
Merged

perf: tool rendering in conversation page#7937
Soulter merged 2 commits intoAstrBotDevs:masterfrom
10knamesmore:fix/7929-conversation-tool-rendering

Conversation

@10knamesmore
Copy link
Copy Markdown
Contributor

@10knamesmore 10knamesmore commented May 1, 2026

close #7929

Modifications / 改动点

仅修改 dashboard/src/views/ConversationPage.vue

Commit 1formattedMessages computed(原 L541)

  • 过滤 role 仅保留 user / assistant,跳过 tool / system / _checkpoint
    → tool 大文本不再进 MarkdownMessagePartMessageList.vue:77),消除卡死
  • tool_call_id 索引 tool 消息的 content
  • assistant.tool_calls 转成 {type:'tool_call'} MessagePart,由现有 ToolCallCard 渲染(MessageList.vue:124),默认折叠
  • 哨兵时间戳处理(历史回放无真实耗时数据):
    • ts: 0ToolCallCard.vue:92startTime <= 0 时早返回,避免显示假的实时 ticking 时长("0.1s, 0.2s...")
    • finished_ts: 1ToolCallCard.vue:131 跳过 setInterval(updateTime, 100),避免每个工具调用各自起一个定时器

Commit 2 — 新增两段 CSS(<style> 块内),父级选择器限定作用域,仅在历史预览内生效,主聊天界面不受影响:

/* 双滚动条:让 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;
}

行为变化tool 消息合并进对应 assistant 的 ToolCallCard.result,不再是独立气泡(与主聊天 UI 一致)。

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

对于这个对话 (python tool)

修改前:

image

工具调用结果被当成bot message, 进入md渲染


修改后

image

进入思考结果

对于一般工具

image

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Stop rendering raw tool messages as standalone chat bubbles in conversation history and instead integrate tool call data into assistant messages for consistent tool call display and improved performance.

Bug Fixes:

  • Prevent large tool outputs in conversation history from being rendered via markdown as bot messages, avoiding UI freezes and incorrect message bubbles.

Enhancements:

  • Aggregate tool role messages into assistant ToolCallCard results based on tool_call_id to align conversation history with the main chat UI.
  • Normalize assistant tool call metadata and timestamps when replaying conversation history so tool calls appear as completed without fake live duration updates.
  • Adjust conversation history styles so tool call arguments/results expand naturally under the main scroll container and hide redundant inline completion status labels.

wanger added 2 commits May 1, 2026 15:39
…CallCard

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 AstrBotDevs#7929
Refs AstrBotDevs#7372 AstrBotDevs#7456
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.
@auto-assign auto-assign Bot requested review from Raven95676 and advent259141 May 1, 2026 07:51
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:webui The bug / feature is about webui(dashboard) of astrbot. labels May 1, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The console.log('处理消息:', ...) inside formattedMessages runs for every message render and will spam the console; consider removing it or gating it behind a debug flag.
  • When building toolResultsById, only the last tool message for a given tool_call_id is preserved; if a tool can stream or emit multiple chunks, you may want to accumulate or merge them instead of overwriting.
  • The fallback finalParts for assistant messages with only tool_calls creates an empty plain part, which can still render an empty bubble; consider skipping the message entirely or making the UI handle a pure tool_call message without the placeholder.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `console.log('处理消息:', ...)` inside `formattedMessages` runs for every message render and will spam the console; consider removing it or gating it behind a debug flag.
- When building `toolResultsById`, only the last `tool` message for a given `tool_call_id` is preserved; if a tool can stream or emit multiple chunks, you may want to accumulate or merge them instead of overwriting.
- The fallback `finalParts` for assistant messages with only `tool_calls` creates an empty `plain` part, which can still render an empty bubble; consider skipping the message entirely or making the UI handle a pure `tool_call` message without the placeholder.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/ConversationPage.vue" line_range="553-543" />
<code_context>
+            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)
</code_context>
<issue_to_address>
**issue (performance):** Debug logging in the hot path may be noisy and impact performance in production.

This `console.log` runs on every rendered message and prints full content, which can be large and may contain sensitive data. Consider guarding it behind a debug flag or removing it before release to avoid overhead and exposing internal data in the console.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dashboard/src/views/ConversationPage.vue
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the formattedMessages logic in ConversationPage.vue to correctly handle tool call results and filter conversation history, while also adjusting CSS for tool call cards to improve layout and hide status labels. Feedback suggests including system messages in the filtered history to maintain context and removing a console.log statement to improve performance and log cleanliness.

Comment thread dashboard/src/views/ConversationPage.vue
@Soulter Soulter changed the title Fix/7929 conversation tool rendering perf: tool rendering in conversation page May 3, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 3, 2026
@Soulter Soulter merged commit af66327 into AstrBotDevs:master May 3, 2026
21 checks passed
Soulter pushed a commit that referenced this pull request May 3, 2026
* 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>
Soulter added a commit that referenced this pull request May 3, 2026
* feat: supports plugin to add skills

* fix tests

* fix: fs tools

* Add tests for plugin skills handling and improve skill management

- Implement test for restricted local member reading plugin skill inventory even if the plugin is inactive.
- Ensure that the skill synchronization process retains built-in skills when local skills are empty, including proper handling of plugin paths.
- Update dashboard tests to verify that plugin details include components when requested.
- Enhance skill metadata enrichment tests to include inactive plugin-provided skills for inventory.
- Add filtering tests for plugin skills based on current configuration, ensuring only allowed plugins are considered and inactive plugins are skipped.

Co-authored-by: Copilot <copilot@github.com>

* fix: handle PPIO platform context-length error messages (#7888)

* fix: 压缩算法删除 user 消息 Bug 修复

* perf: improve truncate algo

* fix: improve context length error detection for PPIO platform compatibility

- Extend error detection to handle PPIO's error message format:
  'The input is longer than the model's context length'
- Add case-insensitive matching using .lower() for robustness
- Maintain backward compatibility with existing 'maximum context length' check

This fixes the issue where PPIO platform models (e.g., ppio/zai-org/glm-5-turbo)
would fail with AgentState.ERROR due to unrecognized context length errors.

---------

Co-authored-by: Soulter <905617992@qq.com>

* fix: 支持微信客服文件消息 (#7923)

* fix: 支持微信客服文件消息

* fix: remove WeCom file message placeholder

* fix(provider): fix Anthropic custom headers and system prompt compatibility (#7587)

* fix(provider): fix Anthropic custom headers and system prompt compatibility

- Pass custom_headers via AsyncAnthropic's `default_headers` parameter
  instead of creating a separate httpx.AsyncClient. This avoids
  `isinstance` check failures when multiple httpx installations exist
  on sys.path (e.g. bundled Python + system Python).

- Use list format for the `system` parameter (`[{"type": "text", ...}]`)
  instead of a plain string. The list format is supported by the official
  Anthropic API and is also compatible with third-party API proxies that
  reject the string format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(provider): fix Anthropic custom headers and system prompt compatibility

- Pass custom_headers via AsyncAnthropic's `default_headers` parameter
  instead of creating a separate httpx.AsyncClient. This avoids
  `isinstance` check failures when multiple httpx installations exist
  on sys.path (e.g. bundled Python + system Python).

- Use list format for the `system` parameter (`[{"type": "text", ...}]`)
  instead of a plain string. The list format is supported by the official
  Anthropic API and is also compatible with third-party API proxies that
  reject the string format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add test unit

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* perf: improve logic of adding models

Co-authored-by: piexian <piexian@users.noreply.github.com>

* chore: remove redundant logger messages and improve log clarity

Co-authored-by: Copilot <copilot@github.com>

* chore: ruff format

* docs: update knowledge base docs

closes: #7962

* fix(#7907): send_message_to_user cron 场景下 session 容错 (#7911)

* fix: send_message_to_user cron 场景下 session 容错 (#7907)

- LLM 在主动场景可能只传 session_id 而非完整三段式,
from_str 失败时用 current_session 补全前两段。

Co-authored-by: Copilot <copilot@github.com>

* fix: 限制 session 补全仅对裸 session_id 生效,避免误修带冒号的错误输入 (#7907)

* feat: add session information to cron job payload

Co-authored-by: Copilot <copilot@github.com>

* fix: improve clarity and consistency of safety mode prompts

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Weilong Liao <37870767+Soulter@users.noreply.github.com>
Co-authored-by: Soulter <905617992@qq.com>

* 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>

* fix: ruff format

* feat: add python tool timeout param (#7953)

* feat: add python tool timeout param

* Update python.py

---------

Co-authored-by: Weilong Liao <37870767+Soulter@users.noreply.github.com>

* fix: 钉钉连接超时后自动重连失败 (#7924)

* fix: improve DingTalk adapter error handling in run() method

* fix: add retry logic for DingTalk SDK task unexpected exit

* fix: use task.add_done_callback to wake thread on task completion, handle UnboundLocalError

* refactor: extract retry logic into handle_retry helper function

---------

Co-authored-by: Blueteemo <Blueteemo@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: leonforcode <leonbeyourside01@gmail.com>
Co-authored-by: AstralSolipsism <134063164+AstralSolipsism@users.noreply.github.com>
Co-authored-by: Pink YuDeer <wer00001@outlook.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: piexian <piexian@users.noreply.github.com>
Co-authored-by: NayukiMeko <ChibaNayuki@163.com>
Co-authored-by: wanger <122891289+10knamesmore@users.noreply.github.com>
Co-authored-by: wanger <wanger@example.com>
Co-authored-by: Haoran Xu <3230105281@zju.edu.cn>
Co-authored-by: 千岚之夏 <108566281+Blueteemo@users.noreply.github.com>
Co-authored-by: Blueteemo <Blueteemo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 工具调用结果过长, 导致webui查看对话历史时页面卡死

2 participants