Repository navigation
feat(messages): add stream, thinking and extra_body - #10
Merged
Merged
Conversation
messages.create() (sync and async) can now stream and request extended thinking on the Anthropic-native /v1/messages endpoint. - stream=True returns Stream / AsyncStream of MessageStreamEvent. Ping events are skipped and error events raise APIError. - thinking is sent unchanged. Message.content and stream events carry thinking / redacted_thinking blocks and thinking / signature deltas. - extra_body is merged into the top level of the request body. - MessageStreamDelta no longer requires a type, so message_delta events (stop_reason, stop_sequence) validate. Events expose usage. - Stream / AsyncStream take a parse function and are generic, so chat and messages share one SSE reader. - A streamed request that fails with an HTTP error raised httpx.ResponseNotRead. The error body is now read first, so the matching APIError is raised, for chat as well. Closes #3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy5vXsRpMhug7v3XwTMZHv
An error event arrives after an HTTP 200, so it always became a bare APIError and retry logic keyed on RateLimitError / InternalServerError never matched. Map the Anthropic error type to its usual status instead (for example rate_limit_error to 429, overloaded_error to 529). Close the response when parsing an event raises, so an error event does not leave the connection open. A non-object error field no longer raises AttributeError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy5vXsRpMhug7v3XwTMZHv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3.
messages.create()(sync and async) gainsstream,thinkingandextra_body. The spec is inopenspec/changes/add-messages-stream-and-thinking/.Behavior
stream=True"stream": trueand returns aStream(orAsyncStream) ofMessageStreamEvent, in arrival order.pingevents are skipped.errorevent raises theAPIErrorsubclass that matches its error type, with the event asbody, and closes the response.rate_limit_errorraisesRateLimitError(429).overloaded_errorraisesInternalServerError(529).[DONE]sentinel.thinkingThinkingBlockandRedactedThinkingBlockcontent blocks, in both responses andcontent_block_startevents.MessageStreamDeltacarriesthinking,signature,stop_reasonandstop_sequence.typeis now optional. Before this, every real stream would fail at themessage_deltaevent, because that delta has notype.usage(MessageDeltaUsage).stop_reasonacceptspause_turn,refusalandmodel_context_window_exceeded.extra_body"extra_body". That behavior is unchanged, because Gemini's OpenAI-compatible endpoint expects the nested form.StreamandAsyncStreamare generic and take a parse function, so chat and messages share one SSE reader.Stream[ChatCompletionChunk].Bug fix found while testing. A streamed request (chat or messages) that got an HTTP error status raised
httpx.ResponseNotRead. The cause was that_raise_for_statusread the body of an unread streaming response. The body is now read first, so the matchingAPIErrorsubclass is raised.Proof
tests/test_messages.py: sync thinking+text stream, with event order, ping skipped, and thinking/signature/text deltas.tests/test_messages.py:message_deltawithstop_reasonandusage.tests/test_messages.py: async text stream.tests/test_messages.py: an error event raises, maps to the right class (sync and async), and closes the response.tests/test_messages.py: a non-stream response with thinking and redacted_thinking blocks, plus top-levelextra_body.tests/test_streaming.py: an HTTP 400 on a chat stream, and a 404 on an async messages stream, raiseAPIError/NotFoundError.ResponseNotRead.main: 111 passed on each of:main.Optional/Dictstyle of these files.openspec validate add-messages-stream-and-thinking --strictpasses.Review: an independent read-only review found no blockers. It raised two P2s, both fixed in the second commit:
APIError.Not in scope: Anthropic server-tool content blocks (
server_tool_use,web_search_tool_result, ...) are still not modelled. A response containing them would fail validation.🤖 Generated with Claude Code
https://claude.ai/code/session_01Uy5vXsRpMhug7v3XwTMZHv