anthropics / anthropics/anthropic-sdk-python

Streaming accumulator crashes when message_start omits usage as shown in thinking docs

Abierto
#1,806 4 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
3.9k
Forks
853
Merge medio
1 d 18 h
PR fusionados (30 d)
11

Descripción

### 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

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.