fix(session): skip empty text parts in assistant message replay#31045
Open
luiscga wants to merge 1 commit into
Open
fix(session): skip empty text parts in assistant message replay#31045luiscga wants to merge 1 commit into
luiscga wants to merge 1 commit into
Conversation
When a model response contains only tool calls, some providers emit a
text-start event followed immediately by text-end with no text-delta
events in between. This leaves a TextPart with text: "" persisted in
the DB.
On the next turn, toModelMessagesEffect replayed this as a
{ type: "text", text: "" } part in the assistant message. Providers
that strictly validate message content (e.g. Azure AI endpoints) reject
this with a 422 error.
The user message loop already guards against this with an explicit
part.text !== "" check. This commit adds the equivalent guard to the
assistant message loop, while preserving the existing special-case that
replaces "" with " " when Anthropic signed reasoning blocks are
present (where the empty text part is a structural separator).
Contributor
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
Contributor
|
The following comment was made by an LLM, it may be inaccurate: Found 2 potentially related PRs:
|
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
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.
Issue for this PR
Closes #31046
Type of change
What does this PR do?
When a model returns only tool calls, some providers emit
text-startwith no followingtext-delta.processor.tsstores aTextPart(text:"")in the DB; on the next turntoModelMessagesEffectreplays it as{"type":"text","text":""}, which strict providers reject (Anthropic protocol, Azure AI, Bedrock — HTTP 422).The user message loop already guards against this with
part.text !== "". This PR adds the equivalent guard to the assistant message loop inmessage-v2.ts. It also extends thehasSignedReasoningcheck to cover Bedrock signatures alongside Anthropic — the@ai-sdk/amazon-bedrockadapter requires an empty-text structural separator when reasoning blocks are present, same as Anthropic signed thinking, so those parts are kept.The AI SDK's built-in empty-text filter in
convertToLanguageModelMessage()has aproviderOptions != nullexception that lets empty parts through when metadata is attached (which happens on Anthropic-protocoltext-startevents). The fix inmessage-v2.tsoperates beforeconvertToModelMessagesis called, so it closes the gap regardless of metadata.PR #27914 fixes the same symptom at the
transform.tslayer for@ai-sdk/openai-compatible. This fix operates one layer earlier and covers all providers.How did you verify your code works?
Updated 3 existing tests that asserted the old pass-through behavior. Added 2 new tests: one for an assistant message whose only content is an empty text part (dropped entirely), one for empty text alongside tool calls (tool calls survive, empty text dropped).
Screenshots / recordings
N/A — backend change.
Checklist