anthropics / anthropics/anthropic-sdk-python
Streaming accumulator crashes when message_start omits usage as shown in thinking docs
- 主要语言
- Python
- 星标
- 3.9k
- 派生
- 853
- 平均合并
- 1 天 18 小时
- 30 天内合并 PR
- 11
描述
### Summary
Anthropic's official [streaming documentation](https://platform.claude.com/docs/en/build-with-claude/streaming) shows a **"Streaming request with thinking"** event sequence where both `message_start.message.usage` and `message_delta.usage` are omitted.
However, the latest Python SDK has a conflicting contract:
1. `Message.usage` is generated as a required field:
https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/types/message.py#L113
2. When it is omitted from `message_start`, the streamed message snapshot contains `usage=None`.
3. The accumulator later unconditionally dereferences that value when a `message_delta` does contain usage:
https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/lib/streaming/_messages.py#L503-L518
This causes the official SDK to crash while consuming an event sequence compatible with the official thinking-streaming example.
### Reproduction
This reproduces with the latest release, `anthropic==0.120.2`, without any third-party gateway:
```python
import anthropic
import httpx
sse = """event: message_start
data: {"type":"message_start","message":{"id":"msg_test","type":"message","role":"assistant","content":[],"model":"claude-test","stop_reason":null,"stop_sequence":null}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hi"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":1}}
event: message_stop
data: {"type":"message_stop"}
"""
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
headers={"content-type": "text/event-stream"},
content=sse,
request=request,
)
client = anthropic.Anthropic(
api_key="test",
http_client=httpx.Client(transport=httpx.MockTransport(handler)),
)
with client.messages.stream(
model="claude-test",
max_tokens=1,
messages=[{"role": "user", "content": "hi"}],
) as stream:
stream.get_final_message()
```
### Actual behavior
```text
File "anthropic/lib/streaming/_messages.py", line 508, in accumulate_event
current_snapshot.usage.output_tokens = event.usage.output_tokens
AttributeError: 'NoneType' object has no attribute 'output_tokens'
and no __dict__ for setting new attributes
```
### Expected behavior / contract question
The documentation and SDK should agree on whether `usage` may be omitted from streaming events.
- If omission is valid, `Message.usage` should be optional and the accumulator should initialize usage when a later delta supplies it (and tolerate streams that never supply it).
- If omission is invalid, the thinking-streaming documentation should include the required usage fields, and the SDK should ideally report a clear response-validation error instead of failing later with an unrelated `AttributeError`.
This was originally observed with a compatible gateway, but the mock transport above demonstrates that the failure is entirely reproducible at the documented event/SDK boundary. Related downstream report: https://github.com/maximhq/bifrost/issues/5885
贡献指南
评估
这个 Issue 还没有评估数据。