diff --git a/.changeset/friendly-schools-drum.md b/.changeset/friendly-schools-drum.md new file mode 100644 index 000000000000..698f084ca647 --- /dev/null +++ b/.changeset/friendly-schools-drum.md @@ -0,0 +1,5 @@ +--- +"@ai-sdk/amazon-bedrock": patch +--- + +fix(amazon-bedrock): preserve empty text blocks when reasoning content is present diff --git a/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts b/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts index 099be70c012c..07120ae89784 100644 --- a/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts +++ b/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts @@ -4628,6 +4628,76 @@ describe('doGenerate', () => { `); }); + it('should preserve empty text blocks between reasoning blocks', async () => { + server.urls[generateUrl].response = { + type: 'json-value', + body: { + output: { + message: { + role: 'assistant', + content: [ + { + reasoningContent: { + reasoningText: { + text: 'thinking...', + signature: 'sig-1', + }, + }, + }, + { text: '' }, + { + reasoningContent: { + reasoningText: { + text: 'more thinking...', + signature: 'sig-2', + }, + }, + }, + { text: 'The answer is 42.' }, + ], + }, + }, + usage: { inputTokens: 4, outputTokens: 34, totalTokens: 38 }, + stopReason: 'stop_sequence', + }, + }; + + const result = await model.doGenerate({ + prompt: TEST_PROMPT, + }); + + expect(result.content).toMatchInlineSnapshot(` + [ + { + "providerMetadata": { + "bedrock": { + "signature": "sig-1", + }, + }, + "text": "thinking...", + "type": "reasoning", + }, + { + "text": "", + "type": "text", + }, + { + "providerMetadata": { + "bedrock": { + "signature": "sig-2", + }, + }, + "text": "more thinking...", + "type": "reasoning", + }, + { + "text": "The answer is 42.", + "type": "text", + }, + ] + `); + }); + it('should extract reasoning text without signature', async () => { server.urls[generateUrl].response = { type: 'json-value', diff --git a/packages/amazon-bedrock/src/bedrock-chat-language-model.ts b/packages/amazon-bedrock/src/bedrock-chat-language-model.ts index 2c752d2645ee..15e99547fe02 100644 --- a/packages/amazon-bedrock/src/bedrock-chat-language-model.ts +++ b/packages/amazon-bedrock/src/bedrock-chat-language-model.ts @@ -462,7 +462,7 @@ export class BedrockChatLanguageModel implements LanguageModelV4 { // map response content to content array for (const part of response.output.message.content) { // text - if (part.text) { + if (part.text != null) { content.push({ type: 'text', text: part.text }); } diff --git a/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.test.ts b/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.test.ts index 525ac3dfe1cd..660f749b0142 100644 --- a/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.test.ts +++ b/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.test.ts @@ -1037,6 +1037,70 @@ describe('assistant messages', () => { system: [], }); }); + + it('should preserve empty text blocks when reasoning blocks are present', async () => { + const result = await convertToBedrockChatMessages([ + { + role: 'user', + content: [{ type: 'text', text: 'Hello' }], + }, + { + role: 'assistant', + content: [ + { + type: 'reasoning', + text: 'thinking...', + providerOptions: { + bedrock: { signature: 'sig-1' }, + }, + }, + { type: 'text', text: '' }, + { + type: 'reasoning', + text: 'more thinking...', + providerOptions: { + bedrock: { signature: 'sig-2' }, + }, + }, + { type: 'text', text: 'response text' }, + { + type: 'tool-call', + toolCallId: 'call-123', + toolName: 'test', + input: {}, + }, + ], + }, + ]); + + expect(result).toEqual({ + messages: [ + { + role: 'user', + content: [{ text: 'Hello' }], + }, + { + role: 'assistant', + content: [ + { + reasoningContent: { + reasoningText: { text: 'thinking...', signature: 'sig-1' }, + }, + }, + { text: '' }, + { + reasoningContent: { + reasoningText: { text: 'more thinking...', signature: 'sig-2' }, + }, + }, + { text: 'response text' }, + { toolUse: { toolUseId: 'call-123', name: 'test', input: {} } }, + ], + }, + ], + system: [], + }); + }); }); describe('tool messages', () => { diff --git a/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.ts b/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.ts index b3448c0f04a6..3dedc792975a 100644 --- a/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.ts +++ b/packages/amazon-bedrock/src/convert-to-bedrock-chat-messages.ts @@ -260,6 +260,9 @@ export async function convertToBedrockChatMessages( const message = block.messages[j]; const isLastMessage = j === block.messages.length - 1; const { content } = message; + const hasReasoningBlocks = content.some( + part => part.type === 'reasoning', + ); for (let k = 0; k < content.length; k++) { const part = content[k]; @@ -267,8 +270,8 @@ export async function convertToBedrockChatMessages( switch (part.type) { case 'text': { - // Skip empty text blocks - if (!part.text.trim()) { + // Skip empty text blocks unless reasoning blocks are present + if (!part.text.trim() && !hasReasoningBlocks) { break; }