[PR #21135] [CLOSED] fix: reconstruct tool_calls from history to prevent model hallucinations #41574

Closed
opened 2026-04-25 13:46:01 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/21135
Author: @Yambr
Created: 2/3/2026
Status: Closed

Base: devHead: fix/tool-calls-reconstruction


📝 Commits (1)

  • f5ea795 fix: reconstruct tool_calls from history to prevent model hallucinations

📊 Changes

2 files changed (+139 additions, -22 deletions)

View changed files

📝 backend/open_webui/utils/middleware.py (+134 -0)
📝 src/lib/utils/index.ts (+5 -22)

📄 Description

Pull Request Checklist

  • Target branch: dev
  • Description: Provided below
  • Changelog: Provided below
  • Documentation: No changes needed
  • Dependencies: No new dependencies
  • Testing: Manually tested with Qwen3 + web_search tool
  • Agentic AI Code: Co-authored with Claude, reviewed and tested by human
  • Code review: Self-reviewed
  • Title Prefix: fix:

Changelog Entry

Description

Fixes #20600 - Tool call results not decoded from HTML entities before sending to LLM

This PR addresses a critical bug where models (especially Qwen3) hallucinate/fake tool calls as text after 2-3 iterations of actual tool use.

Root Cause:

  1. processDetails() converts <details type="tool_calls"> to plain text with HTML-escaped entities
  2. removeAllDetails() strips ALL <details> tags before sending to backend
  3. Model receives history without proper tool call structure and mimics the pattern

Solution:

  • Frontend: Preserve <details type="tool_calls"> tags
  • Backend: Add reconstruct_tool_messages() to reconstruct OpenAI format

Added

  • reconstruct_tool_messages() function in backend/open_webui/utils/middleware.py to parse <details type="tool_calls"> tags and reconstruct proper OpenAI-compatible message structure

Changed

  • removeAllDetails() in src/lib/utils/index.ts: Added negative lookahead to preserve <details type="tool_calls"> tags
  • processDetails() in src/lib/utils/index.ts: Simplified to only remove reasoning and code_interpreter details, leaving tool_calls intact

Fixed

  • #20600 - HTML entities encoding bug in tool call results
  • Model hallucinations where models fake tool calls as text instead of making real tool calls

How It Works

  1. Frontend preserves <details type="tool_calls" id="..." name="..." arguments="..." result="..."> in message content
  2. Backend parses these tags before sending to LLM via reconstruct_tool_messages()
  3. Converts flat assistant messages to proper structure:
    • Assistant message with tool_calls array
    • Tool role messages with tool_call_id and result content

Testing Steps

  1. Enable tools for a model (e.g., web_search)
  2. Trigger a tool call
  3. Send follow-up message in the same conversation
  4. Verify model makes REAL tool calls, not text imitations like:
    <tool_call>{"name": "web_search", "arguments": {...}}</tool_call>
    
  • #20600 - HTML entities encoding bug (this PR fixes)
  • #9435 - Native mode doesn't call model again after tool call (related)

Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/21135 **Author:** [@Yambr](https://github.com/Yambr) **Created:** 2/3/2026 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `fix/tool-calls-reconstruction` --- ### 📝 Commits (1) - [`f5ea795`](https://github.com/open-webui/open-webui/commit/f5ea7954f9b8435e7b84ade33129f71d16b2d334) fix: reconstruct tool_calls from history to prevent model hallucinations ### 📊 Changes **2 files changed** (+139 additions, -22 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/utils/middleware.py` (+134 -0) 📝 `src/lib/utils/index.ts` (+5 -22) </details> ### 📄 Description # Pull Request Checklist - [x] **Target branch:** `dev` - [x] **Description:** Provided below - [x] **Changelog:** Provided below - [x] **Documentation:** No changes needed - [x] **Dependencies:** No new dependencies - [x] **Testing:** Manually tested with Qwen3 + web_search tool - [x] **Agentic AI Code:** Co-authored with Claude, reviewed and tested by human - [x] **Code review:** Self-reviewed - [x] **Title Prefix:** `fix:` --- # Changelog Entry ### Description Fixes #20600 - Tool call results not decoded from HTML entities before sending to LLM This PR addresses a critical bug where models (especially Qwen3) hallucinate/fake tool calls as text after 2-3 iterations of actual tool use. **Root Cause:** 1. `processDetails()` converts `<details type="tool_calls">` to plain text with HTML-escaped entities 2. `removeAllDetails()` strips ALL `<details>` tags before sending to backend 3. Model receives history without proper tool call structure and mimics the pattern **Solution:** - Frontend: Preserve `<details type="tool_calls">` tags - Backend: Add `reconstruct_tool_messages()` to reconstruct OpenAI format ### Added - `reconstruct_tool_messages()` function in `backend/open_webui/utils/middleware.py` to parse `<details type="tool_calls">` tags and reconstruct proper OpenAI-compatible message structure ### Changed - `removeAllDetails()` in `src/lib/utils/index.ts`: Added negative lookahead to preserve `<details type="tool_calls">` tags - `processDetails()` in `src/lib/utils/index.ts`: Simplified to only remove `reasoning` and `code_interpreter` details, leaving `tool_calls` intact ### Fixed - #20600 - HTML entities encoding bug in tool call results - Model hallucinations where models fake tool calls as text instead of making real tool calls --- ### How It Works 1. Frontend preserves `<details type="tool_calls" id="..." name="..." arguments="..." result="...">` in message content 2. Backend parses these tags before sending to LLM via `reconstruct_tool_messages()` 3. Converts flat assistant messages to proper structure: - Assistant message with `tool_calls` array - Tool role messages with `tool_call_id` and result content ### Testing Steps 1. Enable tools for a model (e.g., web_search) 2. Trigger a tool call 3. Send follow-up message in the same conversation 4. Verify model makes REAL tool calls, not text imitations like: ``` <tool_call>{"name": "web_search", "arguments": {...}}</tool_call> ``` ### Related Issues - #20600 - HTML entities encoding bug (this PR fixes) - #9435 - Native mode doesn't call model again after tool call (related) --- ### Contributor License Agreement By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-25 13:46:01 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#41574