[GH-ISSUE #17757] issue: Native tool-calling overwrites tool-emitted chat message updates #18387

Closed
opened 2026-04-20 00:36:23 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @brandco on GitHub (Sep 25, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/17757

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I am using the latest version of Open WebUI.

Installation Method

Docker

Open WebUI Version

v0.6.30

Ollama Version (if applicable)

No response

Operating System

ghcr.io/open-webui/open-webui:main

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

Tool-emitted message updates persist in the visible assistant message during generation, regardless of function-calling mode.

Actual Behavior

In native function-calling, subsequent "chat:completion" events with a full content snapshot replace the message content, effectively overwriting prior tool updates, causing flicker/disappearance.

Steps to Reproduce

In ghcr.io/open-webui/open-webui:main with a model that can support native tool calling:

  1. Enable tools for the model and set params.function_calling = "native" and stream = true.
  2. In a tool function, accept and use the injected event_emitter to emit mid-stream updates:
    • Example: type = "message" with data.content to append, or type = "replace" to set content, or type = "chat:message".
  3. Observe the UI: the tool’s text appears briefly, then disappears/gets overwritten during the model’s streamed response.
  4. Repeat with params.function_calling = "default". In this path, the appended/replaced text persists.
"""
title: Native Tool Emit Bug
author: brandco
description: Minimal reproduction of native tool emit bug.
version: 0.0.1
"""

from __future__ import annotations

import asyncio
from typing import Awaitable, Callable, Optional


class Tools:
    """Expose a single tool that reproduces the message overwrite sequence."""

    def __init__(self):
        """Initialize the Tool."""
        self.citation = True

    async def message_event_reproducer(
        self,
        prompt: str = "Test message",
        __event_emitter__: Optional[Callable[[dict[str, object]], Awaitable[None]]] = None,
    ) -> str:
        """Emit a message to show overwriting."""

        chunk_one = "\n\nStreaming chunk 1.\n\n"
        chunk_two = "\n\nStreaming chunk 2.\n\n"
        final_text = "\n\nFinal text.\n\n"

        if not __event_emitter__:
            return final_text

        await __event_emitter__(
            {
                "type": "message",
                "data": {"content": chunk_one},
            }
        )
        await asyncio.sleep(1)

        await __event_emitter__(
            {
                "type": "message",
                "data": {"content": chunk_two},
            }
        )
        await asyncio.sleep(1)

        await __event_emitter__(
            {
                "type": "message",
                "data": {
                    "content": final_text,
                },
            }
        )
        await asyncio.sleep(1)

        return final_text

Logs & Screenshots

With native tool calling:
Image

With default tool calling:
Image

Additional Information

This issue is related to issue 12214 which was closed prematurely.

In native function-calling, the server constructs content_blocks from the streaming model output and repeatedly emits "chat:completion" events with a full serialized content snapshot (mid-stream and at completion). The UI treats these snapshots as authoritative and sets message.content = content.

  • File: backend/open_webui/utils/middleware.py:2441
    • Adds tools for native path:
      • metadata.params.function_calling == "native" ⇒ metadata["tools"], form_data["tools"].
    • Streaming handler (process_chat_response) emits repeated snapshots:
      • mid-stream and after tool calls, via event_emitter({ type: "chat:completion", data: { content: serialize_content_blocks(...) } }).
      • emits a final done=true snapshot at the end.

Client – Snapshots overwrite prior tool updates

  • File: src/lib/components/chat/Chat.svelte:350
    • chatEventHandler:
      • type === 'message' or 'chat:message:delta' ⇒ message.content += data.content
      • type === 'chat:message' or 'replace' ⇒ message.content = data.content
      • type === 'chat:completion' ⇒ chatCompletionEventHandler(data, ...)
    • chatCompletionEventHandler:
      • if (choices) deltas ⇒ appends to message.content
      • if (content) snapshot ⇒ message.content = content (REPLACE)
Originally created by @brandco on GitHub (Sep 25, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/17757 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I am using the latest version of Open WebUI. ### Installation Method Docker ### Open WebUI Version v0.6.30 ### Ollama Version (if applicable) _No response_ ### Operating System ghcr.io/open-webui/open-webui:main ### Browser (if applicable) _No response_ ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior Tool-emitted message updates persist in the visible assistant message during generation, regardless of function-calling mode. ### Actual Behavior In native function-calling, subsequent "chat:completion" events with a full content snapshot replace the message content, effectively overwriting prior tool updates, causing flicker/disappearance. ### Steps to Reproduce In ghcr.io/open-webui/open-webui:main with a model that can support native tool calling: 1. Enable tools for the model and set params.function_calling = "native" and stream = true. 2. In a tool function, accept and use the injected __event_emitter__ to emit mid-stream updates: - Example: type = "message" with data.content to append, or type = "replace" to set content, or type = "chat:message". 3. Observe the UI: the tool’s text appears briefly, then disappears/gets overwritten during the model’s streamed response. 4. Repeat with params.function_calling = "default". In this path, the appended/replaced text persists. ```python """ title: Native Tool Emit Bug author: brandco description: Minimal reproduction of native tool emit bug. version: 0.0.1 """ from __future__ import annotations import asyncio from typing import Awaitable, Callable, Optional class Tools: """Expose a single tool that reproduces the message overwrite sequence.""" def __init__(self): """Initialize the Tool.""" self.citation = True async def message_event_reproducer( self, prompt: str = "Test message", __event_emitter__: Optional[Callable[[dict[str, object]], Awaitable[None]]] = None, ) -> str: """Emit a message to show overwriting.""" chunk_one = "\n\nStreaming chunk 1.\n\n" chunk_two = "\n\nStreaming chunk 2.\n\n" final_text = "\n\nFinal text.\n\n" if not __event_emitter__: return final_text await __event_emitter__( { "type": "message", "data": {"content": chunk_one}, } ) await asyncio.sleep(1) await __event_emitter__( { "type": "message", "data": {"content": chunk_two}, } ) await asyncio.sleep(1) await __event_emitter__( { "type": "message", "data": { "content": final_text, }, } ) await asyncio.sleep(1) return final_text ``` ### Logs & Screenshots With native tool calling: <img width="1068" height="603" alt="Image" src="https://github.com/user-attachments/assets/59b5ba2a-6ef3-4e14-8b93-5ed14f115716" /> With default tool calling: <img width="1086" height="459" alt="Image" src="https://github.com/user-attachments/assets/227c58c6-a4bc-4548-8f2d-b2e86db24e95" /> ### Additional Information This issue is related to issue [12214](https://github.com/open-webui/open-webui/issues/12214) which was closed prematurely. In native function-calling, the server constructs content_blocks from the streaming model output and repeatedly emits "chat:completion" events with a full serialized content snapshot (mid-stream and at completion). The UI treats these snapshots as authoritative and sets message.content = content. - File: backend/open_webui/utils/middleware.py:2441 - Adds tools for native path: - metadata.params.function_calling == "native" ⇒ metadata["tools"], form_data["tools"]. - Streaming handler (process_chat_response) emits repeated snapshots: - mid-stream and after tool calls, via event_emitter({ type: "chat:completion", data: { content: serialize_content_blocks(...) } }). - emits a final done=true snapshot at the end. Client – Snapshots overwrite prior tool updates - File: src/lib/components/chat/Chat.svelte:350 - chatEventHandler: - type === 'message' or 'chat:message:delta' ⇒ message.content += data.content - type === 'chat:message' or 'replace' ⇒ message.content = data.content - type === 'chat:completion' ⇒ chatCompletionEventHandler(data, ...) - chatCompletionEventHandler: - if (choices) deltas ⇒ appends to message.content - if (content) snapshot ⇒ message.content = content (REPLACE)
GiteaMirror added the bug label 2026-04-20 00:36:23 -05:00
Author
Owner

@rgaricano commented on GitHub (Sep 25, 2025):

It not a bug.
In Native mode, the event emitter approach doesn't work because Native function calling bypasses Open WebUI's custom tool processing pipeline. When function_calling is set to "native", tools are added directly to the form data for the model to handle natively, rather than going through Open WebUI's chat_completion_tools_handler.

<!-- gh-comment-id:3335582409 --> @rgaricano commented on GitHub (Sep 25, 2025): It not a bug. In Native mode, the event emitter approach doesn't work because Native function calling bypasses Open WebUI's custom tool processing pipeline. When function_calling is set to "native", tools are added directly to the form data for the model to handle natively, rather than going through Open WebUI's chat_completion_tools_handler.
Author
Owner

@tjbck commented on GitHub (Sep 25, 2025):

@rgaricano is correct here, you generally should not use message event types in your tool in conjunction with a native function calling.

<!-- gh-comment-id:3335586680 --> @tjbck commented on GitHub (Sep 25, 2025): @rgaricano is correct here, you generally should not use `message` event types in your tool in conjunction with a native function calling.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#18387