From b8fd48ae01e304c6b839f07103994a0897b57bd0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 13:13:26 +0000 Subject: [PATCH] fix(anthropic): silently drop trailing chunks after usage; remove dead _polyfill_result key - streaming_iterator: in sync __next__, after the usage chunk has been merged and emitted, silently consume any trailing provider events via 'continue' instead of forwarding them through the queue. Trailing chunks would translate to content_block_delta or message_delta and violate Anthropic SSE ordering after the final message_delta. The async __anext__ already drops these via 'if not self.queued_usage_chunk:' gating, so this aligns sync and async behavior. - handler: drop unused '_polyfill_result' from ANTHROPIC_ONLY_REQUEST_KEYS. PolyfillResult is passed as an explicit arg to the adapter methods, never through extra_kwargs, so the entry was dead code. Co-authored-by: Yassin Kortam --- .../experimental_pass_through/adapters/handler.py | 2 +- .../adapters/streaming_iterator.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py index 38fbeb65510..4a222744c9d 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: # Anthropic-only keys already mapped by the translator; strip on extra_kwargs re-merge. ANTHROPIC_ONLY_REQUEST_KEYS: frozenset[str] = frozenset( - {"output_config", "context_management", "_polyfill_result"} + {"output_config", "context_management"} ) 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 b51a3c69d65..517ee984006 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py @@ -208,11 +208,13 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper): return self.chunk_queue.popleft() if self.queued_usage_chunk: - # Usage has already been merged + emitted. Pass any trailing - # provider events through directly instead of silently - # dropping them in the for-loop body. - self.chunk_queue.append(processed_chunk) - return self.chunk_queue.popleft() + # Usage has already been merged + emitted. Any trailing + # provider events would violate Anthropic SSE ordering + # (no chunks may follow the final ``message_delta``), so + # silently drop them — matches the async ``__anext__`` + # behavior where the block-handling logic is gated on + # ``not self.queued_usage_chunk``. + continue if should_start_new_block and not self.sent_content_block_finish: # Queue the sequence: content_block_stop -> content_block_start