fix(streaming): resolve unchecked content block index and type safety issues - #1797
fix(streaming): resolve unchecked content block index and type safety issues#1797vjaudir8123-debug wants to merge 1 commit into
Conversation
… issues 1. Solves the `TODO: check index` by padding the content block list to the exact event index. Previously, `content_block_start` would blindly append, meaning out-of-order or skipped indices would cause data corruption or `IndexError`. 2. Replaces `ParsedMessage.construct()` with `construct_type(type_=ParsedMessage)` on `message_start`. Pydantic's `construct()` bypasses validation, which left the `usage` block as a raw dict. This would later crash the stream with `AttributeError: 'dict' object has no attribute 'output_tokens'` on subsequent usage deltas. Co-authored-by: Claude <noreply@anthropic.com>
|
Thanks, but see https://github.com/anthropics/anthropic-sdk-python/issues/1192#issuecomment-… — the API never sends a content_block_delta for an index without a prior content_block_start, and the reports we traced came through intermediaries rewriting the stream. We'd rather not add handling in the accumulator without a first-party reproduction. If you have a request-id from a direct call where this happens, reply on #1192. |
|
Understood on the index checking! Makes total sense that you do not want to add defensive padding for third-party proxies. However, this PR also included a completely separate fix for a Pydantic type safety bug. Since that is a pure Python bug internal to the SDK (unrelated to proxies), I have extracted just the Pydantic fix into a clean, targeted PR here: #1861. |
Summary
This PR addresses two critical bugs in the streaming message parser (
_messages.pyand_beta_messages.py) that can cause runtime crashes or data corruption.1. Missing Content Block Index Validation
The parser previously had a
# TODO: check indexcomment and blindly appended new blocks oncontent_block_start. If events arrived out of order or an index was skipped, the subsequentcontent_block_deltawould either overwrite the wrong block or crash with anIndexError. This PR correctly pads the list up toevent.indexbefore inserting the block.2.
construct()Bypassing Type ValidationThe snapshot initialization used Pydantic
sconstruct(**event.message.to_dict()). Becauseconstruct()skips validation and deep coercion, nested fields likeusagewere left as raw Python dictionaries rather than Pydantic objects. When a subsequent usage delta arrived,current_snapshot.usage.output_tokens = ...would crash withAttributeError: 'dict' object has no attribute 'output_tokens'. This PR fixes it by usingconstruct_type(type_=...)`, ensuring correct instantiation of nested models.