fix(bedrock): send a placeholder for empty assistant turns on Converse - #429
Conversation
The entry landed under the 0.23.0 heading, which shipped on 2026-09-04. Release commit c21e245 renames Unreleased to the version at release time, so a new section is where a post-release fix belongs.
|
The three red workflows are not from this branch.
This branch sits on Separately, I moved the changelog entry into a new |
|
Thanks! |
Fixes #426.
The audit turned up one gap, and it is on the Converse side.
as_converse_messages()appends each turn's content array unconditionally, so an assistant turn with no content blocks goes on the wire ascontent: [], which Converse rejects._as_turn()produces exactly that turn when a response carries no content blocks, including the case wheremessageis absent because a guardrail intervened before the model produced any. This sends the same"[empty string]"placeholder as #421 rather than dropping the turn, so user/assistant alternation stays intact.Empty thinking blocks are already handled here.
content_from_converse_block()readsreasoningContent.get("reasoningText") or {}and then.get("text", ""), so a block carrying only a signature, one with noreasoningTextat all, one where it isNone, and aredactedContentblock all returnContentThinking(thinking="")without raising. I checked those four shapes and changed nothing there.The other two Bedrock providers need nothing either.
BedrockMessagesProvidersubclassesAnthropicProviderand does not override_as_message_params, so #421 covers it, andBedrockResponsesProvidergoes through the OpenAI path.Verified at
c21e2451:test_empty_assistant_turn_gets_a_placeholderfails without the change, withassert [] == [{'text': '[empty string]'}].test_empty_user_turn_is_left_alonepins the scope rather than the bug, so I checked it can fail: dropping the role check from the guard turns it red.ruff check chatlasclean, andpyrightreports only the one pre-existing_content_image.pyerror that main reports too.I could not check the rejection itself, since this machine has no AWS credentials, and botocore's service model puts no minimum on the content array, so nothing client-side complains about
content: []today. A caller who passes their ownmessagesthroughkwargsstill bypasses this path.