From 0adbb24633aa7ba423fd387fcf56117e6b360358 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 28 May 2026 07:13:23 +0000 Subject: [PATCH] fix(anthropic-streaming): insert content_block_stop between held delta and final message_delta When the stream exhausts with both `holding_chunk` (a content_block_delta) and `holding_stop_reason_chunk` (a message_delta) buffered, the after-loop cleanup previously emitted them back-to-back, producing the invalid Anthropic SSE sequence `content_block_delta -> message_delta`. Insert a `content_block_stop` between them in both the sync `__next__` and async `__anext__` paths so the emitted ordering remains `content_block_delta -> content_block_stop -> message_delta`. Co-authored-by: Yassin Kortam --- .../adapters/streaming_iterator.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py index 4f3a8b8ffb7..6366e287c3e 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py @@ -459,6 +459,20 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): if self.holding_chunk is not None: self.chunk_queue.append(self.holding_chunk) self.holding_chunk = None + # If a final ``message_delta`` is also held, the + # dangling ``content_block_delta`` must be followed + # by ``content_block_stop`` before the + # ``message_delta`` so the emitted SSE stays in + # valid Anthropic order (content_block_delta -> + # content_block_stop -> message_delta) rather than + # the invalid content_block_delta -> message_delta. + if self.holding_stop_reason_chunk is not None: + self.chunk_queue.append( + { + "type": "content_block_stop", + "index": self.current_content_block_index, + } + ) if self.holding_stop_reason_chunk is not None: self.chunk_queue.append( self._augment_message_delta_usage( @@ -677,6 +691,20 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): if self.holding_chunk is not None: self.chunk_queue.append(self.holding_chunk) self.holding_chunk = None + # If a final ``message_delta`` is also held, the + # dangling ``content_block_delta`` must be followed + # by ``content_block_stop`` before the + # ``message_delta`` so the emitted SSE stays in + # valid Anthropic order (content_block_delta -> + # content_block_stop -> message_delta) rather than + # the invalid content_block_delta -> message_delta. + if self.holding_stop_reason_chunk is not None: + self.chunk_queue.append( + { + "type": "content_block_stop", + "index": self.current_content_block_index, + } + ) if self.holding_stop_reason_chunk is not None: self.chunk_queue.append( self._augment_message_delta_usage(