From f8ab7279a818fd6a689ee879c15bcf8c70a2e2e8 Mon Sep 17 00:00:00 2001 From: adelnobel Date: Mon, 30 Mar 2026 03:02:27 +0000 Subject: [PATCH] fix(bedrock/converse): forward first delta when contentBlockStart is absent When Bedrock's converse-stream API omits contentBlockStart events (observed with marketplace models such as minimax.minimax-m2.5), the first delta for each new content block arrives without a prior start event. AnthropicStreamWrapper detects the block boundary from the delta itself and correctly emits content_block_stop + content_block_start, but then returns without queuing the triggering processed_chunk. This silently drops the first characters of every new block. Fix: after emitting content_block_start, also queue the processed chunk if it is a content_block_delta. Applied to both __next__ (sync) and __anext__ (async) paths. --- .../adapters/streaming_iterator.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) 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 6bddad09f21..ff56a5ec23c 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py @@ -129,8 +129,6 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): if should_start_new_block and not self.sent_content_block_finish: # Queue the sequence: content_block_stop -> content_block_start - # The trigger chunk itself is not emitted as a delta since the - # content_block_start already carries the relevant information. self.chunk_queue.append( { "type": "content_block_stop", @@ -145,6 +143,13 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): } ) self.sent_content_block_finish = False + # Also forward the triggering delta if it has content. + # When Bedrock omits contentBlockStart events (e.g. for marketplace + # models), the first delta for a new block arrives without a prior + # start event. Dropping it causes the first characters of each block + # to be silently lost. + if processed_chunk.get("type") == "content_block_delta": + self.chunk_queue.append(processed_chunk) return self.chunk_queue.popleft() if ( @@ -305,8 +310,6 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): if not self.queued_usage_chunk: if should_start_new_block and not self.sent_content_block_finish: # Queue the sequence: content_block_stop -> content_block_start - # The trigger chunk itself is not emitted as a delta since the - # content_block_start already carries the relevant information. # 1. Stop current content block self.chunk_queue.append( @@ -328,6 +331,14 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): # Reset state for new block self.sent_content_block_finish = False + # Also forward the triggering delta if it has content. + # When Bedrock omits contentBlockStart events (e.g. for marketplace + # models), the first delta for a new block arrives without a prior + # start event. Dropping it causes the first characters of each block + # to be silently lost. + if processed_chunk.get("type") == "content_block_delta": + self.chunk_queue.append(processed_chunk) + # Return the first queued item return self.chunk_queue.popleft()