fix: use async_maybe_transform in AsyncMessages.stream() to avoid blocking event loop#1231
Open
MaxwellCalkin wants to merge 1 commit intoanthropics:mainfrom
Open
Conversation
…cking event loop In `AsyncMessages.stream()`, the synchronous `maybe_transform()` was called instead of the async variant, causing `_transform_recursive` to block the event loop. This is especially impactful for large message payloads in async contexts (e.g., FastAPI gateways). The fix wraps the `_post` call in an inner `async def make_request()` coroutine that uses `await async_maybe_transform()`. The coroutine object is passed to `AsyncMessageStreamManager` (which accepts any `Awaitable`), so the public API is unchanged — callers still use `async with client.messages.stream(...) as stream:` without `await`. Also fixes the same issue in `beta/messages/messages.py` for both `AsyncMessages.stream()` and `AsyncMessages.parse()`. Fixes anthropics#1195 Note: This is Stainless-generated code. The fix should be ported to the codegen template so it persists across regenerations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
Fixes #1195
AsyncMessages.stream()calls the synchronousmaybe_transform()instead ofasync_maybe_transform(), causing_transform_recursiveto block the event loop. This is especially impactful for large message payloads in async contexts (e.g., FastAPI gateways), where profiling shows ~5-8% CPU time spent in the blocking recursive walk.Changes
src/anthropic/resources/messages/messages.py—AsyncMessages.stream(): Wrapped the_postcall in an innerasync def make_request()coroutine that usesawait async_maybe_transform(). The coroutine object is passed toAsyncMessageStreamManager(which accepts anyAwaitable), so the public API is unchanged — callers still useasync with client.messages.stream(...) as stream:without needingawait.src/anthropic/resources/beta/messages/messages.py— Same fix forAsyncMessages.stream(), plus a fix forAsyncMessages.parse()which had the same syncmaybe_transformissue.Why the inner coroutine approach?
The
stream()method is intentionally a non-asyncdefthat returns anAsyncMessageStreamManager. Making itasync defwould break the public API (users would needasync with await client.messages.stream(...)instead ofasync with client.messages.stream(...)). By wrapping the transform + post in an innerasync def make_request(), we defer both the transform and the HTTP call to the__aenter__phase of the context manager, keeping the API unchanged while ensuring the transform runs asynchronously.Note for maintainers
This is Stainless-generated code. The fix should be ported to the codegen template so it persists across regenerations. Specifically, the stream helper template for async classes should use
async_maybe_transforminstead ofmaybe_transform.Test plan
async with client.messages.stream(...)still works withoutawait