fix(anthropic): self-heal on missing thinking-signature errors from Bedrock/Vertex (#33719)

* fix(anthropic): self-heal on missing thinking-signature errors from Bedrock/Vertex

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(anthropic): narrow thinking signature error marker

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(router): stabilize prompt caching fixture size

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: re-trigger CI

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-07-17 18:18:38 +00:00 committed by GitHub
parent a7d01cb1ac
commit e59add11cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 5 deletions

View file

@ -906,16 +906,17 @@ def strip_advisor_blocks_from_messages(messages: List[Any], replace_with_text: b
def is_anthropic_invalid_thinking_signature_error(error_text: str) -> bool:
"""
Detect Anthropic 400 when encrypted thinking signatures in history do not match
the current deployment (e.g. user rotated API key or switched model endpoint).
Detect Anthropic 400 errors caused by missing or invalid thinking signatures.
Example API message:
Known error formats:
{"message":"messages.2.content.0.thinking.signature.str: Input should be a valid string"}
messages.N.content.M.thinking.signature.str: Input should be a valid string
messages.N.content.M: Invalid `signature` in `thinking` block
"""
if not error_text:
return False
lower = error_text.lower()
return "invalid" in lower and "signature" in lower and "thinking" in lower and "block" in lower
return "thinking" in lower and "signature" in lower and ("invalid" in lower or "valid string" in lower)
def strip_thinking_blocks_from_anthropic_messages(messages: List[Any]) -> List[Any]:

View file

@ -172,7 +172,7 @@ def anthropic_messages():
"content": [
{
"type": "text",
"text": "Here is the full text of a complex legal agreement" * 400,
"text": "Here is the full text of a complex legal agreement" * 500,
"cache_control": {"type": "ephemeral"},
}
],

View file

@ -1261,6 +1261,23 @@ class TestAnthropicThinkingSignatureSelfHeal:
)
assert is_anthropic_invalid_thinking_signature_error(raw) is True
def test_is_anthropic_invalid_thinking_signature_error_positive_bedrock(self):
from litellm.llms.anthropic.common_utils import (
is_anthropic_invalid_thinking_signature_error,
)
# Real user-reported Bedrock scenario
raw = '{"message":"messages.2.content.0.thinking.signature.str: Input should be a valid string"}'
assert is_anthropic_invalid_thinking_signature_error(raw) is True
def test_is_anthropic_invalid_thinking_signature_error_positive_vertex(self):
from litellm.llms.anthropic.common_utils import (
is_anthropic_invalid_thinking_signature_error,
)
raw = "messages.4.content.1.thinking.signature.str: Input should be a valid string"
assert is_anthropic_invalid_thinking_signature_error(raw) is True
def test_is_anthropic_invalid_thinking_signature_error_negative(self):
from litellm.llms.anthropic.common_utils import (
is_anthropic_invalid_thinking_signature_error,
@ -1271,6 +1288,11 @@ class TestAnthropicThinkingSignatureSelfHeal:
is_anthropic_invalid_thinking_signature_error("rate limit exceeded")
is False
)
assert (
is_anthropic_invalid_thinking_signature_error("invalid_request_error: model not found")
is False
)
assert is_anthropic_invalid_thinking_signature_error("thinking signature is malformed") is False
def test_strip_thinking_blocks_from_anthropic_messages(self):
from litellm.llms.anthropic.common_utils import (