This commit is contained in:
Zhu Lei 2026-10-21 03:38:13 +00:00 • committed by GitHub
commit 299a9307bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 0 deletions

View file

@ -1692,6 +1692,9 @@ class LiteLLMAnthropicMessagesAdapter:
assert isinstance(thinking, str)
assert isinstance(signature, str)
# Without a signature the same text arrives again as a
# thinking_delta, so an SSE accumulator seeding from this
# start body would show it twice.
return "thinking", ChatCompletionThinkingBlock(
type="thinking", thinking=thinking if signature else "", signature=signature
)

View file

@ -288,6 +288,7 @@ def test_translate_streaming_openai_chunk_to_anthropic_thinking_content_block():
) = LiteLLMAnthropicMessagesAdapter().translate_streaming_openai_chunk_to_anthropic_content_block(choices=choices)
assert block_type == "thinking"
# Unsigned: the text is delivered by the thinking_delta, so the start body stays empty.
assert content_block_start == {
"type": "thinking",
"thinking": "",

View file

@ -948,6 +948,27 @@ def test_mixed_thinking_start_body_is_empty_and_thinking_not_doubled_sync():
assert _text_deltas(events) == ["Answer."]
def test_pure_thinking_start_body_is_empty_and_thinking_not_doubled_sync():
chunks = [
_thinking_chunk("Hello"),
_thinking_chunk(" world"),
_make_chunk(Delta(content="answer")),
_make_chunk(Delta(content=None), finish_reason="stop"),
]
wrapper = AnthropicStreamWrapper(completion_stream=iter(chunks), model="claude-x")
events = _drain_sync(wrapper)
accumulated = ""
for event in events:
if event.get("type") == "content_block_start" and event["content_block"].get("type") == "thinking":
assert not event["content_block"].get("thinking"), event["content_block"]
accumulated += event["content_block"].get("thinking") or ""
if event.get("type") == "content_block_delta" and event["delta"].get("type") == "thinking_delta":
accumulated += event["delta"]["thinking"]
assert accumulated == "Hello world"
assert _text_deltas(events) == ["answer"]
def test_mixed_chunk_with_tool_argument_continuation_is_not_split_sync():
"""Streaming providers send a tool call's name only on its first chunk;
later chunks carry argument fragments with ``name=None``. Splitting a