Skip to content

♻️ refactor(context engine): tool message normalization#13359

Merged
arvinxx merged 10 commits intolobehub:canaryfrom
cy948:refactor/context-engine-tool-message-normalization
Mar 29, 2026
Merged

♻️ refactor(context engine): tool message normalization#13359
arvinxx merged 10 commits intolobehub:canaryfrom
cy948:refactor/context-engine-tool-message-normalization

Conversation

@cy948
Copy link
Copy Markdown
Contributor

@cy948 cy948 commented Mar 28, 2026

💻 Change Type

  • ✨ feat
  • 🐛 fix
  • ♻️ refactor
  • 💄 style
  • 👷 build
  • ⚡️ perf
  • ✅ test
  • 📝 docs
  • 🔨 chore

🔗 Related Issue

🔀 Description of Change

改造目的是为 context-engine 在整理 tool 调用消息时进行“归一化”的闭环:确保每个保留下来的 tool_call 在最终消息序列里都对应一条可消费的 tool 结果。即使上游含有错误信息,进入 context-engine 后的消息链路仍然是稳定、完整、可继续消费的。

以一次带 tool_calls 的 assistant 消息为例:

  • Processor 会先扫描上下文,收集所有合法的 tool_call_id,并只保留每个 tool_call 的首个有效 tool 结果;
  • 随后按 assistant 的 tool_calls 顺序回填对应的 tool message。
  • 若真实 tool message 缺失, 则自动补一条 synthetic 的失败结果;若真实 tool message 没有正文但带有 pluginError.message,则优先把该错误信息回填到 content,而不是退回默认失败文案。
    最终输出既能清掉重复和脏数据,也能保证下游总能看到与 tool_call 一一对应的结果消息。
  • packages/context-engine/src/processors/ToolMessageReorder.ts:重写 tool message 重排逻辑,改为显式返回
    reorderedMessages 与 removedInvalidTools;先去重 assistant 侧重复 tool_calls,再过滤无效或重复的 tool message,并按
    tool_call 顺序回填结果。
  • packages/context-engine/src/processors/ToolMessageReorder.ts:为缺失的 tool message 增加 synthetic 失败兜底内容,确保
    每个合法 tool_call 都能生成对应的 tool 结果;同时在真实 tool 返回为空时,优先透出 pluginError.message。
  • packages/context-engine/src/processors/ToolMessageReorder.ts:metadata 中的 removedInvalidTools 改为基于实际清理数量
    统计,不再依赖重排前后消息总数差值,日志语义也随之对齐到“移除了多少无效 tool message”。
  • packages/context-engine/src/processors/tests/ToolMessageReorder.test.ts:补充缺失 tool message 自动合成、重复
    tool_call 去重、重复 / 孤儿 tool message 清理、真实错误结果优先于 synthetic fallback 等场景覆盖,并同步更新原有断言。

🧪 How to Test

  • Tested locally
  • Added/updated tests
  • No tests needed

📸 Screenshots / Videos

Before After
... ...

📝 Additional Information

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 28, 2026

@cy948 is attempting to deploy a commit to the LobeHub OSS Team on Vercel.

A member of the Team first needs to authorize it.

@cy948 cy948 marked this pull request as ready for review March 28, 2026 14:06
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.

We've reviewed this pull request using the Sourcery rules engine

@lobehubbot
Copy link
Copy Markdown
Member

@arvinxx - This is a refactor of tool message normalization in the context engine. Please take a look.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 28, 2026

Codecov Report

❌ Patch coverage is 96.15385% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.95%. Comparing base (26449e5) to head (ff3ac72).
⚠️ Report is 8 commits behind head on canary.

Additional details and impacted files
@@            Coverage Diff            @@
##           canary   #13359     +/-   ##
=========================================
  Coverage   66.94%   66.95%             
=========================================
  Files        1903     1903             
  Lines      153684   153721     +37     
  Branches    15534    17693   +2159     
=========================================
+ Hits       102889   102925     +36     
- Misses      50675    50676      +1     
  Partials      120      120             
Flag Coverage Δ
app 58.52% <ø> (+<0.01%) ⬆️
database 96.66% <ø> (ø)
packages/agent-runtime 89.61% <ø> (ø)
packages/context-engine 86.51% <96.15%> (+0.04%) ⬆️
packages/conversation-flow 92.36% <ø> (ø)
packages/file-loaders 87.02% <ø> (ø)
packages/memory-user-memory 66.68% <ø> (ø)
packages/model-bank 99.85% <ø> (ø)
packages/model-runtime 84.44% <ø> (ø)
packages/prompts 67.76% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/utils 90.41% <ø> (ø)
packages/web-crawler 88.82% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store 67.29% <ø> (-0.02%) ⬇️
Services 49.30% <ø> (ø)
Server 67.27% <ø> (ø)
Libs 51.03% <ø> (ø)
Utils 91.01% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 00bab2b849

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +155 to +158
typeof matchedToolMessage.content === 'string' &&
matchedToolMessage.content.length > 0
? matchedToolMessage.content
: pluginErrorMessage || DEFAULT_TOOL_FAILURE_CONTENT,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep empty tool outputs from being rewritten as failures

When a matched tool message has content: '', this branch overwrites it with DEFAULT_TOOL_FAILURE_CONTENT (or pluginError.message). Empty content is a valid successful output in this codebase (e.g., tool invocation stores result.content || result.error?.message || '' in src/store/chat/slices/plugin/actions/pluginTypes.ts:146), so this change can fabricate a failure for successful calls and mislead subsequent model turns into retry/error-recovery paths. The fallback should only be synthesized when the tool message is actually missing, not when a real tool message exists with empty content.

Useful? React with 👍 / 👎.

@arvinxx arvinxx merged commit 5e1738a into lobehub:canary Mar 29, 2026
20 of 21 checks passed
@lobehubbot
Copy link
Copy Markdown
Member

❤️ Great PR @cy948 ❤️

The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our discord and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.

cy948 added a commit to cy948/lobe-chat that referenced this pull request Mar 31, 2026
* ♻️ normalize tool call messages in context engine

* ♻️ prune tool message normalization implementation

* ♻️ prune tool message normalization diff

* ♻️ simplify tool message normalization diff

* ♻️ restore tool message reorder logging

* ♻️ restore reorder tool message shape

* ♻️ restore tool message reorder comment

* ♻️ prune tool message normalization diff

* ♻️ restore tool message reorder shape

* 🐛 fix(context-engine): keep empty tool content in reorder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants