From 75b183632294106d0f45be49918677ae6835c126 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sat, 12 Sep 2026 22:56:59 +0200 Subject: [PATCH] fix: drop thinking blocks when converting Anthropic Messages requests to Chat Completions (#29849) * fix: drop thinking blocks when converting Anthropic Messages requests to Chat Completions Claude Code and other Anthropic SDK clients pointed at /api/v1/messages send the assistant's earlier thinking blocks back with every follow-up request. Since 0.11.0 those blocks were copied into the OpenAI assistant message as content parts of type thinking, a part type Chat Completions does not define. Strict OpenAI-compatible servers such as NVIDIA Dynamo reject the whole request with 400 "data did not match any variant of untagged enum ChatCompletionRequestAssistantMessageContent", so a conversation with a reasoning model died on its second turn. The error reports its position at the very end of the body, which made the request look cut off; it was complete. Thinking and redacted_thinking blocks are now skipped in the conversion, which is what happened before 0.11.0. An assistant turn that held only thinking blocks is kept as an empty assistant message so the turn order survives. Native Anthropic and LiteLLM connections are unaffected because they receive the request untouched, and thinking blocks in responses are still produced. Fixes #29799 * fix: keep signed thinking blocks when converting Anthropic Messages requests Dropping every thinking block also removed the signed ones. Those are the blocks a gateway such as LiteLLM forwards to Anthropic, which needs the signed thinking block of the previous assistant turn when a tool-use turn continues with extended thinking. Only the unsigned blocks are the problem: Open WebUI creates them itself from reasoning_content, and strict Chat Completions backends reject them because thinking is not a content part type they know. Unsigned thinking blocks are now dropped while signed thinking and redacted_thinking blocks are kept, the same rule LiteLLM applies before forwarding to Anthropic and the rule the native chat path already uses for Anthropic reasoning details. Backends that never emit a signature, such as NVIDIA Dynamo and vLLM, keep receiving requests without thinking parts, so the original failure stays fixed. --- backend/open_webui/utils/anthropic.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/utils/anthropic.py b/backend/open_webui/utils/anthropic.py index b6672af48f..36237bdaeb 100644 --- a/backend/open_webui/utils/anthropic.py +++ b/backend/open_webui/utils/anthropic.py @@ -202,7 +202,9 @@ def convert_anthropic_to_openai_payload( ) ) elif block_type in ('thinking', 'redacted_thinking'): - openai_content.append(_copy_cache_control(block, dict(block))) + # Unsigned thinking cannot be replayed upstream + if block_type == 'redacted_thinking' or block.get('signature'): + openai_content.append(_copy_cache_control(block, dict(block))) elif block_type == 'image': source = block.get('source', {}) if source.get('type') == 'base64': @@ -369,7 +371,7 @@ def convert_anthropic_to_openai_payload( msg_dict['content'] = '' msg_dict['tool_calls'] = tool_calls messages.append(msg_dict) - elif openai_content: + elif openai_content or role == 'assistant': messages.append({'role': role, 'content': _finalize_openai_content(openai_content)}) else: messages.append({'role': role, 'content': str(content) if content else ''})