Return signature on anthropic streaming + migrate to signature field instead of signature_delta [MINOR bump] (#9021)

* Fix missing signature_delta in thinking blocks when streaming from Claude 3.7 (#8797)

Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>

* test: update test to enforce signature found

* feat(refactor-signature-param-to-be-'signature'-instead-of-'signature_delta'): keeps it in sync with anthropic

* fix: fix linting error

---------

Co-authored-by: Martin Krasser <krasserm@googlemail.com>
This commit is contained in:
Krish Dholakia 2025-03-05 19:33:54 -08:00 committed by GitHub
parent 17efbf0ee9
commit ec4f665e29
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 19 additions and 10 deletions

View file

@ -2983,7 +2983,7 @@ class BedrockConverseMessagesProcessor:
reasoning_content_blocks: List[BedrockContentBlock] = []
for thinking_block in thinking_blocks:
reasoning_text = thinking_block.get("thinking")
reasoning_signature = thinking_block.get("signature_delta")
reasoning_signature = thinking_block.get("signature")
text_block = BedrockConverseReasoningTextBlock(
text=reasoning_text or "",
)

View file

@ -527,6 +527,7 @@ class ModelResponseIterator:
provider_specific_fields = {}
content_block = ContentBlockDelta(**chunk) # type: ignore
thinking_blocks: List[ChatCompletionThinkingBlock] = []
self.content_blocks.append(content_block)
if "text" in content_block["delta"]:
text = content_block["delta"]["text"]
@ -544,13 +545,13 @@ class ModelResponseIterator:
provider_specific_fields["citation"] = content_block["delta"]["citation"]
elif (
"thinking" in content_block["delta"]
or "signature_delta" == content_block["delta"]
or "signature" in content_block["delta"]
):
thinking_blocks = [
ChatCompletionThinkingBlock(
type="thinking",
thinking=content_block["delta"].get("thinking"),
signature_delta=content_block["delta"].get("signature"),
thinking=content_block["delta"].get("thinking") or "",
signature=content_block["delta"].get("signature"),
)
]
provider_specific_fields["thinking_blocks"] = thinking_blocks

View file

@ -272,7 +272,7 @@ class AmazonConverseConfig(BaseConfig):
optional_params["temperature"] = value
if param == "top_p":
optional_params["topP"] = value
if param == "tools":
if param == "tools" and isinstance(value, list):
optional_params = self._add_tools_to_optional_params(
optional_params=optional_params, tools=value
)
@ -598,7 +598,7 @@ class AmazonConverseConfig(BaseConfig):
if _text is not None:
_thinking_block["thinking"] = _text
if _signature is not None:
_thinking_block["signature_delta"] = _signature
_thinking_block["signature"] = _signature
thinking_blocks_list.append(_thinking_block)
return thinking_blocks_list

View file

@ -360,7 +360,7 @@ class ChatCompletionCachedContent(TypedDict):
class ChatCompletionThinkingBlock(TypedDict, total=False):
type: Required[Literal["thinking"]]
thinking: str
signature_delta: str
signature: str
cache_control: Optional[Union[dict, ChatCompletionCachedContent]]

View file

@ -1188,18 +1188,20 @@ def test_anthropic_thinking_output(model):
assert isinstance(resp.choices[0].message.thinking_blocks, list)
assert len(resp.choices[0].message.thinking_blocks) > 0
assert resp.choices[0].message.thinking_blocks[0]["signature"] is not None
@pytest.mark.parametrize(
"model",
[
"anthropic/claude-3-7-sonnet-20250219",
"bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0",
# "bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0",
],
)
def test_anthropic_thinking_output_stream(model):
# litellm.set_verbose = True
try:
litellm._turn_on_debug()
# litellm._turn_on_debug()
resp = litellm.completion(
model=model,
messages=[{"role": "user", "content": "Tell me a joke."}],
@ -1209,6 +1211,7 @@ def test_anthropic_thinking_output_stream(model):
)
reasoning_content_exists = False
signature_block_exists = False
for chunk in resp:
print(f"chunk 2: {chunk}")
if (
@ -1220,8 +1223,11 @@ def test_anthropic_thinking_output_stream(model):
and isinstance(chunk.choices[0].delta.reasoning_content, str)
):
reasoning_content_exists = True
break
print(chunk.choices[0].delta.thinking_blocks[0])
if chunk.choices[0].delta.thinking_blocks[0].get("signature"):
signature_block_exists = True
assert reasoning_content_exists
assert signature_block_exists
except litellm.Timeout:
pytest.skip("Model is timing out")

View file

@ -4084,6 +4084,7 @@ def test_reasoning_content_completion(model):
)
reasoning_content_exists = False
signature_delta_exists = False
for chunk in resp:
print(f"chunk 2: {chunk}")
if (
@ -4118,3 +4119,4 @@ def test_is_delta_empty():
audio=None,
)
)